From 5f4b9ca997210ae275514463f9fd8184c3f43ee2 Mon Sep 17 00:00:00 2001 From: erabii Date: Tue, 17 Jan 2023 00:40:37 +0000 Subject: [PATCH] Don't configure reload beans unless the properties are set (#1200) --- ...esClientConfigReloadAutoConfiguration.java | 16 +- ...ientEventBasedConfigMapChangeDetector.java | 49 ++- ...ClientEventBasedSecretsChangeDetector.java | 47 ++- ...ientConfigReloadAutoConfigurationTest.java | 321 +++++++++++++++-- ...nfigReloadPropertiesAutoConfiguration.java | 30 ++ .../ConditionalOnConfigMapsReloadEnabled.java | 42 +++ .../ConditionalOnSecretsReloadEnabled.java | 41 +++ ...ot.autoconfigure.AutoConfiguration.imports | 1 + .../Fabric8ConfigReloadAutoConfiguration.java | 18 +- ...ric8EventBasedConfigMapChangeDetector.java | 35 +- ...abric8EventBasedSecretsChangeDetector.java | 35 +- .../config/KubernetesConfigTestBase.java | 6 +- .../ConfigReloadAutoConfigurationTest.java | 325 +++++++++++++++++- .../event/reload/ConfigMapEventReloadIT.java | 26 +- .../src/test/resources/one/deployment.yaml | 2 + .../src/test/resources/three/deployment.yaml | 2 + .../src/test/resources/two/deployment.yaml | 2 + .../src/main/resources/application.yaml | 1 + .../event/reload/SecretsEventReloadIT.java | 20 ++ .../src/test/resources/deployment.yaml | 3 + .../event/reload/ConfigMapEventReloadIT.java | 24 ++ .../src/test/resources/deployment.yaml | 3 + .../src/main/resources/application.yaml | 1 + .../event/reload/SecretsEventsReloadIT.java | 23 ++ .../src/test/resources/deployment.yaml | 3 + 25 files changed, 931 insertions(+), 145 deletions(-) create mode 100644 spring-cloud-kubernetes-commons/src/main/java/org/springframework/cloud/kubernetes/commons/config/reload/ConfigReloadPropertiesAutoConfiguration.java create mode 100644 spring-cloud-kubernetes-commons/src/main/java/org/springframework/cloud/kubernetes/commons/config/reload/condition/ConditionalOnConfigMapsReloadEnabled.java create mode 100644 spring-cloud-kubernetes-commons/src/main/java/org/springframework/cloud/kubernetes/commons/config/reload/condition/ConditionalOnSecretsReloadEnabled.java diff --git a/spring-cloud-kubernetes-client-config/src/main/java/org/springframework/cloud/kubernetes/client/config/reload/KubernetesClientConfigReloadAutoConfiguration.java b/spring-cloud-kubernetes-client-config/src/main/java/org/springframework/cloud/kubernetes/client/config/reload/KubernetesClientConfigReloadAutoConfiguration.java index 896defdc..3abf0843 100644 --- a/spring-cloud-kubernetes-client-config/src/main/java/org/springframework/cloud/kubernetes/client/config/reload/KubernetesClientConfigReloadAutoConfiguration.java +++ b/spring-cloud-kubernetes-client-config/src/main/java/org/springframework/cloud/kubernetes/client/config/reload/KubernetesClientConfigReloadAutoConfiguration.java @@ -23,7 +23,8 @@ import org.springframework.boot.actuate.autoconfigure.info.InfoEndpointAutoConfi import org.springframework.boot.autoconfigure.AutoConfigureAfter; import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; -import org.springframework.boot.context.properties.EnableConfigurationProperties; +import org.springframework.boot.autoconfigure.condition.ConditionalOnCloudPlatform; +import org.springframework.boot.cloud.CloudPlatform; import org.springframework.cloud.autoconfigure.RefreshAutoConfiguration; import org.springframework.cloud.autoconfigure.RefreshEndpointAutoConfiguration; import org.springframework.cloud.commons.util.TaskSchedulerWrapper; @@ -34,14 +35,16 @@ import org.springframework.cloud.kubernetes.client.config.KubernetesClientConfig import org.springframework.cloud.kubernetes.client.config.KubernetesClientSecretsPropertySource; import org.springframework.cloud.kubernetes.client.config.KubernetesClientSecretsPropertySourceLocator; import org.springframework.cloud.kubernetes.commons.KubernetesNamespaceProvider; -import org.springframework.cloud.kubernetes.commons.config.ConditionalOnKubernetesAndConfigEnabled; import org.springframework.cloud.kubernetes.commons.config.reload.ConfigReloadAutoConfiguration; import org.springframework.cloud.kubernetes.commons.config.reload.ConfigReloadProperties; +import org.springframework.cloud.kubernetes.commons.config.reload.ConfigReloadPropertiesAutoConfiguration; import org.springframework.cloud.kubernetes.commons.config.reload.ConfigurationChangeDetector; import org.springframework.cloud.kubernetes.commons.config.reload.ConfigurationUpdateStrategy; import org.springframework.cloud.kubernetes.commons.config.reload.PollingConfigMapChangeDetector; import org.springframework.cloud.kubernetes.commons.config.reload.PollingSecretsChangeDetector; +import org.springframework.cloud.kubernetes.commons.config.reload.condition.ConditionalOnConfigMapsReloadEnabled; import org.springframework.cloud.kubernetes.commons.config.reload.condition.ConditionalOnKubernetesReloadEnabled; +import org.springframework.cloud.kubernetes.commons.config.reload.condition.ConditionalOnSecretsReloadEnabled; import org.springframework.cloud.kubernetes.commons.config.reload.condition.EventReloadDetectionMode; import org.springframework.cloud.kubernetes.commons.config.reload.condition.PollingReloadDetectionMode; import org.springframework.context.annotation.Bean; @@ -55,12 +58,11 @@ import org.springframework.scheduling.TaskScheduler; * @author Ryan Baxter */ @Configuration(proxyBeanMethods = false) -@ConditionalOnKubernetesAndConfigEnabled +@ConditionalOnCloudPlatform(CloudPlatform.KUBERNETES) @ConditionalOnKubernetesReloadEnabled @ConditionalOnClass({ EndpointAutoConfiguration.class, RestartEndpoint.class, ContextRefresher.class }) @AutoConfigureAfter({ InfoEndpointAutoConfiguration.class, RefreshEndpointAutoConfiguration.class, - RefreshAutoConfiguration.class }) -@EnableConfigurationProperties(ConfigReloadProperties.class) + RefreshAutoConfiguration.class, ConfigReloadPropertiesAutoConfiguration.class }) @Import(ConfigReloadAutoConfiguration.class) public class KubernetesClientConfigReloadAutoConfiguration { @@ -72,6 +74,7 @@ public class KubernetesClientConfigReloadAutoConfiguration { * @return a bean that listen to configuration changes and fire a reload. */ @Bean + @ConditionalOnConfigMapsReloadEnabled @ConditionalOnBean(KubernetesClientConfigMapPropertySourceLocator.class) @Conditional(PollingReloadDetectionMode.class) public ConfigurationChangeDetector configMapPropertyChangePollingWatcher(ConfigReloadProperties properties, @@ -92,6 +95,7 @@ public class KubernetesClientConfigReloadAutoConfiguration { * @return a bean that listen to configuration changes and fire a reload. */ @Bean + @ConditionalOnSecretsReloadEnabled @ConditionalOnBean(KubernetesClientSecretsPropertySourceLocator.class) @Conditional(PollingReloadDetectionMode.class) public ConfigurationChangeDetector secretsPropertyChangePollingWatcher(ConfigReloadProperties properties, @@ -112,6 +116,7 @@ public class KubernetesClientConfigReloadAutoConfiguration { * @return a bean that listen to configMap change events and fire a reload. */ @Bean + @ConditionalOnConfigMapsReloadEnabled @ConditionalOnBean(KubernetesClientConfigMapPropertySourceLocator.class) @Conditional(EventReloadDetectionMode.class) public ConfigurationChangeDetector configMapPropertyChangeEventWatcher(ConfigReloadProperties properties, @@ -132,6 +137,7 @@ public class KubernetesClientConfigReloadAutoConfiguration { * @return a bean that listen to secrets change events and fire a reload. */ @Bean + @ConditionalOnSecretsReloadEnabled @ConditionalOnBean(KubernetesClientSecretsPropertySourceLocator.class) @Conditional(EventReloadDetectionMode.class) public ConfigurationChangeDetector secretsPropertyChangeEventWatcher(ConfigReloadProperties properties, diff --git a/spring-cloud-kubernetes-client-config/src/main/java/org/springframework/cloud/kubernetes/client/config/reload/KubernetesClientEventBasedConfigMapChangeDetector.java b/spring-cloud-kubernetes-client-config/src/main/java/org/springframework/cloud/kubernetes/client/config/reload/KubernetesClientEventBasedConfigMapChangeDetector.java index 1874efb8..9a7522ca 100644 --- a/spring-cloud-kubernetes-client-config/src/main/java/org/springframework/cloud/kubernetes/client/config/reload/KubernetesClientEventBasedConfigMapChangeDetector.java +++ b/spring-cloud-kubernetes-client-config/src/main/java/org/springframework/cloud/kubernetes/client/config/reload/KubernetesClientEventBasedConfigMapChangeDetector.java @@ -57,8 +57,6 @@ public class KubernetesClientEventBasedConfigMapChangeDetector extends Configura private final KubernetesClientConfigMapPropertySourceLocator propertySourceLocator; - private final boolean monitorConfigMaps; - private final SharedInformerFactory factory; private final List> informers = new ArrayList<>(); @@ -103,41 +101,38 @@ public class KubernetesClientEventBasedConfigMapChangeDetector extends Configura // certificate authorities for the cluster. This results in SSL errors. // See https://github.com/spring-cloud/spring-cloud-kubernetes/issues/885 this.factory = new SharedInformerFactory(createApiClientForInformerClient()); - this.monitorConfigMaps = properties.monitoringConfigMaps(); this.enableReloadFiltering = properties.enableReloadFiltering(); namespaces = namespaces(kubernetesNamespaceProvider, properties, "configmap"); } @PostConstruct void inform() { - if (monitorConfigMaps) { - LOG.info(() -> "Kubernetes event-based configMap change detector activated"); + LOG.info(() -> "Kubernetes event-based configMap change detector activated"); - namespaces.forEach(namespace -> { - SharedIndexInformer informer; - String filter = null; + namespaces.forEach(namespace -> { + SharedIndexInformer informer; + String filter = null; - if (enableReloadFiltering) { - filter = ConfigReloadProperties.RELOAD_LABEL_FILTER + "=true"; - LOG.debug(() -> "added configmap informer for namespace : " + namespace + " with enabled filter"); - } - else { - LOG.debug(() -> "added configmap informer for namespace : " + namespace); - } + if (enableReloadFiltering) { + filter = ConfigReloadProperties.RELOAD_LABEL_FILTER + "=true"; + LOG.debug(() -> "added configmap informer for namespace : " + namespace + " with enabled filter"); + } + else { + LOG.debug(() -> "added configmap informer for namespace : " + namespace); + } - String filterOnInformerLabel = filter; - informer = factory - .sharedIndexInformerFor( - (CallGeneratorParams params) -> coreV1Api.listNamespacedConfigMapCall(namespace, null, - null, null, null, filterOnInformerLabel, null, params.resourceVersion, null, - params.timeoutSeconds, params.watch, null), - V1ConfigMap.class, V1ConfigMapList.class); - informer.addEventHandler(handler); - informers.add(informer); - }); + String filterOnInformerLabel = filter; + informer = factory + .sharedIndexInformerFor( + (CallGeneratorParams params) -> coreV1Api.listNamespacedConfigMapCall(namespace, null, null, + null, null, filterOnInformerLabel, null, params.resourceVersion, null, + params.timeoutSeconds, params.watch, null), + V1ConfigMap.class, V1ConfigMapList.class); + informer.addEventHandler(handler); + informers.add(informer); + }); - factory.startAllRegisteredInformers(); - } + factory.startAllRegisteredInformers(); } @PreDestroy diff --git a/spring-cloud-kubernetes-client-config/src/main/java/org/springframework/cloud/kubernetes/client/config/reload/KubernetesClientEventBasedSecretsChangeDetector.java b/spring-cloud-kubernetes-client-config/src/main/java/org/springframework/cloud/kubernetes/client/config/reload/KubernetesClientEventBasedSecretsChangeDetector.java index 18e029b2..c6a5dbf0 100644 --- a/spring-cloud-kubernetes-client-config/src/main/java/org/springframework/cloud/kubernetes/client/config/reload/KubernetesClientEventBasedSecretsChangeDetector.java +++ b/spring-cloud-kubernetes-client-config/src/main/java/org/springframework/cloud/kubernetes/client/config/reload/KubernetesClientEventBasedSecretsChangeDetector.java @@ -57,8 +57,6 @@ public class KubernetesClientEventBasedSecretsChangeDetector extends Configurati private final KubernetesClientSecretsPropertySourceLocator propertySourceLocator; - private final boolean monitorSecrets; - private final SharedInformerFactory factory; private final List> informers = new ArrayList<>(); @@ -103,40 +101,37 @@ public class KubernetesClientEventBasedSecretsChangeDetector extends Configurati // certificate authorities for the cluster. This results in SSL errors. // See https://github.com/spring-cloud/spring-cloud-kubernetes/issues/885 this.factory = new SharedInformerFactory(createApiClientForInformerClient()); - this.monitorSecrets = properties.monitoringSecrets(); this.enableReloadFiltering = properties.enableReloadFiltering(); namespaces = namespaces(kubernetesNamespaceProvider, properties, "secret"); } @PostConstruct void inform() { - if (monitorSecrets) { - LOG.info(() -> "Kubernetes event-based secrets change detector activated"); + LOG.info(() -> "Kubernetes event-based secrets change detector activated"); - namespaces.forEach(namespace -> { - SharedIndexInformer informer; - String filter = null; + namespaces.forEach(namespace -> { + SharedIndexInformer informer; + String filter = null; - if (enableReloadFiltering) { - filter = ConfigReloadProperties.RELOAD_LABEL_FILTER + "=true"; - LOG.debug(() -> "added secret informer for namespace : " + namespace + " with enabled filter"); - } - else { - LOG.debug(() -> "added secret informer for namespace : " + namespace); - } + if (enableReloadFiltering) { + filter = ConfigReloadProperties.RELOAD_LABEL_FILTER + "=true"; + LOG.debug(() -> "added secret informer for namespace : " + namespace + " with enabled filter"); + } + else { + LOG.debug(() -> "added secret informer for namespace : " + namespace); + } - String filterOnInformerLabel = filter; - informer = factory.sharedIndexInformerFor( - (CallGeneratorParams params) -> coreV1Api.listNamespacedSecretCall(namespace, null, null, null, - null, filterOnInformerLabel, null, params.resourceVersion, null, params.timeoutSeconds, - params.watch, null), - V1Secret.class, V1SecretList.class); - informer.addEventHandler(handler); - informers.add(informer); - }); + String filterOnInformerLabel = filter; + informer = factory.sharedIndexInformerFor( + (CallGeneratorParams params) -> coreV1Api.listNamespacedSecretCall(namespace, null, null, null, + null, filterOnInformerLabel, null, params.resourceVersion, null, params.timeoutSeconds, + params.watch, null), + V1Secret.class, V1SecretList.class); + informer.addEventHandler(handler); + informers.add(informer); + }); - factory.startAllRegisteredInformers(); - } + factory.startAllRegisteredInformers(); } @PreDestroy diff --git a/spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/KubernetesClientConfigReloadAutoConfigurationTest.java b/spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/KubernetesClientConfigReloadAutoConfigurationTest.java index f5ec8257..3e017b20 100644 --- a/spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/KubernetesClientConfigReloadAutoConfigurationTest.java +++ b/spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/KubernetesClientConfigReloadAutoConfigurationTest.java @@ -18,7 +18,9 @@ package org.springframework.cloud.kubernetes.client.config; import java.util.ArrayList; import java.util.Arrays; +import java.util.Comparator; import java.util.List; +import java.util.Map; import com.github.tomakehurst.wiremock.WireMockServer; import com.github.tomakehurst.wiremock.client.WireMock; @@ -30,6 +32,7 @@ import io.kubernetes.client.openapi.models.V1ConfigMapList; import io.kubernetes.client.openapi.models.V1ObjectMetaBuilder; import io.kubernetes.client.util.ClientBuilder; import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -46,9 +49,15 @@ import org.springframework.cloud.autoconfigure.RefreshAutoConfiguration; import org.springframework.cloud.autoconfigure.RefreshEndpointAutoConfiguration; import org.springframework.cloud.kubernetes.client.KubernetesClientAutoConfiguration; import org.springframework.cloud.kubernetes.client.config.reload.KubernetesClientConfigReloadAutoConfiguration; +import org.springframework.cloud.kubernetes.client.config.reload.KubernetesClientEventBasedConfigMapChangeDetector; +import org.springframework.cloud.kubernetes.client.config.reload.KubernetesClientEventBasedSecretsChangeDetector; import org.springframework.cloud.kubernetes.commons.KubernetesClientProperties; import org.springframework.cloud.kubernetes.commons.config.KubernetesBootstrapConfiguration; import org.springframework.cloud.kubernetes.commons.config.reload.ConfigReloadAutoConfiguration; +import org.springframework.cloud.kubernetes.commons.config.reload.ConfigReloadPropertiesAutoConfiguration; +import org.springframework.cloud.kubernetes.commons.config.reload.ConfigurationChangeDetector; +import org.springframework.cloud.kubernetes.commons.config.reload.PollingConfigMapChangeDetector; +import org.springframework.cloud.kubernetes.commons.config.reload.PollingSecretsChangeDetector; import org.springframework.context.ConfigurableApplicationContext; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @@ -77,7 +86,7 @@ public class KubernetesClientConfigReloadAutoConfigurationTest { context = new SpringApplicationBuilder(PropertyPlaceholderAutoConfiguration.class, LocalTestConfig.class, ConfigReloadAutoConfiguration.class, RefreshAutoConfiguration.class, EndpointAutoConfiguration.class, InfoEndpointAutoConfiguration.class, RefreshEndpointAutoConfiguration.class, - ConfigurationPropertiesBindingPostProcessor.class, + ConfigurationPropertiesBindingPostProcessor.class, ConfigReloadPropertiesAutoConfiguration.class, ConfigurationPropertiesRebinderAutoConfiguration.class, KubernetesClientBootstrapConfiguration.class, KubernetesBootstrapConfiguration.class, KubernetesClientConfigReloadAutoConfiguration.class) .web(org.springframework.boot.WebApplicationType.NONE).properties(envArray).run(); @@ -111,7 +120,7 @@ public class KubernetesClientConfigReloadAutoConfigurationTest { // 1. watchers @Test - void kubernetesWatchersWhenKubernetesDisabled() throws Exception { + void kubernetesWatchersWhenKubernetesDisabled() { setup(); assertThat(context.containsBean("configMapPropertySourceLocator")).isFalse(); assertThat(context.containsBean("secretsPropertySourceLocator")).isFalse(); @@ -122,7 +131,7 @@ public class KubernetesClientConfigReloadAutoConfigurationTest { } @Test - void kubernetesWatchersWhenConfigDisabled() throws Exception { + void kubernetesWatchersWhenConfigDisabled() { setup("spring.cloud.kubernetes.config.enabled=false"); assertThat(context.containsBean("configMapPropertyChangePollingWatcher")).isFalse(); assertThat(context.containsBean("secretsPropertyChangePollingWatcher")).isFalse(); @@ -131,7 +140,7 @@ public class KubernetesClientConfigReloadAutoConfigurationTest { } @Test - void kubernetesWatchersWhenReloadDisabled() throws Exception { + void kubernetesWatchersWhenReloadDisabled() { setup("spring.cloud.kubernetes.reload.enabled=false"); assertThat(context.containsBean("configMapPropertyChangePollingWatcher")).isFalse(); assertThat(context.containsBean("secretsPropertyChangePollingWatcher")).isFalse(); @@ -140,7 +149,7 @@ public class KubernetesClientConfigReloadAutoConfigurationTest { } @Test - void kubernetesReloadEnabledButSecretAndConfigDisabled() throws Exception { + void kubernetesReloadEnabledButSecretAndConfigDisabled() { setup("spring.cloud.kubernetes.reload.enabled=true", "spring.cloud.kubernetes.config.enabled=false", "spring.cloud.kubernetes.secrets.enabled=false"); assertThat(context.containsBean("configMapPropertyChangePollingWatcher")).isFalse(); @@ -149,46 +158,314 @@ public class KubernetesClientConfigReloadAutoConfigurationTest { assertThat(context.containsBean("secretsPropertyChangeEventWatcher")).isFalse(); } + /** + *
+	 *     - reload mode is enabled (via event reload)
+	 *     - spring.cloud.kubernetes.reload.monitoring-configMaps is true by default
+	 *
+	 *     - config map event watcher is picked up
+	 *     - config map polling watcher is not picked up
+	 *     - secrets event watcher is not picked up
+	 *     - secrets polling watcher is not picked up
+	 * 
+ */ @Test - void kubernetesReloadEnabledWithPolling() throws Exception { - setup("spring.cloud.kubernetes.reload.enabled=true", "spring.cloud.kubernetes.reload.mode=polling", - "spring.main.cloud-platform=KUBERNETES"); - assertThat(context.containsBean("configMapPropertySourceLocator")).isTrue(); - assertThat(context.containsBean("secretsPropertySourceLocator")).isTrue(); - assertThat(context.containsBean("configMapPropertyChangePollingWatcher")).isTrue(); - assertThat(context.containsBean("secretsPropertyChangePollingWatcher")).isTrue(); - assertThat(context.containsBean("configMapPropertyChangeEventWatcher")).isFalse(); - assertThat(context.containsBean("secretsPropertyChangeEventWatcher")).isFalse(); + void reloadEventEnabledMonitoringConfigMapsEnabledByDefault() { + setup("spring.cloud.kubernetes.reload.enabled=true", "spring.main.cloud-platform=KUBERNETES"); + Map map = context.getBeansOfType(ConfigurationChangeDetector.class); + Assertions.assertEquals(map.size(), 1); + Assertions.assertTrue(map.values().iterator().next().getClass() + .isAssignableFrom(KubernetesClientEventBasedConfigMapChangeDetector.class)); } + /** + *
+	 *     - reload mode is enabled (via event reload)
+	 *     - spring.cloud.kubernetes.reload.monitoring-configMaps is true by default
+	 *
+	 *     - config map event watcher is picked up
+	 *     - config map polling watcher is not picked up
+	 *     - secrets event watcher is not picked up
+	 *     - secrets polling watcher is not picked up
+	 * 
+ */ @Test - void kubernetesReloadEnabledWithEvent() throws Exception { + void reloadEventEnabledMonitoringConfigMapsEnabled() { setup("spring.cloud.kubernetes.reload.enabled=true", "spring.cloud.kubernetes.reload.mode=event", "spring.main.cloud-platform=KUBERNETES"); - assertThat(context.containsBean("configMapPropertyChangePollingWatcher")).isFalse(); - assertThat(context.containsBean("secretsPropertyChangePollingWatcher")).isFalse(); - assertThat(context.containsBean("configMapPropertyChangeEventWatcher")).isTrue(); - assertThat(context.containsBean("secretsPropertyChangeEventWatcher")).isTrue(); + Map map = context.getBeansOfType(ConfigurationChangeDetector.class); + Assertions.assertEquals(map.size(), 1); + Assertions.assertTrue(map.values().iterator().next().getClass() + .isAssignableFrom(KubernetesClientEventBasedConfigMapChangeDetector.class)); + } + + /** + *
+	 *     - reload mode is enabled (via event reload)
+	 *     - spring.cloud.kubernetes.reload.monitoring-configMaps is false
+	 *
+	 *     - config map event watcher is not picked up
+	 *     - config map polling watcher is not picked up
+	 *     - secrets event watcher is not picked up
+	 *     - secrets polling watcher is not picked up
+	 * 
+ */ + @Test + void reloadEventEnabledMonitoringConfigMapsDisabled() { + setup("spring.cloud.kubernetes.reload.enabled=true", "spring.cloud.kubernetes.reload.mode=event", + "spring.main.cloud-platform=KUBERNETES", "spring.cloud.kubernetes.reload.monitoring-configMaps=false"); + Map map = context.getBeansOfType(ConfigurationChangeDetector.class); + Assertions.assertEquals(map.size(), 0); + } + + /** + *
+	 *     - reload mode is enabled (via poll reload)
+	 *     - spring.cloud.kubernetes.reload.monitoring-configMaps is false
+	 *
+	 *     - config map event watcher is not picked up
+	 *     - config map polling watcher is not picked up
+	 *     - secrets event watcher is not picked up
+	 *     - secrets polling watcher is not picked up
+	 * 
+ */ + @Test + void reloadPollingEnabledMonitoringConfigMapsDisabledMonitoringSecretsDisabled() { + setup("spring.cloud.kubernetes.reload.enabled=true", "spring.cloud.kubernetes.reload.mode=polling", + "spring.main.cloud-platform=KUBERNETES", "spring.cloud.kubernetes.reload.monitoring-configMaps=false"); + Map map = context.getBeansOfType(ConfigurationChangeDetector.class); + Assertions.assertEquals(map.size(), 0); + } + + /** + *
+	 *     - reload mode is enabled (via poll reload)
+	 *     - spring.cloud.kubernetes.reload.monitoring-configMaps is true by default
+	 *
+	 *     - config map event watcher is not picked up
+	 *     - config map polling watcher is not picked up
+	 *     - secrets event watcher is not picked up
+	 *     - config map polling watcher is not picked up
+	 * 
+ */ + @Test + void reloadPollingEnabledMonitoringConfigMapsEnabled() { + setup("spring.cloud.kubernetes.reload.enabled=true", "spring.cloud.kubernetes.reload.mode=polling", + "spring.main.cloud-platform=KUBERNETES"); + Map map = context.getBeansOfType(ConfigurationChangeDetector.class); + Assertions.assertEquals(map.size(), 1); + Assertions.assertTrue( + map.values().iterator().next().getClass().isAssignableFrom(PollingConfigMapChangeDetector.class)); + } + + /** + *
+	 *     - reload mode is enabled (via event reload)
+	 *     - spring.cloud.kubernetes.reload.monitoring-secrets is true
+	 *     - spring.cloud.kubernetes.reload.monitoring-configMaps is false
+	 *
+	 *     - config map event watcher is not picked up
+	 *     - config map polling watcher is not picked up
+	 *     - secrets event watcher is picked up
+	 *     - secrets polling watcher is not picked up
+	 * 
+ */ + @Test + void reloadEventEnabledMonitoringConfigMapsDisabledMonitoringSecretsEnabled() { + setup("spring.cloud.kubernetes.reload.enabled=true", "spring.main.cloud-platform=KUBERNETES", + "spring.cloud.kubernetes.reload.monitoring-secrets=true", + "spring.cloud.kubernetes.reload.monitoring-configMaps=false", + "spring.cloud.kubernetes.reload.mode=event"); + Map map = context.getBeansOfType(ConfigurationChangeDetector.class); + Assertions.assertEquals(map.size(), 1); + Assertions.assertTrue(map.values().iterator().next().getClass() + .isAssignableFrom(KubernetesClientEventBasedSecretsChangeDetector.class)); + } + + /** + *
+	 *     - reload mode is enabled (via event reload)
+	 *     - spring.cloud.kubernetes.reload.monitoring-secrets is true
+	 *     - spring.cloud.kubernetes.reload.monitoring-configMaps is false
+	 *
+	 *     - config map event watcher is not picked up
+	 *     - config map polling watcher is not picked up
+	 *     - secrets event watcher is not picked up
+	 *     - secrets polling watcher is picked up
+	 * 
+ */ + @Test + void reloadPollingEnabledMonitoringConfigMapsDisabledMonitoringSecretsEnabled() { + setup("spring.cloud.kubernetes.reload.enabled=true", "spring.main.cloud-platform=KUBERNETES", + "spring.cloud.kubernetes.reload.monitoring-secrets=true", + "spring.cloud.kubernetes.reload.monitoring-configMaps=false", + "spring.cloud.kubernetes.reload.mode=polling"); + Map map = context.getBeansOfType(ConfigurationChangeDetector.class); + Assertions.assertEquals(map.size(), 1); + Assertions.assertTrue( + map.values().iterator().next().getClass().isAssignableFrom(PollingSecretsChangeDetector.class)); + } + + /** + *
+	 *     - reload mode is enabled (via event reload)
+	 *     - spring.cloud.kubernetes.reload.monitoring-secrets is true
+	 *     - spring.cloud.kubernetes.reload.monitoring-configMaps is true
+	 *
+	 *     - config map event watcher is picked up
+	 *     - config map polling watcher is not picked up
+	 *     - secrets event watcher is picked up
+	 *     - secrets polling watcher is not picked up
+	 * 
+ */ + @Test + void reloadEventEnabledMonitoringConfigMapsEnabledMonitoringSecretsEnabled() { + setup("spring.cloud.kubernetes.reload.enabled=true", "spring.main.cloud-platform=KUBERNETES", + "spring.cloud.kubernetes.reload.monitoring-secrets=true", + "spring.cloud.kubernetes.reload.monitoring-configMaps=true", + "spring.cloud.kubernetes.reload.mode=event"); + Map map = context.getBeansOfType(ConfigurationChangeDetector.class); + Assertions.assertEquals(map.size(), 2); + List result = map.values().stream() + .sorted(Comparator.comparing(x -> x.getClass().getName())).toList(); + Assertions.assertEquals(result.get(0).getClass(), KubernetesClientEventBasedConfigMapChangeDetector.class); + Assertions.assertEquals(result.get(1).getClass(), KubernetesClientEventBasedSecretsChangeDetector.class); + } + + /** + *
+	 *     - reload mode is enabled (via event reload)
+	 *     - spring.cloud.kubernetes.reload.monitoring-secrets is true
+	 *     - spring.cloud.kubernetes.reload.monitoring-configMaps is true
+	 *
+	 *     - config map event watcher is not picked up
+	 *     - config map polling watcher is picked up
+	 *     - secrets event watcher is not picked up
+	 *     - secrets polling watcher is picked up
+	 * 
+ */ + @Test + void reloadPollingEnabledMonitoringConfigMapsEnabledMonitoringSecretsEnabled() { + setup("spring.cloud.kubernetes.reload.enabled=true", "spring.main.cloud-platform=KUBERNETES", + "spring.cloud.kubernetes.reload.monitoring-secrets=true", + "spring.cloud.kubernetes.reload.monitoring-configMaps=true", + "spring.cloud.kubernetes.reload.mode=polling"); + Map map = context.getBeansOfType(ConfigurationChangeDetector.class); + Assertions.assertEquals(map.size(), 2); + List result = map.values().stream() + .sorted(Comparator.comparing(x -> x.getClass().getName())).toList(); + Assertions.assertEquals(result.get(0).getClass(), PollingConfigMapChangeDetector.class); + Assertions.assertEquals(result.get(1).getClass(), PollingSecretsChangeDetector.class); + } + + /** + *
+	 *     - reload mode is enabled (via event reload)
+	 *     - spring.cloud.kubernetes.reload.monitoring-configMaps is false
+	 *     - spring.cloud.kubernetes.reload.monitoring-secrets is false
+	 *
+	 *     - config map event watcher is not picked up
+	 *     - config map polling watcher is not picked up
+	 *     - secrets event watcher is not picked up
+	 *     - secrets polling watcher is not picked up
+	 * 
+ */ + @Test + void reloadEventEnabledMonitoringConfigMapsDisabledMonitoringSecretsDisabled() { + setup("spring.cloud.kubernetes.reload.enabled=true", "spring.cloud.kubernetes.reload.mode=event", + "spring.main.cloud-platform=KUBERNETES", "spring.cloud.kubernetes.reload.monitoring-configMaps=false", + "spring.cloud.kubernetes.reload.monitoring-secrets=false"); + Map map = context.getBeansOfType(ConfigurationChangeDetector.class); + Assertions.assertEquals(map.size(), 0); + } + + /** + *
+	 *     - reload mode is enabled (via poll reload)
+	 *     - spring.cloud.kubernetes.reload.monitoring-configMaps is false
+	 *     - spring.cloud.kubernetes.reload.monitoring-secrets is false
+	 *
+	 *     - config map event watcher is not picked up
+	 *     - config map polling watcher is not picked up
+	 *     - secrets event watcher is not picked up
+	 *     - secrets polling watcher is not picked up
+	 * 
+ */ + @Test + void reloadPollingEnabledMonitorConfigMapsDisabledMonitoringSecretsDisabled() { + setup("spring.cloud.kubernetes.reload.enabled=true", "spring.cloud.kubernetes.reload.mode=polling", + "spring.main.cloud-platform=KUBERNETES", "spring.cloud.kubernetes.reload.monitoring-configMaps=false", + "spring.cloud.kubernetes.reload.monitoring-secrets=false"); + Map map = context.getBeansOfType(ConfigurationChangeDetector.class); + Assertions.assertEquals(map.size(), 0); + } + + /** + *
+	 *     - reload mode is enabled (via event reload)
+	 *     - spring.cloud.kubernetes.reload.monitoring-configMaps is true
+	 *     - spring.cloud.kubernetes.reload.monitoring-secrets is false
+	 *
+	 *     - config map event watcher is picked up
+	 *     - config map polling watcher is not picked up
+	 *     - secrets event watcher is not picked up
+	 *     - secrets polling watcher is not picked up
+	 * 
+ */ + @Test + void reloadEventEnabledMonitoringConfigMapsEnabledMonitoringSecretsDisabled() { + setup("spring.cloud.kubernetes.reload.enabled=true", "spring.cloud.kubernetes.reload.mode=event", + "spring.main.cloud-platform=KUBERNETES", "spring.cloud.kubernetes.reload.monitoring-configMaps=true", + "spring.cloud.kubernetes.reload.monitoring-secrets=false"); + Map map = context.getBeansOfType(ConfigurationChangeDetector.class); + Assertions.assertEquals(map.size(), 1); + List result = map.values().stream() + .sorted(Comparator.comparing(x -> x.getClass().getName())).toList(); + Assertions.assertEquals(result.get(0).getClass(), KubernetesClientEventBasedConfigMapChangeDetector.class); + } + + /** + *
+	 *     - reload mode is enabled (via polling reload)
+	 *     - spring.cloud.kubernetes.reload.monitoring-configMaps is true
+	 *     - spring.cloud.kubernetes.reload.monitoring-secrets is false
+	 *
+	 *     - config map event watcher is not picked up
+	 *     - config map polling watcher is picked up
+	 *     - secrets event watcher is not picked up
+	 *     - secrets polling watcher is not picked up
+	 * 
+ */ + @Test + void reloadPollingEnabledMonitoringConfigMapsEnabledMonitoringSecretsDisabled() { + setup("spring.cloud.kubernetes.reload.enabled=true", "spring.cloud.kubernetes.reload.mode=polling", + "spring.main.cloud-platform=KUBERNETES", "spring.cloud.kubernetes.reload.monitoring-configMaps=true", + "spring.cloud.kubernetes.reload.monitoring-secrets=false"); + Map map = context.getBeansOfType(ConfigurationChangeDetector.class); + Assertions.assertEquals(map.size(), 1); + List result = map.values().stream() + .sorted(Comparator.comparing(x -> x.getClass().getName())).toList(); + Assertions.assertEquals(result.get(0).getClass(), PollingConfigMapChangeDetector.class); } // 2. config and secrets property source locators @Test - void kubernetesConfigAndSecretEnabledByDefault() throws Exception { + void kubernetesConfigAndSecretEnabledByDefault() { setup("spring.main.cloud-platform=KUBERNETES"); assertThat(context.containsBean("configMapPropertySourceLocator")).isTrue(); assertThat(context.containsBean("secretsPropertySourceLocator")).isTrue(); } @Test - void kubernetesConfigEnabledButSecretDisabled() throws Exception { + void kubernetesConfigEnabledButSecretDisabled() { setup("spring.cloud.kubernetes.secrets.enabled=false", "spring.main.cloud-platform=KUBERNETES"); assertThat(context.containsBean("configMapPropertySourceLocator")).isTrue(); assertThat(context.containsBean("secretsPropertySourceLocator")).isFalse(); } @Test - void kubernetesSecretsEnabledButConfigDisabled() throws Exception { + void kubernetesSecretsEnabledButConfigDisabled() { setup("spring.cloud.kubernetes.config.enabled=false", "spring.main.cloud-platform=KUBERNETES"); assertThat(context.containsBean("configMapPropertySourceLocator")).isFalse(); assertThat(context.containsBean("secretsPropertySourceLocator")).isTrue(); diff --git a/spring-cloud-kubernetes-commons/src/main/java/org/springframework/cloud/kubernetes/commons/config/reload/ConfigReloadPropertiesAutoConfiguration.java b/spring-cloud-kubernetes-commons/src/main/java/org/springframework/cloud/kubernetes/commons/config/reload/ConfigReloadPropertiesAutoConfiguration.java new file mode 100644 index 00000000..5f51e15c --- /dev/null +++ b/spring-cloud-kubernetes-commons/src/main/java/org/springframework/cloud/kubernetes/commons/config/reload/ConfigReloadPropertiesAutoConfiguration.java @@ -0,0 +1,30 @@ +/* + * Copyright 2013-2023 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.cloud.kubernetes.commons.config.reload; + +import org.springframework.boot.autoconfigure.condition.ConditionalOnCloudPlatform; +import org.springframework.boot.cloud.CloudPlatform; +import org.springframework.boot.context.properties.EnableConfigurationProperties; + +/** + * @author wind57 + */ +@ConditionalOnCloudPlatform(CloudPlatform.KUBERNETES) +@EnableConfigurationProperties(ConfigReloadProperties.class) +public class ConfigReloadPropertiesAutoConfiguration { + +} diff --git a/spring-cloud-kubernetes-commons/src/main/java/org/springframework/cloud/kubernetes/commons/config/reload/condition/ConditionalOnConfigMapsReloadEnabled.java b/spring-cloud-kubernetes-commons/src/main/java/org/springframework/cloud/kubernetes/commons/config/reload/condition/ConditionalOnConfigMapsReloadEnabled.java new file mode 100644 index 00000000..3eefcc82 --- /dev/null +++ b/spring-cloud-kubernetes-commons/src/main/java/org/springframework/cloud/kubernetes/commons/config/reload/condition/ConditionalOnConfigMapsReloadEnabled.java @@ -0,0 +1,42 @@ +/* + * Copyright 2013-2023 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.cloud.kubernetes.commons.config.reload.condition; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Inherited; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; + +/** + * Provides a more succinct conditional for: + * spring.cloud.kubernetes.reload.monitoring-configMaps. + * + * @author wind57 + */ +@Target({ ElementType.TYPE, ElementType.METHOD }) +@Retention(RetentionPolicy.RUNTIME) +@Documented +@Inherited +@ConditionalOnProperty(name = "spring.cloud.kubernetes.reload.monitoring-configMaps", havingValue = "true", + matchIfMissing = true) +public @interface ConditionalOnConfigMapsReloadEnabled { + +} diff --git a/spring-cloud-kubernetes-commons/src/main/java/org/springframework/cloud/kubernetes/commons/config/reload/condition/ConditionalOnSecretsReloadEnabled.java b/spring-cloud-kubernetes-commons/src/main/java/org/springframework/cloud/kubernetes/commons/config/reload/condition/ConditionalOnSecretsReloadEnabled.java new file mode 100644 index 00000000..f90bbefd --- /dev/null +++ b/spring-cloud-kubernetes-commons/src/main/java/org/springframework/cloud/kubernetes/commons/config/reload/condition/ConditionalOnSecretsReloadEnabled.java @@ -0,0 +1,41 @@ +/* + * Copyright 2013-2023 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.cloud.kubernetes.commons.config.reload.condition; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Inherited; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; + +/** + * Provides a more succinct conditional for: + * spring.cloud.kubernetes.reload.monitoring-secrets. + * + * @author wind57 + */ +@Target({ ElementType.TYPE, ElementType.METHOD }) +@Retention(RetentionPolicy.RUNTIME) +@Documented +@Inherited +@ConditionalOnProperty(name = "spring.cloud.kubernetes.reload.monitoring-secrets", havingValue = "true") +public @interface ConditionalOnSecretsReloadEnabled { + +} diff --git a/spring-cloud-kubernetes-commons/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports b/spring-cloud-kubernetes-commons/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports index 903e1a2c..142075c3 100644 --- a/spring-cloud-kubernetes-commons/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports +++ b/spring-cloud-kubernetes-commons/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports @@ -1,2 +1,3 @@ org.springframework.cloud.kubernetes.commons.KubernetesCommonsAutoConfiguration org.springframework.cloud.kubernetes.commons.config.reload.ConfigReloadAutoConfiguration +org.springframework.cloud.kubernetes.commons.config.reload.ConfigReloadPropertiesAutoConfiguration diff --git a/spring-cloud-kubernetes-fabric8-config/src/main/java/org/springframework/cloud/kubernetes/fabric8/config/reload/Fabric8ConfigReloadAutoConfiguration.java b/spring-cloud-kubernetes-fabric8-config/src/main/java/org/springframework/cloud/kubernetes/fabric8/config/reload/Fabric8ConfigReloadAutoConfiguration.java index af97144f..7f95cc03 100644 --- a/spring-cloud-kubernetes-fabric8-config/src/main/java/org/springframework/cloud/kubernetes/fabric8/config/reload/Fabric8ConfigReloadAutoConfiguration.java +++ b/spring-cloud-kubernetes-fabric8-config/src/main/java/org/springframework/cloud/kubernetes/fabric8/config/reload/Fabric8ConfigReloadAutoConfiguration.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2022 the original author or authors. + * Copyright 2013-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -23,21 +23,24 @@ import org.springframework.boot.actuate.autoconfigure.info.InfoEndpointAutoConfi import org.springframework.boot.autoconfigure.AutoConfigureAfter; import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; -import org.springframework.boot.context.properties.EnableConfigurationProperties; +import org.springframework.boot.autoconfigure.condition.ConditionalOnCloudPlatform; +import org.springframework.boot.cloud.CloudPlatform; import org.springframework.cloud.autoconfigure.RefreshAutoConfiguration; import org.springframework.cloud.autoconfigure.RefreshEndpointAutoConfiguration; import org.springframework.cloud.commons.util.TaskSchedulerWrapper; import org.springframework.cloud.context.refresh.ContextRefresher; import org.springframework.cloud.context.restart.RestartEndpoint; import org.springframework.cloud.kubernetes.commons.KubernetesNamespaceProvider; -import org.springframework.cloud.kubernetes.commons.config.ConditionalOnKubernetesAndConfigEnabled; import org.springframework.cloud.kubernetes.commons.config.reload.ConfigReloadAutoConfiguration; import org.springframework.cloud.kubernetes.commons.config.reload.ConfigReloadProperties; +import org.springframework.cloud.kubernetes.commons.config.reload.ConfigReloadPropertiesAutoConfiguration; import org.springframework.cloud.kubernetes.commons.config.reload.ConfigurationChangeDetector; import org.springframework.cloud.kubernetes.commons.config.reload.ConfigurationUpdateStrategy; import org.springframework.cloud.kubernetes.commons.config.reload.PollingConfigMapChangeDetector; import org.springframework.cloud.kubernetes.commons.config.reload.PollingSecretsChangeDetector; +import org.springframework.cloud.kubernetes.commons.config.reload.condition.ConditionalOnConfigMapsReloadEnabled; import org.springframework.cloud.kubernetes.commons.config.reload.condition.ConditionalOnKubernetesReloadEnabled; +import org.springframework.cloud.kubernetes.commons.config.reload.condition.ConditionalOnSecretsReloadEnabled; import org.springframework.cloud.kubernetes.commons.config.reload.condition.EventReloadDetectionMode; import org.springframework.cloud.kubernetes.commons.config.reload.condition.PollingReloadDetectionMode; import org.springframework.cloud.kubernetes.fabric8.config.Fabric8ConfigMapPropertySource; @@ -58,12 +61,11 @@ import org.springframework.scheduling.TaskScheduler; * @author Kris Iyer */ @Configuration(proxyBeanMethods = false) -@ConditionalOnKubernetesAndConfigEnabled +@ConditionalOnCloudPlatform(CloudPlatform.KUBERNETES) @ConditionalOnKubernetesReloadEnabled @ConditionalOnClass({ EndpointAutoConfiguration.class, RestartEndpoint.class, ContextRefresher.class }) @AutoConfigureAfter({ InfoEndpointAutoConfiguration.class, RefreshEndpointAutoConfiguration.class, - RefreshAutoConfiguration.class }) -@EnableConfigurationProperties(ConfigReloadProperties.class) + RefreshAutoConfiguration.class, ConfigReloadPropertiesAutoConfiguration.class }) @Import(ConfigReloadAutoConfiguration.class) public class Fabric8ConfigReloadAutoConfiguration { @@ -75,6 +77,7 @@ public class Fabric8ConfigReloadAutoConfiguration { * @return a bean that listen to configuration changes and fire a reload. */ @Bean + @ConditionalOnConfigMapsReloadEnabled @ConditionalOnBean(Fabric8ConfigMapPropertySourceLocator.class) @Conditional(PollingReloadDetectionMode.class) public ConfigurationChangeDetector configMapPropertyChangePollingWatcher(ConfigReloadProperties properties, @@ -95,6 +98,7 @@ public class Fabric8ConfigReloadAutoConfiguration { * @return a bean that listen to configuration changes and fire a reload. */ @Bean + @ConditionalOnSecretsReloadEnabled @ConditionalOnBean(Fabric8SecretsPropertySourceLocator.class) @Conditional(PollingReloadDetectionMode.class) public ConfigurationChangeDetector secretsPropertyChangePollingWatcher(ConfigReloadProperties properties, @@ -114,6 +118,7 @@ public class Fabric8ConfigReloadAutoConfiguration { * @return a bean that listen to configMap change events and fire a reload. */ @Bean + @ConditionalOnConfigMapsReloadEnabled @ConditionalOnBean(Fabric8ConfigMapPropertySourceLocator.class) @Conditional(EventReloadDetectionMode.class) public ConfigurationChangeDetector configMapPropertyChangeEventWatcher(ConfigReloadProperties properties, @@ -133,6 +138,7 @@ public class Fabric8ConfigReloadAutoConfiguration { * @return a bean that listen to secrets change events and fire a reload. */ @Bean + @ConditionalOnSecretsReloadEnabled @ConditionalOnBean(Fabric8SecretsPropertySourceLocator.class) @Conditional(EventReloadDetectionMode.class) public ConfigurationChangeDetector secretsPropertyChangeEventWatcher(ConfigReloadProperties properties, diff --git a/spring-cloud-kubernetes-fabric8-config/src/main/java/org/springframework/cloud/kubernetes/fabric8/config/reload/Fabric8EventBasedConfigMapChangeDetector.java b/spring-cloud-kubernetes-fabric8-config/src/main/java/org/springframework/cloud/kubernetes/fabric8/config/reload/Fabric8EventBasedConfigMapChangeDetector.java index 0904ff4d..f8a98fdc 100644 --- a/spring-cloud-kubernetes-fabric8-config/src/main/java/org/springframework/cloud/kubernetes/fabric8/config/reload/Fabric8EventBasedConfigMapChangeDetector.java +++ b/spring-cloud-kubernetes-fabric8-config/src/main/java/org/springframework/cloud/kubernetes/fabric8/config/reload/Fabric8EventBasedConfigMapChangeDetector.java @@ -58,8 +58,6 @@ public class Fabric8EventBasedConfigMapChangeDetector extends ConfigurationChang private final KubernetesClient kubernetesClient; - private final boolean monitoringConfigMaps; - private final List> informers = new ArrayList<>(); private final Set namespaces; @@ -74,31 +72,28 @@ public class Fabric8EventBasedConfigMapChangeDetector extends ConfigurationChang this.kubernetesClient = kubernetesClient; this.fabric8ConfigMapPropertySourceLocator = fabric8ConfigMapPropertySourceLocator; this.enableReloadFiltering = properties.enableReloadFiltering(); - monitoringConfigMaps = properties.monitoringConfigMaps(); namespaces = namespaces(kubernetesClient, namespaceProvider, properties, "configmap"); } @PostConstruct private void inform() { - if (monitoringConfigMaps) { - LOG.info("Kubernetes event-based configMap change detector activated"); + LOG.info("Kubernetes event-based configMap change detector activated"); - namespaces.forEach(namespace -> { - SharedIndexInformer informer; - if (enableReloadFiltering) { - informer = kubernetesClient.configMaps().inNamespace(namespace) - .withLabels(Map.of(ConfigReloadProperties.RELOAD_LABEL_FILTER, "true")).inform(); - LOG.debug("added configmap informer for namespace : " + namespace + " with enabled filter"); - } - else { - informer = kubernetesClient.configMaps().inNamespace(namespace).inform(); - LOG.debug("added configmap informer for namespace : " + namespace); - } + namespaces.forEach(namespace -> { + SharedIndexInformer informer; + if (enableReloadFiltering) { + informer = kubernetesClient.configMaps().inNamespace(namespace) + .withLabels(Map.of(ConfigReloadProperties.RELOAD_LABEL_FILTER, "true")).inform(); + LOG.debug("added configmap informer for namespace : " + namespace + " with enabled filter"); + } + else { + informer = kubernetesClient.configMaps().inNamespace(namespace).inform(); + LOG.debug("added configmap informer for namespace : " + namespace); + } - informer.addEventHandler(new ConfigMapInformerAwareEventHandler(informer)); - informers.add(informer); - }); - } + informer.addEventHandler(new ConfigMapInformerAwareEventHandler(informer)); + informers.add(informer); + }); } @PreDestroy diff --git a/spring-cloud-kubernetes-fabric8-config/src/main/java/org/springframework/cloud/kubernetes/fabric8/config/reload/Fabric8EventBasedSecretsChangeDetector.java b/spring-cloud-kubernetes-fabric8-config/src/main/java/org/springframework/cloud/kubernetes/fabric8/config/reload/Fabric8EventBasedSecretsChangeDetector.java index 664cebfa..dca6e192 100644 --- a/spring-cloud-kubernetes-fabric8-config/src/main/java/org/springframework/cloud/kubernetes/fabric8/config/reload/Fabric8EventBasedSecretsChangeDetector.java +++ b/spring-cloud-kubernetes-fabric8-config/src/main/java/org/springframework/cloud/kubernetes/fabric8/config/reload/Fabric8EventBasedSecretsChangeDetector.java @@ -58,8 +58,6 @@ public class Fabric8EventBasedSecretsChangeDetector extends ConfigurationChangeD private final KubernetesClient kubernetesClient; - private final boolean monitorSecrets; - private final List> informers = new ArrayList<>(); private final Set namespaces; @@ -74,7 +72,6 @@ public class Fabric8EventBasedSecretsChangeDetector extends ConfigurationChangeD this.kubernetesClient = kubernetesClient; this.fabric8SecretsPropertySourceLocator = fabric8SecretsPropertySourceLocator; this.enableReloadFiltering = properties.enableReloadFiltering(); - monitorSecrets = properties.monitoringSecrets(); namespaces = namespaces(kubernetesClient, namespaceProvider, properties, "secrets"); } @@ -88,25 +85,23 @@ public class Fabric8EventBasedSecretsChangeDetector extends ConfigurationChangeD @PostConstruct private void inform() { - if (monitorSecrets) { - LOG.info("Kubernetes event-based secrets change detector activated"); + LOG.info("Kubernetes event-based secrets change detector activated"); - namespaces.forEach(namespace -> { - SharedIndexInformer informer; - if (enableReloadFiltering) { - informer = kubernetesClient.secrets().inNamespace(namespace) - .withLabels(Map.of(ConfigReloadProperties.RELOAD_LABEL_FILTER, "true")).inform(); - LOG.debug("added secret informer for namespace : " + namespace + " with enabled filter"); - } - else { - informer = kubernetesClient.secrets().inNamespace(namespace).inform(); - LOG.debug("added secret informer for namespace : " + namespace); - } + namespaces.forEach(namespace -> { + SharedIndexInformer informer; + if (enableReloadFiltering) { + informer = kubernetesClient.secrets().inNamespace(namespace) + .withLabels(Map.of(ConfigReloadProperties.RELOAD_LABEL_FILTER, "true")).inform(); + LOG.debug("added secret informer for namespace : " + namespace + " with enabled filter"); + } + else { + informer = kubernetesClient.secrets().inNamespace(namespace).inform(); + LOG.debug("added secret informer for namespace : " + namespace); + } - informer.addEventHandler(new SecretInformerAwareEventHandler(informer)); - informers.add(informer); - }); - } + informer.addEventHandler(new SecretInformerAwareEventHandler(informer)); + informers.add(informer); + }); } protected void onEvent(Secret secret) { diff --git a/spring-cloud-kubernetes-fabric8-config/src/test/java/org/springframework/cloud/kubernetes/fabric8/config/KubernetesConfigTestBase.java b/spring-cloud-kubernetes-fabric8-config/src/test/java/org/springframework/cloud/kubernetes/fabric8/config/KubernetesConfigTestBase.java index 462d8b44..b92043a0 100644 --- a/spring-cloud-kubernetes-fabric8-config/src/test/java/org/springframework/cloud/kubernetes/fabric8/config/KubernetesConfigTestBase.java +++ b/spring-cloud-kubernetes-fabric8-config/src/test/java/org/springframework/cloud/kubernetes/fabric8/config/KubernetesConfigTestBase.java @@ -21,10 +21,12 @@ import java.util.stream.Stream; import org.junit.jupiter.api.AfterEach; +import org.springframework.boot.WebApplicationType; import org.springframework.boot.autoconfigure.context.PropertyPlaceholderAutoConfiguration; import org.springframework.boot.builder.SpringApplicationBuilder; import org.springframework.cloud.autoconfigure.RefreshAutoConfiguration; import org.springframework.cloud.bootstrap.BootstrapConfiguration; +import org.springframework.cloud.kubernetes.commons.config.reload.ConfigReloadPropertiesAutoConfiguration; import org.springframework.cloud.kubernetes.fabric8.config.reload.Fabric8ConfigReloadAutoConfiguration; import org.springframework.context.ConfigurableApplicationContext; @@ -44,8 +46,8 @@ public class KubernetesConfigTestBase { protected void setup(Class mockClientConfiguration, String... env) { String[] properties = Stream.concat(Arrays.stream(commonProperties), Arrays.stream(env)).toArray(String[]::new); context = new SpringApplicationBuilder(PropertyPlaceholderAutoConfiguration.class, mockClientConfiguration, - BootstrapConfiguration.class, Fabric8ConfigReloadAutoConfiguration.class, - RefreshAutoConfiguration.class).web(org.springframework.boot.WebApplicationType.NONE) + BootstrapConfiguration.class, Fabric8ConfigReloadAutoConfiguration.class, ConfigReloadPropertiesAutoConfiguration.class, + RefreshAutoConfiguration.class).web(WebApplicationType.NONE) .properties(properties).run(); } diff --git a/spring-cloud-kubernetes-fabric8-config/src/test/java/org/springframework/cloud/kubernetes/fabric8/config/reload/ConfigReloadAutoConfigurationTest.java b/spring-cloud-kubernetes-fabric8-config/src/test/java/org/springframework/cloud/kubernetes/fabric8/config/reload/ConfigReloadAutoConfigurationTest.java index fc2c4bba..31b89817 100644 --- a/spring-cloud-kubernetes-fabric8-config/src/test/java/org/springframework/cloud/kubernetes/fabric8/config/reload/ConfigReloadAutoConfigurationTest.java +++ b/spring-cloud-kubernetes-fabric8-config/src/test/java/org/springframework/cloud/kubernetes/fabric8/config/reload/ConfigReloadAutoConfigurationTest.java @@ -16,18 +16,25 @@ package org.springframework.cloud.kubernetes.fabric8.config.reload; +import java.util.Comparator; import java.util.HashMap; +import java.util.List; +import java.util.Map; import io.fabric8.kubernetes.api.model.ConfigMap; import io.fabric8.kubernetes.api.model.ConfigMapBuilder; import io.fabric8.kubernetes.client.Config; import io.fabric8.kubernetes.client.KubernetesClient; import io.fabric8.kubernetes.client.server.mock.EnableKubernetesMockClient; +import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; +import org.springframework.cloud.kubernetes.commons.config.reload.ConfigurationChangeDetector; +import org.springframework.cloud.kubernetes.commons.config.reload.PollingConfigMapChangeDetector; +import org.springframework.cloud.kubernetes.commons.config.reload.PollingSecretsChangeDetector; import org.springframework.cloud.kubernetes.fabric8.config.KubernetesConfigTestBase; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @@ -41,7 +48,7 @@ import static org.assertj.core.api.Assertions.assertThat; * detector and update strategy beans won't be available. **/ @EnableKubernetesMockClient(crud = true, https = false) -public class ConfigReloadAutoConfigurationTest extends KubernetesConfigTestBase { +class ConfigReloadAutoConfigurationTest extends KubernetesConfigTestBase { private static final String APPLICATION_NAME = "application"; @@ -53,7 +60,7 @@ public class ConfigReloadAutoConfigurationTest extends KubernetesConfigTestBase private static KubernetesClient mockClient; @BeforeAll - public static void setUpBeforeClass() { + static void setUpBeforeClass() { // Configure the kubernetes master url to point to the mock server System.setProperty(Config.KUBERNETES_MASTER_SYSTEM_PROPERTY, mockClient.getConfiguration().getMasterUrl()); @@ -76,44 +83,330 @@ public class ConfigReloadAutoConfigurationTest extends KubernetesConfigTestBase } @BeforeEach - public void beforeEach() { + void beforeEach() { commonProperties = new String[] { "spring.cloud.bootstrap.enabled=true" }; } @Test - public void kubernetesConfigReloadDisabled() { + void kubernetesConfigReloadDisabled() { setup(KubernetesClientTestConfiguration.class, "spring.cloud.kubernetes.reload.enabled=false"); assertThat(this.getContext().containsBean("configurationChangeDetector")).isFalse(); assertThat(this.getContext().containsBean("configurationUpdateStrategy")).isFalse(); } @Test - public void kubernetesConfigReloadWhenKubernetesConfigDisabled() { + void kubernetesConfigReloadWhenKubernetesConfigDisabled() { setup(KubernetesClientTestConfiguration.class, "spring.cloud.kubernetes.config.enabled=false"); assertThat(this.getContext().containsBean("configurationChangeDetector")).isFalse(); assertThat(this.getContext().containsBean("configurationUpdateStrategy")).isFalse(); } @Test - public void kubernetesConfigReloadWhenKubernetesDisabled() { + void kubernetesConfigReloadWhenKubernetesDisabled() { setup(KubernetesClientTestConfiguration.class); assertThat(this.getContext().containsBean("configurationChangeDetector")).isFalse(); assertThat(this.getContext().containsBean("configurationUpdateStrategy")).isFalse(); } + /** + *
+	 *     - reload mode is enabled (via event reload)
+	 *     - spring.cloud.kubernetes.reload.monitoring-configMaps is true by default
+	 *
+	 *     - config map event watcher is picked up
+	 *     - config map polling watcher is not picked up
+	 *     - secrets event watcher is not picked up
+	 *     - secrets polling watcher is not picked up
+	 * 
+ */ @Test - public void kubernetesReloadEnabled() { - setup(KubernetesClientTestConfiguration.class, "spring.main.cloud-platform=KUBERNETES", - "spring.cloud.kubernetes.config.enabled=true", "spring.cloud.kubernetes.secrets.enabled=true", - "spring.cloud.kubernetes.reload.enabled=true"); - assertThat(this.getContext().containsBean("configMapPropertySourceLocator")).isTrue(); - assertThat(this.getContext().containsBean("secretsPropertySourceLocator")).isTrue(); - assertThat(this.getContext().containsBean("configMapPropertyChangeEventWatcher")).isTrue(); - assertThat(this.getContext().containsBean("secretsPropertyChangeEventWatcher")).isTrue(); + void reloadEventEnabledMonitoringConfigMapsEnabledByDefault() { + setup(KubernetesClientTestConfiguration.class, "spring.cloud.kubernetes.reload.enabled=true", + "spring.main.cloud-platform=KUBERNETES"); + Map map = getContext().getBeansOfType(ConfigurationChangeDetector.class); + Assertions.assertEquals(map.size(), 1); + Assertions.assertTrue(map.values().iterator().next().getClass() + .isAssignableFrom(Fabric8EventBasedConfigMapChangeDetector.class)); + } + + /** + *
+	 *     - reload mode is enabled (via event reload)
+	 *     - spring.cloud.kubernetes.reload.monitoring-configMaps is true by default
+	 *
+	 *     - config map event watcher is picked up
+	 *     - config map polling watcher is not picked up
+	 *     - secrets event watcher is not picked up
+	 *     - secrets polling watcher is not picked up
+	 * 
+ */ + @Test + void reloadEventEnabledMonitoringConfigMapsEnabled() { + setup(KubernetesClientTestConfiguration.class, "spring.cloud.kubernetes.reload.enabled=true", + "spring.cloud.kubernetes.reload.mode=event", "spring.main.cloud-platform=KUBERNETES"); + Map map = getContext().getBeansOfType(ConfigurationChangeDetector.class); + Assertions.assertEquals(map.size(), 1); + Assertions.assertTrue(map.values().iterator().next().getClass() + .isAssignableFrom(Fabric8EventBasedConfigMapChangeDetector.class)); + } + + /** + *
+	 *     - reload mode is enabled (via event reload)
+	 *     - spring.cloud.kubernetes.reload.monitoring-configMaps is false
+	 *
+	 *     - config map event watcher is not picked up
+	 *     - config map polling watcher is not picked up
+	 *     - secrets event watcher is not picked up
+	 *     - secrets polling watcher is not picked up
+	 * 
+ */ + @Test + void reloadEventEnabledMonitoringConfigMapsDisabled() { + setup(KubernetesClientTestConfiguration.class, "spring.cloud.kubernetes.reload.enabled=true", + "spring.cloud.kubernetes.reload.mode=event", "spring.main.cloud-platform=KUBERNETES", + "spring.cloud.kubernetes.reload.monitoring-configMaps=false"); + Map map = getContext().getBeansOfType(ConfigurationChangeDetector.class); + Assertions.assertEquals(map.size(), 0); + } + + /** + *
+	 *     - reload mode is enabled (via poll reload)
+	 *     - spring.cloud.kubernetes.reload.monitoring-configMaps is false
+	 *
+	 *     - config map event watcher is not picked up
+	 *     - config map polling watcher is not picked up
+	 *     - secrets event watcher is not picked up
+	 *     - secrets polling watcher is not picked up
+	 * 
+ */ + @Test + void reloadPollingEnabledMonitoringConfigMapsDisabledMonitoringSecretsDisabled() { + setup(KubernetesClientTestConfiguration.class, "spring.cloud.kubernetes.reload.enabled=true", + "spring.cloud.kubernetes.reload.mode=polling", "spring.main.cloud-platform=KUBERNETES", + "spring.cloud.kubernetes.reload.monitoring-configMaps=false"); + Map map = getContext().getBeansOfType(ConfigurationChangeDetector.class); + Assertions.assertEquals(map.size(), 0); + } + + /** + *
+	 *     - reload mode is enabled (via poll reload)
+	 *     - spring.cloud.kubernetes.reload.monitoring-configMaps is true by default
+	 *
+	 *     - config map event watcher is not picked up
+	 *     - config map polling watcher is not picked up
+	 *     - secrets event watcher is not picked up
+	 *     - config map polling watcher is not picked up
+	 * 
+ */ + @Test + void reloadPollingEnabledMonitoringConfigMapsEnabled() { + setup(KubernetesClientTestConfiguration.class, "spring.cloud.kubernetes.reload.enabled=true", + "spring.cloud.kubernetes.reload.mode=polling", "spring.main.cloud-platform=KUBERNETES"); + Map map = getContext().getBeansOfType(ConfigurationChangeDetector.class); + Assertions.assertEquals(map.size(), 1); + Assertions.assertTrue( + map.values().iterator().next().getClass().isAssignableFrom(PollingConfigMapChangeDetector.class)); + } + + /** + *
+	 *     - reload mode is enabled (via event reload)
+	 *     - spring.cloud.kubernetes.reload.monitoring-secrets is true
+	 *     - spring.cloud.kubernetes.reload.monitoring-configMaps is false
+	 *
+	 *     - config map event watcher is not picked up
+	 *     - config map polling watcher is not picked up
+	 *     - secrets event watcher is picked up
+	 *     - secrets polling watcher is not picked up
+	 * 
+ */ + @Test + void reloadEventEnabledMonitoringConfigMapsDisabledMonitoringSecretsEnabled() { + setup(KubernetesClientTestConfiguration.class, "spring.cloud.kubernetes.reload.enabled=true", + "spring.main.cloud-platform=KUBERNETES", "spring.cloud.kubernetes.reload.monitoring-secrets=true", + "spring.cloud.kubernetes.reload.monitoring-configMaps=false", + "spring.cloud.kubernetes.reload.mode=event"); + Map map = getContext().getBeansOfType(ConfigurationChangeDetector.class); + Assertions.assertEquals(map.size(), 1); + Assertions.assertTrue(map.values().iterator().next().getClass() + .isAssignableFrom(Fabric8EventBasedSecretsChangeDetector.class)); + } + + /** + *
+	 *     - reload mode is enabled (via event reload)
+	 *     - spring.cloud.kubernetes.reload.monitoring-secrets is true
+	 *     - spring.cloud.kubernetes.reload.monitoring-configMaps is false
+	 *
+	 *     - config map event watcher is not picked up
+	 *     - config map polling watcher is not picked up
+	 *     - secrets event watcher is not picked up
+	 *     - secrets polling watcher is picked up
+	 * 
+ */ + @Test + void reloadPollingEnabledMonitoringConfigMapsDisabledMonitoringSecretsEnabled() { + setup(KubernetesClientTestConfiguration.class, "spring.cloud.kubernetes.reload.enabled=true", + "spring.main.cloud-platform=KUBERNETES", "spring.cloud.kubernetes.reload.monitoring-secrets=true", + "spring.cloud.kubernetes.reload.monitoring-configMaps=false", + "spring.cloud.kubernetes.reload.mode=polling"); + Map map = getContext().getBeansOfType(ConfigurationChangeDetector.class); + Assertions.assertEquals(map.size(), 1); + Assertions.assertTrue( + map.values().iterator().next().getClass().isAssignableFrom(PollingSecretsChangeDetector.class)); + } + + /** + *
+	 *     - reload mode is enabled (via event reload)
+	 *     - spring.cloud.kubernetes.reload.monitoring-secrets is true
+	 *     - spring.cloud.kubernetes.reload.monitoring-configMaps is true
+	 *
+	 *     - config map event watcher is picked up
+	 *     - config map polling watcher is not picked up
+	 *     - secrets event watcher is picked up
+	 *     - secrets polling watcher is not picked up
+	 * 
+ */ + @Test + void reloadEventEnabledMonitoringConfigMapsEnabledMonitoringSecretsEnabled() { + setup(KubernetesClientTestConfiguration.class, "spring.cloud.kubernetes.reload.enabled=true", + "spring.main.cloud-platform=KUBERNETES", "spring.cloud.kubernetes.reload.monitoring-secrets=true", + "spring.cloud.kubernetes.reload.monitoring-configMaps=true", + "spring.cloud.kubernetes.reload.mode=event"); + Map map = getContext().getBeansOfType(ConfigurationChangeDetector.class); + Assertions.assertEquals(map.size(), 2); + List result = map.values().stream() + .sorted(Comparator.comparing(x -> x.getClass().getName())).toList(); + Assertions.assertEquals(result.get(0).getClass(), Fabric8EventBasedConfigMapChangeDetector.class); + Assertions.assertEquals(result.get(1).getClass(), Fabric8EventBasedSecretsChangeDetector.class); + } + + /** + *
+	 *     - reload mode is enabled (via event reload)
+	 *     - spring.cloud.kubernetes.reload.monitoring-secrets is true
+	 *     - spring.cloud.kubernetes.reload.monitoring-configMaps is true
+	 *
+	 *     - config map event watcher is not picked up
+	 *     - config map polling watcher is picked up
+	 *     - secrets event watcher is not picked up
+	 *     - secrets polling watcher is picked up
+	 * 
+ */ + @Test + void reloadPollingEnabledMonitoringConfigMapsEnabledMonitoringSecretsEnabled() { + setup(KubernetesClientTestConfiguration.class, "spring.cloud.kubernetes.reload.enabled=true", + "spring.main.cloud-platform=KUBERNETES", "spring.cloud.kubernetes.reload.monitoring-secrets=true", + "spring.cloud.kubernetes.reload.monitoring-configMaps=true", + "spring.cloud.kubernetes.reload.mode=polling"); + Map map = getContext().getBeansOfType(ConfigurationChangeDetector.class); + Assertions.assertEquals(map.size(), 2); + List result = map.values().stream() + .sorted(Comparator.comparing(x -> x.getClass().getName())).toList(); + Assertions.assertEquals(result.get(0).getClass(), PollingConfigMapChangeDetector.class); + Assertions.assertEquals(result.get(1).getClass(), PollingSecretsChangeDetector.class); + } + + /** + *
+	 *     - reload mode is enabled (via event reload)
+	 *     - spring.cloud.kubernetes.reload.monitoring-configMaps is false
+	 *     - spring.cloud.kubernetes.reload.monitoring-secrets is false
+	 *
+	 *     - config map event watcher is not picked up
+	 *     - config map polling watcher is not picked up
+	 *     - secrets event watcher is not picked up
+	 *     - secrets polling watcher is not picked up
+	 * 
+ */ + @Test + void reloadEventEnabledMonitoringConfigMapsDisabledMonitoringSecretsDisabled() { + setup(KubernetesClientTestConfiguration.class, "spring.cloud.kubernetes.reload.enabled=true", + "spring.cloud.kubernetes.reload.mode=event", "spring.main.cloud-platform=KUBERNETES", + "spring.cloud.kubernetes.reload.monitoring-configMaps=false", + "spring.cloud.kubernetes.reload.monitoring-secrets=false"); + Map map = getContext().getBeansOfType(ConfigurationChangeDetector.class); + Assertions.assertEquals(map.size(), 0); + } + + /** + *
+	 *     - reload mode is enabled (via poll reload)
+	 *     - spring.cloud.kubernetes.reload.monitoring-configMaps is false
+	 *     - spring.cloud.kubernetes.reload.monitoring-secrets is false
+	 *
+	 *     - config map event watcher is not picked up
+	 *     - config map polling watcher is not picked up
+	 *     - secrets event watcher is not picked up
+	 *     - secrets polling watcher is not picked up
+	 * 
+ */ + @Test + void reloadPollingEnabledMonitorConfigMapsDisabledMonitoringSecretsDisabled() { + setup(KubernetesClientTestConfiguration.class, "spring.cloud.kubernetes.reload.enabled=true", + "spring.cloud.kubernetes.reload.mode=polling", "spring.main.cloud-platform=KUBERNETES", + "spring.cloud.kubernetes.reload.monitoring-configMaps=false", + "spring.cloud.kubernetes.reload.monitoring-secrets=false"); + Map map = getContext().getBeansOfType(ConfigurationChangeDetector.class); + Assertions.assertEquals(map.size(), 0); + } + + /** + *
+	 *     - reload mode is enabled (via event reload)
+	 *     - spring.cloud.kubernetes.reload.monitoring-configMaps is true
+	 *     - spring.cloud.kubernetes.reload.monitoring-secrets is false
+	 *
+	 *     - config map event watcher is picked up
+	 *     - config map polling watcher is not picked up
+	 *     - secrets event watcher is not picked up
+	 *     - secrets polling watcher is not picked up
+	 * 
+ */ + @Test + void reloadEventEnabledMonitoringConfigMapsEnabledMonitoringSecretsDisabled() { + setup(KubernetesClientTestConfiguration.class, "spring.cloud.kubernetes.reload.enabled=true", + "spring.cloud.kubernetes.reload.mode=event", "spring.main.cloud-platform=KUBERNETES", + "spring.cloud.kubernetes.reload.monitoring-configMaps=true", + "spring.cloud.kubernetes.reload.monitoring-secrets=false"); + Map map = getContext().getBeansOfType(ConfigurationChangeDetector.class); + Assertions.assertEquals(map.size(), 1); + List result = map.values().stream() + .sorted(Comparator.comparing(x -> x.getClass().getName())).toList(); + Assertions.assertEquals(result.get(0).getClass(), Fabric8EventBasedConfigMapChangeDetector.class); + } + + /** + *
+	 *     - reload mode is enabled (via polling reload)
+	 *     - spring.cloud.kubernetes.reload.monitoring-configMaps is true
+	 *     - spring.cloud.kubernetes.reload.monitoring-secrets is false
+	 *
+	 *     - config map event watcher is not picked up
+	 *     - config map polling watcher is picked up
+	 *     - secrets event watcher is not picked up
+	 *     - secrets polling watcher is not picked up
+	 * 
+ */ + @Test + void reloadPollingEnabledMonitoringConfigMapsEnabledMonitoringSecretsDisabled() { + setup(KubernetesClientTestConfiguration.class, "spring.cloud.kubernetes.reload.enabled=true", + "spring.cloud.kubernetes.reload.mode=polling", "spring.main.cloud-platform=KUBERNETES", + "spring.cloud.kubernetes.reload.monitoring-configMaps=true", + "spring.cloud.kubernetes.reload.monitoring-secrets=false"); + Map map = getContext().getBeansOfType(ConfigurationChangeDetector.class); + Assertions.assertEquals(map.size(), 1); + List result = map.values().stream() + .sorted(Comparator.comparing(x -> x.getClass().getName())).toList(); + Assertions.assertEquals(result.get(0).getClass(), PollingConfigMapChangeDetector.class); } @Test - public void kubernetesReloadEnabledButSecretDisabled() { + void kubernetesReloadEnabledButSecretDisabled() { setup(KubernetesClientTestConfiguration.class, "spring.main.cloud-platform=KUBERNETES", "spring.cloud.kubernetes.config.enabled=true", "spring.cloud.kubernetes.secrets.enabled=false", "spring.cloud.kubernetes.reload.enabled=true"); @@ -124,7 +417,7 @@ public class ConfigReloadAutoConfigurationTest extends KubernetesConfigTestBase } @Test - public void kubernetesReloadEnabledButSecretAndConfigDisabled() { + void kubernetesReloadEnabledButSecretAndConfigDisabled() { setup(KubernetesClientTestConfiguration.class, "spring.cloud.kubernetes.config.enabled=false", "spring.cloud.kubernetes.secrets.enabled=false", "spring.cloud.kubernetes.reload.enabled=true"); assertThat(this.getContext().containsBean("configMapPropertySourceLocator")).isFalse(); diff --git a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-client-configmap-event-reload/src/test/java/org/springframework/cloud/kubernetes/client/configmap/event/reload/ConfigMapEventReloadIT.java b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-client-configmap-event-reload/src/test/java/org/springframework/cloud/kubernetes/client/configmap/event/reload/ConfigMapEventReloadIT.java index 14dd80e2..ec30cb50 100644 --- a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-client-configmap-event-reload/src/test/java/org/springframework/cloud/kubernetes/client/configmap/event/reload/ConfigMapEventReloadIT.java +++ b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-client-configmap-event-reload/src/test/java/org/springframework/cloud/kubernetes/client/configmap/event/reload/ConfigMapEventReloadIT.java @@ -94,6 +94,8 @@ class ConfigMapEventReloadIT { @Test void testInformFromOneNamespaceEventNotTriggered() throws Exception { manifests("one", Phase.CREATE); + assertLogStatement(true, "added configmap informer for namespace"); + assertLogStatement(false, "added secret informer for namespace"); WebClient webClient = builder().baseUrl("localhost/left").build(); String result = webClient.method(HttpMethod.GET).retrieve().bodyToMono(String.class).retryWhen(retrySpec()) @@ -136,6 +138,8 @@ class ConfigMapEventReloadIT { @Test void testInformFromOneNamespaceEventTriggered() throws Exception { manifests("two", Phase.CREATE); + assertLogStatement(true, "added configmap informer for namespace"); + assertLogStatement(false, "added secret informer for namespace"); // read the value from the right-configmap WebClient webClient = builder().baseUrl("localhost/right").build(); @@ -176,6 +180,10 @@ class ConfigMapEventReloadIT { @Test void testInform() throws Exception { manifests("three", Phase.CREATE); + // wait a little for the logs to be available + LockSupport.parkNanos(TimeUnit.SECONDS.toNanos(10)); + assertLogStatement(true, "added configmap informer for namespace"); + assertLogStatement(false, "added secret informer for namespace"); // read the initial value from the right-configmap WebClient rightWebClient = builder().baseUrl("localhost/right").build(); @@ -270,6 +278,23 @@ class ConfigMapEventReloadIT { } + /** + * assert that only config map logs are present, not secrets. + */ + private void assertLogStatement(boolean contains, String log) throws Exception { + String appPodName = K3S + .execInContainer("kubectl", "get", "pods", "-l", + "app=spring-cloud-kubernetes-client-configmap-event-reload", "-o=name", "--no-headers") + .getStdout(); + String allLogs = K3S.execInContainer("kubectl", "logs", appPodName.trim()).getStdout(); + if (contains) { + Assertions.assertTrue(allLogs.contains(log)); + } + else { + Assertions.assertFalse(allLogs.contains(log)); + } + } + private WebClient.Builder builder() { return WebClient.builder().clientConnector(new ReactorClientHttpConnector(HttpClient.create())); } @@ -278,7 +303,6 @@ class ConfigMapEventReloadIT { return Retry.fixedDelay(120, Duration.ofSeconds(1)).filter(Objects::nonNull); } - @SuppressWarnings({ "unchecked", "raw" }) private static void replaceConfigMap(V1ConfigMap configMap, String name) throws ApiException { api.replaceNamespacedConfigMap(name, "right", configMap, null, null, null, null); } diff --git a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-client-configmap-event-reload/src/test/resources/one/deployment.yaml b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-client-configmap-event-reload/src/test/resources/one/deployment.yaml index 7d6f8c11..81c754c2 100644 --- a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-client-configmap-event-reload/src/test/resources/one/deployment.yaml +++ b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-client-configmap-event-reload/src/test/resources/one/deployment.yaml @@ -29,3 +29,5 @@ spec: env: - name: SPRING_PROFILES_ACTIVE value: one + - name: LOGGING_LEVEL_ORG_SPRINGFRAMEWORK_CLOUD_KUBERNETES_CLIENT_CONFIG_RELOAD + value: DEBUG diff --git a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-client-configmap-event-reload/src/test/resources/three/deployment.yaml b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-client-configmap-event-reload/src/test/resources/three/deployment.yaml index c3aaddb6..f2f45775 100644 --- a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-client-configmap-event-reload/src/test/resources/three/deployment.yaml +++ b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-client-configmap-event-reload/src/test/resources/three/deployment.yaml @@ -29,3 +29,5 @@ spec: env: - name: SPRING_PROFILES_ACTIVE value: three + - name: LOGGING_LEVEL_ORG_SPRINGFRAMEWORK_CLOUD_KUBERNETES_CLIENT_CONFIG_RELOAD + value: DEBUG diff --git a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-client-configmap-event-reload/src/test/resources/two/deployment.yaml b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-client-configmap-event-reload/src/test/resources/two/deployment.yaml index feb16a2c..caa0d041 100644 --- a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-client-configmap-event-reload/src/test/resources/two/deployment.yaml +++ b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-client-configmap-event-reload/src/test/resources/two/deployment.yaml @@ -29,3 +29,5 @@ spec: env: - name: SPRING_PROFILES_ACTIVE value: two + - name: LOGGING_LEVEL_ORG_SPRINGFRAMEWORK_CLOUD_KUBERNETES_CLIENT_CONFIG_RELOAD + value: DEBUG diff --git a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-client-secrets-event-reload/src/main/resources/application.yaml b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-client-secrets-event-reload/src/main/resources/application.yaml index 4356bd6a..ed832dbe 100644 --- a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-client-secrets-event-reload/src/main/resources/application.yaml +++ b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-client-secrets-event-reload/src/main/resources/application.yaml @@ -14,6 +14,7 @@ spring: strategy: shutdown mode: event monitoring-secrets: true + monitoring-configMaps: false secrets: enabled: true enable-api: true diff --git a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-client-secrets-event-reload/src/test/java/org/springframework/cloud/kubernetes/client/secrets/event/reload/SecretsEventReloadIT.java b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-client-secrets-event-reload/src/test/java/org/springframework/cloud/kubernetes/client/secrets/event/reload/SecretsEventReloadIT.java index 8b9d67a7..b294fc3c 100644 --- a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-client-secrets-event-reload/src/test/java/org/springframework/cloud/kubernetes/client/secrets/event/reload/SecretsEventReloadIT.java +++ b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-client-secrets-event-reload/src/test/java/org/springframework/cloud/kubernetes/client/secrets/event/reload/SecretsEventReloadIT.java @@ -27,6 +27,7 @@ import io.kubernetes.client.openapi.models.V1Secret; import io.kubernetes.client.openapi.models.V1Service; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; import org.testcontainers.k3s.K3sContainer; @@ -83,6 +84,8 @@ class SecretsEventReloadIT { @Test void testSecretReload() throws Exception { configK8sClientIt(Phase.CREATE); + assertLogStatement(false, "added configmap informer for namespace"); + assertLogStatement(true, "added secret informer for namespace"); testSecretEventReload(); } @@ -122,6 +125,23 @@ class SecretsEventReloadIT { } } + /** + * assert that only config map logs are present, not secrets. + */ + private void assertLogStatement(boolean contains, String log) throws Exception { + String appPodName = K3S + .execInContainer("kubectl", "get", "pods", "-l", + "app=spring-cloud-kubernetes-client-secrets-event-reload", "-o=name", "--no-headers") + .getStdout(); + String allLogs = K3S.execInContainer("kubectl", "logs", appPodName.trim()).getStdout(); + if (contains) { + Assertions.assertTrue(allLogs.contains(log)); + } + else { + Assertions.assertFalse(allLogs.contains(log)); + } + } + private WebClient.Builder builder() { return WebClient.builder().clientConnector(new ReactorClientHttpConnector(HttpClient.create())); } diff --git a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-client-secrets-event-reload/src/test/resources/deployment.yaml b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-client-secrets-event-reload/src/test/resources/deployment.yaml index 74cdd163..116475e4 100644 --- a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-client-secrets-event-reload/src/test/resources/deployment.yaml +++ b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-client-secrets-event-reload/src/test/resources/deployment.yaml @@ -26,3 +26,6 @@ spec: path: /actuator/health/liveness ports: - containerPort: 8080 + env: + - name: LOGGING_LEVEL_ORG_SPRINGFRAMEWORK_CLOUD_KUBERNETES_CLIENT_CONFIG_RELOAD + value: DEBUG diff --git a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-configmap-event-reload/src/test/java/org/springframework/cloud/kubernetes/fabric8/configmap/event/reload/ConfigMapEventReloadIT.java b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-configmap-event-reload/src/test/java/org/springframework/cloud/kubernetes/fabric8/configmap/event/reload/ConfigMapEventReloadIT.java index afc9b511..ec146a0d 100644 --- a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-configmap-event-reload/src/test/java/org/springframework/cloud/kubernetes/fabric8/configmap/event/reload/ConfigMapEventReloadIT.java +++ b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-configmap-event-reload/src/test/java/org/springframework/cloud/kubernetes/fabric8/configmap/event/reload/ConfigMapEventReloadIT.java @@ -100,6 +100,8 @@ class ConfigMapEventReloadIT { @Test void testInformFromOneNamespaceEventNotTriggered() { manifests("one", Phase.CREATE); + assertLogStatement(true, "added configmap informer for namespace"); + assertLogStatement(false, "added secret informer for namespace"); WebClient webClient = builder().baseUrl("localhost/left").build(); String result = webClient.method(HttpMethod.GET).retrieve().bodyToMono(String.class).retryWhen(retrySpec()) @@ -142,6 +144,8 @@ class ConfigMapEventReloadIT { @Test void testInformFromOneNamespaceEventTriggered() { manifests("two", Phase.CREATE); + assertLogStatement(true, "added configmap informer for namespace"); + assertLogStatement(false, "added secret informer for namespace"); // read the value from the right-configmap WebClient webClient = builder().baseUrl("localhost/right").build(); @@ -181,6 +185,8 @@ class ConfigMapEventReloadIT { @Test void testInform() { manifests("three", Phase.CREATE); + assertLogStatement(true, "added configmap informer for namespace"); + assertLogStatement(false, "added secret informer for namespace"); // read the initial value from the right-configmap WebClient rightWebClient = builder().baseUrl("localhost/right").build(); @@ -282,6 +288,24 @@ class ConfigMapEventReloadIT { } + private void assertLogStatement(boolean contains, String log) { + try { + String appPodName = K3S.execInContainer("kubectl", "get", "pods", "-l", + "app=spring-cloud-kubernetes-fabric8-client-configmap-event-reload", "-o=name", "--no-headers") + .getStdout(); + String allLogs = K3S.execInContainer("kubectl", "logs", appPodName.trim()).getStdout(); + if (contains) { + Assertions.assertTrue(allLogs.contains(log)); + } + else { + Assertions.assertFalse(allLogs.contains(log)); + } + } + catch (Exception e) { + throw new RuntimeException(e); + } + } + private WebClient.Builder builder() { return WebClient.builder().clientConnector(new ReactorClientHttpConnector(HttpClient.create())); } diff --git a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-configmap-event-reload/src/test/resources/deployment.yaml b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-configmap-event-reload/src/test/resources/deployment.yaml index 864f23d5..a7fe7eac 100644 --- a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-configmap-event-reload/src/test/resources/deployment.yaml +++ b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-configmap-event-reload/src/test/resources/deployment.yaml @@ -26,3 +26,6 @@ spec: path: /actuator/health/liveness ports: - containerPort: 8080 + env: + - name: LOGGING_LEVEL_ORG_SPRINGFRAMEWORK_CLOUD_KUBERNETES_FABRIC8_CONFIG_RELOAD + value: DEBUG diff --git a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-secrets-event-reload/src/main/resources/application.yaml b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-secrets-event-reload/src/main/resources/application.yaml index d4b67d9c..bd33ead1 100644 --- a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-secrets-event-reload/src/main/resources/application.yaml +++ b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-secrets-event-reload/src/main/resources/application.yaml @@ -14,6 +14,7 @@ spring: monitoring-secrets: true strategy: shutdown mode: event + monitoring-configMaps: false secrets: enabled: true enable-api: true diff --git a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-secrets-event-reload/src/test/java/org/springframework/cloud/kubernetes/fabric8/secrets/event/reload/SecretsEventsReloadIT.java b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-secrets-event-reload/src/test/java/org/springframework/cloud/kubernetes/fabric8/secrets/event/reload/SecretsEventsReloadIT.java index e2dcaa38..96156479 100644 --- a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-secrets-event-reload/src/test/java/org/springframework/cloud/kubernetes/fabric8/secrets/event/reload/SecretsEventsReloadIT.java +++ b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-secrets-event-reload/src/test/java/org/springframework/cloud/kubernetes/fabric8/secrets/event/reload/SecretsEventsReloadIT.java @@ -82,6 +82,8 @@ class SecretsEventsReloadIT { @Test void test() { + assertLogStatement(false, "added configmap informer for namespace"); + assertLogStatement(true, "added secret informer for namespace"); WebClient webClient = builder().baseUrl("localhost/key").build(); String result = webClient.method(HttpMethod.GET).retrieve().bodyToMono(String.class).retryWhen(retrySpec()) .block(); @@ -127,6 +129,27 @@ class SecretsEventsReloadIT { } + /** + * assert that only config map logs are present, not secrets. + */ + private void assertLogStatement(boolean contains, String log) { + try { + String appPodName = K3S.execInContainer("kubectl", "get", "pods", "-l", + "app=spring-cloud-kubernetes-fabric8-client-secrets-event-reload", "-o=name", "--no-headers") + .getStdout(); + String allLogs = K3S.execInContainer("kubectl", "logs", appPodName.trim()).getStdout(); + if (contains) { + Assertions.assertTrue(allLogs.contains(log)); + } + else { + Assertions.assertFalse(allLogs.contains(log)); + } + } + catch (Exception e) { + throw new RuntimeException(e); + } + } + private WebClient.Builder builder() { return WebClient.builder().clientConnector(new ReactorClientHttpConnector(HttpClient.create())); } diff --git a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-secrets-event-reload/src/test/resources/deployment.yaml b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-secrets-event-reload/src/test/resources/deployment.yaml index 91451229..ce326181 100644 --- a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-secrets-event-reload/src/test/resources/deployment.yaml +++ b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-fabric8-client-secrets-event-reload/src/test/resources/deployment.yaml @@ -26,3 +26,6 @@ spec: path: /actuator/health/liveness ports: - containerPort: 8080 + env: + - name: LOGGING_LEVEL_ORG_SPRINGFRAMEWORK_CLOUD_KUBERNETES_FABRIC8_CONFIG_RELOAD + value: DEBUG