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 f8bd7ad541..af28d3cbd2 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 @@ -18,6 +18,7 @@ import java.util.ArrayList; import java.util.List; +import java.util.Map; import java.util.Objects; import java.util.Set; @@ -45,6 +46,7 @@ import org.springframework.core.log.LogAccessor; import static org.springframework.cloud.kubernetes.client.KubernetesClientUtils.createApiClientForInformerClient; +import static org.springframework.cloud.kubernetes.client.KubernetesClientUtils.labelSelector; import static org.springframework.cloud.kubernetes.client.config.KubernetesClientConfigUtils.namespaces; /** @@ -73,6 +75,8 @@ public class KubernetesClientEventBasedConfigMapChangeDetector extends Configura private final boolean monitoringConfigMaps; + private final Map configMapsLabels; + private final ResourceEventHandler handler = new ResourceEventHandler<>() { @Override @@ -113,6 +117,7 @@ public KubernetesClientEventBasedConfigMapChangeDetector(CoreV1Api coreV1Api, Co this.apiClient = createApiClientForInformerClient(); this.enableReloadFiltering = properties.enableReloadFiltering(); this.monitoringConfigMaps = properties.monitoringConfigMaps(); + this.configMapsLabels = properties.configMapsLabels(); namespaces = namespaces(kubernetesNamespaceProvider, properties, "configmap"); } @@ -122,13 +127,19 @@ void inform() { if (monitoringConfigMaps) { LOG.info(() -> "Kubernetes event-based configMap change detector activated"); - String filter; + Map labelSelector; if (enableReloadFiltering) { - filter = ConfigReloadProperties.RELOAD_LABEL_FILTER + "=true"; + LOG.warn(() -> "enable reload filtering is deprecated and will be removed in the next major release"); + LOG.warn(() -> "use spring.cloud.kubernetes.reload.config-maps-labels instead"); + if (!configMapsLabels.isEmpty()) { + LOG.warn(() -> "spring.cloud.kubernetes.reload.config-maps-labels is not empty, but " + + "spring.cloud.kubernetes.reload.enable-reload-filtering is enabled and will override the former"); + } + labelSelector = Map.of(ConfigReloadProperties.RELOAD_LABEL_FILTER, "true"); } else { - filter = null; + labelSelector = configMapsLabels; } namespaces.forEach(namespace -> { @@ -141,10 +152,10 @@ void inform() { .timeoutSeconds(params.timeoutSeconds) .resourceVersion(params.resourceVersion) .watch(params.watch) - .labelSelector(filter) + .labelSelector(labelSelector(labelSelector)) .buildCall(null), V1ConfigMap.class, V1ConfigMapList.class); - LOG.debug(() -> "added configmap informer for namespace : " + namespace + " with filter : " + filter); + LOG.debug(() -> "configmap informer for namespace : " + namespace + " with filter : " + labelSelector); informer.addEventHandler(handler); informers.add(informer); 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 8248e23b6f..6d1c0f1a53 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 @@ -47,6 +47,7 @@ import org.springframework.core.log.LogAccessor; import static org.springframework.cloud.kubernetes.client.KubernetesClientUtils.createApiClientForInformerClient; +import static org.springframework.cloud.kubernetes.client.KubernetesClientUtils.labelSelector; import static org.springframework.cloud.kubernetes.client.config.KubernetesClientConfigUtils.namespaces; /** @@ -75,6 +76,8 @@ public class KubernetesClientEventBasedSecretsChangeDetector extends Configurati private final boolean monitoringSecrets; + private final Map secretsLabels; + private final ResourceEventHandler handler = new ResourceEventHandler<>() { @Override @@ -116,6 +119,7 @@ public KubernetesClientEventBasedSecretsChangeDetector(CoreV1Api coreV1Api, Conf this.apiClient = createApiClientForInformerClient(); this.enableReloadFiltering = properties.enableReloadFiltering(); this.monitoringSecrets = properties.monitoringSecrets(); + this.secretsLabels = properties.secretsLabels(); namespaces = namespaces(kubernetesNamespaceProvider, properties, "secret"); } @@ -123,13 +127,19 @@ public KubernetesClientEventBasedSecretsChangeDetector(CoreV1Api coreV1Api, Conf void inform() { LOG.info(() -> "Kubernetes event-based secrets change detector activated"); - String filter; + Map labelSelector; if (enableReloadFiltering) { - filter = ConfigReloadProperties.RELOAD_LABEL_FILTER + "=true"; + LOG.warn(() -> "enable reload filtering is deprecated and will be removed in the next major release"); + LOG.warn(() -> "use spring.cloud.kubernetes.secrets-labels instead"); + if (!secretsLabels.isEmpty()) { + LOG.warn(() -> "spring.cloud.kubernetes.reload.secrets-labels is not empty, but " + + "spring.cloud.kubernetes.reload.enable-reload-filtering is enabled and will override the former"); + } + labelSelector = Map.of(ConfigReloadProperties.RELOAD_LABEL_FILTER, "true"); } else { - filter = null; + labelSelector = secretsLabels; } if (monitoringSecrets) { @@ -143,10 +153,10 @@ void inform() { .timeoutSeconds(params.timeoutSeconds) .resourceVersion(params.resourceVersion) .watch(params.watch) - .labelSelector(filter) + .labelSelector(labelSelector(labelSelector)) .buildCall(null), V1Secret.class, V1SecretList.class); - LOG.debug(() -> "added secret informer for namespace : " + namespace + " with filter : " + filter); + LOG.debug(() -> "secret informer for namespace : " + namespace + " with filter : " + secretsLabels); informer.addEventHandler(handler); informers.add(informer); diff --git a/spring-cloud-kubernetes-commons/src/main/java/org/springframework/cloud/kubernetes/commons/config/reload/ConfigReloadProperties.java b/spring-cloud-kubernetes-commons/src/main/java/org/springframework/cloud/kubernetes/commons/config/reload/ConfigReloadProperties.java index ac04ba1b2d..1d0a89ff44 100644 --- a/spring-cloud-kubernetes-commons/src/main/java/org/springframework/cloud/kubernetes/commons/config/reload/ConfigReloadProperties.java +++ b/spring-cloud-kubernetes-commons/src/main/java/org/springframework/cloud/kubernetes/commons/config/reload/ConfigReloadProperties.java @@ -17,9 +17,11 @@ package org.springframework.cloud.kubernetes.commons.config.reload; import java.time.Duration; +import java.util.Map; import java.util.Set; import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.boot.context.properties.bind.ConstructorBinding; import org.springframework.boot.context.properties.bind.DefaultValue; /** @@ -27,7 +29,9 @@ * * @param enabled Enables the Kubernetes configuration reload on change. * @param monitoringConfigMaps Enables monitoring on secrets to detect changes. + * @param configMapsLabels Labels for which to watch config maps. * @param monitoringSecrets Monitor secrets or not. + * @param secretsLabels Labels for which to watch secrets for. * @param strategy Sets reload strategy for Kubernetes configuration reload on change. * @param mode Sets the detection mode for Kubernetes configuration reload. * @param period Sets the polling period to use when the detection mode is POLLING. @@ -44,22 +48,54 @@ * @author Nicola Ferraro */ @ConfigurationProperties(prefix = "spring.cloud.kubernetes.reload") -public record ConfigReloadProperties(boolean enabled, @DefaultValue("true") boolean monitoringConfigMaps, - boolean monitoringSecrets, @DefaultValue("REFRESH") ReloadStrategy strategy, - @DefaultValue("EVENT") ReloadDetectionMode mode, @DefaultValue("15000ms") Duration period, - @DefaultValue Set namespaces, boolean enableReloadFiltering, - @DefaultValue("2s") Duration maxWaitForRestart) { +public record ConfigReloadProperties(boolean enabled, boolean monitoringConfigMaps, + Map configMapsLabels, boolean monitoringSecrets, Map secretsLabels, + ReloadStrategy strategy, ReloadDetectionMode mode, Duration period, Set namespaces, + boolean enableReloadFiltering, Duration maxWaitForRestart) { + + @ConstructorBinding + public ConfigReloadProperties(boolean enabled, @DefaultValue("true") boolean monitoringConfigMaps, + @DefaultValue Map configMapsLabels, boolean monitoringSecrets, + @DefaultValue Map secretsLabels, @DefaultValue("REFRESH") ReloadStrategy strategy, + @DefaultValue("EVENT") ReloadDetectionMode mode, @DefaultValue("15000ms") Duration period, + @DefaultValue Set namespaces, boolean enableReloadFiltering, + @DefaultValue("2s") Duration maxWaitForRestart) { + + this.enabled = enabled; + this.monitoringConfigMaps = monitoringConfigMaps; + this.configMapsLabels = configMapsLabels; + this.monitoringSecrets = monitoringSecrets; + this.secretsLabels = secretsLabels; + this.strategy = strategy; + this.mode = mode; + this.period = period; + this.namespaces = namespaces; + this.enableReloadFiltering = enableReloadFiltering; + this.maxWaitForRestart = maxWaitForRestart; + } + + @Deprecated(forRemoval = true) + public ConfigReloadProperties(boolean enabled, @DefaultValue("true") boolean monitoringConfigMaps, + boolean monitoringSecrets, @DefaultValue("REFRESH") ReloadStrategy strategy, + @DefaultValue("EVENT") ReloadDetectionMode mode, @DefaultValue("15000ms") Duration period, + @DefaultValue Set namespaces, boolean enableReloadFiltering, + @DefaultValue("2s") Duration maxWaitForRestart) { + + this(enabled, monitoringConfigMaps, Map.of(), monitoringSecrets, Map.of(), strategy, mode, period, namespaces, + enableReloadFiltering, maxWaitForRestart); + } /** * default instance. */ - public static ConfigReloadProperties DEFAULT = new ConfigReloadProperties(false, true, false, - ReloadStrategy.REFRESH, ReloadDetectionMode.EVENT, Duration.ofMillis(15000), Set.of(), false, + public static final ConfigReloadProperties DEFAULT = new ConfigReloadProperties(false, true, Map.of(), false, + Map.of(), ReloadStrategy.REFRESH, ReloadDetectionMode.EVENT, Duration.ofMillis(15000), Set.of(), false, Duration.ofSeconds(2)); /** * label for filtering sources. */ + @Deprecated(forRemoval = true) public static final String RELOAD_LABEL_FILTER = "spring.cloud.kubernetes.config.informer.enabled"; /** diff --git a/spring-cloud-kubernetes-commons/src/test/java/org/springframework/cloud/kubernetes/commons/config/ConfigReloadPropertiesTests.java b/spring-cloud-kubernetes-commons/src/test/java/org/springframework/cloud/kubernetes/commons/config/ConfigReloadPropertiesTests.java index 63120f0814..574187e72e 100644 --- a/spring-cloud-kubernetes-commons/src/test/java/org/springframework/cloud/kubernetes/commons/config/ConfigReloadPropertiesTests.java +++ b/spring-cloud-kubernetes-commons/src/test/java/org/springframework/cloud/kubernetes/commons/config/ConfigReloadPropertiesTests.java @@ -17,8 +17,8 @@ package org.springframework.cloud.kubernetes.commons.config; import java.time.Duration; +import java.util.Map; -import org.assertj.core.api.Assertions; import org.junit.jupiter.api.Test; import org.springframework.boot.context.properties.EnableConfigurationProperties; @@ -26,10 +26,10 @@ import org.springframework.cloud.kubernetes.commons.config.reload.ConfigReloadProperties; import org.springframework.context.annotation.Configuration; +import static org.assertj.core.api.Assertions.assertThat; + /** * @author wind57 - * - * Tests binding, since we moved from a class to a record */ class ConfigReloadPropertiesTests { @@ -37,15 +37,18 @@ class ConfigReloadPropertiesTests { void testDefaults() { new ApplicationContextRunner().withUserConfiguration(Config.class).run(context -> { ConfigReloadProperties properties = context.getBean(ConfigReloadProperties.class); - Assertions.assertThat(properties).isNotNull(); - Assertions.assertThat(properties.enabled()).isFalse(); - Assertions.assertThat(properties.monitoringConfigMaps()).isTrue(); - Assertions.assertThat(properties.monitoringSecrets()).isFalse(); - Assertions.assertThat(ConfigReloadProperties.ReloadStrategy.REFRESH).isEqualTo(properties.strategy()); - Assertions.assertThat(ConfigReloadProperties.ReloadDetectionMode.EVENT).isEqualTo(properties.mode()); - Assertions.assertThat(Duration.ofMillis(15000)).isEqualTo(properties.period()); - Assertions.assertThat(properties.namespaces().isEmpty()).isTrue(); - Assertions.assertThat(Duration.ofSeconds(2)).isEqualTo(properties.maxWaitForRestart()); + assertThat(properties).isNotNull(); + assertThat(properties.enabled()).isFalse(); + assertThat(properties.monitoringConfigMaps()).isTrue(); + assertThat(properties.configMapsLabels()).isEmpty(); + assertThat(properties.monitoringSecrets()).isFalse(); + assertThat(properties.secretsLabels()).isEmpty(); + assertThat(ConfigReloadProperties.ReloadStrategy.REFRESH).isEqualTo(properties.strategy()); + assertThat(ConfigReloadProperties.ReloadDetectionMode.EVENT).isEqualTo(properties.mode()); + assertThat(Duration.ofMillis(15000)).isEqualTo(properties.period()); + assertThat(properties.namespaces().isEmpty()).isTrue(); + assertThat(properties.enableReloadFiltering()).isFalse(); + assertThat(Duration.ofSeconds(2)).isEqualTo(properties.maxWaitForRestart()); }); } @@ -54,22 +57,28 @@ void testNonDefaults() { new ApplicationContextRunner().withUserConfiguration(Config.class) .withPropertyValues("spring.cloud.kubernetes.reload.enabled=true", "spring.cloud.kubernetes.reload.monitoring-config-maps=false", + "spring.cloud.kubernetes.reload.config-maps-labels[aa]=bb", "spring.cloud.kubernetes.reload.monitoring-secrets=true", + "spring.cloud.kubernetes.reload.secrets-labels[cc]=dd", "spring.cloud.kubernetes.reload.strategy=SHUTDOWN", "spring.cloud.kubernetes.reload.mode=POLLING", "spring.cloud.kubernetes.reload.period=1000ms", "spring.cloud.kubernetes.reload.namespaces[0]=a", "spring.cloud.kubernetes.reload.namespaces[1]=b", + "spring.cloud.kubernetes.reload.enable-reload-filtering=true", "spring.cloud.kubernetes.reload.max-wait-for-restart=5s") .run(context -> { ConfigReloadProperties properties = context.getBean(ConfigReloadProperties.class); - Assertions.assertThat(properties).isNotNull(); - Assertions.assertThat(properties.enabled()).isTrue(); - Assertions.assertThat(properties.monitoringConfigMaps()).isFalse(); - Assertions.assertThat(properties.monitoringSecrets()).isTrue(); - Assertions.assertThat(ConfigReloadProperties.ReloadStrategy.SHUTDOWN).isEqualTo(properties.strategy()); - Assertions.assertThat(ConfigReloadProperties.ReloadDetectionMode.POLLING).isEqualTo(properties.mode()); - Assertions.assertThat(Duration.ofMillis(1000)).isEqualTo(properties.period()); - Assertions.assertThat(properties.namespaces()).containsExactlyInAnyOrder("a", "b"); - Assertions.assertThat(Duration.ofSeconds(5)).isEqualTo(properties.maxWaitForRestart()); + assertThat(properties).isNotNull(); + assertThat(properties.enabled()).isTrue(); + assertThat(properties.monitoringConfigMaps()).isFalse(); + assertThat(properties.configMapsLabels()).containsExactlyInAnyOrderEntriesOf(Map.of("aa", "bb")); + assertThat(properties.monitoringSecrets()).isTrue(); + assertThat(properties.secretsLabels()).containsExactlyInAnyOrderEntriesOf(Map.of("cc", "dd")); + assertThat(ConfigReloadProperties.ReloadStrategy.SHUTDOWN).isEqualTo(properties.strategy()); + assertThat(ConfigReloadProperties.ReloadDetectionMode.POLLING).isEqualTo(properties.mode()); + assertThat(Duration.ofMillis(1000)).isEqualTo(properties.period()); + assertThat(properties.namespaces()).containsExactlyInAnyOrder("a", "b"); + assertThat(properties.enableReloadFiltering()).isTrue(); + assertThat(Duration.ofSeconds(5)).isEqualTo(properties.maxWaitForRestart()); }); } 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 e8d24dae8e..7cd2da0bff 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 @@ -70,6 +70,8 @@ public class Fabric8EventBasedConfigMapChangeDetector extends ConfigurationChang private final boolean monitorConfigMaps; + private final Map configMapsLabels; + public Fabric8EventBasedConfigMapChangeDetector(AbstractEnvironment environment, ConfigReloadProperties properties, KubernetesClient kubernetesClient, ConfigurationUpdateStrategy strategy, Fabric8ConfigMapPropertySourceLocator fabric8ConfigMapPropertySourceLocator, @@ -80,6 +82,7 @@ public Fabric8EventBasedConfigMapChangeDetector(AbstractEnvironment environment, this.fabric8ConfigMapPropertySourceLocator = fabric8ConfigMapPropertySourceLocator; this.enableReloadFiltering = properties.enableReloadFiltering(); this.monitorConfigMaps = properties.monitoringConfigMaps(); + this.configMapsLabels = properties.configMapsLabels(); namespaces = namespaces(kubernetesClient, namespaceProvider, properties, "configmap"); } @@ -92,10 +95,16 @@ private void inform() { Map labelSelector; if (enableReloadFiltering) { + LOG.warn(() -> "enable reload filtering is deprecated and will be removed in the next major release"); + LOG.warn(() -> "use spring.cloud.kubernetes.reload.config-maps-labels instead"); + if (!configMapsLabels.isEmpty()) { + LOG.warn(() -> "spring.cloud.kubernetes.reload.config-maps-labels is not empty, but " + + "spring.cloud.kubernetes.reload.enable-reload-filtering is enabled and will override the former"); + } labelSelector = Map.of(ConfigReloadProperties.RELOAD_LABEL_FILTER, "true"); } else { - labelSelector = Map.of(); + labelSelector = configMapsLabels; } namespaces.forEach(namespace -> { 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 197ddf62e2..73bfabdf19 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 @@ -70,6 +70,8 @@ public class Fabric8EventBasedSecretsChangeDetector extends ConfigurationChangeD private final boolean monitorSecrets; + private final Map secretsLabels; + public Fabric8EventBasedSecretsChangeDetector(AbstractEnvironment environment, ConfigReloadProperties properties, KubernetesClient kubernetesClient, ConfigurationUpdateStrategy strategy, Fabric8SecretsPropertySourceLocator fabric8SecretsPropertySourceLocator, @@ -80,6 +82,7 @@ public Fabric8EventBasedSecretsChangeDetector(AbstractEnvironment environment, C this.fabric8SecretsPropertySourceLocator = fabric8SecretsPropertySourceLocator; this.enableReloadFiltering = properties.enableReloadFiltering(); this.monitorSecrets = properties.monitoringSecrets(); + secretsLabels = properties.configMapsLabels(); namespaces = namespaces(kubernetesClient, namespaceProvider, properties, "secrets"); } @@ -100,10 +103,16 @@ private void inform() { Map labelSelector; if (enableReloadFiltering) { + LOG.warn(() -> "enable reload filtering is deprecated and will be removed in the next major release"); + LOG.warn(() -> "use spring.cloud.kubernetes.secrets-labels instead"); + if (!secretsLabels.isEmpty()) { + LOG.warn(() -> "spring.cloud.kubernetes.reload.secrets-labels is not empty, but " + + "spring.cloud.kubernetes.reload.enable-reload-filtering is enabled and will override the former"); + } labelSelector = Map.of(ConfigReloadProperties.RELOAD_LABEL_FILTER, "true"); } else { - labelSelector = Map.of(); + labelSelector = secretsLabels; } namespaces.forEach(namespace -> {