From 4d55f69023084e25fb5277df36ff35529d7a0ca9 Mon Sep 17 00:00:00 2001 From: Ryan Baxter Date: Wed, 23 Oct 2024 10:55:18 -0400 Subject: [PATCH] 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 5f40858e..423c7269 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.apache.commons.logging.LogFactory; 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 @@ public final class ConfigUtils { } 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 509be0d3..4b66c760 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 @@ package org.springframework.cloud.kubernetes.fabric8; 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.KubernetesClientProperties; 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 class Fabric8AutoConfiguration { 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 08c9c71f..278541ea 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.KubernetesConfigDataL 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 @@ public class Fabric8ConfigDataLocationResolver extends KubernetesConfigDataLocat 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; }