From 36c190ef91d547952c6737518e5f39ba692dc466 Mon Sep 17 00:00:00 2001 From: Ryan Baxter <524254+ryanjbaxter@users.noreply.github.com> Date: Fri, 18 Oct 2024 09:19:14 -0400 Subject: [PATCH 1/4] Fixing checkstyle errors --- .../commons/config/ConfigMapPropertySourceLocator.java | 3 ++- .../commons/config/SecretsPropertySourceLocator.java | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/spring-cloud-kubernetes-commons/src/main/java/org/springframework/cloud/kubernetes/commons/config/ConfigMapPropertySourceLocator.java b/spring-cloud-kubernetes-commons/src/main/java/org/springframework/cloud/kubernetes/commons/config/ConfigMapPropertySourceLocator.java index 1ca300b5bb..7c2688472b 100644 --- a/spring-cloud-kubernetes-commons/src/main/java/org/springframework/cloud/kubernetes/commons/config/ConfigMapPropertySourceLocator.java +++ b/spring-cloud-kubernetes-commons/src/main/java/org/springframework/cloud/kubernetes/commons/config/ConfigMapPropertySourceLocator.java @@ -22,6 +22,7 @@ import java.util.Collection; import java.util.HashMap; import java.util.LinkedHashSet; +import java.util.Locale; import java.util.Map; import java.util.Set; import java.util.function.Function; @@ -120,7 +121,7 @@ private void addPropertySourcesFromPaths(Environment environment, CompositePrope }).toList().forEach(p -> { try { String content = new String(Files.readAllBytes(p)).trim(); - String filename = p.toAbsolutePath().toString().toLowerCase(); + String filename = p.toAbsolutePath().toString().toLowerCase(Locale.ROOT); if (filename.endsWith(".properties")) { addPropertySourceIfNeeded(c -> PROPERTIES_TO_MAP.apply(KEY_VALUE_TO_PROPERTIES.apply(c)), content, filename, composite); diff --git a/spring-cloud-kubernetes-commons/src/main/java/org/springframework/cloud/kubernetes/commons/config/SecretsPropertySourceLocator.java b/spring-cloud-kubernetes-commons/src/main/java/org/springframework/cloud/kubernetes/commons/config/SecretsPropertySourceLocator.java index 2eb33c801c..ca954a6ef0 100644 --- a/spring-cloud-kubernetes-commons/src/main/java/org/springframework/cloud/kubernetes/commons/config/SecretsPropertySourceLocator.java +++ b/spring-cloud-kubernetes-commons/src/main/java/org/springframework/cloud/kubernetes/commons/config/SecretsPropertySourceLocator.java @@ -26,6 +26,7 @@ import java.util.EnumSet; import java.util.HashSet; import java.util.List; +import java.util.Locale; import java.util.Objects; import java.util.Set; import java.util.function.BiConsumer; @@ -177,7 +178,7 @@ private SecretsPropertySource property(Path filePath) { try { String content = new String(Files.readAllBytes(filePath)).trim(); - String sourceName = fileName.toLowerCase(); + String sourceName = fileName.toLowerCase(Locale.ROOT); SourceData sourceData = new SourceData(sourceName, Collections.singletonMap(fileName, content)); return new SecretsPropertySource(sourceData); } From c6429b3ef54a17e2bf698d8a31267eaaac40f8e2 Mon Sep 17 00:00:00 2001 From: Ryan Baxter Date: Wed, 23 Oct 2024 10:55:18 -0400 Subject: [PATCH 2/4] Close Fabric8 Client When Context Is Stopped (#1780) --- .../cloud/kubernetes/commons/config/ConfigUtils.java | 11 ++++++++++- .../kubernetes/fabric8/Fabric8AutoConfiguration.java | 11 +++++++++++ .../config/Fabric8ConfigDataLocationResolver.java | 5 ++++- 3 files changed, 25 insertions(+), 2 deletions(-) diff --git a/spring-cloud-kubernetes-commons/src/main/java/org/springframework/cloud/kubernetes/commons/config/ConfigUtils.java b/spring-cloud-kubernetes-commons/src/main/java/org/springframework/cloud/kubernetes/commons/config/ConfigUtils.java index 5f40858eab..423c72696d 100644 --- a/spring-cloud-kubernetes-commons/src/main/java/org/springframework/cloud/kubernetes/commons/config/ConfigUtils.java +++ b/spring-cloud-kubernetes-commons/src/main/java/org/springframework/cloud/kubernetes/commons/config/ConfigUtils.java @@ -36,6 +36,7 @@ import org.springframework.boot.BootstrapRegistry; import org.springframework.boot.ConfigurableBootstrapContext; +import org.springframework.context.ApplicationListener; import org.springframework.core.env.Environment; import org.springframework.core.style.ToStringCreator; import org.springframework.util.CollectionUtils; @@ -60,6 +61,8 @@ public final class ConfigUtils { || sourceName.endsWith("-" + activeProfile + ".yaml") || sourceName.endsWith("-" + activeProfile + ".properties"); + private static final ApplicationListener NO_OP = (e) -> { }; + private ConfigUtils() { } @@ -329,17 +332,23 @@ private static Map decodeData(Map data) { } public static void registerSingle(ConfigurableBootstrapContext bootstrapContext, Class cls, T instance, - String name) { + String name, ApplicationListener listener) { bootstrapContext.registerIfAbsent(cls, BootstrapRegistry.InstanceSupplier.of(instance)); bootstrapContext.addCloseListener(event -> { if (event.getApplicationContext().getBeanFactory().getSingleton(name) == null) { event.getApplicationContext() .getBeanFactory() .registerSingleton(name, event.getBootstrapContext().get(cls)); + event.getApplicationContext().addApplicationListener(listener); } }); } + public static void registerSingle(ConfigurableBootstrapContext bootstrapContext, Class cls, T instance, + String name) { + registerSingle(bootstrapContext, cls, instance, name, NO_OP); + } + /** * append prefix to the keys and return a new Map with the new values. */ diff --git a/spring-cloud-kubernetes-fabric8-autoconfig/src/main/java/org/springframework/cloud/kubernetes/fabric8/Fabric8AutoConfiguration.java b/spring-cloud-kubernetes-fabric8-autoconfig/src/main/java/org/springframework/cloud/kubernetes/fabric8/Fabric8AutoConfiguration.java index 509be0d32b..4b66c76054 100644 --- a/spring-cloud-kubernetes-fabric8-autoconfig/src/main/java/org/springframework/cloud/kubernetes/fabric8/Fabric8AutoConfiguration.java +++ b/spring-cloud-kubernetes-fabric8-autoconfig/src/main/java/org/springframework/cloud/kubernetes/fabric8/Fabric8AutoConfiguration.java @@ -18,11 +18,13 @@ import java.time.Duration; +import io.fabric8.kubernetes.client.Client; import io.fabric8.kubernetes.client.Config; import io.fabric8.kubernetes.client.ConfigBuilder; import io.fabric8.kubernetes.client.KubernetesClient; import io.fabric8.kubernetes.client.KubernetesClientBuilder; +import org.springframework.beans.factory.BeanFactoryUtils; import org.springframework.boot.autoconfigure.AutoConfigureAfter; import org.springframework.boot.autoconfigure.condition.ConditionalOnCloudPlatform; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; @@ -31,6 +33,8 @@ import org.springframework.cloud.kubernetes.commons.KubernetesCommonsAutoConfiguration; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; +import org.springframework.context.event.ContextClosedEvent; +import org.springframework.context.event.EventListener; /** * Auto configuration for Kubernetes. @@ -117,4 +121,11 @@ public Fabric8PodUtils kubernetesPodUtils(KubernetesClient client) { return new Fabric8PodUtils(client); } + @EventListener + void onContextClosed(ContextClosedEvent event) { + // Clean up any open connections from the KubernetesClient when the context is closed + BeanFactoryUtils.beansOfTypeIncludingAncestors(event.getApplicationContext(), KubernetesClient.class).values() + .forEach(Client::close); + } + } diff --git a/spring-cloud-kubernetes-fabric8-config/src/main/java/org/springframework/cloud/kubernetes/fabric8/config/Fabric8ConfigDataLocationResolver.java b/spring-cloud-kubernetes-fabric8-config/src/main/java/org/springframework/cloud/kubernetes/fabric8/config/Fabric8ConfigDataLocationResolver.java index 08c9c71f5b..278541ea66 100644 --- a/spring-cloud-kubernetes-fabric8-config/src/main/java/org/springframework/cloud/kubernetes/fabric8/config/Fabric8ConfigDataLocationResolver.java +++ b/spring-cloud-kubernetes-fabric8-config/src/main/java/org/springframework/cloud/kubernetes/fabric8/config/Fabric8ConfigDataLocationResolver.java @@ -34,6 +34,8 @@ import org.springframework.cloud.kubernetes.commons.config.SecretsConfigProperties; import org.springframework.cloud.kubernetes.commons.config.SecretsPropertySourceLocator; import org.springframework.cloud.kubernetes.fabric8.Fabric8AutoConfiguration; +import org.springframework.context.ApplicationListener; +import org.springframework.context.event.ContextClosedEvent; import org.springframework.core.env.Environment; import static org.springframework.cloud.kubernetes.commons.config.ConfigUtils.registerSingle; @@ -90,7 +92,8 @@ private KubernetesClient registerConfigAndClient(ConfigurableBootstrapContext bo registerSingle(bootstrapContext, Config.class, config, "fabric8Config"); KubernetesClient kubernetesClient = new Fabric8AutoConfiguration().kubernetesClient(config); - registerSingle(bootstrapContext, KubernetesClient.class, kubernetesClient, "configKubernetesClient"); + registerSingle(bootstrapContext, KubernetesClient.class, kubernetesClient, "configKubernetesClient", + (ApplicationListener) event -> kubernetesClient.close()); return kubernetesClient; } From 4d55f69023084e25fb5277df36ff35529d7a0ca9 Mon Sep 17 00:00:00 2001 From: Ryan Baxter Date: Wed, 23 Oct 2024 10:55:18 -0400 Subject: [PATCH 3/4] Close Fabric8 Client When Context Is Stopped (#1780) --- .../cloud/kubernetes/commons/config/ConfigUtils.java | 11 ++++++++++- .../kubernetes/fabric8/Fabric8AutoConfiguration.java | 11 +++++++++++ .../config/Fabric8ConfigDataLocationResolver.java | 5 ++++- 3 files changed, 25 insertions(+), 2 deletions(-) diff --git a/spring-cloud-kubernetes-commons/src/main/java/org/springframework/cloud/kubernetes/commons/config/ConfigUtils.java b/spring-cloud-kubernetes-commons/src/main/java/org/springframework/cloud/kubernetes/commons/config/ConfigUtils.java index 5f40858eab..423c72696d 100644 --- a/spring-cloud-kubernetes-commons/src/main/java/org/springframework/cloud/kubernetes/commons/config/ConfigUtils.java +++ b/spring-cloud-kubernetes-commons/src/main/java/org/springframework/cloud/kubernetes/commons/config/ConfigUtils.java @@ -36,6 +36,7 @@ import org.springframework.boot.BootstrapRegistry; import org.springframework.boot.ConfigurableBootstrapContext; +import org.springframework.context.ApplicationListener; import org.springframework.core.env.Environment; import org.springframework.core.style.ToStringCreator; import org.springframework.util.CollectionUtils; @@ -60,6 +61,8 @@ public final class ConfigUtils { || sourceName.endsWith("-" + activeProfile + ".yaml") || sourceName.endsWith("-" + activeProfile + ".properties"); + private static final ApplicationListener NO_OP = (e) -> { }; + private ConfigUtils() { } @@ -329,17 +332,23 @@ private static Map decodeData(Map data) { } public static void registerSingle(ConfigurableBootstrapContext bootstrapContext, Class cls, T instance, - String name) { + String name, ApplicationListener listener) { bootstrapContext.registerIfAbsent(cls, BootstrapRegistry.InstanceSupplier.of(instance)); bootstrapContext.addCloseListener(event -> { if (event.getApplicationContext().getBeanFactory().getSingleton(name) == null) { event.getApplicationContext() .getBeanFactory() .registerSingleton(name, event.getBootstrapContext().get(cls)); + event.getApplicationContext().addApplicationListener(listener); } }); } + public static void registerSingle(ConfigurableBootstrapContext bootstrapContext, Class cls, T instance, + String name) { + registerSingle(bootstrapContext, cls, instance, name, NO_OP); + } + /** * append prefix to the keys and return a new Map with the new values. */ diff --git a/spring-cloud-kubernetes-fabric8-autoconfig/src/main/java/org/springframework/cloud/kubernetes/fabric8/Fabric8AutoConfiguration.java b/spring-cloud-kubernetes-fabric8-autoconfig/src/main/java/org/springframework/cloud/kubernetes/fabric8/Fabric8AutoConfiguration.java index 509be0d32b..4b66c76054 100644 --- a/spring-cloud-kubernetes-fabric8-autoconfig/src/main/java/org/springframework/cloud/kubernetes/fabric8/Fabric8AutoConfiguration.java +++ b/spring-cloud-kubernetes-fabric8-autoconfig/src/main/java/org/springframework/cloud/kubernetes/fabric8/Fabric8AutoConfiguration.java @@ -18,11 +18,13 @@ import java.time.Duration; +import io.fabric8.kubernetes.client.Client; import io.fabric8.kubernetes.client.Config; import io.fabric8.kubernetes.client.ConfigBuilder; import io.fabric8.kubernetes.client.KubernetesClient; import io.fabric8.kubernetes.client.KubernetesClientBuilder; +import org.springframework.beans.factory.BeanFactoryUtils; import org.springframework.boot.autoconfigure.AutoConfigureAfter; import org.springframework.boot.autoconfigure.condition.ConditionalOnCloudPlatform; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; @@ -31,6 +33,8 @@ import org.springframework.cloud.kubernetes.commons.KubernetesCommonsAutoConfiguration; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; +import org.springframework.context.event.ContextClosedEvent; +import org.springframework.context.event.EventListener; /** * Auto configuration for Kubernetes. @@ -117,4 +121,11 @@ public Fabric8PodUtils kubernetesPodUtils(KubernetesClient client) { return new Fabric8PodUtils(client); } + @EventListener + void onContextClosed(ContextClosedEvent event) { + // Clean up any open connections from the KubernetesClient when the context is closed + BeanFactoryUtils.beansOfTypeIncludingAncestors(event.getApplicationContext(), KubernetesClient.class).values() + .forEach(Client::close); + } + } diff --git a/spring-cloud-kubernetes-fabric8-config/src/main/java/org/springframework/cloud/kubernetes/fabric8/config/Fabric8ConfigDataLocationResolver.java b/spring-cloud-kubernetes-fabric8-config/src/main/java/org/springframework/cloud/kubernetes/fabric8/config/Fabric8ConfigDataLocationResolver.java index 08c9c71f5b..278541ea66 100644 --- a/spring-cloud-kubernetes-fabric8-config/src/main/java/org/springframework/cloud/kubernetes/fabric8/config/Fabric8ConfigDataLocationResolver.java +++ b/spring-cloud-kubernetes-fabric8-config/src/main/java/org/springframework/cloud/kubernetes/fabric8/config/Fabric8ConfigDataLocationResolver.java @@ -34,6 +34,8 @@ import org.springframework.cloud.kubernetes.commons.config.SecretsConfigProperties; import org.springframework.cloud.kubernetes.commons.config.SecretsPropertySourceLocator; import org.springframework.cloud.kubernetes.fabric8.Fabric8AutoConfiguration; +import org.springframework.context.ApplicationListener; +import org.springframework.context.event.ContextClosedEvent; import org.springframework.core.env.Environment; import static org.springframework.cloud.kubernetes.commons.config.ConfigUtils.registerSingle; @@ -90,7 +92,8 @@ private KubernetesClient registerConfigAndClient(ConfigurableBootstrapContext bo registerSingle(bootstrapContext, Config.class, config, "fabric8Config"); KubernetesClient kubernetesClient = new Fabric8AutoConfiguration().kubernetesClient(config); - registerSingle(bootstrapContext, KubernetesClient.class, kubernetesClient, "configKubernetesClient"); + registerSingle(bootstrapContext, KubernetesClient.class, kubernetesClient, "configKubernetesClient", + (ApplicationListener) event -> kubernetesClient.close()); return kubernetesClient; } From 0c8a398310ea2024232754b5e60a15234043e1ac Mon Sep 17 00:00:00 2001 From: erabii Date: Wed, 23 Oct 2024 21:12:28 +0300 Subject: [PATCH 4/4] more improvements in IT (#1771) --- .../integration/tests/commons/Commons.java | 20 +++++++++++++++++-- .../tests/commons/fabric8_client/Util.java | 2 +- .../tests/commons/native_client/Util.java | 2 +- 3 files changed, 20 insertions(+), 4 deletions(-) diff --git a/spring-cloud-kubernetes-test-support/src/main/java/org/springframework/cloud/kubernetes/integration/tests/commons/Commons.java b/spring-cloud-kubernetes-test-support/src/main/java/org/springframework/cloud/kubernetes/integration/tests/commons/Commons.java index 3282aa81fd..c6221e855f 100644 --- a/spring-cloud-kubernetes-test-support/src/main/java/org/springframework/cloud/kubernetes/integration/tests/commons/Commons.java +++ b/spring-cloud-kubernetes-test-support/src/main/java/org/springframework/cloud/kubernetes/integration/tests/commons/Commons.java @@ -124,6 +124,11 @@ public static void assertReloadLogStatements(String left, String right, String a * create a tar, copy it in the running k3s and load this tar as an image. */ public static void loadImage(String image, String tag, String tarName, K3sContainer container) throws Exception { + + if (imageAlreadyInK3s(container, tarName)) { + return; + } + // save image try (SaveImageCmd saveImageCmd = container.getDockerClient().saveImageCmd(image)) { InputStream imageStream = saveImageCmd.withTag(tag).exec(); @@ -182,7 +187,7 @@ public static void load(K3sContainer container, String tarName, String imageName try { LOG.info("no tars found, will resort to pulling the image"); LOG.info("using : " + imageVersion + " for : " + imageNameForDownload); - pullImage(imageNameForDownload, imageVersion, container); + pullImage(imageNameForDownload, imageVersion, tarName, container); loadImage(imageNameForDownload, imageVersion, tarName, container); } catch (Exception e) { @@ -205,7 +210,13 @@ public static void validateImage(String image, K3sContainer container) { } } - public static void pullImage(String image, String tag, K3sContainer container) throws InterruptedException { + public static void pullImage(String image, String tag, String tarName, K3sContainer container) + throws InterruptedException { + + if (imageAlreadyInK3s(container, tarName)) { + return; + } + try (PullImageCmd pullImageCmd = container.getDockerClient().pullImageCmd(image)) { pullImageCmd.withTag(tag).start().awaitCompletion(); } @@ -265,6 +276,11 @@ private static void loadImageFromPath(String tarName, K3sContainer container) { } private static boolean imageAlreadyInK3s(K3sContainer container, String tarName) { + + if (tarName == null) { + return false; + } + try { boolean present = container.execInContainer("sh", "-c", "ctr images list | grep " + tarName) .getStdout() diff --git a/spring-cloud-kubernetes-test-support/src/main/java/org/springframework/cloud/kubernetes/integration/tests/commons/fabric8_client/Util.java b/spring-cloud-kubernetes-test-support/src/main/java/org/springframework/cloud/kubernetes/integration/tests/commons/fabric8_client/Util.java index bc1bfa43b4..8d98a078e0 100644 --- a/spring-cloud-kubernetes-test-support/src/main/java/org/springframework/cloud/kubernetes/integration/tests/commons/fabric8_client/Util.java +++ b/spring-cloud-kubernetes-test-support/src/main/java/org/springframework/cloud/kubernetes/integration/tests/commons/fabric8_client/Util.java @@ -103,7 +103,7 @@ public void createAndWait(String namespace, String name, @Nullable Deployment de } else { String[] image = imageFromDeployment.split(":", 2); - pullImage(image[0], image[1], container); + pullImage(image[0], image[1], name, container); loadImage(image[0], image[1], name, container); } diff --git a/spring-cloud-kubernetes-test-support/src/main/java/org/springframework/cloud/kubernetes/integration/tests/commons/native_client/Util.java b/spring-cloud-kubernetes-test-support/src/main/java/org/springframework/cloud/kubernetes/integration/tests/commons/native_client/Util.java index c9de654263..bb24aa0176 100644 --- a/spring-cloud-kubernetes-test-support/src/main/java/org/springframework/cloud/kubernetes/integration/tests/commons/native_client/Util.java +++ b/spring-cloud-kubernetes-test-support/src/main/java/org/springframework/cloud/kubernetes/integration/tests/commons/native_client/Util.java @@ -136,7 +136,7 @@ public void createAndWait(String namespace, String name, V1Deployment deployment } else { String[] image = imageFromDeployment.split(":", 2); - pullImage(image[0], image[1], container); + pullImage(image[0], image[1], name, container); loadImage(image[0], image[1], name, container); }