From 751a8aacd83de57cf05256a0c7e493926a752652 Mon Sep 17 00:00:00 2001 From: Rohan Kumar Date: Mon, 26 Dec 2022 16:20:28 +0530 Subject: [PATCH] refactor (jkube-kit) : Move ConfigMap related helper methods in KubernetesResourceUtil Related to https://github.com/fabric8io/kubernetes-client/issues/4184 Move creation of ConfigMap from file related logic to KubernetesResourceUtil. This would later be replaced by KubernetesClient's KubernetesResourceUtil methods on next upgrade. Signed-off-by: Rohan Kumar --- .../api/util/KubernetesResourceUtil.java | 99 +++++++++++++++++++ .../api/util/KubernetesResourceUtilTest.java | 92 +++++++++++++++++ .../configmap-directory/prod.properties | 1 + .../configmap-directory/test.properties | 1 + .../kubernetes-resource-util/test.bin | 1 + .../enricher/generic/ConfigMapEnricher.java | 56 ++--------- 6 files changed, 204 insertions(+), 46 deletions(-) create mode 100644 jkube-kit/enricher/api/src/test/resources/kubernetes-resource-util/configmap-directory/prod.properties create mode 100644 jkube-kit/enricher/api/src/test/resources/kubernetes-resource-util/configmap-directory/test.properties create mode 100644 jkube-kit/enricher/api/src/test/resources/kubernetes-resource-util/test.bin diff --git a/jkube-kit/enricher/api/src/main/java/org/eclipse/jkube/kit/enricher/api/util/KubernetesResourceUtil.java b/jkube-kit/enricher/api/src/main/java/org/eclipse/jkube/kit/enricher/api/util/KubernetesResourceUtil.java index 1b8f7aa2e8..afaf6a91a4 100644 --- a/jkube-kit/enricher/api/src/main/java/org/eclipse/jkube/kit/enricher/api/util/KubernetesResourceUtil.java +++ b/jkube-kit/enricher/api/src/main/java/org/eclipse/jkube/kit/enricher/api/util/KubernetesResourceUtil.java @@ -21,8 +21,17 @@ import java.lang.reflect.Method; import java.net.URL; import java.net.UnknownHostException; +import java.nio.ByteBuffer; +import java.nio.charset.CharacterCodingException; +import java.nio.charset.CodingErrorAction; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.LinkOption; +import java.nio.file.Path; +import java.util.AbstractMap; import java.util.ArrayList; import java.util.Arrays; +import java.util.Base64; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; @@ -31,6 +40,7 @@ import java.util.Objects; import java.util.Set; import java.util.regex.Matcher; +import java.util.stream.Stream; import io.fabric8.kubernetes.client.utils.Serialization; import org.eclipse.jkube.kit.common.KitLogger; @@ -619,6 +629,95 @@ public static HasMetadata mergeResources(HasMetadata item1, HasMetadata item2, K return item1; } + /** + * Create a ConfigMap entry based on file contents + * + * @param key key for entry + * @param file file path whose contents would be used in value of entry + * @return an entry containing key and value + * @deprecated Should be replaced with Fabric8 Kubernetes Client's methods + * @throws IOException in case of error while reading file + */ + @Deprecated + public static Map.Entry createConfigMapEntry(final String key, final Path file) throws IOException { + final byte[] bytes = Files.readAllBytes(file); + if (isFileWithBinaryContent(file)) { + final String value = Base64.getEncoder().encodeToString(bytes); + return new AbstractMap.SimpleEntry<>(key, value); + } else { + return new AbstractMap.SimpleEntry<>(key, new String(bytes)); + } + } + + /** + * Whether a file is binary file or not + * + * @param file file to check + * @return boolean value indicating whether file is binary file or not + * @deprecated Should be replaced with Fabric8 Kubernetes Client's methods + * @throws IOException in case of failure while reading file + */ + @Deprecated + public static boolean isFileWithBinaryContent(final Path file) throws IOException { + final byte[] bytes = Files.readAllBytes(file); + try { + StandardCharsets.UTF_8.newDecoder() + .onMalformedInput(CodingErrorAction.REPORT) + .onUnmappableCharacter(CodingErrorAction.REPORT) + .decode(ByteBuffer.wrap(bytes)); + return false; + } catch (CharacterCodingException e) { + return true; + } + } + + /** + * Add ConfigMap entries from a directory to current ConfigMap + * @param configMapBuilder ConfigMap builder object + * @param path path to directory + * @deprecated Should be replaced with Fabric8 Kubernetes Client's methods + * @throws IOException in case of failure while reading directory + */ + @Deprecated + public static void addNewEntriesFromDirectoryToExistingConfigMap(ConfigMapBuilder configMapBuilder, final Path path) + throws IOException { + try (Stream files = Files.list(path)) { + files.filter(p -> !Files.isDirectory(p, LinkOption.NOFOLLOW_LINKS)).forEach(file -> { + try { + addNewEntryToExistingConfigMap(configMapBuilder, createConfigMapEntry(file.getFileName().toString(), file), file); + } catch (IOException e) { + throw new IllegalArgumentException(e); + } + }); + } + } + + /** + * Add single entry to ConfigMap + * + * @param configMapBuilder ConfigMap builder object + * @param entry key value pair which will be added to data/binaryData + * @param file file which needs to be processed + * @deprecated Should be replaced with Fabric8 Kubernetes Client's methods + * @throws IOException in case of failure while reading file + */ + @Deprecated + public static void addNewEntryToExistingConfigMap(ConfigMapBuilder configMapBuilder, Map.Entry entry, final Path file) + throws IOException { + if (isFileWithBinaryContent(file)) { + configMapBuilder.addToBinaryData(entry.getKey(), entry.getValue()); + } else { + configMapBuilder.addToData(entry.getKey(), entry.getValue()); + } + } + + public static void addNewConfigMapEntriesToExistingConfigMap(ConfigMapBuilder configMapBuilder, String key, Path filePath) throws IOException { + if (Files.isDirectory(filePath, LinkOption.NOFOLLOW_LINKS)) { + addNewEntriesFromDirectoryToExistingConfigMap(configMapBuilder, filePath); + } else { + addNewEntryToExistingConfigMap(configMapBuilder, createConfigMapEntry(key, filePath), filePath); + } + } protected static HasMetadata mergeConfigMaps(ConfigMap cm1, ConfigMap cm2, KitLogger log, boolean switchOnLocalCustomisation) { ConfigMap cm1OrCopy = cm1; diff --git a/jkube-kit/enricher/api/src/test/java/org/eclipse/jkube/kit/enricher/api/util/KubernetesResourceUtilTest.java b/jkube-kit/enricher/api/src/test/java/org/eclipse/jkube/kit/enricher/api/util/KubernetesResourceUtilTest.java index 646f0d60c9..e1ce434a84 100644 --- a/jkube-kit/enricher/api/src/test/java/org/eclipse/jkube/kit/enricher/api/util/KubernetesResourceUtilTest.java +++ b/jkube-kit/enricher/api/src/test/java/org/eclipse/jkube/kit/enricher/api/util/KubernetesResourceUtilTest.java @@ -13,6 +13,8 @@ */ package org.eclipse.jkube.kit.enricher.api.util; +import io.fabric8.kubernetes.api.model.ConfigMap; +import io.fabric8.kubernetes.api.model.ConfigMapBuilder; import io.fabric8.kubernetes.api.model.GenericKubernetesResource; import io.fabric8.kubernetes.api.model.HasMetadata; import io.fabric8.kubernetes.api.model.KubernetesListBuilder; @@ -24,6 +26,7 @@ import io.fabric8.kubernetes.api.model.batch.v1.Job; import io.fabric8.kubernetes.api.model.networking.v1.Ingress; import io.fabric8.kubernetes.api.model.networking.v1.NetworkPolicy; +import org.assertj.core.api.InstanceOfAssertFactories; import org.eclipse.jkube.kit.config.image.ImageConfiguration; import org.eclipse.jkube.kit.config.resource.GroupArtifactVersion; import org.junit.jupiter.api.BeforeAll; @@ -39,6 +42,8 @@ import java.io.File; import java.io.FileNotFoundException; import java.io.IOException; +import java.net.URL; +import java.nio.file.Paths; import java.util.Collections; import java.util.List; import java.util.Map; @@ -53,6 +58,8 @@ import static org.eclipse.jkube.kit.enricher.api.util.KubernetesResourceUtil.DEFAULT_RESOURCE_VERSIONING; import static org.eclipse.jkube.kit.enricher.api.util.KubernetesResourceUtil.FILENAME_TO_KIND_MAPPER; import static org.eclipse.jkube.kit.enricher.api.util.KubernetesResourceUtil.KIND_TO_FILENAME_MAPPER; +import static org.eclipse.jkube.kit.enricher.api.util.KubernetesResourceUtil.addNewConfigMapEntriesToExistingConfigMap; +import static org.eclipse.jkube.kit.enricher.api.util.KubernetesResourceUtil.createConfigMapEntry; import static org.eclipse.jkube.kit.enricher.api.util.KubernetesResourceUtil.getNameWithSuffix; import static org.eclipse.jkube.kit.enricher.api.util.KubernetesResourceUtil.initializeKindFilenameMapper; import static org.junit.jupiter.params.provider.Arguments.arguments; @@ -395,6 +402,91 @@ void updateKindFilenameMappings_whenAddsCronTabMapping_updatesKindToFileNameMapp assertThat(FILENAME_TO_KIND_MAPPER).containsKey("foo"); } + @Test + void createConfigMapEntry_whenKeyAndPathProvided_thenShouldCreateEntryWithFileContents() throws IOException { + // Given + URL fileUrl = getClass().getResource("/kubernetes-resource-util/configmap-directory/test.properties"); + assertThat(fileUrl).isNotNull(); + + // When + Map.Entry entry = createConfigMapEntry("custom-key", Paths.get(fileUrl.getFile())); + + // Then + assertThat(entry) + .satisfies(e -> assertThat(e.getKey()).isEqualTo("custom-key")) + .satisfies(e -> assertThat(e.getValue()).isEqualTo("db.url=jdbc:mysql://localhost:3306/sample_db")); + } + + @Test + void createConfigMapEntry_whenBinaryFileProvided_thenShouldCreateEntryWithFileContents() throws IOException { + // Given + URL fileUrl = getClass().getResource("/kubernetes-resource-util/test.bin"); + assertThat(fileUrl).isNotNull(); + + // When + Map.Entry entry = createConfigMapEntry("custom-key", Paths.get(fileUrl.getFile())); + + // Then + assertThat(entry) + .satisfies(e -> assertThat(e.getKey()).isEqualTo("custom-key")) + .satisfies(e -> assertThat(e.getValue()).isEqualTo("wA==")); + } + + @Test + void addNewConfigMapEntriesToExistingConfigMap_whenFileProvided_thenShouldCreateConfigMapWithFile() throws IOException { + // Given + URL fileUrl = getClass().getResource("/kubernetes-resource-util/configmap-directory/test.properties"); + assertThat(fileUrl).isNotNull(); + ConfigMapBuilder configMapBuilder = new ConfigMapBuilder(); + + // When + addNewConfigMapEntriesToExistingConfigMap(configMapBuilder, "custom-key", Paths.get(fileUrl.getFile())); + + // Then + assertThat(configMapBuilder.build()) + .asInstanceOf(InstanceOfAssertFactories.type(ConfigMap.class)) + .extracting(ConfigMap::getData) + .asInstanceOf(InstanceOfAssertFactories.MAP) + .containsEntry("custom-key", "db.url=jdbc:mysql://localhost:3306/sample_db"); + } + + @Test + void addNewConfigMapEntriesToExistingConfigMap_whenBinaryFileProvided_thenShouldCreateConfigMapWithBinaryContent() throws IOException { + // Given + URL fileUrl = getClass().getResource("/kubernetes-resource-util/test.bin"); + assertThat(fileUrl).isNotNull(); + ConfigMapBuilder configMapBuilder = new ConfigMapBuilder(); + + // When + addNewConfigMapEntriesToExistingConfigMap(configMapBuilder, "custom-key", Paths.get(fileUrl.getFile())); + + // Then + assertThat(configMapBuilder.build()) + .asInstanceOf(InstanceOfAssertFactories.type(ConfigMap.class)) + .extracting(ConfigMap::getBinaryData) + .asInstanceOf(InstanceOfAssertFactories.MAP) + .containsEntry("custom-key", "wA=="); + } + + @Test + void addNewConfigMapEntriesToExistingConfigMap_whenDirectoryProvided_thenShouldCreateConfigMapWithFilesInDir() throws IOException { + // Given + URL fileUrl = getClass().getResource("/kubernetes-resource-util/configmap-directory"); + assertThat(fileUrl).isNotNull(); + ConfigMapBuilder configMapBuilder = new ConfigMapBuilder(); + + // When + addNewConfigMapEntriesToExistingConfigMap(configMapBuilder, "custom-key", Paths.get(fileUrl.getFile())); + + // Then + assertThat(configMapBuilder.build()) + .asInstanceOf(InstanceOfAssertFactories.type(ConfigMap.class)) + .extracting(ConfigMap::getData) + .asInstanceOf(InstanceOfAssertFactories.MAP) + .containsEntry("test.properties", "db.url=jdbc:mysql://localhost:3306/sample_db") + .containsEntry("prod.properties", "db.url=jdbc:mysql://prod.example.com:3306/sample_db"); + } + private static PodSpec defaultPodSpec() { return new PodSpecBuilder() .addNewContainer() diff --git a/jkube-kit/enricher/api/src/test/resources/kubernetes-resource-util/configmap-directory/prod.properties b/jkube-kit/enricher/api/src/test/resources/kubernetes-resource-util/configmap-directory/prod.properties new file mode 100644 index 0000000000..f448408fd0 --- /dev/null +++ b/jkube-kit/enricher/api/src/test/resources/kubernetes-resource-util/configmap-directory/prod.properties @@ -0,0 +1 @@ +db.url=jdbc:mysql://prod.example.com:3306/sample_db \ No newline at end of file diff --git a/jkube-kit/enricher/api/src/test/resources/kubernetes-resource-util/configmap-directory/test.properties b/jkube-kit/enricher/api/src/test/resources/kubernetes-resource-util/configmap-directory/test.properties new file mode 100644 index 0000000000..93fb7ca537 --- /dev/null +++ b/jkube-kit/enricher/api/src/test/resources/kubernetes-resource-util/configmap-directory/test.properties @@ -0,0 +1 @@ +db.url=jdbc:mysql://localhost:3306/sample_db \ No newline at end of file diff --git a/jkube-kit/enricher/api/src/test/resources/kubernetes-resource-util/test.bin b/jkube-kit/enricher/api/src/test/resources/kubernetes-resource-util/test.bin new file mode 100644 index 0000000000..e7754cae5a --- /dev/null +++ b/jkube-kit/enricher/api/src/test/resources/kubernetes-resource-util/test.bin @@ -0,0 +1 @@ +À \ No newline at end of file diff --git a/jkube-kit/enricher/generic/src/main/java/org/eclipse/jkube/enricher/generic/ConfigMapEnricher.java b/jkube-kit/enricher/generic/src/main/java/org/eclipse/jkube/enricher/generic/ConfigMapEnricher.java index d73f1e1cd3..d234f096b8 100644 --- a/jkube-kit/enricher/generic/src/main/java/org/eclipse/jkube/enricher/generic/ConfigMapEnricher.java +++ b/jkube-kit/enricher/generic/src/main/java/org/eclipse/jkube/enricher/generic/ConfigMapEnricher.java @@ -13,22 +13,16 @@ */ package org.eclipse.jkube.enricher.generic; -import static java.util.Collections.singletonMap; +import static org.eclipse.jkube.kit.enricher.api.util.KubernetesResourceUtil.addNewConfigMapEntriesToExistingConfigMap; +import static org.eclipse.jkube.kit.enricher.api.util.KubernetesResourceUtil.addNewEntryToExistingConfigMap; +import static org.eclipse.jkube.kit.enricher.api.util.KubernetesResourceUtil.createConfigMapEntry; import java.io.IOException; -import java.nio.ByteBuffer; -import java.nio.charset.CharacterCodingException; -import java.nio.charset.CodingErrorAction; -import java.nio.charset.StandardCharsets; -import java.nio.file.Files; -import java.nio.file.LinkOption; -import java.nio.file.Path; import java.nio.file.Paths; -import java.util.Base64; +import java.nio.file.Path; import java.util.Iterator; import java.util.Map; import java.util.Set; -import java.util.stream.Stream; import org.eclipse.jkube.kit.config.resource.ConfigMapEntry; import org.eclipse.jkube.kit.config.resource.PlatformMode; @@ -85,46 +79,14 @@ private void addConfigMapFromAnnotations(final Map annotations, final String key = entry.getKey(); if (key.startsWith(PREFIX_ANNOTATION) || key.startsWith(CONFIGMAP_PREFIX_ANNOTATION)) { - addConfigMapEntryFromDirOrFile(configMapBuilder, getOutput(key), entry.getValue()); + Path filePath = Paths.get(entry.getValue()); + addNewConfigMapEntriesToExistingConfigMap(configMapBuilder, getOutput(key), filePath); it.remove(); } } } - private void addConfigMapEntryFromDirOrFile(final ConfigMapBuilder configMapBuilder, final String key, - final String dirOrFilePath) throws IOException { - final Path path = Paths.get(dirOrFilePath); - - if (Files.isDirectory(path, LinkOption.NOFOLLOW_LINKS)) { - try (Stream files = Files.list(path)) { - files.filter(p -> !Files.isDirectory(p, LinkOption.NOFOLLOW_LINKS)).forEach(file -> { - try { - addConfigMapEntryFromFile(configMapBuilder, file.getFileName().toString(), file); - } catch (IOException e) { - throw new IllegalArgumentException(e); - } - }); - } - } else { - addConfigMapEntryFromFile(configMapBuilder, key, path); - } - } - private void addConfigMapEntryFromFile(final ConfigMapBuilder configMapBuilder, final String key, final Path file) - throws IOException { - final byte[] bytes = Files.readAllBytes(file); - try { - StandardCharsets.UTF_8.newDecoder() - .onMalformedInput(CodingErrorAction.REPORT) - .onUnmappableCharacter(CodingErrorAction.REPORT) - .decode(ByteBuffer.wrap(bytes)); - final String value = new String(bytes); - configMapBuilder.addToData(singletonMap(key, value)); - } catch (CharacterCodingException e) { - final String value = Base64.getEncoder().encodeToString(bytes); - configMapBuilder.addToBinaryData(singletonMap(key, value)); - } - } private String getOutput(String key) { if (key.startsWith(PREFIX_ANNOTATION)) { @@ -168,10 +130,12 @@ private io.fabric8.kubernetes.api.model.ConfigMap createConfigMapFromConfigurati } else { final String file = configMapEntry.getFile(); if (file != null) { + final Path filePath = Paths.get(file); if (name == null) { - name = Paths.get(file).getFileName().toString(); + name = filePath.getFileName().toString(); } - addConfigMapEntryFromDirOrFile(configMapBuilder, name, file); + Map.Entry fileEntry = createConfigMapEntry(name, filePath); + addNewEntryToExistingConfigMap(configMapBuilder, fileEntry, filePath); } } }