From 032014e7b841bb3346f2a9a097fe9bd5983a30e9 Mon Sep 17 00:00:00 2001 From: wind57 Date: Sat, 4 Dec 2021 07:59:07 -0800 Subject: [PATCH 01/21] test --- delme.sh | 1 + 1 file changed, 1 insertion(+) create mode 100644 delme.sh diff --git a/delme.sh b/delme.sh new file mode 100644 index 0000000000..707d90cb73 --- /dev/null +++ b/delme.sh @@ -0,0 +1 @@ +dadsa \ No newline at end of file From 10889fd1fc9c5ec3a32699d70d4a0e914aae7577 Mon Sep 17 00:00:00 2001 From: wind57 Date: Thu, 16 Dec 2021 04:15:40 -0800 Subject: [PATCH 02/21] fix @Nested tests not running --- .circleci/config.yml | 2 +- pom.xml | 10 ++++++++++ 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 2f197c14d8..7eed9f3449 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -44,7 +44,7 @@ jobs: echo $CLASSNAMES TEST_ARG=$(echo $CLASSNAMES | sed 's/ /,/g') echo $TEST_ARG - ./mvnw -s .settings.xml -DfailIfNoTests=false -Dtest=$TEST_ARG -e clean org.jacoco:jacoco-maven-plugin:prepare-agent test -U -P sonar -nsu --batch-mode -Dmaven.test.redirectTestOutputToFile=true -Dorg.slf4j.simpleLogger.log.org.apache.maven.cli.transfer.Slf4jMavenTransferListener=warn + ./mvnw -s .settings.xml -DfailIfNoTests=false -DtestsToRun=$TEST_ARG -e clean org.jacoco:jacoco-maven-plugin:prepare-agent test -U -P sonar -nsu --batch-mode -Dmaven.test.redirectTestOutputToFile=true -Dorg.slf4j.simpleLogger.log.org.apache.maven.cli.transfer.Slf4jMavenTransferListener=warn mkdir -p $HOME/artifacts/junit/ find . -type f -regex ".*/spring-cloud-*.*/target/*.*" -exec cp {} $HOME/artifacts/ \; find . -type f -regex ".*/target/.*-reports/.*" -exec cp {} $HOME/artifacts/junit/ \; diff --git a/pom.xml b/pom.xml index 191dae7c2d..e25caca33c 100644 --- a/pom.xml +++ b/pom.xml @@ -204,6 +204,11 @@ all false + + + + ${testsToRun} + @@ -356,6 +361,11 @@ all false + + + + ${testsToRun} + ${surefireArgLine} From b041c00ea5cac0bb3e7bd43bc75ee470920cc3fe Mon Sep 17 00:00:00 2001 From: wind57 Date: Thu, 13 Jan 2022 15:33:08 +0200 Subject: [PATCH 03/21] trigger again From a34ac473971fca316b05a0b886de000f05220b1e Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Wed, 13 Jul 2022 08:43:39 +0000 Subject: [PATCH 04/21] Add renovate.json --- renovate.json | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 renovate.json diff --git a/renovate.json b/renovate.json new file mode 100644 index 0000000000..7bd954555f --- /dev/null +++ b/renovate.json @@ -0,0 +1,6 @@ +{ + "$schema": "https://docs.renovatebot.com/renovate-schema.json", + "extends": [ + "config:base" + ] +} From 618f25aeccb763010ba7777653994148514c8d99 Mon Sep 17 00:00:00 2001 From: erabii Date: Tue, 14 Mar 2023 10:39:50 +0200 Subject: [PATCH 05/21] Delete renovate.json --- renovate.json | 6 ------ 1 file changed, 6 deletions(-) delete mode 100644 renovate.json diff --git a/renovate.json b/renovate.json deleted file mode 100644 index 7bd954555f..0000000000 --- a/renovate.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "$schema": "https://docs.renovatebot.com/renovate-schema.json", - "extends": [ - "config:base" - ] -} From 100a9cd110d5c5e614573d8c3726b21788c1b550 Mon Sep 17 00:00:00 2001 From: erabii Date: Tue, 14 Mar 2023 10:40:02 +0200 Subject: [PATCH 06/21] Delete delme.sh --- delme.sh | 1 - 1 file changed, 1 deletion(-) delete mode 100644 delme.sh diff --git a/delme.sh b/delme.sh deleted file mode 100644 index 707d90cb73..0000000000 --- a/delme.sh +++ /dev/null @@ -1 +0,0 @@ -dadsa \ No newline at end of file From 31523cf039757919b49fcdfef768ee5220df57e7 Mon Sep 17 00:00:00 2001 From: wind57 Date: Wed, 4 Sep 2024 18:51:44 +0300 Subject: [PATCH 07/21] dirty/started work --- ...etryableConfigMapPropertySourceLocator.java | 4 ++-- ...aRetryableSecretsPropertySourceLocator.java | 4 ++-- .../config/ConfigMapConfigProperties.java | 2 +- .../config/ConfigMapPropertySourceLocator.java | 5 +++-- .../commons/config/NamedSourceData.java | 7 ++++--- .../config/SecretsConfigProperties.java | 2 +- .../config/SecretsPropertySourceLocator.java | 10 +++++----- .../ConfigMapConfigPropertiesBindingTests.java | 6 ++++-- .../config/ConfigMapConfigPropertiesTests.java | 18 +++++++++--------- ...ernetesConfigDataLocationResolverTests.java | 4 ++-- .../SecretsConfigPropertiesBindingTests.java | 6 ++++-- .../config/SecretsConfigPropertiesTests.java | 18 +++++++++--------- .../fabric8/config/Fabric8ConfigContext.java | 2 +- .../Fabric8ConfigMapPropertySourceLocator.java | 5 +++-- .../Fabric8SecretsPropertySourceLocator.java | 5 +++-- ...edConfigMapContextToSourceDataProvider.java | 7 ++++--- ...NamedSecretContextToSourceDataProvider.java | 7 ++++--- ...onfigMapPropertySourceLocatorMockTests.java | 4 ++-- ...aConfigFailFastEnabledButRetryDisabled.java | 2 +- 19 files changed, 64 insertions(+), 54 deletions(-) diff --git a/spring-cloud-kubernetes-commons/src/main/java/org/springframework/cloud/kubernetes/commons/config/ConfigDataRetryableConfigMapPropertySourceLocator.java b/spring-cloud-kubernetes-commons/src/main/java/org/springframework/cloud/kubernetes/commons/config/ConfigDataRetryableConfigMapPropertySourceLocator.java index 6bb155bca9..c1925f409d 100644 --- a/spring-cloud-kubernetes-commons/src/main/java/org/springframework/cloud/kubernetes/commons/config/ConfigDataRetryableConfigMapPropertySourceLocator.java +++ b/spring-cloud-kubernetes-commons/src/main/java/org/springframework/cloud/kubernetes/commons/config/ConfigDataRetryableConfigMapPropertySourceLocator.java @@ -65,8 +65,8 @@ public ConfigDataRetryableConfigMapPropertySourceLocator( @Override protected MapPropertySource getMapPropertySource(NormalizedSource normalizedSource, - ConfigurableEnvironment environment) { - return configMapPropertySourceLocator.getMapPropertySource(normalizedSource, environment); + ConfigurableEnvironment environment, boolean singleRead) { + return configMapPropertySourceLocator.getMapPropertySource(normalizedSource, environment, singleRead); } @Override diff --git a/spring-cloud-kubernetes-commons/src/main/java/org/springframework/cloud/kubernetes/commons/config/ConfigDataRetryableSecretsPropertySourceLocator.java b/spring-cloud-kubernetes-commons/src/main/java/org/springframework/cloud/kubernetes/commons/config/ConfigDataRetryableSecretsPropertySourceLocator.java index d0c70e72f3..32b20e075c 100644 --- a/spring-cloud-kubernetes-commons/src/main/java/org/springframework/cloud/kubernetes/commons/config/ConfigDataRetryableSecretsPropertySourceLocator.java +++ b/spring-cloud-kubernetes-commons/src/main/java/org/springframework/cloud/kubernetes/commons/config/ConfigDataRetryableSecretsPropertySourceLocator.java @@ -73,8 +73,8 @@ public Collection> locateCollection(Environment environment) { @Override protected SecretsPropertySource getPropertySource(ConfigurableEnvironment environment, - NormalizedSource normalizedSource) { - return this.secretsPropertySourceLocator.getPropertySource(environment, normalizedSource); + NormalizedSource normalizedSource, boolean singleRead) { + return this.secretsPropertySourceLocator.getPropertySource(environment, normalizedSource, singleRead); } public SecretsPropertySourceLocator getSecretsPropertySourceLocator() { diff --git a/spring-cloud-kubernetes-commons/src/main/java/org/springframework/cloud/kubernetes/commons/config/ConfigMapConfigProperties.java b/spring-cloud-kubernetes-commons/src/main/java/org/springframework/cloud/kubernetes/commons/config/ConfigMapConfigProperties.java index beaf33d370..a184d0069f 100644 --- a/spring-cloud-kubernetes-commons/src/main/java/org/springframework/cloud/kubernetes/commons/config/ConfigMapConfigProperties.java +++ b/spring-cloud-kubernetes-commons/src/main/java/org/springframework/cloud/kubernetes/commons/config/ConfigMapConfigProperties.java @@ -40,7 +40,7 @@ public record ConfigMapConfigProperties(@DefaultValue("true") boolean enableApi, @DefaultValue List sources, @DefaultValue Map labels, @DefaultValue("true") boolean enabled, String name, String namespace, boolean useNameAsPrefix, @DefaultValue("true") boolean includeProfileSpecificSources, boolean failFast, - @DefaultValue RetryProperties retry) { + @DefaultValue RetryProperties retry, @DefaultValue("true") boolean singleRead) { /** * Prefix for Kubernetes config maps configuration properties. diff --git a/spring-cloud-kubernetes-commons/src/main/java/org/springframework/cloud/kubernetes/commons/config/ConfigMapPropertySourceLocator.java b/spring-cloud-kubernetes-commons/src/main/java/org/springframework/cloud/kubernetes/commons/config/ConfigMapPropertySourceLocator.java index 1ca300b5bb..1d68477896 100644 --- a/spring-cloud-kubernetes-commons/src/main/java/org/springframework/cloud/kubernetes/commons/config/ConfigMapPropertySourceLocator.java +++ b/spring-cloud-kubernetes-commons/src/main/java/org/springframework/cloud/kubernetes/commons/config/ConfigMapPropertySourceLocator.java @@ -71,7 +71,7 @@ public ConfigMapPropertySourceLocator(ConfigMapConfigProperties properties, Conf } protected abstract MapPropertySource getMapPropertySource(NormalizedSource normalizedSource, - ConfigurableEnvironment environment); + ConfigurableEnvironment environment, boolean singleRead); @Override public PropertySource locate(Environment environment) { @@ -81,7 +81,8 @@ public PropertySource locate(Environment environment) { if (this.properties.enableApi()) { Set sources = new LinkedHashSet<>(this.properties.determineSources(environment)); LOG.debug("Config Map normalized sources : " + sources); - sources.forEach(s -> composite.addFirstPropertySource(getMapPropertySource(s, env))); + sources.forEach( + s -> composite.addFirstPropertySource(getMapPropertySource(s, env, properties.singleRead()))); } addPropertySourcesFromPaths(environment, composite); diff --git a/spring-cloud-kubernetes-commons/src/main/java/org/springframework/cloud/kubernetes/commons/config/NamedSourceData.java b/spring-cloud-kubernetes-commons/src/main/java/org/springframework/cloud/kubernetes/commons/config/NamedSourceData.java index f98464650c..0c5713b7d9 100644 --- a/spring-cloud-kubernetes-commons/src/main/java/org/springframework/cloud/kubernetes/commons/config/NamedSourceData.java +++ b/spring-cloud-kubernetes-commons/src/main/java/org/springframework/cloud/kubernetes/commons/config/NamedSourceData.java @@ -32,7 +32,7 @@ public abstract class NamedSourceData { public final SourceData compute(String sourceName, ConfigUtils.Prefix prefix, String target, boolean profileSources, - boolean failFast, String namespace, String[] activeProfiles) { + boolean failFast, String namespace, String[] activeProfiles, boolean singleRead) { LinkedHashSet sourceNames = new LinkedHashSet<>(); // first comes non-profile based source @@ -67,10 +67,11 @@ public final SourceData compute(String sourceName, ConfigUtils.Prefix prefix, St } String names = data.names().stream().sorted().collect(Collectors.joining(PROPERTY_SOURCE_NAME_SEPARATOR)); - return new SourceData(generateSourceName(target, names, namespace, activeProfiles), data.data()); + return new SourceData(generateSourceName(target, names, namespace, activeProfiles, singleRead), data.data()); } - protected String generateSourceName(String target, String sourceName, String namespace, String[] activeProfiles) { + protected String generateSourceName(String target, String sourceName, String namespace, String[] activeProfiles, + boolean singleRead) { return ConfigUtils.sourceName(target, sourceName, namespace); } diff --git a/spring-cloud-kubernetes-commons/src/main/java/org/springframework/cloud/kubernetes/commons/config/SecretsConfigProperties.java b/spring-cloud-kubernetes-commons/src/main/java/org/springframework/cloud/kubernetes/commons/config/SecretsConfigProperties.java index baef450972..88d3bed147 100644 --- a/spring-cloud-kubernetes-commons/src/main/java/org/springframework/cloud/kubernetes/commons/config/SecretsConfigProperties.java +++ b/spring-cloud-kubernetes-commons/src/main/java/org/springframework/cloud/kubernetes/commons/config/SecretsConfigProperties.java @@ -41,7 +41,7 @@ public record SecretsConfigProperties(boolean enableApi, @DefaultValue Map paths, @DefaultValue List sources, @DefaultValue("true") boolean enabled, String name, String namespace, boolean useNameAsPrefix, @DefaultValue("true") boolean includeProfileSpecificSources, boolean failFast, - @DefaultValue RetryProperties retry) { + @DefaultValue RetryProperties retry, @DefaultValue("true") boolean singleRead) { /** * Prefix for Kubernetes secrets configuration properties. diff --git a/spring-cloud-kubernetes-commons/src/main/java/org/springframework/cloud/kubernetes/commons/config/SecretsPropertySourceLocator.java b/spring-cloud-kubernetes-commons/src/main/java/org/springframework/cloud/kubernetes/commons/config/SecretsPropertySourceLocator.java index 2eb33c801c..36c03126d9 100644 --- a/spring-cloud-kubernetes-commons/src/main/java/org/springframework/cloud/kubernetes/commons/config/SecretsPropertySourceLocator.java +++ b/spring-cloud-kubernetes-commons/src/main/java/org/springframework/cloud/kubernetes/commons/config/SecretsPropertySourceLocator.java @@ -86,8 +86,8 @@ public PropertySource locate(Environment environment) { putPathConfig(composite); if (this.properties.enableApi()) { - uniqueSources - .forEach(s -> composite.addPropertySource(getSecretsPropertySourceForSingleSecret(env, s))); + uniqueSources.forEach(s -> composite + .addPropertySource(getSecretsPropertySourceForSingleSecret(env, s, properties.singleRead()))); } cache.discardAll(); @@ -102,13 +102,13 @@ public Collection> locateCollection(Environment environment) { } private SecretsPropertySource getSecretsPropertySourceForSingleSecret(ConfigurableEnvironment environment, - NormalizedSource normalizedSource) { + NormalizedSource normalizedSource, boolean singleRead) { - return getPropertySource(environment, normalizedSource); + return getPropertySource(environment, normalizedSource, singleRead); } protected abstract SecretsPropertySource getPropertySource(ConfigurableEnvironment environment, - NormalizedSource normalizedSource); + NormalizedSource normalizedSource, boolean singleRead); protected void putPathConfig(CompositePropertySource composite) { diff --git a/spring-cloud-kubernetes-commons/src/test/java/org/springframework/cloud/kubernetes/commons/config/ConfigMapConfigPropertiesBindingTests.java b/spring-cloud-kubernetes-commons/src/test/java/org/springframework/cloud/kubernetes/commons/config/ConfigMapConfigPropertiesBindingTests.java index bab1e9d20d..9377111257 100644 --- a/spring-cloud-kubernetes-commons/src/test/java/org/springframework/cloud/kubernetes/commons/config/ConfigMapConfigPropertiesBindingTests.java +++ b/spring-cloud-kubernetes-commons/src/test/java/org/springframework/cloud/kubernetes/commons/config/ConfigMapConfigPropertiesBindingTests.java @@ -53,6 +53,7 @@ void testWithDefaults() { Assertions.assertEquals(props.retry().maxInterval(), 2000L); Assertions.assertEquals(props.retry().maxAttempts(), 6); Assertions.assertTrue(props.retry().enabled()); + Assertions.assertTrue(props.singleRead()); }); } @@ -77,7 +78,8 @@ void testWithNonDefaults() { "spring.cloud.kubernetes.config.retry.multiplier=1.2", "spring.cloud.kubernetes.config.retry.max-interval=3", "spring.cloud.kubernetes.config.retry.max-attempts=4", - "spring.cloud.kubernetes.config.retry.enabled=false") + "spring.cloud.kubernetes.config.retry.enabled=false", + "spring.cloud.kubernetes.config.single-read=false") .run(context -> { ConfigMapConfigProperties props = context.getBean(ConfigMapConfigProperties.class); Assertions.assertNotNull(props); @@ -113,7 +115,7 @@ void testWithNonDefaults() { Assertions.assertEquals(retryProperties.multiplier(), 1.2); Assertions.assertEquals(retryProperties.maxInterval(), 3); Assertions.assertFalse(retryProperties.enabled()); - + Assertions.assertFalse(props.singleRead()); }); } diff --git a/spring-cloud-kubernetes-commons/src/test/java/org/springframework/cloud/kubernetes/commons/config/ConfigMapConfigPropertiesTests.java b/spring-cloud-kubernetes-commons/src/test/java/org/springframework/cloud/kubernetes/commons/config/ConfigMapConfigPropertiesTests.java index f4b0a98cc0..40354b0530 100644 --- a/spring-cloud-kubernetes-commons/src/test/java/org/springframework/cloud/kubernetes/commons/config/ConfigMapConfigPropertiesTests.java +++ b/spring-cloud-kubernetes-commons/src/test/java/org/springframework/cloud/kubernetes/commons/config/ConfigMapConfigPropertiesTests.java @@ -47,7 +47,7 @@ class ConfigMapConfigPropertiesTests { @Test void testUseNameAsPrefixUnsetEmptySources() { ConfigMapConfigProperties properties = new ConfigMapConfigProperties(true, List.of(), List.of(), Map.of(), true, - "config-map-a", "spring-k8s", false, false, false, RetryProperties.DEFAULT); + "config-map-a", "spring-k8s", false, false, false, RetryProperties.DEFAULT, true); List sources = properties.determineSources(new MockEnvironment()); Assertions.assertEquals(sources.size(), 1, "empty sources must generate a List with a single NormalizedSource"); @@ -73,7 +73,7 @@ void testUseNameAsPrefixUnsetEmptySources() { @Test void testUseNameAsPrefixSetEmptySources() { ConfigMapConfigProperties properties = new ConfigMapConfigProperties(true, List.of(), List.of(), Map.of(), true, - "config-map-a", "spring-k8s", true, false, false, RetryProperties.DEFAULT); + "config-map-a", "spring-k8s", true, false, false, RetryProperties.DEFAULT, true); List sources = properties.determineSources(new MockEnvironment()); Assertions.assertEquals(sources.size(), 1, "empty sources must generate a List with a single NormalizedSource"); @@ -105,7 +105,7 @@ void testUseNameAsPrefixUnsetNonEmptySources() { Collections.emptyMap(), null, null, null); ConfigMapConfigProperties properties = new ConfigMapConfigProperties(true, List.of(), List.of(one), Map.of(), - true, "config-map-a", "spring-k8s", true, false, false, RetryProperties.DEFAULT); + true, "config-map-a", "spring-k8s", true, false, false, RetryProperties.DEFAULT, true); List sources = properties.determineSources(new MockEnvironment()); Assertions.assertEquals(sources.size(), 1, "a single NormalizedSource is expected"); @@ -148,7 +148,7 @@ void testUseNameAsPrefixSetNonEmptySources() { Collections.emptyMap(), null, true, null); ConfigMapConfigProperties properties = new ConfigMapConfigProperties(true, List.of(), List.of(one, two, three), - Map.of(), true, "config-map-a", "spring-k8s", true, false, false, RetryProperties.DEFAULT); + Map.of(), true, "config-map-a", "spring-k8s", true, false, false, RetryProperties.DEFAULT, true); List sources = properties.determineSources(new MockEnvironment()); Assertions.assertEquals(sources.size(), 3, "3 NormalizedSources are expected"); @@ -198,7 +198,7 @@ void testMultipleCases() { ConfigMapConfigProperties properties = new ConfigMapConfigProperties(true, List.of(), List.of(one, two, three, four), Map.of(), true, "config-map-a", "spring-k8s", true, false, false, - RetryProperties.DEFAULT); + RetryProperties.DEFAULT, true); List sources = properties.determineSources(new MockEnvironment()); Assertions.assertEquals(sources.size(), 4, "4 NormalizedSources are expected"); @@ -230,7 +230,7 @@ void testMultipleCases() { void testUseIncludeProfileSpecificSourcesNoChanges() { ConfigMapConfigProperties properties = new ConfigMapConfigProperties(true, List.of(), List.of(), Map.of(), true, - "config-map-a", "spring-k8s", false, true, false, RetryProperties.DEFAULT); + "config-map-a", "spring-k8s", false, true, false, RetryProperties.DEFAULT, true); List sources = properties.determineSources(new MockEnvironment()); Assertions.assertEquals(sources.size(), 1, "empty sources must generate a List with a single NormalizedSource"); @@ -259,7 +259,7 @@ void testUseIncludeProfileSpecificSourcesNoChanges() { void testUseIncludeProfileSpecificSourcesDefaultChanged() { ConfigMapConfigProperties properties = new ConfigMapConfigProperties(true, List.of(), List.of(), Map.of(), true, - "config-map-a", "spring-k8s", false, false, false, RetryProperties.DEFAULT); + "config-map-a", "spring-k8s", false, false, false, RetryProperties.DEFAULT, true); List sources = properties.determineSources(new MockEnvironment()); Assertions.assertEquals(sources.size(), 1, "empty sources must generate a List with a single NormalizedSource"); @@ -303,7 +303,7 @@ void testUseIncludeProfileSpecificSourcesDefaultChangedSourceOverride() { Collections.emptyMap(), null, null, false); ConfigMapConfigProperties properties = new ConfigMapConfigProperties(true, List.of(), List.of(one, two, three), - Map.of(), true, "config-map-a", "spring-k8s", false, false, false, RetryProperties.DEFAULT); + Map.of(), true, "config-map-a", "spring-k8s", false, false, false, RetryProperties.DEFAULT, true); List sources = properties.determineSources(new MockEnvironment()); Assertions.assertEquals(sources.size(), 3); @@ -361,7 +361,7 @@ void testLabelsMultipleCases() { ConfigMapConfigProperties properties = new ConfigMapConfigProperties(true, List.of(), List.of(one, two, three, four), Map.of(), true, "config-map-a", "spring-k8s", false, false, false, - RetryProperties.DEFAULT); + RetryProperties.DEFAULT, true); List sources = properties.determineSources(new MockEnvironment()); // we get 8 property sources, since "named" ones with "application" are diff --git a/spring-cloud-kubernetes-commons/src/test/java/org/springframework/cloud/kubernetes/commons/config/KubernetesConfigDataLocationResolverTests.java b/spring-cloud-kubernetes-commons/src/test/java/org/springframework/cloud/kubernetes/commons/config/KubernetesConfigDataLocationResolverTests.java index e830ac4a08..59369be802 100644 --- a/spring-cloud-kubernetes-commons/src/test/java/org/springframework/cloud/kubernetes/commons/config/KubernetesConfigDataLocationResolverTests.java +++ b/spring-cloud-kubernetes-commons/src/test/java/org/springframework/cloud/kubernetes/commons/config/KubernetesConfigDataLocationResolverTests.java @@ -225,10 +225,10 @@ void testResolveProfileSpecificFour() { // 'one' and 'two' prove that we have not registered ConfigMapConfigProperties and // SecretsConfigProperties in the bootstrap context ConfigMapConfigProperties one = new ConfigMapConfigProperties(false, List.of(), List.of(), Map.of(), false, - null, null, false, false, false, null); + null, null, false, false, false, null, true); SecretsConfigProperties two = new SecretsConfigProperties(false, Map.of(), List.of(), List.of(), false, null, - null, false, false, false, null); + null, false, false, false, null, true); KubernetesClientProperties kubernetesClientProperties = RESOLVER_CONTEXT.getBootstrapContext() .get(KubernetesClientProperties.class); diff --git a/spring-cloud-kubernetes-commons/src/test/java/org/springframework/cloud/kubernetes/commons/config/SecretsConfigPropertiesBindingTests.java b/spring-cloud-kubernetes-commons/src/test/java/org/springframework/cloud/kubernetes/commons/config/SecretsConfigPropertiesBindingTests.java index 0e390eabde..0e958bae23 100644 --- a/spring-cloud-kubernetes-commons/src/test/java/org/springframework/cloud/kubernetes/commons/config/SecretsConfigPropertiesBindingTests.java +++ b/spring-cloud-kubernetes-commons/src/test/java/org/springframework/cloud/kubernetes/commons/config/SecretsConfigPropertiesBindingTests.java @@ -53,6 +53,7 @@ void testWithDefaults() { Assertions.assertEquals(props.retry().maxInterval(), 2000L); Assertions.assertEquals(props.retry().maxAttempts(), 6); Assertions.assertTrue(props.retry().enabled()); + Assertions.assertTrue(props.singleRead()); }); } @@ -77,7 +78,8 @@ void testWithNonDefaults() { "spring.cloud.kubernetes.secrets.retry.multiplier=1.2", "spring.cloud.kubernetes.secrets.retry.max-interval=3", "spring.cloud.kubernetes.secrets.retry.max-attempts=4", - "spring.cloud.kubernetes.secrets.retry.enabled=false") + "spring.cloud.kubernetes.secrets.retry.enabled=false", + "spring.cloud.kubernetes.secrets.single-read=false") .run(context -> { SecretsConfigProperties props = context.getBean(SecretsConfigProperties.class); Assertions.assertNotNull(props); @@ -113,7 +115,7 @@ void testWithNonDefaults() { Assertions.assertEquals(retryProperties.multiplier(), 1.2); Assertions.assertEquals(retryProperties.maxInterval(), 3); Assertions.assertFalse(retryProperties.enabled()); - + Assertions.assertFalse(props.singleRead()); }); } diff --git a/spring-cloud-kubernetes-commons/src/test/java/org/springframework/cloud/kubernetes/commons/config/SecretsConfigPropertiesTests.java b/spring-cloud-kubernetes-commons/src/test/java/org/springframework/cloud/kubernetes/commons/config/SecretsConfigPropertiesTests.java index cb3cd6439f..afba918064 100644 --- a/spring-cloud-kubernetes-commons/src/test/java/org/springframework/cloud/kubernetes/commons/config/SecretsConfigPropertiesTests.java +++ b/spring-cloud-kubernetes-commons/src/test/java/org/springframework/cloud/kubernetes/commons/config/SecretsConfigPropertiesTests.java @@ -40,7 +40,7 @@ class SecretsConfigPropertiesTests { void emptySourcesSecretName() { SecretsConfigProperties properties = new SecretsConfigProperties(false, Map.of(), List.of(), List.of(), true, - null, "namespace", false, true, false, RetryProperties.DEFAULT); + null, "namespace", false, true, false, RetryProperties.DEFAULT, true); List source = properties.determineSources(new MockEnvironment()); Assertions.assertEquals(source.size(), 1); @@ -81,7 +81,7 @@ void multipleSources() { Map.of("three", "3"), null, false, false); SecretsConfigProperties properties = new SecretsConfigProperties(false, Map.of(), List.of(), - List.of(one, two, three), true, null, "namespace", false, true, false, RetryProperties.DEFAULT); + List.of(one, two, three), true, null, "namespace", false, true, false, RetryProperties.DEFAULT, true); List result = properties.determineSources(new MockEnvironment()); Assertions.assertEquals(result.size(), 6); @@ -126,7 +126,7 @@ void multipleSources() { void testUseNameAsPrefixUnsetEmptySources() { SecretsConfigProperties properties = new SecretsConfigProperties(false, Map.of(), List.of(), List.of(), true, - "secret-a", "namespace", false, true, false, RetryProperties.DEFAULT); + "secret-a", "namespace", false, true, false, RetryProperties.DEFAULT, true); List sources = properties.determineSources(new MockEnvironment()); Assertions.assertEquals(sources.size(), 1, "empty sources must generate a List with a single NormalizedSource"); @@ -152,7 +152,7 @@ void testUseNameAsPrefixUnsetEmptySources() { void testUseNameAsPrefixSetEmptySources() { SecretsConfigProperties properties = new SecretsConfigProperties(false, Map.of(), List.of(), List.of(), true, - "secret-a", "namespace", true, true, false, RetryProperties.DEFAULT); + "secret-a", "namespace", true, true, false, RetryProperties.DEFAULT, true); List sources = properties.determineSources(new MockEnvironment()); Assertions.assertEquals(sources.size(), 1, "empty sources must generate a List with a single NormalizedSource"); @@ -184,7 +184,7 @@ void testUseNameAsPrefixUnsetNonEmptySources() { null, true, false); SecretsConfigProperties properties = new SecretsConfigProperties(false, Map.of(), List.of(), List.of(one), true, - "secret-one", null, false, true, false, RetryProperties.DEFAULT); + "secret-one", null, false, true, false, RetryProperties.DEFAULT, true); List sources = properties.determineSources(new MockEnvironment()); Assertions.assertEquals(sources.size(), 1, "a single NormalizedSource is expected"); @@ -227,7 +227,7 @@ void testUseNameAsPrefixSetNonEmptySources() { Map.of(), null, true, false); SecretsConfigProperties properties = new SecretsConfigProperties(false, Map.of(), List.of(), - List.of(one, two, three), true, "secret-one", null, false, true, false, RetryProperties.DEFAULT); + List.of(one, two, three), true, "secret-one", null, false, true, false, RetryProperties.DEFAULT, true); List sources = properties.determineSources(new MockEnvironment()); Assertions.assertEquals(sources.size(), 3, "3 NormalizedSources are expected"); @@ -277,7 +277,7 @@ void testMultipleCases() { SecretsConfigProperties properties = new SecretsConfigProperties(false, Map.of(), List.of(), List.of(one, two, three, four), true, "secret-one", "spring-k8s", false, false, false, - RetryProperties.DEFAULT); + RetryProperties.DEFAULT, true); List sources = properties.determineSources(new MockEnvironment()); Assertions.assertEquals(sources.size(), 4, "4 NormalizedSources are expected"); @@ -336,8 +336,8 @@ void testLabelsMultipleCases() { Map.of("fourth-label", "secret-four"), null, false, false); SecretsConfigProperties properties = new SecretsConfigProperties(false, Map.of(), List.of(), - List.of(one, two, three, four), false, null, "spring-k8s", false, false, false, - RetryProperties.DEFAULT); + List.of(one, two, three, four), false, null, "spring-k8s", false, false, false, RetryProperties.DEFAULT, + true); List sources = properties.determineSources(new MockEnvironment()); // we get 8 property sources, since "named" ones with "application" are diff --git a/spring-cloud-kubernetes-fabric8-config/src/main/java/org/springframework/cloud/kubernetes/fabric8/config/Fabric8ConfigContext.java b/spring-cloud-kubernetes-fabric8-config/src/main/java/org/springframework/cloud/kubernetes/fabric8/config/Fabric8ConfigContext.java index 0e51ac0ce9..33f1c49d51 100644 --- a/spring-cloud-kubernetes-fabric8-config/src/main/java/org/springframework/cloud/kubernetes/fabric8/config/Fabric8ConfigContext.java +++ b/spring-cloud-kubernetes-fabric8-config/src/main/java/org/springframework/cloud/kubernetes/fabric8/config/Fabric8ConfigContext.java @@ -28,5 +28,5 @@ * @author wind57 */ record Fabric8ConfigContext(KubernetesClient client, NormalizedSource normalizedSource, String namespace, - Environment environment) { + Environment environment, boolean singleRead) { } diff --git a/spring-cloud-kubernetes-fabric8-config/src/main/java/org/springframework/cloud/kubernetes/fabric8/config/Fabric8ConfigMapPropertySourceLocator.java b/spring-cloud-kubernetes-fabric8-config/src/main/java/org/springframework/cloud/kubernetes/fabric8/config/Fabric8ConfigMapPropertySourceLocator.java index 2231293e7e..090166fdbe 100644 --- a/spring-cloud-kubernetes-fabric8-config/src/main/java/org/springframework/cloud/kubernetes/fabric8/config/Fabric8ConfigMapPropertySourceLocator.java +++ b/spring-cloud-kubernetes-fabric8-config/src/main/java/org/springframework/cloud/kubernetes/fabric8/config/Fabric8ConfigMapPropertySourceLocator.java @@ -52,12 +52,13 @@ public class Fabric8ConfigMapPropertySourceLocator extends ConfigMapPropertySour @Override protected MapPropertySource getMapPropertySource(NormalizedSource normalizedSource, - ConfigurableEnvironment environment) { + ConfigurableEnvironment environment, boolean singleRead) { // NormalizedSource has a namespace, but users can skip it. // In such cases we try to get it elsewhere String namespace = getApplicationNamespace(this.client, normalizedSource.namespace().orElse(null), normalizedSource.target(), provider); - Fabric8ConfigContext context = new Fabric8ConfigContext(client, normalizedSource, namespace, environment); + Fabric8ConfigContext context = new Fabric8ConfigContext(client, normalizedSource, namespace, environment, + singleRead); return new Fabric8ConfigMapPropertySource(context); } diff --git a/spring-cloud-kubernetes-fabric8-config/src/main/java/org/springframework/cloud/kubernetes/fabric8/config/Fabric8SecretsPropertySourceLocator.java b/spring-cloud-kubernetes-fabric8-config/src/main/java/org/springframework/cloud/kubernetes/fabric8/config/Fabric8SecretsPropertySourceLocator.java index 2d098ebfab..70e081dba3 100644 --- a/spring-cloud-kubernetes-fabric8-config/src/main/java/org/springframework/cloud/kubernetes/fabric8/config/Fabric8SecretsPropertySourceLocator.java +++ b/spring-cloud-kubernetes-fabric8-config/src/main/java/org/springframework/cloud/kubernetes/fabric8/config/Fabric8SecretsPropertySourceLocator.java @@ -52,12 +52,13 @@ public class Fabric8SecretsPropertySourceLocator extends SecretsPropertySourceLo @Override protected SecretsPropertySource getPropertySource(ConfigurableEnvironment environment, - NormalizedSource normalizedSource) { + NormalizedSource normalizedSource, boolean singleRead) { // NormalizedSource has a namespace, but users can skip it. // In such cases we try to get it elsewhere String namespace = getApplicationNamespace(client, normalizedSource.namespace().orElse(null), normalizedSource.target(), provider); - Fabric8ConfigContext context = new Fabric8ConfigContext(client, normalizedSource, namespace, environment); + Fabric8ConfigContext context = new Fabric8ConfigContext(client, normalizedSource, namespace, environment, + singleRead); return new Fabric8SecretsPropertySource(context); } diff --git a/spring-cloud-kubernetes-fabric8-config/src/main/java/org/springframework/cloud/kubernetes/fabric8/config/NamedConfigMapContextToSourceDataProvider.java b/spring-cloud-kubernetes-fabric8-config/src/main/java/org/springframework/cloud/kubernetes/fabric8/config/NamedConfigMapContextToSourceDataProvider.java index 3ac61a7ca5..19a7eae0d3 100644 --- a/spring-cloud-kubernetes-fabric8-config/src/main/java/org/springframework/cloud/kubernetes/fabric8/config/NamedConfigMapContextToSourceDataProvider.java +++ b/spring-cloud-kubernetes-fabric8-config/src/main/java/org/springframework/cloud/kubernetes/fabric8/config/NamedConfigMapContextToSourceDataProvider.java @@ -53,11 +53,11 @@ public Fabric8ContextToSourceData get() { @Override protected String generateSourceName(String target, String sourceName, String namespace, - String[] activeProfiles) { + String[] activeProfiles, boolean singleRead) { if (source.appendProfileToName()) { return ConfigUtils.sourceName(target, sourceName, namespace, activeProfiles); } - return super.generateSourceName(target, sourceName, namespace, activeProfiles); + return super.generateSourceName(target, sourceName, namespace, activeProfiles, singleRead); } @Override @@ -66,7 +66,8 @@ public MultipleSourcesContainer dataSupplier(LinkedHashSet sourceNames) context.environment()); } }.compute(source.name().orElseThrow(), source.prefix(), source.target(), source.profileSpecificSources(), - source.failFast(), context.namespace(), context.environment().getActiveProfiles()); + source.failFast(), context.namespace(), context.environment().getActiveProfiles(), + context.singleRead()); }; } diff --git a/spring-cloud-kubernetes-fabric8-config/src/main/java/org/springframework/cloud/kubernetes/fabric8/config/NamedSecretContextToSourceDataProvider.java b/spring-cloud-kubernetes-fabric8-config/src/main/java/org/springframework/cloud/kubernetes/fabric8/config/NamedSecretContextToSourceDataProvider.java index 5a2356085d..021adee049 100644 --- a/spring-cloud-kubernetes-fabric8-config/src/main/java/org/springframework/cloud/kubernetes/fabric8/config/NamedSecretContextToSourceDataProvider.java +++ b/spring-cloud-kubernetes-fabric8-config/src/main/java/org/springframework/cloud/kubernetes/fabric8/config/NamedSecretContextToSourceDataProvider.java @@ -43,11 +43,11 @@ public Fabric8ContextToSourceData get() { return new NamedSourceData() { @Override protected String generateSourceName(String target, String sourceName, String namespace, - String[] activeProfiles) { + String[] activeProfiles, boolean singleRead) { if (source.appendProfileToName()) { return ConfigUtils.sourceName(target, sourceName, namespace, activeProfiles); } - return super.generateSourceName(target, sourceName, namespace, activeProfiles); + return super.generateSourceName(target, sourceName, namespace, activeProfiles, singleRead); } @Override @@ -56,7 +56,8 @@ public MultipleSourcesContainer dataSupplier(LinkedHashSet sourceNames) context.environment()); } }.compute(source.name().orElseThrow(), source.prefix(), source.target(), source.profileSpecificSources(), - source.failFast(), context.namespace(), context.environment().getActiveProfiles()); + source.failFast(), context.namespace(), context.environment().getActiveProfiles(), + context.singleRead()); }; } diff --git a/spring-cloud-kubernetes-fabric8-config/src/test/java/org/springframework/cloud/kubernetes/fabric8/config/Fabric8ConfigMapPropertySourceLocatorMockTests.java b/spring-cloud-kubernetes-fabric8-config/src/test/java/org/springframework/cloud/kubernetes/fabric8/config/Fabric8ConfigMapPropertySourceLocatorMockTests.java index 2e9f1421a7..8dbbcf3feb 100644 --- a/spring-cloud-kubernetes-fabric8-config/src/test/java/org/springframework/cloud/kubernetes/fabric8/config/Fabric8ConfigMapPropertySourceLocatorMockTests.java +++ b/spring-cloud-kubernetes-fabric8-config/src/test/java/org/springframework/cloud/kubernetes/fabric8/config/Fabric8ConfigMapPropertySourceLocatorMockTests.java @@ -47,13 +47,13 @@ class Fabric8ConfigMapPropertySourceLocatorMockTests { void constructorWithoutClientNamespaceMustFail() { ConfigMapConfigProperties configMapConfigProperties = new ConfigMapConfigProperties(true, List.of(), List.of(), - Map.of(), true, "name", null, false, true, false, RetryProperties.DEFAULT); + Map.of(), true, "name", null, false, true, false, RetryProperties.DEFAULT, true); Mockito.when(client.getNamespace()).thenReturn(null); Fabric8ConfigMapPropertySourceLocator source = new Fabric8ConfigMapPropertySourceLocator(client, configMapConfigProperties, new KubernetesNamespaceProvider(new MockEnvironment())); NormalizedSource normalizedSource = new NamedConfigMapNormalizedSource("name", null, false, PREFIX, false); - assertThatThrownBy(() -> source.getMapPropertySource(normalizedSource, new MockEnvironment())) + assertThatThrownBy(() -> source.getMapPropertySource(normalizedSource, new MockEnvironment(), true)) .isInstanceOf(NamespaceResolutionFailedException.class); } diff --git a/spring-cloud-kubernetes-fabric8-config/src/test/java/org/springframework/cloud/kubernetes/fabric8/config/locator_retry/fail_fast_enabled_retry_disabled/ConfigDataConfigFailFastEnabledButRetryDisabled.java b/spring-cloud-kubernetes-fabric8-config/src/test/java/org/springframework/cloud/kubernetes/fabric8/config/locator_retry/fail_fast_enabled_retry_disabled/ConfigDataConfigFailFastEnabledButRetryDisabled.java index a3bc41c037..e451c57785 100644 --- a/spring-cloud-kubernetes-fabric8-config/src/test/java/org/springframework/cloud/kubernetes/fabric8/config/locator_retry/fail_fast_enabled_retry_disabled/ConfigDataConfigFailFastEnabledButRetryDisabled.java +++ b/spring-cloud-kubernetes-fabric8-config/src/test/java/org/springframework/cloud/kubernetes/fabric8/config/locator_retry/fail_fast_enabled_retry_disabled/ConfigDataConfigFailFastEnabledButRetryDisabled.java @@ -78,7 +78,7 @@ static class LocalConfig { ConfigMapConfigProperties properties(Environment environment) { return new ConfigMapConfigProperties(true, List.of(), List.of(), Map.of(), true, null, null, false, true, Boolean.parseBoolean(environment.getProperty("spring.cloud.kubernetes.config.fail-fast")), - RetryProperties.DEFAULT); + RetryProperties.DEFAULT, true); } } From b0660860434d88dc2012b33fcae8688a5a60e30b Mon Sep 17 00:00:00 2001 From: wind57 Date: Fri, 6 Sep 2024 12:22:55 +0300 Subject: [PATCH 08/21] dirty --- ...dConfigMapContextToSourceDataProvider.java | 8 +- ...tryableConfigMapPropertySourceLocator.java | 4 +- ...RetryableSecretsPropertySourceLocator.java | 4 +- .../config/ConfigMapConfigProperties.java | 5 +- .../ConfigMapPropertySourceLocator.java | 6 +- .../commons/config/ConfigUtils.java | 7 +- .../commons/config/NamedSourceData.java | 9 +- .../config/SecretsConfigProperties.java | 2 +- .../config/SecretsPropertySourceLocator.java | 10 +- .../kubernetes/commons/config/SourceData.java | 2 +- ...ConfigMapConfigPropertiesBindingTests.java | 6 +- .../SecretsConfigPropertiesBindingTests.java | 4 +- .../fabric8/config/Fabric8ConfigContext.java | 2 +- .../Fabric8ConfigDataLocationResolver.java | 2 +- ...Fabric8ConfigMapPropertySourceLocator.java | 6 +- ...=> Fabric8ConfigMapsNamespaceBatched.java} | 15 +-- .../fabric8/config/Fabric8ConfigUtils.java | 112 ++++++++++++++++-- .../Fabric8SecretsPropertySourceLocator.java | 4 +- .../config/Fabric8SourcesNonBatched.java | 80 +++++++++++++ ...dConfigMapContextToSourceDataProvider.java | 8 +- ...amedSecretContextToSourceDataProvider.java | 6 +- .../fabric8/config/ConfigMapsTest.java | 2 +- .../Fabric8ConfigMapPropertySourceTests.java | 2 +- .../config/Fabric8ConfigUtilsTests.java | 2 +- ...igMapContextToSourceDataProviderTests.java | 2 +- ...igMapContextToSourceDataProviderTests.java | 2 +- 26 files changed, 240 insertions(+), 72 deletions(-) rename spring-cloud-kubernetes-fabric8-config/src/main/java/org/springframework/cloud/kubernetes/fabric8/config/{Fabric8ConfigMapsCache.java => Fabric8ConfigMapsNamespaceBatched.java} (81%) create mode 100644 spring-cloud-kubernetes-fabric8-config/src/main/java/org/springframework/cloud/kubernetes/fabric8/config/Fabric8SourcesNonBatched.java diff --git a/spring-cloud-kubernetes-client-config/src/main/java/org/springframework/cloud/kubernetes/client/config/NamedConfigMapContextToSourceDataProvider.java b/spring-cloud-kubernetes-client-config/src/main/java/org/springframework/cloud/kubernetes/client/config/NamedConfigMapContextToSourceDataProvider.java index 56645a4c51..18c1668d96 100644 --- a/spring-cloud-kubernetes-client-config/src/main/java/org/springframework/cloud/kubernetes/client/config/NamedConfigMapContextToSourceDataProvider.java +++ b/spring-cloud-kubernetes-client-config/src/main/java/org/springframework/cloud/kubernetes/client/config/NamedConfigMapContextToSourceDataProvider.java @@ -45,11 +45,11 @@ public KubernetesClientContextToSourceData get() { return new NamedSourceData() { @Override protected String generateSourceName(String target, String sourceName, String namespace, - String[] activeProfiles) { + String[] activeProfiles, boolean namespacedBatchRead) { if (source.appendProfileToName()) { return ConfigUtils.sourceName(target, sourceName, namespace, activeProfiles); } - return super.generateSourceName(target, sourceName, namespace, activeProfiles); + return super.generateSourceName(target, sourceName, namespace, activeProfiles, namespacedBatchRead); } @Override @@ -58,7 +58,9 @@ public MultipleSourcesContainer dataSupplier(LinkedHashSet sourceNames) sourceNames, context.environment(), context.includeDefaultProfileData()); } }.compute(source.name().orElseThrow(), source.prefix(), source.target(), source.profileSpecificSources(), - source.failFast(), context.namespace(), context.environment().getActiveProfiles()); + source.failFast(), context.namespace(), context.environment().getActiveProfiles(), + context.si + ); }; } diff --git a/spring-cloud-kubernetes-commons/src/main/java/org/springframework/cloud/kubernetes/commons/config/ConfigDataRetryableConfigMapPropertySourceLocator.java b/spring-cloud-kubernetes-commons/src/main/java/org/springframework/cloud/kubernetes/commons/config/ConfigDataRetryableConfigMapPropertySourceLocator.java index c1925f409d..90a7afe585 100644 --- a/spring-cloud-kubernetes-commons/src/main/java/org/springframework/cloud/kubernetes/commons/config/ConfigDataRetryableConfigMapPropertySourceLocator.java +++ b/spring-cloud-kubernetes-commons/src/main/java/org/springframework/cloud/kubernetes/commons/config/ConfigDataRetryableConfigMapPropertySourceLocator.java @@ -65,8 +65,8 @@ public ConfigDataRetryableConfigMapPropertySourceLocator( @Override protected MapPropertySource getMapPropertySource(NormalizedSource normalizedSource, - ConfigurableEnvironment environment, boolean singleRead) { - return configMapPropertySourceLocator.getMapPropertySource(normalizedSource, environment, singleRead); + ConfigurableEnvironment environment, boolean namespacedBatchRead) { + return configMapPropertySourceLocator.getMapPropertySource(normalizedSource, environment, namespacedBatchRead); } @Override diff --git a/spring-cloud-kubernetes-commons/src/main/java/org/springframework/cloud/kubernetes/commons/config/ConfigDataRetryableSecretsPropertySourceLocator.java b/spring-cloud-kubernetes-commons/src/main/java/org/springframework/cloud/kubernetes/commons/config/ConfigDataRetryableSecretsPropertySourceLocator.java index 32b20e075c..2e14df8a34 100644 --- a/spring-cloud-kubernetes-commons/src/main/java/org/springframework/cloud/kubernetes/commons/config/ConfigDataRetryableSecretsPropertySourceLocator.java +++ b/spring-cloud-kubernetes-commons/src/main/java/org/springframework/cloud/kubernetes/commons/config/ConfigDataRetryableSecretsPropertySourceLocator.java @@ -73,8 +73,8 @@ public Collection> locateCollection(Environment environment) { @Override protected SecretsPropertySource getPropertySource(ConfigurableEnvironment environment, - NormalizedSource normalizedSource, boolean singleRead) { - return this.secretsPropertySourceLocator.getPropertySource(environment, normalizedSource, singleRead); + NormalizedSource normalizedSource, boolean namespacedBatchRead) { + return this.secretsPropertySourceLocator.getPropertySource(environment, normalizedSource, namespacedBatchRead); } public SecretsPropertySourceLocator getSecretsPropertySourceLocator() { diff --git a/spring-cloud-kubernetes-commons/src/main/java/org/springframework/cloud/kubernetes/commons/config/ConfigMapConfigProperties.java b/spring-cloud-kubernetes-commons/src/main/java/org/springframework/cloud/kubernetes/commons/config/ConfigMapConfigProperties.java index a184d0069f..4241707068 100644 --- a/spring-cloud-kubernetes-commons/src/main/java/org/springframework/cloud/kubernetes/commons/config/ConfigMapConfigProperties.java +++ b/spring-cloud-kubernetes-commons/src/main/java/org/springframework/cloud/kubernetes/commons/config/ConfigMapConfigProperties.java @@ -19,7 +19,6 @@ import java.util.ArrayList; import java.util.List; import java.util.Map; -import java.util.stream.Collectors; import java.util.stream.Stream; import org.springframework.boot.context.properties.ConfigurationProperties; @@ -40,7 +39,7 @@ public record ConfigMapConfigProperties(@DefaultValue("true") boolean enableApi, @DefaultValue List sources, @DefaultValue Map labels, @DefaultValue("true") boolean enabled, String name, String namespace, boolean useNameAsPrefix, @DefaultValue("true") boolean includeProfileSpecificSources, boolean failFast, - @DefaultValue RetryProperties retry, @DefaultValue("true") boolean singleRead) { + @DefaultValue RetryProperties retry, @DefaultValue("true") boolean namespacedBatchRead) { /** * Prefix for Kubernetes config maps configuration properties. @@ -67,7 +66,7 @@ public List determineSources(Environment environment) { return this.sources.stream() .flatMap(s -> s.normalize(this.name, this.namespace, this.labels, this.includeProfileSpecificSources, this.failFast, this.useNameAsPrefix, environment)) - .collect(Collectors.toList()); + .toList(); } /** diff --git a/spring-cloud-kubernetes-commons/src/main/java/org/springframework/cloud/kubernetes/commons/config/ConfigMapPropertySourceLocator.java b/spring-cloud-kubernetes-commons/src/main/java/org/springframework/cloud/kubernetes/commons/config/ConfigMapPropertySourceLocator.java index 1d68477896..9f430675c7 100644 --- a/spring-cloud-kubernetes-commons/src/main/java/org/springframework/cloud/kubernetes/commons/config/ConfigMapPropertySourceLocator.java +++ b/spring-cloud-kubernetes-commons/src/main/java/org/springframework/cloud/kubernetes/commons/config/ConfigMapPropertySourceLocator.java @@ -71,7 +71,7 @@ public ConfigMapPropertySourceLocator(ConfigMapConfigProperties properties, Conf } protected abstract MapPropertySource getMapPropertySource(NormalizedSource normalizedSource, - ConfigurableEnvironment environment, boolean singleRead); + ConfigurableEnvironment environment, boolean namespacedBatchRead); @Override public PropertySource locate(Environment environment) { @@ -81,8 +81,8 @@ public PropertySource locate(Environment environment) { if (this.properties.enableApi()) { Set sources = new LinkedHashSet<>(this.properties.determineSources(environment)); LOG.debug("Config Map normalized sources : " + sources); - sources.forEach( - s -> composite.addFirstPropertySource(getMapPropertySource(s, env, properties.singleRead()))); + sources.forEach(s -> composite + .addFirstPropertySource(getMapPropertySource(s, env, properties.namespacedBatchRead()))); } addPropertySourcesFromPaths(environment, composite); diff --git a/spring-cloud-kubernetes-commons/src/main/java/org/springframework/cloud/kubernetes/commons/config/ConfigUtils.java b/spring-cloud-kubernetes-commons/src/main/java/org/springframework/cloud/kubernetes/commons/config/ConfigUtils.java index 5f40858eab..93e6828a68 100644 --- a/spring-cloud-kubernetes-commons/src/main/java/org/springframework/cloud/kubernetes/commons/config/ConfigUtils.java +++ b/spring-cloud-kubernetes-commons/src/main/java/org/springframework/cloud/kubernetes/commons/config/ConfigUtils.java @@ -216,9 +216,10 @@ public static MultipleSourcesContainer processNamedData(List sourceNames = new LinkedHashSet<>(); // first comes non-profile based source @@ -67,11 +67,12 @@ public final SourceData compute(String sourceName, ConfigUtils.Prefix prefix, St } String names = data.names().stream().sorted().collect(Collectors.joining(PROPERTY_SOURCE_NAME_SEPARATOR)); - return new SourceData(generateSourceName(target, names, namespace, activeProfiles, singleRead), data.data()); + return new SourceData(generateSourceName(target, names, namespace, activeProfiles, namespacedBatchRead), + data.data()); } protected String generateSourceName(String target, String sourceName, String namespace, String[] activeProfiles, - boolean singleRead) { + boolean namespacedBatchRead) { return ConfigUtils.sourceName(target, sourceName, namespace); } @@ -82,6 +83,6 @@ protected String generateSourceName(String target, String sourceName, String nam * preserve the order: non-profile source first and then the rest * @return an Entry that holds the names of the source that were found and their data */ - public abstract MultipleSourcesContainer dataSupplier(LinkedHashSet sourceNames); + protected abstract MultipleSourcesContainer dataSupplier(LinkedHashSet sourceNames); } diff --git a/spring-cloud-kubernetes-commons/src/main/java/org/springframework/cloud/kubernetes/commons/config/SecretsConfigProperties.java b/spring-cloud-kubernetes-commons/src/main/java/org/springframework/cloud/kubernetes/commons/config/SecretsConfigProperties.java index 88d3bed147..cad2549e2a 100644 --- a/spring-cloud-kubernetes-commons/src/main/java/org/springframework/cloud/kubernetes/commons/config/SecretsConfigProperties.java +++ b/spring-cloud-kubernetes-commons/src/main/java/org/springframework/cloud/kubernetes/commons/config/SecretsConfigProperties.java @@ -41,7 +41,7 @@ public record SecretsConfigProperties(boolean enableApi, @DefaultValue Map paths, @DefaultValue List sources, @DefaultValue("true") boolean enabled, String name, String namespace, boolean useNameAsPrefix, @DefaultValue("true") boolean includeProfileSpecificSources, boolean failFast, - @DefaultValue RetryProperties retry, @DefaultValue("true") boolean singleRead) { + @DefaultValue RetryProperties retry, @DefaultValue("true") boolean namespacedBatchRead) { /** * Prefix for Kubernetes secrets configuration properties. diff --git a/spring-cloud-kubernetes-commons/src/main/java/org/springframework/cloud/kubernetes/commons/config/SecretsPropertySourceLocator.java b/spring-cloud-kubernetes-commons/src/main/java/org/springframework/cloud/kubernetes/commons/config/SecretsPropertySourceLocator.java index 36c03126d9..d02b076f63 100644 --- a/spring-cloud-kubernetes-commons/src/main/java/org/springframework/cloud/kubernetes/commons/config/SecretsPropertySourceLocator.java +++ b/spring-cloud-kubernetes-commons/src/main/java/org/springframework/cloud/kubernetes/commons/config/SecretsPropertySourceLocator.java @@ -86,8 +86,8 @@ public PropertySource locate(Environment environment) { putPathConfig(composite); if (this.properties.enableApi()) { - uniqueSources.forEach(s -> composite - .addPropertySource(getSecretsPropertySourceForSingleSecret(env, s, properties.singleRead()))); + uniqueSources.forEach(s -> composite.addPropertySource( + getSecretsPropertySourceForSingleSecret(env, s, properties.namespacedBatchRead()))); } cache.discardAll(); @@ -102,13 +102,13 @@ public Collection> locateCollection(Environment environment) { } private SecretsPropertySource getSecretsPropertySourceForSingleSecret(ConfigurableEnvironment environment, - NormalizedSource normalizedSource, boolean singleRead) { + NormalizedSource normalizedSource, boolean namespacedBatchRead) { - return getPropertySource(environment, normalizedSource, singleRead); + return getPropertySource(environment, normalizedSource, namespacedBatchRead); } protected abstract SecretsPropertySource getPropertySource(ConfigurableEnvironment environment, - NormalizedSource normalizedSource, boolean singleRead); + NormalizedSource normalizedSource, boolean namespacedBatchRead); protected void putPathConfig(CompositePropertySource composite) { diff --git a/spring-cloud-kubernetes-commons/src/main/java/org/springframework/cloud/kubernetes/commons/config/SourceData.java b/spring-cloud-kubernetes-commons/src/main/java/org/springframework/cloud/kubernetes/commons/config/SourceData.java index 29b4280e22..83d27f0bbc 100644 --- a/spring-cloud-kubernetes-commons/src/main/java/org/springframework/cloud/kubernetes/commons/config/SourceData.java +++ b/spring-cloud-kubernetes-commons/src/main/java/org/springframework/cloud/kubernetes/commons/config/SourceData.java @@ -25,7 +25,7 @@ * * @author wind57 */ -public final record SourceData(String sourceName, Map sourceData) { +public record SourceData(String sourceName, Map sourceData) { public static SourceData emptyRecord(String sourceName) { return new SourceData(sourceName, Collections.emptyMap()); diff --git a/spring-cloud-kubernetes-commons/src/test/java/org/springframework/cloud/kubernetes/commons/config/ConfigMapConfigPropertiesBindingTests.java b/spring-cloud-kubernetes-commons/src/test/java/org/springframework/cloud/kubernetes/commons/config/ConfigMapConfigPropertiesBindingTests.java index 9377111257..4aa0f7f2b6 100644 --- a/spring-cloud-kubernetes-commons/src/test/java/org/springframework/cloud/kubernetes/commons/config/ConfigMapConfigPropertiesBindingTests.java +++ b/spring-cloud-kubernetes-commons/src/test/java/org/springframework/cloud/kubernetes/commons/config/ConfigMapConfigPropertiesBindingTests.java @@ -53,7 +53,7 @@ void testWithDefaults() { Assertions.assertEquals(props.retry().maxInterval(), 2000L); Assertions.assertEquals(props.retry().maxAttempts(), 6); Assertions.assertTrue(props.retry().enabled()); - Assertions.assertTrue(props.singleRead()); + Assertions.assertTrue(props.namespacedBatchRead()); }); } @@ -79,7 +79,7 @@ void testWithNonDefaults() { "spring.cloud.kubernetes.config.retry.max-interval=3", "spring.cloud.kubernetes.config.retry.max-attempts=4", "spring.cloud.kubernetes.config.retry.enabled=false", - "spring.cloud.kubernetes.config.single-read=false") + "spring.cloud.kubernetes.config.namespaced-batch-read=false") .run(context -> { ConfigMapConfigProperties props = context.getBean(ConfigMapConfigProperties.class); Assertions.assertNotNull(props); @@ -115,7 +115,7 @@ void testWithNonDefaults() { Assertions.assertEquals(retryProperties.multiplier(), 1.2); Assertions.assertEquals(retryProperties.maxInterval(), 3); Assertions.assertFalse(retryProperties.enabled()); - Assertions.assertFalse(props.singleRead()); + Assertions.assertFalse(props.namespacedBatchRead()); }); } diff --git a/spring-cloud-kubernetes-commons/src/test/java/org/springframework/cloud/kubernetes/commons/config/SecretsConfigPropertiesBindingTests.java b/spring-cloud-kubernetes-commons/src/test/java/org/springframework/cloud/kubernetes/commons/config/SecretsConfigPropertiesBindingTests.java index 0e958bae23..22889b921a 100644 --- a/spring-cloud-kubernetes-commons/src/test/java/org/springframework/cloud/kubernetes/commons/config/SecretsConfigPropertiesBindingTests.java +++ b/spring-cloud-kubernetes-commons/src/test/java/org/springframework/cloud/kubernetes/commons/config/SecretsConfigPropertiesBindingTests.java @@ -53,7 +53,7 @@ void testWithDefaults() { Assertions.assertEquals(props.retry().maxInterval(), 2000L); Assertions.assertEquals(props.retry().maxAttempts(), 6); Assertions.assertTrue(props.retry().enabled()); - Assertions.assertTrue(props.singleRead()); + Assertions.assertTrue(props.namespacedBatchRead()); }); } @@ -115,7 +115,7 @@ void testWithNonDefaults() { Assertions.assertEquals(retryProperties.multiplier(), 1.2); Assertions.assertEquals(retryProperties.maxInterval(), 3); Assertions.assertFalse(retryProperties.enabled()); - Assertions.assertFalse(props.singleRead()); + Assertions.assertFalse(props.namespacedBatchRead()); }); } diff --git a/spring-cloud-kubernetes-fabric8-config/src/main/java/org/springframework/cloud/kubernetes/fabric8/config/Fabric8ConfigContext.java b/spring-cloud-kubernetes-fabric8-config/src/main/java/org/springframework/cloud/kubernetes/fabric8/config/Fabric8ConfigContext.java index 33f1c49d51..517a0d0569 100644 --- a/spring-cloud-kubernetes-fabric8-config/src/main/java/org/springframework/cloud/kubernetes/fabric8/config/Fabric8ConfigContext.java +++ b/spring-cloud-kubernetes-fabric8-config/src/main/java/org/springframework/cloud/kubernetes/fabric8/config/Fabric8ConfigContext.java @@ -28,5 +28,5 @@ * @author wind57 */ record Fabric8ConfigContext(KubernetesClient client, NormalizedSource normalizedSource, String namespace, - Environment environment, boolean singleRead) { + Environment environment, boolean namespacedBatchRead) { } diff --git a/spring-cloud-kubernetes-fabric8-config/src/main/java/org/springframework/cloud/kubernetes/fabric8/config/Fabric8ConfigDataLocationResolver.java b/spring-cloud-kubernetes-fabric8-config/src/main/java/org/springframework/cloud/kubernetes/fabric8/config/Fabric8ConfigDataLocationResolver.java index 08c9c71f5b..511278b0cd 100644 --- a/spring-cloud-kubernetes-fabric8-config/src/main/java/org/springframework/cloud/kubernetes/fabric8/config/Fabric8ConfigDataLocationResolver.java +++ b/spring-cloud-kubernetes-fabric8-config/src/main/java/org/springframework/cloud/kubernetes/fabric8/config/Fabric8ConfigDataLocationResolver.java @@ -63,7 +63,7 @@ protected void registerBeans(ConfigDataLocationResolverContext resolverContext, kubernetesClient, configMapProperties, namespaceProvider); if (isRetryEnabledForConfigMap(configMapProperties)) { configMapPropertySourceLocator = new ConfigDataRetryableConfigMapPropertySourceLocator( - configMapPropertySourceLocator, configMapProperties, new Fabric8ConfigMapsCache()); + configMapPropertySourceLocator, configMapProperties, new Fabric8ConfigMapsNamespaceBatched()); } registerSingle(bootstrapContext, ConfigMapPropertySourceLocator.class, configMapPropertySourceLocator, diff --git a/spring-cloud-kubernetes-fabric8-config/src/main/java/org/springframework/cloud/kubernetes/fabric8/config/Fabric8ConfigMapPropertySourceLocator.java b/spring-cloud-kubernetes-fabric8-config/src/main/java/org/springframework/cloud/kubernetes/fabric8/config/Fabric8ConfigMapPropertySourceLocator.java index 090166fdbe..ff131d1af5 100644 --- a/spring-cloud-kubernetes-fabric8-config/src/main/java/org/springframework/cloud/kubernetes/fabric8/config/Fabric8ConfigMapPropertySourceLocator.java +++ b/spring-cloud-kubernetes-fabric8-config/src/main/java/org/springframework/cloud/kubernetes/fabric8/config/Fabric8ConfigMapPropertySourceLocator.java @@ -45,20 +45,20 @@ public class Fabric8ConfigMapPropertySourceLocator extends ConfigMapPropertySour Fabric8ConfigMapPropertySourceLocator(KubernetesClient client, ConfigMapConfigProperties properties, KubernetesNamespaceProvider provider) { - super(properties, new Fabric8ConfigMapsCache()); + super(properties, new Fabric8ConfigMapsNamespaceBatched()); this.client = client; this.provider = provider; } @Override protected MapPropertySource getMapPropertySource(NormalizedSource normalizedSource, - ConfigurableEnvironment environment, boolean singleRead) { + ConfigurableEnvironment environment, boolean namespacedBatchRead) { // NormalizedSource has a namespace, but users can skip it. // In such cases we try to get it elsewhere String namespace = getApplicationNamespace(this.client, normalizedSource.namespace().orElse(null), normalizedSource.target(), provider); Fabric8ConfigContext context = new Fabric8ConfigContext(client, normalizedSource, namespace, environment, - singleRead); + namespacedBatchRead); return new Fabric8ConfigMapPropertySource(context); } diff --git a/spring-cloud-kubernetes-fabric8-config/src/main/java/org/springframework/cloud/kubernetes/fabric8/config/Fabric8ConfigMapsCache.java b/spring-cloud-kubernetes-fabric8-config/src/main/java/org/springframework/cloud/kubernetes/fabric8/config/Fabric8ConfigMapsNamespaceBatched.java similarity index 81% rename from spring-cloud-kubernetes-fabric8-config/src/main/java/org/springframework/cloud/kubernetes/fabric8/config/Fabric8ConfigMapsCache.java rename to spring-cloud-kubernetes-fabric8-config/src/main/java/org/springframework/cloud/kubernetes/fabric8/config/Fabric8ConfigMapsNamespaceBatched.java index 65436eb82f..f7078cd85a 100644 --- a/spring-cloud-kubernetes-fabric8-config/src/main/java/org/springframework/cloud/kubernetes/fabric8/config/Fabric8ConfigMapsCache.java +++ b/spring-cloud-kubernetes-fabric8-config/src/main/java/org/springframework/cloud/kubernetes/fabric8/config/Fabric8ConfigMapsNamespaceBatched.java @@ -18,9 +18,7 @@ import java.util.List; import java.util.concurrent.ConcurrentHashMap; -import java.util.stream.Collectors; -import io.fabric8.kubernetes.api.model.ConfigMap; import io.fabric8.kubernetes.client.KubernetesClient; import org.apache.commons.logging.LogFactory; @@ -28,15 +26,17 @@ import org.springframework.cloud.kubernetes.commons.config.StrippedSourceContainer; import org.springframework.core.log.LogAccessor; +import static org.springframework.cloud.kubernetes.fabric8.config.Fabric8ConfigUtils.strippedConfigMaps; + /** * A cache of ConfigMaps per namespace. Makes sure we read config maps only once from a * namespace. * * @author wind57 */ -final class Fabric8ConfigMapsCache implements ConfigMapCache { +final class Fabric8ConfigMapsNamespaceBatched implements ConfigMapCache { - private static final LogAccessor LOG = new LogAccessor(LogFactory.getLog(Fabric8ConfigMapsCache.class)); + private static final LogAccessor LOG = new LogAccessor(LogFactory.getLog(Fabric8ConfigMapsNamespaceBatched.class)); /** * at the moment our loading of config maps is using a single thread, but might change @@ -66,11 +66,4 @@ static List byNamespace(KubernetesClient client, String return result; } - private static List strippedConfigMaps(List configMaps) { - return configMaps.stream() - .map(configMap -> new StrippedSourceContainer(configMap.getMetadata().getLabels(), - configMap.getMetadata().getName(), configMap.getData())) - .collect(Collectors.toList()); - } - } diff --git a/spring-cloud-kubernetes-fabric8-config/src/main/java/org/springframework/cloud/kubernetes/fabric8/config/Fabric8ConfigUtils.java b/spring-cloud-kubernetes-fabric8-config/src/main/java/org/springframework/cloud/kubernetes/fabric8/config/Fabric8ConfigUtils.java index 9c533d473e..dcf7870cfd 100644 --- a/spring-cloud-kubernetes-fabric8-config/src/main/java/org/springframework/cloud/kubernetes/fabric8/config/Fabric8ConfigUtils.java +++ b/spring-cloud-kubernetes-fabric8-config/src/main/java/org/springframework/cloud/kubernetes/fabric8/config/Fabric8ConfigUtils.java @@ -21,6 +21,7 @@ import java.util.Map; import java.util.Set; +import io.fabric8.kubernetes.api.model.ConfigMap; import io.fabric8.kubernetes.client.KubernetesClient; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; @@ -33,6 +34,10 @@ import org.springframework.cloud.kubernetes.fabric8.Fabric8Utils; import org.springframework.core.env.Environment; +import static org.springframework.cloud.kubernetes.fabric8.config.Fabric8SourcesNonBatched.byLabels; +import static org.springframework.cloud.kubernetes.fabric8.config.Fabric8SourcesNonBatched.byName; +import static org.springframework.cloud.kubernetes.fabric8.config.Fabric8ConfigMapsNamespaceBatched.byNamespace; + /** * Utility class that works with configuration properties. * @@ -88,8 +93,20 @@ static MultipleSourcesContainer secretsDataByLabels(KubernetesClient client, Str * */ static MultipleSourcesContainer configMapsDataByLabels(KubernetesClient client, String namespace, - Map labels, Environment environment, Set profiles) { - List strippedConfigMaps = strippedConfigMaps(client, namespace); + Map labels, Environment environment, Set profiles, boolean namespacedBatchRead) { + + List strippedConfigMaps; + + if (namespacedBatchRead) { + LOG.debug("Will read all configmaps in namespace : " + namespace); + strippedConfigMaps = strippedConfigMapsBatchRead(client, namespace); + } + else { + LOG.debug("Will read individual configmaps in namespace : " + namespace + " with labels : " + + labels); + strippedConfigMaps = strippedConfigMapsByLabel(client, namespace, labels); + } + if (strippedConfigMaps.isEmpty()) { return MultipleSourcesContainer.empty(); } @@ -106,8 +123,18 @@ static MultipleSourcesContainer configMapsDataByLabels(KubernetesClient client, * */ static MultipleSourcesContainer secretsDataByName(KubernetesClient client, String namespace, - LinkedHashSet sourceNames, Environment environment) { - List strippedSecrets = strippedSecrets(client, namespace); + LinkedHashSet sourceNames, Environment environment, boolean namespacedBatchRead) { + + List strippedSecrets; + + if (namespacedBatchRead) { + strippedSecrets = strippedSecretsBatchRead(client, namespace); + } + else { + + } + + if (strippedSecrets.isEmpty()) { return MultipleSourcesContainer.empty(); } @@ -123,16 +150,81 @@ static MultipleSourcesContainer secretsDataByName(KubernetesClient client, Strin * */ static MultipleSourcesContainer configMapsDataByName(KubernetesClient client, String namespace, - LinkedHashSet sourceNames, Environment environment) { - List strippedConfigMaps = strippedConfigMaps(client, namespace); + LinkedHashSet sourceNames, Environment environment, boolean namespacedBatchRead) { + + List strippedConfigMaps; + + if (namespacedBatchRead) { + LOG.debug("Will read all configmaps in namespace : " + namespace); + strippedConfigMaps = strippedConfigMapsBatchRead(client, namespace); + } + else { + LOG.debug("Will read individual configmaps in namespace : " + namespace + " with names : " + + sourceNames); + strippedConfigMaps = strippedConfigMapsByName(client, namespace, sourceNames); + } + if (strippedConfigMaps.isEmpty()) { return MultipleSourcesContainer.empty(); } return ConfigUtils.processNamedData(strippedConfigMaps, environment, sourceNames, namespace, false); } - private static List strippedConfigMaps(KubernetesClient client, String namespace) { - List strippedConfigMaps = Fabric8ConfigMapsCache.byNamespace(client, namespace); + static List strippedConfigMaps(List configMaps) { + return configMaps.stream() + .map(configMap -> new StrippedSourceContainer(configMap.getMetadata().getLabels(), + configMap.getMetadata().getName(), configMap.getData())) + .toList(); + } + + /** + * read specific configmaps (by name) in the given namespaces, and strip them down + * to only needed fields. + */ + private static List strippedConfigMapsByName( + KubernetesClient client, String namespace, LinkedHashSet sourceNames) { + List strippedConfigMaps = byName(client, namespace, sourceNames); + if (strippedConfigMaps.isEmpty()) { + LOG.debug("No configmaps in namespace '" + namespace + "'"); + } + + return strippedConfigMaps; + } + + /** + * read specific secrets (by name) in the given namespaces, and strip them down + * to only needed fields. + */ + private static List strippedSecretsByName( + KubernetesClient client, String namespace, LinkedHashSet sourceNames) { + List strippedSecrets = secretsByName(client, namespace, sourceNames); + if (strippedConfigMaps.isEmpty()) { + LOG.debug("No configmaps in namespace '" + namespace + "'"); + } + + return strippedConfigMaps; + } + + /** + * read specific configmaps (by labels) in the given namespaces, and strip them down + * to only needed fields. + */ + private static List strippedConfigMapsByLabel( + KubernetesClient client, String namespace, Map labels) { + List strippedConfigMaps = byLabels(client, namespace, labels); + if (strippedConfigMaps.isEmpty()) { + LOG.debug("No configmaps in namespace '" + namespace + "'"); + } + + return strippedConfigMaps; + } + + /** + * read all configmaps in the given namespace (batch), and strip them down + * to only needed fields. + */ + private static List strippedConfigMapsBatchRead(KubernetesClient client, String namespace) { + List strippedConfigMaps = byNamespace(client, namespace); if (strippedConfigMaps.isEmpty()) { LOG.debug("No configmaps in namespace '" + namespace + "'"); } @@ -140,8 +232,8 @@ private static List strippedConfigMaps(KubernetesClient return strippedConfigMaps; } - private static List strippedSecrets(KubernetesClient client, String namespace) { - List strippedSecrets = Fabric8SecretsCache.byNamespace(client, namespace); + private static List strippedSecretsBatchRead(KubernetesClient client, String namespace) { + List strippedSecrets = byNamespace(client, namespace); if (strippedSecrets.isEmpty()) { LOG.debug("No secrets in namespace '" + namespace + "'"); } diff --git a/spring-cloud-kubernetes-fabric8-config/src/main/java/org/springframework/cloud/kubernetes/fabric8/config/Fabric8SecretsPropertySourceLocator.java b/spring-cloud-kubernetes-fabric8-config/src/main/java/org/springframework/cloud/kubernetes/fabric8/config/Fabric8SecretsPropertySourceLocator.java index 70e081dba3..3e335bf295 100644 --- a/spring-cloud-kubernetes-fabric8-config/src/main/java/org/springframework/cloud/kubernetes/fabric8/config/Fabric8SecretsPropertySourceLocator.java +++ b/spring-cloud-kubernetes-fabric8-config/src/main/java/org/springframework/cloud/kubernetes/fabric8/config/Fabric8SecretsPropertySourceLocator.java @@ -52,13 +52,13 @@ public class Fabric8SecretsPropertySourceLocator extends SecretsPropertySourceLo @Override protected SecretsPropertySource getPropertySource(ConfigurableEnvironment environment, - NormalizedSource normalizedSource, boolean singleRead) { + NormalizedSource normalizedSource, boolean namespacedBatchRead) { // NormalizedSource has a namespace, but users can skip it. // In such cases we try to get it elsewhere String namespace = getApplicationNamespace(client, normalizedSource.namespace().orElse(null), normalizedSource.target(), provider); Fabric8ConfigContext context = new Fabric8ConfigContext(client, normalizedSource, namespace, environment, - singleRead); + namespacedBatchRead); return new Fabric8SecretsPropertySource(context); } diff --git a/spring-cloud-kubernetes-fabric8-config/src/main/java/org/springframework/cloud/kubernetes/fabric8/config/Fabric8SourcesNonBatched.java b/spring-cloud-kubernetes-fabric8-config/src/main/java/org/springframework/cloud/kubernetes/fabric8/config/Fabric8SourcesNonBatched.java new file mode 100644 index 0000000000..c0470ff480 --- /dev/null +++ b/spring-cloud-kubernetes-fabric8-config/src/main/java/org/springframework/cloud/kubernetes/fabric8/config/Fabric8SourcesNonBatched.java @@ -0,0 +1,80 @@ +/* + * Copyright 2013-2024 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.fabric8.config; + +import java.util.ArrayList; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Map; + +import io.fabric8.kubernetes.api.model.ConfigMap; +import io.fabric8.kubernetes.client.KubernetesClient; +import org.apache.commons.logging.LogFactory; +import org.springframework.cloud.kubernetes.commons.config.StrippedSourceContainer; +import org.springframework.core.log.LogAccessor; + +import static org.springframework.cloud.kubernetes.fabric8.config.Fabric8ConfigUtils.strippedConfigMaps; + +/** + * non batch reads (not reading in the whole namespace) of configmaps + * and secrets. + * + * @author wind57 + */ +final class Fabric8SourcesNonBatched { + + private static final LogAccessor LOG = new LogAccessor(LogFactory.getLog(Fabric8SourcesNonBatched.class)); + + private Fabric8SourcesNonBatched() { + + } + + /** + * read configmaps by name, one by one, without caching them. + */ + static List byName(KubernetesClient client, String namespace, + LinkedHashSet sourceNames) { + + List configMaps = new ArrayList<>(sourceNames.size()); + + for (String sourceName : sourceNames) { + ConfigMap configMap = client.configMaps().inNamespace(namespace).withName(sourceName).get(); + if (configMap != null) { + LOG.debug("Loaded config map '" + sourceName + "'"); + configMaps.add(configMap); + } + } + + return strippedConfigMaps(configMaps); + } + + /** + * read configmaps by labels, without caching them. + */ + static List byLabels(KubernetesClient client, String namespace, + Map labels) { + + List configMaps = client.configMaps() + .inNamespace(namespace) + .withLabels(labels) + .list() + .getItems(); + + return strippedConfigMaps(configMaps); + } + +} diff --git a/spring-cloud-kubernetes-fabric8-config/src/main/java/org/springframework/cloud/kubernetes/fabric8/config/NamedConfigMapContextToSourceDataProvider.java b/spring-cloud-kubernetes-fabric8-config/src/main/java/org/springframework/cloud/kubernetes/fabric8/config/NamedConfigMapContextToSourceDataProvider.java index 19a7eae0d3..2280dae3f2 100644 --- a/spring-cloud-kubernetes-fabric8-config/src/main/java/org/springframework/cloud/kubernetes/fabric8/config/NamedConfigMapContextToSourceDataProvider.java +++ b/spring-cloud-kubernetes-fabric8-config/src/main/java/org/springframework/cloud/kubernetes/fabric8/config/NamedConfigMapContextToSourceDataProvider.java @@ -53,21 +53,21 @@ public Fabric8ContextToSourceData get() { @Override protected String generateSourceName(String target, String sourceName, String namespace, - String[] activeProfiles, boolean singleRead) { + String[] activeProfiles, boolean namespacedBatchRead) { if (source.appendProfileToName()) { return ConfigUtils.sourceName(target, sourceName, namespace, activeProfiles); } - return super.generateSourceName(target, sourceName, namespace, activeProfiles, singleRead); + return super.generateSourceName(target, sourceName, namespace, activeProfiles, namespacedBatchRead); } @Override public MultipleSourcesContainer dataSupplier(LinkedHashSet sourceNames) { return Fabric8ConfigUtils.configMapsDataByName(context.client(), context.namespace(), sourceNames, - context.environment()); + context.environment(), context.namespacedBatchRead()); } }.compute(source.name().orElseThrow(), source.prefix(), source.target(), source.profileSpecificSources(), source.failFast(), context.namespace(), context.environment().getActiveProfiles(), - context.singleRead()); + context.namespacedBatchRead()); }; } diff --git a/spring-cloud-kubernetes-fabric8-config/src/main/java/org/springframework/cloud/kubernetes/fabric8/config/NamedSecretContextToSourceDataProvider.java b/spring-cloud-kubernetes-fabric8-config/src/main/java/org/springframework/cloud/kubernetes/fabric8/config/NamedSecretContextToSourceDataProvider.java index 021adee049..d817a35db7 100644 --- a/spring-cloud-kubernetes-fabric8-config/src/main/java/org/springframework/cloud/kubernetes/fabric8/config/NamedSecretContextToSourceDataProvider.java +++ b/spring-cloud-kubernetes-fabric8-config/src/main/java/org/springframework/cloud/kubernetes/fabric8/config/NamedSecretContextToSourceDataProvider.java @@ -43,11 +43,11 @@ public Fabric8ContextToSourceData get() { return new NamedSourceData() { @Override protected String generateSourceName(String target, String sourceName, String namespace, - String[] activeProfiles, boolean singleRead) { + String[] activeProfiles, boolean namespacedBatchRead) { if (source.appendProfileToName()) { return ConfigUtils.sourceName(target, sourceName, namespace, activeProfiles); } - return super.generateSourceName(target, sourceName, namespace, activeProfiles, singleRead); + return super.generateSourceName(target, sourceName, namespace, activeProfiles, namespacedBatchRead); } @Override @@ -57,7 +57,7 @@ public MultipleSourcesContainer dataSupplier(LinkedHashSet sourceNames) } }.compute(source.name().orElseThrow(), source.prefix(), source.target(), source.profileSpecificSources(), source.failFast(), context.namespace(), context.environment().getActiveProfiles(), - context.singleRead()); + context.namespacedBatchRead()); }; } diff --git a/spring-cloud-kubernetes-fabric8-config/src/test/java/org/springframework/cloud/kubernetes/fabric8/config/ConfigMapsTest.java b/spring-cloud-kubernetes-fabric8-config/src/test/java/org/springframework/cloud/kubernetes/fabric8/config/ConfigMapsTest.java index ee1093fe90..e151b7b0e3 100644 --- a/spring-cloud-kubernetes-fabric8-config/src/test/java/org/springframework/cloud/kubernetes/fabric8/config/ConfigMapsTest.java +++ b/spring-cloud-kubernetes-fabric8-config/src/test/java/org/springframework/cloud/kubernetes/fabric8/config/ConfigMapsTest.java @@ -42,7 +42,7 @@ class ConfigMapsTest { @AfterEach void afterEach() { - new Fabric8ConfigMapsCache().discardAll(); + new Fabric8ConfigMapsNamespaceBatched().discardAll(); } @Test diff --git a/spring-cloud-kubernetes-fabric8-config/src/test/java/org/springframework/cloud/kubernetes/fabric8/config/Fabric8ConfigMapPropertySourceTests.java b/spring-cloud-kubernetes-fabric8-config/src/test/java/org/springframework/cloud/kubernetes/fabric8/config/Fabric8ConfigMapPropertySourceTests.java index 482e8ba32d..6f770bd45b 100644 --- a/spring-cloud-kubernetes-fabric8-config/src/test/java/org/springframework/cloud/kubernetes/fabric8/config/Fabric8ConfigMapPropertySourceTests.java +++ b/spring-cloud-kubernetes-fabric8-config/src/test/java/org/springframework/cloud/kubernetes/fabric8/config/Fabric8ConfigMapPropertySourceTests.java @@ -44,7 +44,7 @@ class Fabric8ConfigMapPropertySourceTests { @AfterEach void afterEach() { - new Fabric8ConfigMapsCache().discardAll(); + new Fabric8ConfigMapsNamespaceBatched().discardAll(); } @Test diff --git a/spring-cloud-kubernetes-fabric8-config/src/test/java/org/springframework/cloud/kubernetes/fabric8/config/Fabric8ConfigUtilsTests.java b/spring-cloud-kubernetes-fabric8-config/src/test/java/org/springframework/cloud/kubernetes/fabric8/config/Fabric8ConfigUtilsTests.java index ddacddc4e1..7410c73b9f 100644 --- a/spring-cloud-kubernetes-fabric8-config/src/test/java/org/springframework/cloud/kubernetes/fabric8/config/Fabric8ConfigUtilsTests.java +++ b/spring-cloud-kubernetes-fabric8-config/src/test/java/org/springframework/cloud/kubernetes/fabric8/config/Fabric8ConfigUtilsTests.java @@ -46,7 +46,7 @@ class Fabric8ConfigUtilsTests { @AfterEach void afterEach() { - new Fabric8ConfigMapsCache().discardAll(); + new Fabric8ConfigMapsNamespaceBatched().discardAll(); new Fabric8SecretsCache().discardAll(); } diff --git a/spring-cloud-kubernetes-fabric8-config/src/test/java/org/springframework/cloud/kubernetes/fabric8/config/LabeledConfigMapContextToSourceDataProviderTests.java b/spring-cloud-kubernetes-fabric8-config/src/test/java/org/springframework/cloud/kubernetes/fabric8/config/LabeledConfigMapContextToSourceDataProviderTests.java index 7342c4c568..37b18f190b 100644 --- a/spring-cloud-kubernetes-fabric8-config/src/test/java/org/springframework/cloud/kubernetes/fabric8/config/LabeledConfigMapContextToSourceDataProviderTests.java +++ b/spring-cloud-kubernetes-fabric8-config/src/test/java/org/springframework/cloud/kubernetes/fabric8/config/LabeledConfigMapContextToSourceDataProviderTests.java @@ -80,7 +80,7 @@ static void beforeAll() { @AfterEach void afterEach() { mockClient.configMaps().inNamespace(NAMESPACE).delete(); - new Fabric8ConfigMapsCache().discardAll(); + new Fabric8ConfigMapsNamespaceBatched().discardAll(); } /** diff --git a/spring-cloud-kubernetes-fabric8-config/src/test/java/org/springframework/cloud/kubernetes/fabric8/config/NamedConfigMapContextToSourceDataProviderTests.java b/spring-cloud-kubernetes-fabric8-config/src/test/java/org/springframework/cloud/kubernetes/fabric8/config/NamedConfigMapContextToSourceDataProviderTests.java index 79f2f134ed..3dff39a8e7 100644 --- a/spring-cloud-kubernetes-fabric8-config/src/test/java/org/springframework/cloud/kubernetes/fabric8/config/NamedConfigMapContextToSourceDataProviderTests.java +++ b/spring-cloud-kubernetes-fabric8-config/src/test/java/org/springframework/cloud/kubernetes/fabric8/config/NamedConfigMapContextToSourceDataProviderTests.java @@ -71,7 +71,7 @@ static void beforeAll() { @AfterEach void afterEach() { mockClient.configMaps().inNamespace(NAMESPACE).delete(); - new Fabric8ConfigMapsCache().discardAll(); + new Fabric8ConfigMapsNamespaceBatched().discardAll(); } /** From e77ae54ca1669e5067c3a2778c26fa6822b0b930 Mon Sep 17 00:00:00 2001 From: wind57 Date: Sun, 8 Sep 2024 22:58:37 +0300 Subject: [PATCH 09/21] before working on fabric8 tests --- .../Fabric8ConfigDataLocationResolver.java | 2 +- .../Fabric8ConfigMapsNamespaceBatched.java | 2 +- .../fabric8/config/Fabric8ConfigUtils.java | 72 ++++++++++++++----- ...va => Fabric8SecretsNamespaceBatched.java} | 17 ++--- .../Fabric8SecretsPropertySourceLocator.java | 2 +- .../config/Fabric8SourcesNonBatched.java | 40 ++++++++++- ...dConfigMapContextToSourceDataProvider.java | 2 +- ...eledSecretContextToSourceDataProvider.java | 2 +- ...amedSecretContextToSourceDataProvider.java | 2 +- .../config/Fabric8ConfigUtilsTests.java | 2 +- ...ecretContextToSourceDataProviderTests.java | 2 +- ...ecretContextToSourceDataProviderTests.java | 2 +- 12 files changed, 106 insertions(+), 41 deletions(-) rename spring-cloud-kubernetes-fabric8-config/src/main/java/org/springframework/cloud/kubernetes/fabric8/config/{Fabric8SecretsCache.java => Fabric8SecretsNamespaceBatched.java} (78%) diff --git a/spring-cloud-kubernetes-fabric8-config/src/main/java/org/springframework/cloud/kubernetes/fabric8/config/Fabric8ConfigDataLocationResolver.java b/spring-cloud-kubernetes-fabric8-config/src/main/java/org/springframework/cloud/kubernetes/fabric8/config/Fabric8ConfigDataLocationResolver.java index 511278b0cd..982266f250 100644 --- a/spring-cloud-kubernetes-fabric8-config/src/main/java/org/springframework/cloud/kubernetes/fabric8/config/Fabric8ConfigDataLocationResolver.java +++ b/spring-cloud-kubernetes-fabric8-config/src/main/java/org/springframework/cloud/kubernetes/fabric8/config/Fabric8ConfigDataLocationResolver.java @@ -75,7 +75,7 @@ protected void registerBeans(ConfigDataLocationResolverContext resolverContext, kubernetesClient, secretsProperties, namespaceProvider); if (isRetryEnabledForSecrets(secretsProperties)) { secretsPropertySourceLocator = new ConfigDataRetryableSecretsPropertySourceLocator( - secretsPropertySourceLocator, secretsProperties, new Fabric8SecretsCache()); + secretsPropertySourceLocator, secretsProperties, new Fabric8SecretsNamespaceBatched()); } registerSingle(bootstrapContext, SecretsPropertySourceLocator.class, secretsPropertySourceLocator, diff --git a/spring-cloud-kubernetes-fabric8-config/src/main/java/org/springframework/cloud/kubernetes/fabric8/config/Fabric8ConfigMapsNamespaceBatched.java b/spring-cloud-kubernetes-fabric8-config/src/main/java/org/springframework/cloud/kubernetes/fabric8/config/Fabric8ConfigMapsNamespaceBatched.java index f7078cd85a..4ef8d6837d 100644 --- a/spring-cloud-kubernetes-fabric8-config/src/main/java/org/springframework/cloud/kubernetes/fabric8/config/Fabric8ConfigMapsNamespaceBatched.java +++ b/spring-cloud-kubernetes-fabric8-config/src/main/java/org/springframework/cloud/kubernetes/fabric8/config/Fabric8ConfigMapsNamespaceBatched.java @@ -49,7 +49,7 @@ public void discardAll() { CACHE.clear(); } - static List byNamespace(KubernetesClient client, String namespace) { + static List configMapsByNamespace(KubernetesClient client, String namespace) { boolean[] b = new boolean[1]; List result = CACHE.computeIfAbsent(namespace, x -> { b[0] = true; diff --git a/spring-cloud-kubernetes-fabric8-config/src/main/java/org/springframework/cloud/kubernetes/fabric8/config/Fabric8ConfigUtils.java b/spring-cloud-kubernetes-fabric8-config/src/main/java/org/springframework/cloud/kubernetes/fabric8/config/Fabric8ConfigUtils.java index dcf7870cfd..ea31da1f4f 100644 --- a/spring-cloud-kubernetes-fabric8-config/src/main/java/org/springframework/cloud/kubernetes/fabric8/config/Fabric8ConfigUtils.java +++ b/spring-cloud-kubernetes-fabric8-config/src/main/java/org/springframework/cloud/kubernetes/fabric8/config/Fabric8ConfigUtils.java @@ -22,6 +22,7 @@ import java.util.Set; import io.fabric8.kubernetes.api.model.ConfigMap; +import io.fabric8.kubernetes.api.model.Secret; import io.fabric8.kubernetes.client.KubernetesClient; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; @@ -34,9 +35,12 @@ import org.springframework.cloud.kubernetes.fabric8.Fabric8Utils; import org.springframework.core.env.Environment; -import static org.springframework.cloud.kubernetes.fabric8.config.Fabric8SourcesNonBatched.byLabels; -import static org.springframework.cloud.kubernetes.fabric8.config.Fabric8SourcesNonBatched.byName; -import static org.springframework.cloud.kubernetes.fabric8.config.Fabric8ConfigMapsNamespaceBatched.byNamespace; +import static org.springframework.cloud.kubernetes.fabric8.config.Fabric8SourcesNonBatched.configMapsByLabels; +import static org.springframework.cloud.kubernetes.fabric8.config.Fabric8SourcesNonBatched.configMapsByName; +import static org.springframework.cloud.kubernetes.fabric8.config.Fabric8SourcesNonBatched.secretsByLabels; +import static org.springframework.cloud.kubernetes.fabric8.config.Fabric8SourcesNonBatched.secretsByName; +import static org.springframework.cloud.kubernetes.fabric8.config.Fabric8ConfigMapsNamespaceBatched.configMapsByNamespace; +import static org.springframework.cloud.kubernetes.fabric8.config.Fabric8SecretsNamespaceBatched.secretsByNamespace; /** * Utility class that works with configuration properties. @@ -74,11 +78,20 @@ public static Set namespaces(KubernetesClient client, KubernetesNamespac * */ static MultipleSourcesContainer secretsDataByLabels(KubernetesClient client, String namespace, - Map labels, Environment environment, Set profiles) { - List strippedSecrets = strippedSecrets(client, namespace); - if (strippedSecrets.isEmpty()) { - return MultipleSourcesContainer.empty(); + Map labels, Environment environment, Set profiles, boolean namespacedBatchRead) { + + List strippedSecrets; + + if (namespacedBatchRead) { + LOG.debug("Will read all secrets in namespace : " + namespace); + strippedSecrets = strippedSecretsBatchRead(client, namespace); + } + else { + LOG.debug("Will read individual secrets in namespace : " + namespace + " with labels : " + + labels); + strippedSecrets = strippedSecretsByLabels(client, namespace, labels); } + return ConfigUtils.processLabeledData(strippedSecrets, environment, labels, namespace, profiles, true); } @@ -104,7 +117,7 @@ static MultipleSourcesContainer configMapsDataByLabels(KubernetesClient client, else { LOG.debug("Will read individual configmaps in namespace : " + namespace + " with labels : " + labels); - strippedConfigMaps = strippedConfigMapsByLabel(client, namespace, labels); + strippedConfigMaps = strippedConfigMapsByLabels(client, namespace, labels); } if (strippedConfigMaps.isEmpty()) { @@ -128,13 +141,15 @@ static MultipleSourcesContainer secretsDataByName(KubernetesClient client, Strin List strippedSecrets; if (namespacedBatchRead) { + LOG.debug("Will read all secrets in namespace : " + namespace); strippedSecrets = strippedSecretsBatchRead(client, namespace); } else { - + LOG.debug("Will read individual secrets in namespace : " + namespace + " with names : " + + sourceNames); + strippedSecrets = strippedSecretsByName(client, namespace, sourceNames); } - if (strippedSecrets.isEmpty()) { return MultipleSourcesContainer.empty(); } @@ -177,13 +192,20 @@ static List strippedConfigMaps(List configMa .toList(); } + static List strippedSecrets(List secrets) { + return secrets.stream() + .map(secret -> new StrippedSourceContainer(secret.getMetadata().getLabels(), secret.getMetadata().getName(), + secret.getData())) + .toList(); + } + /** * read specific configmaps (by name) in the given namespaces, and strip them down * to only needed fields. */ private static List strippedConfigMapsByName( KubernetesClient client, String namespace, LinkedHashSet sourceNames) { - List strippedConfigMaps = byName(client, namespace, sourceNames); + List strippedConfigMaps = configMapsByName(client, namespace, sourceNames); if (strippedConfigMaps.isEmpty()) { LOG.debug("No configmaps in namespace '" + namespace + "'"); } @@ -198,20 +220,20 @@ private static List strippedConfigMapsByName( private static List strippedSecretsByName( KubernetesClient client, String namespace, LinkedHashSet sourceNames) { List strippedSecrets = secretsByName(client, namespace, sourceNames); - if (strippedConfigMaps.isEmpty()) { - LOG.debug("No configmaps in namespace '" + namespace + "'"); + if (strippedSecrets.isEmpty()) { + LOG.debug("No secrets in namespace '" + namespace + "'"); } - return strippedConfigMaps; + return strippedSecrets; } /** * read specific configmaps (by labels) in the given namespaces, and strip them down * to only needed fields. */ - private static List strippedConfigMapsByLabel( + private static List strippedConfigMapsByLabels( KubernetesClient client, String namespace, Map labels) { - List strippedConfigMaps = byLabels(client, namespace, labels); + List strippedConfigMaps = configMapsByLabels(client, namespace, labels); if (strippedConfigMaps.isEmpty()) { LOG.debug("No configmaps in namespace '" + namespace + "'"); } @@ -219,12 +241,26 @@ private static List strippedConfigMapsByLabel( return strippedConfigMaps; } + /** + * read specific configmaps (by labels) in the given namespaces, and strip them down + * to only needed fields. + */ + private static List strippedSecretsByLabels( + KubernetesClient client, String namespace, Map labels) { + List strippedSecrets = secretsByLabels(client, namespace, labels); + if (strippedSecrets.isEmpty()) { + LOG.debug("No secrets in namespace '" + namespace + "'"); + } + + return strippedSecrets; + } + /** * read all configmaps in the given namespace (batch), and strip them down * to only needed fields. */ private static List strippedConfigMapsBatchRead(KubernetesClient client, String namespace) { - List strippedConfigMaps = byNamespace(client, namespace); + List strippedConfigMaps = configMapsByNamespace(client, namespace); if (strippedConfigMaps.isEmpty()) { LOG.debug("No configmaps in namespace '" + namespace + "'"); } @@ -233,7 +269,7 @@ private static List strippedConfigMapsBatchRead(Kuberne } private static List strippedSecretsBatchRead(KubernetesClient client, String namespace) { - List strippedSecrets = byNamespace(client, namespace); + List strippedSecrets = secretsByNamespace(client, namespace); if (strippedSecrets.isEmpty()) { LOG.debug("No secrets in namespace '" + namespace + "'"); } diff --git a/spring-cloud-kubernetes-fabric8-config/src/main/java/org/springframework/cloud/kubernetes/fabric8/config/Fabric8SecretsCache.java b/spring-cloud-kubernetes-fabric8-config/src/main/java/org/springframework/cloud/kubernetes/fabric8/config/Fabric8SecretsNamespaceBatched.java similarity index 78% rename from spring-cloud-kubernetes-fabric8-config/src/main/java/org/springframework/cloud/kubernetes/fabric8/config/Fabric8SecretsCache.java rename to spring-cloud-kubernetes-fabric8-config/src/main/java/org/springframework/cloud/kubernetes/fabric8/config/Fabric8SecretsNamespaceBatched.java index d27b8c7a84..85f85ca76d 100644 --- a/spring-cloud-kubernetes-fabric8-config/src/main/java/org/springframework/cloud/kubernetes/fabric8/config/Fabric8SecretsCache.java +++ b/spring-cloud-kubernetes-fabric8-config/src/main/java/org/springframework/cloud/kubernetes/fabric8/config/Fabric8SecretsNamespaceBatched.java @@ -18,9 +18,7 @@ import java.util.List; import java.util.concurrent.ConcurrentHashMap; -import java.util.stream.Collectors; -import io.fabric8.kubernetes.api.model.Secret; import io.fabric8.kubernetes.client.KubernetesClient; import org.apache.commons.logging.LogFactory; @@ -28,15 +26,17 @@ import org.springframework.cloud.kubernetes.commons.config.StrippedSourceContainer; import org.springframework.core.log.LogAccessor; +import static org.springframework.cloud.kubernetes.fabric8.config.Fabric8ConfigUtils.strippedSecrets; + /** * A cache of ConfigMaps per namespace. Makes sure we read config maps only once from a * namespace. * * @author wind57 */ -final class Fabric8SecretsCache implements SecretsCache { +final class Fabric8SecretsNamespaceBatched implements SecretsCache { - private static final LogAccessor LOG = new LogAccessor(LogFactory.getLog(Fabric8SecretsCache.class)); + private static final LogAccessor LOG = new LogAccessor(LogFactory.getLog(Fabric8SecretsNamespaceBatched.class)); /** * at the moment our loading of config maps is using a single thread, but might change @@ -49,7 +49,7 @@ public void discardAll() { CACHE.clear(); } - static List byNamespace(KubernetesClient client, String namespace) { + static List secretsByNamespace(KubernetesClient client, String namespace) { boolean[] b = new boolean[1]; List result = CACHE.computeIfAbsent(namespace, x -> { b[0] = true; @@ -66,11 +66,4 @@ static List byNamespace(KubernetesClient client, String return result; } - private static List strippedSecrets(List secrets) { - return secrets.stream() - .map(secret -> new StrippedSourceContainer(secret.getMetadata().getLabels(), secret.getMetadata().getName(), - secret.getData())) - .collect(Collectors.toList()); - } - } diff --git a/spring-cloud-kubernetes-fabric8-config/src/main/java/org/springframework/cloud/kubernetes/fabric8/config/Fabric8SecretsPropertySourceLocator.java b/spring-cloud-kubernetes-fabric8-config/src/main/java/org/springframework/cloud/kubernetes/fabric8/config/Fabric8SecretsPropertySourceLocator.java index 3e335bf295..e1f8a3d1ac 100644 --- a/spring-cloud-kubernetes-fabric8-config/src/main/java/org/springframework/cloud/kubernetes/fabric8/config/Fabric8SecretsPropertySourceLocator.java +++ b/spring-cloud-kubernetes-fabric8-config/src/main/java/org/springframework/cloud/kubernetes/fabric8/config/Fabric8SecretsPropertySourceLocator.java @@ -45,7 +45,7 @@ public class Fabric8SecretsPropertySourceLocator extends SecretsPropertySourceLo Fabric8SecretsPropertySourceLocator(KubernetesClient client, SecretsConfigProperties properties, KubernetesNamespaceProvider provider) { - super(properties, new Fabric8SecretsCache()); + super(properties, new Fabric8SecretsNamespaceBatched()); this.client = client; this.provider = provider; } diff --git a/spring-cloud-kubernetes-fabric8-config/src/main/java/org/springframework/cloud/kubernetes/fabric8/config/Fabric8SourcesNonBatched.java b/spring-cloud-kubernetes-fabric8-config/src/main/java/org/springframework/cloud/kubernetes/fabric8/config/Fabric8SourcesNonBatched.java index c0470ff480..1c1cd0a216 100644 --- a/spring-cloud-kubernetes-fabric8-config/src/main/java/org/springframework/cloud/kubernetes/fabric8/config/Fabric8SourcesNonBatched.java +++ b/spring-cloud-kubernetes-fabric8-config/src/main/java/org/springframework/cloud/kubernetes/fabric8/config/Fabric8SourcesNonBatched.java @@ -22,12 +22,14 @@ import java.util.Map; import io.fabric8.kubernetes.api.model.ConfigMap; +import io.fabric8.kubernetes.api.model.Secret; import io.fabric8.kubernetes.client.KubernetesClient; import org.apache.commons.logging.LogFactory; import org.springframework.cloud.kubernetes.commons.config.StrippedSourceContainer; import org.springframework.core.log.LogAccessor; import static org.springframework.cloud.kubernetes.fabric8.config.Fabric8ConfigUtils.strippedConfigMaps; +import static org.springframework.cloud.kubernetes.fabric8.config.Fabric8ConfigUtils.strippedSecrets; /** * non batch reads (not reading in the whole namespace) of configmaps @@ -46,7 +48,7 @@ private Fabric8SourcesNonBatched() { /** * read configmaps by name, one by one, without caching them. */ - static List byName(KubernetesClient client, String namespace, + static List configMapsByName(KubernetesClient client, String namespace, LinkedHashSet sourceNames) { List configMaps = new ArrayList<>(sourceNames.size()); @@ -62,10 +64,29 @@ static List byName(KubernetesClient client, String name return strippedConfigMaps(configMaps); } + /** + * read secrets by name, one by one, without caching them. + */ + static List secretsByName(KubernetesClient client, String namespace, + LinkedHashSet sourceNames) { + + List secrets = new ArrayList<>(sourceNames.size()); + + for (String sourceName : sourceNames) { + Secret secret = client.secrets().inNamespace(namespace).withName(sourceName).get(); + if (secret != null) { + LOG.debug("Loaded config map '" + sourceName + "'"); + secrets.add(secret); + } + } + + return strippedSecrets(secrets); + } + /** * read configmaps by labels, without caching them. */ - static List byLabels(KubernetesClient client, String namespace, + static List configMapsByLabels(KubernetesClient client, String namespace, Map labels) { List configMaps = client.configMaps() @@ -77,4 +98,19 @@ static List byLabels(KubernetesClient client, String na return strippedConfigMaps(configMaps); } + /** + * read secrets by labels, without caching them. + */ + static List secretsByLabels(KubernetesClient client, String namespace, + Map labels) { + + List secrets = client.secrets() + .inNamespace(namespace) + .withLabels(labels) + .list() + .getItems(); + + return strippedSecrets(secrets); + } + } diff --git a/spring-cloud-kubernetes-fabric8-config/src/main/java/org/springframework/cloud/kubernetes/fabric8/config/LabeledConfigMapContextToSourceDataProvider.java b/spring-cloud-kubernetes-fabric8-config/src/main/java/org/springframework/cloud/kubernetes/fabric8/config/LabeledConfigMapContextToSourceDataProvider.java index 8058f041dd..9e2fa3178a 100644 --- a/spring-cloud-kubernetes-fabric8-config/src/main/java/org/springframework/cloud/kubernetes/fabric8/config/LabeledConfigMapContextToSourceDataProvider.java +++ b/spring-cloud-kubernetes-fabric8-config/src/main/java/org/springframework/cloud/kubernetes/fabric8/config/LabeledConfigMapContextToSourceDataProvider.java @@ -57,7 +57,7 @@ public Fabric8ContextToSourceData get() { @Override public MultipleSourcesContainer dataSupplier(Map labels, Set profiles) { return Fabric8ConfigUtils.configMapsDataByLabels(context.client(), context.namespace(), labels, - context.environment(), profiles); + context.environment(), profiles, context.namespacedBatchRead()); } }.compute(source.labels(), source.prefix(), source.target(), source.profileSpecificSources(), diff --git a/spring-cloud-kubernetes-fabric8-config/src/main/java/org/springframework/cloud/kubernetes/fabric8/config/LabeledSecretContextToSourceDataProvider.java b/spring-cloud-kubernetes-fabric8-config/src/main/java/org/springframework/cloud/kubernetes/fabric8/config/LabeledSecretContextToSourceDataProvider.java index efad27a7ba..13ff59ae1f 100644 --- a/spring-cloud-kubernetes-fabric8-config/src/main/java/org/springframework/cloud/kubernetes/fabric8/config/LabeledSecretContextToSourceDataProvider.java +++ b/spring-cloud-kubernetes-fabric8-config/src/main/java/org/springframework/cloud/kubernetes/fabric8/config/LabeledSecretContextToSourceDataProvider.java @@ -56,7 +56,7 @@ public Fabric8ContextToSourceData get() { @Override public MultipleSourcesContainer dataSupplier(Map labels, Set profiles) { return Fabric8ConfigUtils.secretsDataByLabels(context.client(), context.namespace(), labels, - context.environment(), profiles); + context.environment(), profiles, context.namespacedBatchRead()); } }.compute(source.labels(), source.prefix(), source.target(), source.profileSpecificSources(), diff --git a/spring-cloud-kubernetes-fabric8-config/src/main/java/org/springframework/cloud/kubernetes/fabric8/config/NamedSecretContextToSourceDataProvider.java b/spring-cloud-kubernetes-fabric8-config/src/main/java/org/springframework/cloud/kubernetes/fabric8/config/NamedSecretContextToSourceDataProvider.java index d817a35db7..ee48fcd25a 100644 --- a/spring-cloud-kubernetes-fabric8-config/src/main/java/org/springframework/cloud/kubernetes/fabric8/config/NamedSecretContextToSourceDataProvider.java +++ b/spring-cloud-kubernetes-fabric8-config/src/main/java/org/springframework/cloud/kubernetes/fabric8/config/NamedSecretContextToSourceDataProvider.java @@ -53,7 +53,7 @@ protected String generateSourceName(String target, String sourceName, String nam @Override public MultipleSourcesContainer dataSupplier(LinkedHashSet sourceNames) { return Fabric8ConfigUtils.secretsDataByName(context.client(), context.namespace(), sourceNames, - context.environment()); + context.environment(), context.namespacedBatchRead()); } }.compute(source.name().orElseThrow(), source.prefix(), source.target(), source.profileSpecificSources(), source.failFast(), context.namespace(), context.environment().getActiveProfiles(), diff --git a/spring-cloud-kubernetes-fabric8-config/src/test/java/org/springframework/cloud/kubernetes/fabric8/config/Fabric8ConfigUtilsTests.java b/spring-cloud-kubernetes-fabric8-config/src/test/java/org/springframework/cloud/kubernetes/fabric8/config/Fabric8ConfigUtilsTests.java index 7410c73b9f..2327efdc3d 100644 --- a/spring-cloud-kubernetes-fabric8-config/src/test/java/org/springframework/cloud/kubernetes/fabric8/config/Fabric8ConfigUtilsTests.java +++ b/spring-cloud-kubernetes-fabric8-config/src/test/java/org/springframework/cloud/kubernetes/fabric8/config/Fabric8ConfigUtilsTests.java @@ -47,7 +47,7 @@ class Fabric8ConfigUtilsTests { @AfterEach void afterEach() { new Fabric8ConfigMapsNamespaceBatched().discardAll(); - new Fabric8SecretsCache().discardAll(); + new Fabric8SecretsNamespaceBatched().discardAll(); } // secret "my-secret" is deployed without any labels; we search for it by labels diff --git a/spring-cloud-kubernetes-fabric8-config/src/test/java/org/springframework/cloud/kubernetes/fabric8/config/LabeledSecretContextToSourceDataProviderTests.java b/spring-cloud-kubernetes-fabric8-config/src/test/java/org/springframework/cloud/kubernetes/fabric8/config/LabeledSecretContextToSourceDataProviderTests.java index 835e831c89..b6c2a6d2df 100644 --- a/spring-cloud-kubernetes-fabric8-config/src/test/java/org/springframework/cloud/kubernetes/fabric8/config/LabeledSecretContextToSourceDataProviderTests.java +++ b/spring-cloud-kubernetes-fabric8-config/src/test/java/org/springframework/cloud/kubernetes/fabric8/config/LabeledSecretContextToSourceDataProviderTests.java @@ -83,7 +83,7 @@ static void beforeAll() { @AfterEach void afterEach() { mockClient.secrets().inNamespace(NAMESPACE).delete(); - new Fabric8SecretsCache().discardAll(); + new Fabric8SecretsNamespaceBatched().discardAll(); } /** diff --git a/spring-cloud-kubernetes-fabric8-config/src/test/java/org/springframework/cloud/kubernetes/fabric8/config/NamedSecretContextToSourceDataProviderTests.java b/spring-cloud-kubernetes-fabric8-config/src/test/java/org/springframework/cloud/kubernetes/fabric8/config/NamedSecretContextToSourceDataProviderTests.java index 2888d33294..909b0342ed 100644 --- a/spring-cloud-kubernetes-fabric8-config/src/test/java/org/springframework/cloud/kubernetes/fabric8/config/NamedSecretContextToSourceDataProviderTests.java +++ b/spring-cloud-kubernetes-fabric8-config/src/test/java/org/springframework/cloud/kubernetes/fabric8/config/NamedSecretContextToSourceDataProviderTests.java @@ -70,7 +70,7 @@ static void beforeAll() { @AfterEach void afterEach() { mockClient.secrets().inNamespace(NAMESPACE).delete(); - new Fabric8SecretsCache().discardAll(); + new Fabric8SecretsNamespaceBatched().discardAll(); } /** From 4e89d23493fb7d9a0eb11308d4ed815e7fdf665e Mon Sep 17 00:00:00 2001 From: wind57 Date: Sat, 21 Sep 2024 10:54:56 +0300 Subject: [PATCH 10/21] fix --- .../config/KubernetesClientConfigUtils.java | 20 ++-- ...dConfigMapContextToSourceDataProvider.java | 8 +- ...eledSecretContextToSourceDataProvider.java | 8 +- ...netesClientSecretsPropertySourceTests.java | 2 +- ...igMapContextToSourceDataProviderTests.java | 13 +-- ...ecretContextToSourceDataProviderTests.java | 40 ++++---- .../LabeledConfigMapWithProfileTests.java | 6 +- .../LabeledSecretWithProfileTests.java | 7 +- ...ConfigMapWithProfileConfigurationStub.java | 9 +- ...ledSecretWithProfileConfigurationStub.java | 9 +- .../commons/config/ConfigUtils.java | 33 +------ .../config/LabeledSecretNormalizedSource.java | 13 +-- .../commons/config/LabeledSourceData.java | 12 +-- .../config/SecretsConfigProperties.java | 4 +- .../LabeledSecretNormalizedSourceTests.java | 23 ++--- .../config/SecretsConfigPropertiesTests.java | 5 - .../fabric8/config/Fabric8ConfigUtils.java | 20 ++-- ...dConfigMapContextToSourceDataProvider.java | 8 +- ...eledSecretContextToSourceDataProvider.java | 8 +- .../config/Fabric8ConfigUtilsTests.java | 18 ++-- ...Fabric8SecretsPropertySourceMockTests.java | 4 +- ...igMapContextToSourceDataProviderTests.java | 68 ++----------- ...ecretContextToSourceDataProviderTests.java | 96 +++++-------------- .../LabeledConfigMapWithProfile.java | 22 ++--- .../LabeledSecretWithProfile.java | 22 ++--- .../labeled-configmap-with-profile.yaml | 2 - .../labeled-secret-with-profile.yaml | 2 - 27 files changed, 138 insertions(+), 344 deletions(-) diff --git a/spring-cloud-kubernetes-client-config/src/main/java/org/springframework/cloud/kubernetes/client/config/KubernetesClientConfigUtils.java b/spring-cloud-kubernetes-client-config/src/main/java/org/springframework/cloud/kubernetes/client/config/KubernetesClientConfigUtils.java index 21bc92b9ee..0dc869b322 100644 --- a/spring-cloud-kubernetes-client-config/src/main/java/org/springframework/cloud/kubernetes/client/config/KubernetesClientConfigUtils.java +++ b/spring-cloud-kubernetes-client-config/src/main/java/org/springframework/cloud/kubernetes/client/config/KubernetesClientConfigUtils.java @@ -65,38 +65,34 @@ public static Set namespaces(KubernetesNamespaceProvider provider, Confi *
 	 *     1. read all secrets in the provided namespace
 	 *     2. from the above, filter the ones that we care about (filter by labels)
-	 *     3. with secret names from (2), find out if there are any profile based secrets (if profiles is not empty)
-	 *     4. concat (2) and (3) and these are the secrets we are interested in
-	 *     5. see if any of the secrets from (4) has a single yaml/properties file
-	 *     6. gather all the names of the secrets (from 4) + data they hold
+	 *     3. see if any of the secrets from (2) has a single yaml/properties file
+	 *     4. gather all the names of the secrets + data they hold
 	 * 
*/ static MultipleSourcesContainer secretsDataByLabels(CoreV1Api coreV1Api, String namespace, - Map labels, Environment environment, Set profiles) { + Map labels, Environment environment) { List strippedSecrets = strippedSecrets(coreV1Api, namespace); if (strippedSecrets.isEmpty()) { return MultipleSourcesContainer.empty(); } - return ConfigUtils.processLabeledData(strippedSecrets, environment, labels, namespace, profiles, DECODE); + return ConfigUtils.processLabeledData(strippedSecrets, environment, labels, namespace, DECODE); } /** *
 	 *     1. read all config maps in the provided namespace
 	 *     2. from the above, filter the ones that we care about (filter by labels)
-	 *     3. with config maps names from (2), find out if there are any profile based ones (if profiles is not empty)
-	 *     4. concat (2) and (3) and these are the config maps we are interested in
-	 *     5. see if any from (4) has a single yaml/properties file
-	 *     6. gather all the names of the config maps (from 4) + data they hold
+	 *     3. see if any from (2) has a single yaml/properties file
+	 *     4. gather all the names of the config maps + data they hold
 	 * 
*/ static MultipleSourcesContainer configMapsDataByLabels(CoreV1Api coreV1Api, String namespace, - Map labels, Environment environment, Set profiles) { + Map labels, Environment environment) { List strippedConfigMaps = strippedConfigMaps(coreV1Api, namespace); if (strippedConfigMaps.isEmpty()) { return MultipleSourcesContainer.empty(); } - return ConfigUtils.processLabeledData(strippedConfigMaps, environment, labels, namespace, profiles, DECODE); + return ConfigUtils.processLabeledData(strippedConfigMaps, environment, labels, namespace, DECODE); } /** diff --git a/spring-cloud-kubernetes-client-config/src/main/java/org/springframework/cloud/kubernetes/client/config/LabeledConfigMapContextToSourceDataProvider.java b/spring-cloud-kubernetes-client-config/src/main/java/org/springframework/cloud/kubernetes/client/config/LabeledConfigMapContextToSourceDataProvider.java index 6d4381ffd2..ee135b3835 100644 --- a/spring-cloud-kubernetes-client-config/src/main/java/org/springframework/cloud/kubernetes/client/config/LabeledConfigMapContextToSourceDataProvider.java +++ b/spring-cloud-kubernetes-client-config/src/main/java/org/springframework/cloud/kubernetes/client/config/LabeledConfigMapContextToSourceDataProvider.java @@ -17,7 +17,6 @@ package org.springframework.cloud.kubernetes.client.config; import java.util.Map; -import java.util.Set; import java.util.function.Supplier; import org.springframework.cloud.kubernetes.commons.config.LabeledConfigMapNormalizedSource; @@ -49,13 +48,12 @@ public KubernetesClientContextToSourceData get() { return new LabeledSourceData() { @Override - public MultipleSourcesContainer dataSupplier(Map labels, Set profiles) { + public MultipleSourcesContainer dataSupplier(Map labels) { return KubernetesClientConfigUtils.configMapsDataByLabels(context.client(), context.namespace(), - labels, context.environment(), profiles); + labels, context.environment()); } - }.compute(source.labels(), source.prefix(), source.target(), source.profileSpecificSources(), - source.failFast(), context.namespace(), context.environment().getActiveProfiles()); + }.compute(source.labels(), source.prefix(), source.target(), source.failFast(), context.namespace()); }; } diff --git a/spring-cloud-kubernetes-client-config/src/main/java/org/springframework/cloud/kubernetes/client/config/LabeledSecretContextToSourceDataProvider.java b/spring-cloud-kubernetes-client-config/src/main/java/org/springframework/cloud/kubernetes/client/config/LabeledSecretContextToSourceDataProvider.java index 9cf72cbc0b..723e680ebd 100644 --- a/spring-cloud-kubernetes-client-config/src/main/java/org/springframework/cloud/kubernetes/client/config/LabeledSecretContextToSourceDataProvider.java +++ b/spring-cloud-kubernetes-client-config/src/main/java/org/springframework/cloud/kubernetes/client/config/LabeledSecretContextToSourceDataProvider.java @@ -17,7 +17,6 @@ package org.springframework.cloud.kubernetes.client.config; import java.util.Map; -import java.util.Set; import java.util.function.Supplier; import org.springframework.cloud.kubernetes.commons.config.LabeledSecretNormalizedSource; @@ -55,13 +54,12 @@ public KubernetesClientContextToSourceData get() { return new LabeledSourceData() { @Override - public MultipleSourcesContainer dataSupplier(Map labels, Set profiles) { + public MultipleSourcesContainer dataSupplier(Map labels) { return KubernetesClientConfigUtils.secretsDataByLabels(context.client(), context.namespace(), - labels, context.environment(), profiles); + labels, context.environment()); } - }.compute(source.labels(), source.prefix(), source.target(), source.profileSpecificSources(), - source.failFast(), context.namespace(), context.environment().getActiveProfiles()); + }.compute(source.labels(), source.prefix(), source.target(), source.failFast(), context.namespace()); }; } diff --git a/spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/KubernetesClientSecretsPropertySourceTests.java b/spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/KubernetesClientSecretsPropertySourceTests.java index 4cc2b8d231..90b792337c 100644 --- a/spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/KubernetesClientSecretsPropertySourceTests.java +++ b/spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/KubernetesClientSecretsPropertySourceTests.java @@ -167,7 +167,7 @@ void secretLabelsTest() { Map labels = new HashMap<>(); labels.put("spring.cloud.kubernetes.secret", "true"); - NormalizedSource source = new LabeledSecretNormalizedSource("default", labels, false, false); + NormalizedSource source = new LabeledSecretNormalizedSource("default", labels, false); KubernetesClientConfigContext context = new KubernetesClientConfigContext(api, source, "default", new MockEnvironment()); diff --git a/spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/LabeledConfigMapContextToSourceDataProviderTests.java b/spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/LabeledConfigMapContextToSourceDataProviderTests.java index 44840858e0..d84280bb0e 100644 --- a/spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/LabeledConfigMapContextToSourceDataProviderTests.java +++ b/spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/LabeledConfigMapContextToSourceDataProviderTests.java @@ -408,9 +408,8 @@ void searchWithLabelsOneConfigMapFound() { /** * two configmaps are deployed: "color-configmap" with label: "{color:blue}" and - * "color-configmap-k8s" with label: "{color:red}". We search by "{color:blue}" and - * find one configmap. Since profiles are enabled, we will also be reading - * "color-configmap-k8s", even if its labels do not match provided ones. + * "color-configmap-k8s" with label: "{color:blue}". We search by "{color:blue}" and + * find them both. */ @Test void searchWithLabelsOneConfigMapFoundAndOneFromProfileFound() { @@ -425,7 +424,7 @@ void searchWithLabelsOneConfigMapFoundAndOneFromProfileFound() { V1ConfigMap two = new V1ConfigMapBuilder() .withMetadata(new V1ObjectMetaBuilder().withName("color-configmap-k8s") - .withLabels(RED_LABEL) + .withLabels(BLUE_LABEL) .withNamespace(NAMESPACE) .build()) .addToData("two", "2") @@ -436,7 +435,6 @@ void searchWithLabelsOneConfigMapFoundAndOneFromProfileFound() { stubCall(configMapList); CoreV1Api api = new CoreV1Api(); MockEnvironment environment = new MockEnvironment(); - environment.setActiveProfiles("k8s"); NormalizedSource source = new LabeledConfigMapNormalizedSource(NAMESPACE, BLUE_LABEL, true, ConfigUtils.Prefix.DELAYED, true); @@ -490,7 +488,7 @@ void searchWithLabelsTwoConfigMapsFoundAndOneFromProfileFound() { V1ConfigMap colorConfigmapK8s = new V1ConfigMapBuilder() .withMetadata(new V1ObjectMetaBuilder().withName("color-configmap-k8s") - .withLabels(RED_LABEL) + .withLabels(BLUE_LABEL) .withNamespace(NAMESPACE) .build()) .addToData("four", "4") @@ -498,7 +496,7 @@ void searchWithLabelsTwoConfigMapsFoundAndOneFromProfileFound() { V1ConfigMap shapeConfigmapK8s = new V1ConfigMapBuilder() .withMetadata(new V1ObjectMetaBuilder().withName("shape-configmap-k8s") - .withLabels(Map.of("shape", "triangle")) + .withLabels(BLUE_LABEL) .withNamespace(NAMESPACE) .build()) .addToData("five", "5") @@ -513,7 +511,6 @@ void searchWithLabelsTwoConfigMapsFoundAndOneFromProfileFound() { stubCall(configMapList); CoreV1Api api = new CoreV1Api(); MockEnvironment environment = new MockEnvironment(); - environment.setActiveProfiles("k8s"); NormalizedSource source = new LabeledConfigMapNormalizedSource(NAMESPACE, BLUE_LABEL, true, ConfigUtils.Prefix.DELAYED, true); diff --git a/spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/LabeledSecretContextToSourceDataProviderTests.java b/spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/LabeledSecretContextToSourceDataProviderTests.java index 9a666509ce..34a743e828 100644 --- a/spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/LabeledSecretContextToSourceDataProviderTests.java +++ b/spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/LabeledSecretContextToSourceDataProviderTests.java @@ -107,7 +107,7 @@ void noMatch() { // blue does not match red NormalizedSource source = new LabeledSecretNormalizedSource(NAMESPACE, - Collections.singletonMap("color", "blue"), false, false); + Collections.singletonMap("color", "blue"), false); KubernetesClientConfigContext context = new KubernetesClientConfigContext(api, source, NAMESPACE, new MockEnvironment()); @@ -135,7 +135,7 @@ void singleSecretMatchAgainstLabels() { stubCall(secretList); CoreV1Api api = new CoreV1Api(); - NormalizedSource source = new LabeledSecretNormalizedSource(NAMESPACE, LABELS, false, false); + NormalizedSource source = new LabeledSecretNormalizedSource(NAMESPACE, LABELS, false); KubernetesClientConfigContext context = new KubernetesClientConfigContext(api, source, NAMESPACE, new MockEnvironment()); @@ -167,7 +167,7 @@ void twoSecretsMatchAgainstLabels() { stubCall(secretList); CoreV1Api api = new CoreV1Api(); - NormalizedSource source = new LabeledSecretNormalizedSource(NAMESPACE, RED_LABEL, false, false); + NormalizedSource source = new LabeledSecretNormalizedSource(NAMESPACE, RED_LABEL, false); KubernetesClientConfigContext context = new KubernetesClientConfigContext(api, source, NAMESPACE, new MockEnvironment()); @@ -192,7 +192,7 @@ void namespaceMatch() { stubCall(secretList); CoreV1Api api = new CoreV1Api(); - NormalizedSource source = new LabeledSecretNormalizedSource(NAMESPACE + "nope", LABELS, false, false); + NormalizedSource source = new LabeledSecretNormalizedSource(NAMESPACE + "nope", LABELS, false); KubernetesClientConfigContext context = new KubernetesClientConfigContext(api, source, NAMESPACE, new MockEnvironment()); @@ -225,8 +225,7 @@ void testWithPrefix() { CoreV1Api api = new CoreV1Api(); ConfigUtils.Prefix prefix = ConfigUtils.findPrefix("me", false, false, null); - NormalizedSource source = new LabeledSecretNormalizedSource(NAMESPACE, Map.of("color", "blue"), false, prefix, - false); + NormalizedSource source = new LabeledSecretNormalizedSource(NAMESPACE, Map.of("color", "blue"), false, prefix); KubernetesClientConfigContext context = new KubernetesClientConfigContext(api, source, NAMESPACE, new MockEnvironment()); @@ -271,7 +270,7 @@ void testTwoSecretsWithPrefix() { CoreV1Api api = new CoreV1Api(); NormalizedSource source = new LabeledSecretNormalizedSource(NAMESPACE, Map.of("color", "blue"), false, - ConfigUtils.Prefix.DELAYED, false); + ConfigUtils.Prefix.DELAYED); KubernetesClientConfigContext context = new KubernetesClientConfigContext(api, source, NAMESPACE, new MockEnvironment()); @@ -303,7 +302,7 @@ void testTwoSecretsWithPrefix() { /** * two secrets are deployed: secret "color-secret" with label: "{color:blue}" and * "shape-secret" with label: "{shape:round}". We search by "{color:blue}" and find - * one secret. profile based sources are enabled, but it has no effect. + * one secret. */ @Test void searchWithLabelsOneSecretFound() { @@ -330,7 +329,7 @@ void searchWithLabelsOneSecretFound() { CoreV1Api api = new CoreV1Api(); NormalizedSource source = new LabeledSecretNormalizedSource(NAMESPACE, Map.of("color", "blue"), false, - ConfigUtils.Prefix.DEFAULT, true); + ConfigUtils.Prefix.DEFAULT); KubernetesClientConfigContext context = new KubernetesClientConfigContext(api, source, NAMESPACE, new MockEnvironment()); @@ -345,9 +344,8 @@ void searchWithLabelsOneSecretFound() { /** * two secrets are deployed: secret "color-secret" with label: "{color:blue}" and - * "color-secret-k8s" with label: "{color:red}". We search by "{color:blue}" and find - * one secret. Since profiles are enabled, we will also be reading "color-secret-k8s", - * even if its labels do not match provided ones. + * "color-secret-k8s" with label: "{color:blue}". We search by "{color:blue}" and find + * both. */ @Test void searchWithLabelsOneSecretFoundAndOneFromProfileFound() { @@ -361,7 +359,7 @@ void searchWithLabelsOneSecretFoundAndOneFromProfileFound() { .build(); V1Secret shapeSecret = new V1SecretBuilder() - .withMetadata(new V1ObjectMetaBuilder().withLabels(Map.of("color", "red")) + .withMetadata(new V1ObjectMetaBuilder().withLabels(Map.of("color", "blue")) .withNamespace(NAMESPACE) .withName("color-secret-k8s") .build()) @@ -373,10 +371,9 @@ void searchWithLabelsOneSecretFoundAndOneFromProfileFound() { stubCall(secretList); CoreV1Api api = new CoreV1Api(); MockEnvironment environment = new MockEnvironment(); - environment.setActiveProfiles("k8s"); NormalizedSource source = new LabeledSecretNormalizedSource(NAMESPACE, Map.of("color", "blue"), false, - ConfigUtils.Prefix.DELAYED, true); + ConfigUtils.Prefix.DELAYED); KubernetesClientConfigContext context = new KubernetesClientConfigContext(api, source, NAMESPACE, environment); KubernetesClientContextToSourceData data = new LabeledSecretContextToSourceDataProvider().get(); @@ -426,7 +423,7 @@ void searchWithLabelsTwoSecretsFoundAndOneFromProfileFound() { .build(); V1Secret colorSecretK8s = new V1SecretBuilder() - .withMetadata(new V1ObjectMetaBuilder().withLabels(Map.of("color", "red")) + .withMetadata(new V1ObjectMetaBuilder().withLabels(Map.of("color", "blue")) .withNamespace(NAMESPACE) .withName("color-secret-k8s") .build()) @@ -434,7 +431,7 @@ void searchWithLabelsTwoSecretsFoundAndOneFromProfileFound() { .build(); V1Secret shapeSecretK8s = new V1SecretBuilder() - .withMetadata(new V1ObjectMetaBuilder().withLabels(Map.of("shape", "triangle")) + .withMetadata(new V1ObjectMetaBuilder().withLabels(Map.of("color", "blue")) .withNamespace(NAMESPACE) .withName("shape-secret-k8s") .build()) @@ -450,10 +447,9 @@ void searchWithLabelsTwoSecretsFoundAndOneFromProfileFound() { stubCall(secretList); CoreV1Api api = new CoreV1Api(); MockEnvironment environment = new MockEnvironment(); - environment.setActiveProfiles("k8s"); NormalizedSource source = new LabeledSecretNormalizedSource(NAMESPACE, Map.of("color", "blue"), false, - ConfigUtils.Prefix.DELAYED, true); + ConfigUtils.Prefix.DELAYED); KubernetesClientConfigContext context = new KubernetesClientConfigContext(api, source, NAMESPACE, environment); KubernetesClientContextToSourceData data = new LabeledSecretContextToSourceDataProvider().get(); @@ -493,7 +489,7 @@ void testYaml() { CoreV1Api api = new CoreV1Api(); NormalizedSource source = new LabeledSecretNormalizedSource(NAMESPACE, Map.of("color", "blue"), false, - ConfigUtils.Prefix.DEFAULT, true); + ConfigUtils.Prefix.DEFAULT); KubernetesClientConfigContext context = new KubernetesClientConfigContext(api, source, NAMESPACE, new MockEnvironment()); @@ -538,7 +534,7 @@ void cache(CapturedOutput output) { CoreV1Api api = new CoreV1Api(); NormalizedSource redSource = new LabeledSecretNormalizedSource(NAMESPACE, Map.of("color", "red"), false, - ConfigUtils.Prefix.DEFAULT, false); + ConfigUtils.Prefix.DEFAULT); KubernetesClientConfigContext redContext = new KubernetesClientConfigContext(api, redSource, NAMESPACE, new MockEnvironment()); KubernetesClientContextToSourceData redData = new LabeledSecretContextToSourceDataProvider().get(); @@ -550,7 +546,7 @@ void cache(CapturedOutput output) { Assertions.assertTrue(output.getAll().contains("Loaded all secrets in namespace '" + NAMESPACE + "'")); NormalizedSource greenSource = new LabeledSecretNormalizedSource(NAMESPACE, Map.of("color", "green"), false, - ConfigUtils.Prefix.DEFAULT, false); + ConfigUtils.Prefix.DEFAULT); KubernetesClientConfigContext greenContext = new KubernetesClientConfigContext(api, greenSource, NAMESPACE, new MockEnvironment()); KubernetesClientContextToSourceData greenData = new LabeledSecretContextToSourceDataProvider().get(); diff --git a/spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/applications/labeled_config_map_with_profile/LabeledConfigMapWithProfileTests.java b/spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/applications/labeled_config_map_with_profile/LabeledConfigMapWithProfileTests.java index cc4b6798b2..b5d4422a19 100644 --- a/spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/applications/labeled_config_map_with_profile/LabeledConfigMapWithProfileTests.java +++ b/spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/applications/labeled_config_map_with_profile/LabeledConfigMapWithProfileTests.java @@ -66,10 +66,8 @@ void testBlue() { /** *
-	 *   this one is taken from : ""green-configmap.green-configmap-k8s.green-configmap-prod.green-purple-configmap.green-purple-configmap-k8s"".
-	 *   We find "green-configmap" by labels, also "green-configmap-k8s", "green-configmap-prod" exists,
-	 *   because "includeProfileSpecificSources=true" is set. Also "green-purple-configmap" and "green-purple-configmap-k8s"
-	 *   are found.
+	 *   this one is taken from : "green-configmap.green-configmap-k8s.green-configmap-prod.green-purple-configmap.green-purple-configmap-k8s".
+	 *   We find "green-configmap", "green-configmap-k8s", "green-configmap-prod" by labels.
 	 * 
*/ @Test diff --git a/spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/applications/labeled_secret_with_profile/LabeledSecretWithProfileTests.java b/spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/applications/labeled_secret_with_profile/LabeledSecretWithProfileTests.java index b66dab4a1c..fc409b2ce6 100644 --- a/spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/applications/labeled_secret_with_profile/LabeledSecretWithProfileTests.java +++ b/spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/applications/labeled_secret_with_profile/LabeledSecretWithProfileTests.java @@ -60,8 +60,7 @@ static void afterAll() { /** *
-	 *     this one is taken from : "blue.one". We find "color-secret" by labels, and
-	 *     "color-secrets-k8s" exists, but "includeProfileSpecificSources=false", thus not taken.
+	 *     this one is taken from : "blue.one". We find "color-secret" by labels.
 	 *     Since "explicitPrefix=blue", we take "blue.one"
 	 * 
*/ @@ -79,9 +78,7 @@ void testBlue() { /** *
 	 *   this one is taken from : "green-purple-secret.green-purple-secret-k8s.green-secret.green-secret-k8s.green-secret-prod".
-	 *   We find "green-secret" by labels, also "green-secrets-k8s" and "green-secrets-prod" exists,
-	 *   because "includeProfileSpecificSources=true" is set. Also "green-purple-secret" and "green-purple-secret-k8s"
-	 * 	 are found.
+	 *   We find "green-secret", "green-secrets-k8s" and "green-secrets-prod" by labels.
 	 * 
*/ @Test diff --git a/spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/bootstrap/stubs/LabeledConfigMapWithProfileConfigurationStub.java b/spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/bootstrap/stubs/LabeledConfigMapWithProfileConfigurationStub.java index 8e1ed61203..3133bd27e5 100644 --- a/spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/bootstrap/stubs/LabeledConfigMapWithProfileConfigurationStub.java +++ b/spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/bootstrap/stubs/LabeledConfigMapWithProfileConfigurationStub.java @@ -73,9 +73,6 @@ public ApiClient apiClient(WireMockServer wireMockServer) { * - configmap with name "color-configmap-k8s", with labels : "{color: not-blue}" * - configmap with name "green-configmap-k8s", with labels : "{color: green-k8s}" * - configmap with name "green-configmap-prod", with labels : "{color: green-prod}" - * - * # a test that proves order: first read non-profile based configmaps, thus profile based - * # configmaps override non-profile ones. * - configmap with name "green-purple-configmap", labels "{color: green, shape: round}", data: "{eight: 8}" * - configmap with name "green-purple-configmap-k8s", labels "{color: black}", data: "{eight: eight-ish}" * @@ -112,7 +109,7 @@ public static void stubData() { V1ConfigMap greenConfigMapK8s = new V1ConfigMapBuilder() .withMetadata(new V1ObjectMetaBuilder().withName("green-configmap-k8s") .withNamespace("spring-k8s") - .withLabels(Map.of("color", "green-k8s")) + .withLabels(Map.of("color", "green")) .build()) .addToData(Collections.singletonMap("six", "6")) .build(); @@ -121,7 +118,7 @@ public static void stubData() { V1ConfigMap greenConfigMapProd = new V1ConfigMapBuilder() .withMetadata(new V1ObjectMetaBuilder().withName("green-configmap-prod") .withNamespace("spring-k8s") - .withLabels(Map.of("color", "green-prod")) + .withLabels(Map.of("color", "green")) .build()) .addToData(Collections.singletonMap("seven", "7")) .build(); @@ -157,7 +154,7 @@ public static void stubData() { V1ConfigMap greenPurpleConfigMapK8s = new V1ConfigMapBuilder() .withMetadata(new V1ObjectMetaBuilder().withName("green-purple-configmap-k8s") .withNamespace("spring-k8s") - .withLabels(Map.of("color", "black")) + .withLabels(Map.of("color", "green")) .build()) .addToData(Collections.singletonMap("eight", "eight-ish")) .build(); diff --git a/spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/bootstrap/stubs/LabeledSecretWithProfileConfigurationStub.java b/spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/bootstrap/stubs/LabeledSecretWithProfileConfigurationStub.java index c59ca39f1c..072bc291f8 100644 --- a/spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/bootstrap/stubs/LabeledSecretWithProfileConfigurationStub.java +++ b/spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/bootstrap/stubs/LabeledSecretWithProfileConfigurationStub.java @@ -74,9 +74,6 @@ public ApiClient apiClient(WireMockServer wireMockServer) { * - secret with name "color-secret-k8s", with labels : "{color: not-blue}" * - secret with name "green-secret-k8s", with labels : "{color: green-k8s}" * - secret with name "green-secret-prod", with labels : "{color: green-prod}" - * - * # a test that proves order: first read non-profile based secrets, thus profile based - * # secrets override non-profile ones. * - secret with name "green-purple-secret", labels "{color: green, shape: round}", data: "{eight: 8}" * - secret with name "green-purple-secret-k8s", labels "{color: black}", data: "{eight: eight-ish}" * @@ -113,7 +110,7 @@ public static void stubData() { V1Secret greenSecretK8s = new V1SecretBuilder() .withMetadata(new V1ObjectMetaBuilder().withName("green-secret-k8s") .withNamespace("spring-k8s") - .withLabels(Map.of("color", "green-k8s")) + .withLabels(Map.of("color", "green")) .build()) .addToData(Collections.singletonMap("six", "6".getBytes(StandardCharsets.UTF_8))) .build(); @@ -122,7 +119,7 @@ public static void stubData() { V1Secret shapeSecretProd = new V1SecretBuilder() .withMetadata(new V1ObjectMetaBuilder().withName("green-secret-prod") .withNamespace("spring-k8s") - .withLabels(Map.of("color", "green-prod")) + .withLabels(Map.of("color", "green")) .build()) .addToData(Collections.singletonMap("seven", "7".getBytes(StandardCharsets.UTF_8))) .build(); @@ -158,7 +155,7 @@ public static void stubData() { V1Secret greenPurpleSecretK8s = new V1SecretBuilder() .withMetadata(new V1ObjectMetaBuilder().withName("green-purple-secret-k8s") .withNamespace("spring-k8s") - .withLabels(Map.of("color", "black")) + .withLabels(Map.of("color", "green")) .build()) .addToData(Collections.singletonMap("eight", "eight-ish".getBytes(StandardCharsets.UTF_8))) .build(); diff --git a/spring-cloud-kubernetes-commons/src/main/java/org/springframework/cloud/kubernetes/commons/config/ConfigUtils.java b/spring-cloud-kubernetes-commons/src/main/java/org/springframework/cloud/kubernetes/commons/config/ConfigUtils.java index 5f40858eab..d1c9c00d75 100644 --- a/spring-cloud-kubernetes-commons/src/main/java/org/springframework/cloud/kubernetes/commons/config/ConfigUtils.java +++ b/spring-cloud-kubernetes-commons/src/main/java/org/springframework/cloud/kubernetes/commons/config/ConfigUtils.java @@ -16,7 +16,6 @@ package org.springframework.cloud.kubernetes.commons.config; -import java.util.ArrayList; import java.util.Arrays; import java.util.Base64; import java.util.HashMap; @@ -24,7 +23,6 @@ import java.util.List; import java.util.Map; import java.util.Optional; -import java.util.Set; import java.util.function.BiPredicate; import java.util.function.BooleanSupplier; import java.util.function.Function; @@ -266,13 +264,11 @@ static BooleanSupplier rawDataContainsProfileBasedSource(List activeProf /** * transforms raw data from one or multiple sources into an entry of source names and * flattened data that they all hold (potentially overriding entries without any - * defined order). This method first searches by labels, find the sources, then uses - * these names to find any profile based sources. + * defined order). */ public static MultipleSourcesContainer processLabeledData(List containers, - Environment environment, Map labels, String namespace, Set profiles, - boolean decode) { + Environment environment, Map labels, String namespace, boolean decode) { // find sources by provided labels List byLabels = containers.stream().filter(one -> { @@ -281,33 +277,10 @@ public static MultipleSourcesContainer processLabeledData(List sourceNamesByLabelsWithProfile = new ArrayList<>(); - if (profiles != null && !profiles.isEmpty()) { - for (StrippedSourceContainer one : byLabels) { - for (String profile : profiles) { - String name = one.name() + "-" + profile; - sourceNamesByLabelsWithProfile.add(name); - } - } - } - - // once we know sources by labels (and thus their names), we can find out - // profiles based sources from the above. This would get all sources - // we are interested in. - List byProfile = containers.stream() - .filter(one -> sourceNamesByLabelsWithProfile.contains(one.name())) - .toList(); - - // this makes sure that we first have "app" and then "app-dev" in the list - List all = new ArrayList<>(byLabels.size() + byProfile.size()); - all.addAll(byLabels); - all.addAll(byProfile); - LinkedHashSet sourceNames = new LinkedHashSet<>(); Map result = new HashMap<>(); - all.forEach(source -> { + byLabels.forEach(source -> { String foundSourceName = source.name(); LOG.debug("Loaded source with name : '" + foundSourceName + " in namespace: '" + namespace + "'"); sourceNames.add(foundSourceName); diff --git a/spring-cloud-kubernetes-commons/src/main/java/org/springframework/cloud/kubernetes/commons/config/LabeledSecretNormalizedSource.java b/spring-cloud-kubernetes-commons/src/main/java/org/springframework/cloud/kubernetes/commons/config/LabeledSecretNormalizedSource.java index aecd0f35fe..4f32097c0e 100644 --- a/spring-cloud-kubernetes-commons/src/main/java/org/springframework/cloud/kubernetes/commons/config/LabeledSecretNormalizedSource.java +++ b/spring-cloud-kubernetes-commons/src/main/java/org/springframework/cloud/kubernetes/commons/config/LabeledSecretNormalizedSource.java @@ -31,22 +31,17 @@ public final class LabeledSecretNormalizedSource extends NormalizedSource { private final ConfigUtils.Prefix prefix; - private final boolean includeProfileSpecificSources; - public LabeledSecretNormalizedSource(String namespace, Map labels, boolean failFast, - ConfigUtils.Prefix prefix, boolean includeProfileSpecificSources) { + ConfigUtils.Prefix prefix) { super(null, namespace, failFast); this.labels = Collections.unmodifiableMap(Objects.requireNonNull(labels)); this.prefix = Objects.requireNonNull(prefix); - this.includeProfileSpecificSources = includeProfileSpecificSources; } - public LabeledSecretNormalizedSource(String namespace, Map labels, boolean failFast, - boolean includeProfileSpecificSources) { + public LabeledSecretNormalizedSource(String namespace, Map labels, boolean failFast) { super(null, namespace, failFast); this.labels = Collections.unmodifiableMap(Objects.requireNonNull(labels)); this.prefix = ConfigUtils.Prefix.DEFAULT; - this.includeProfileSpecificSources = includeProfileSpecificSources; } /** @@ -60,10 +55,6 @@ public ConfigUtils.Prefix prefix() { return prefix; } - public boolean profileSpecificSources() { - return this.includeProfileSpecificSources; - } - @Override public NormalizedSourceType type() { return NormalizedSourceType.LABELED_SECRET; diff --git a/spring-cloud-kubernetes-commons/src/main/java/org/springframework/cloud/kubernetes/commons/config/LabeledSourceData.java b/spring-cloud-kubernetes-commons/src/main/java/org/springframework/cloud/kubernetes/commons/config/LabeledSourceData.java index df4e6aab1f..40a6005ea0 100644 --- a/spring-cloud-kubernetes-commons/src/main/java/org/springframework/cloud/kubernetes/commons/config/LabeledSourceData.java +++ b/spring-cloud-kubernetes-commons/src/main/java/org/springframework/cloud/kubernetes/commons/config/LabeledSourceData.java @@ -16,9 +16,7 @@ package org.springframework.cloud.kubernetes.commons.config; -import java.util.Arrays; import java.util.Map; -import java.util.Set; import java.util.stream.Collectors; import static org.springframework.cloud.kubernetes.commons.config.ConfigUtils.onException; @@ -33,16 +31,12 @@ public abstract class LabeledSourceData { public final SourceData compute(Map labels, ConfigUtils.Prefix prefix, String target, - boolean profileSources, boolean failFast, String namespace, String[] activeProfiles) { + boolean failFast, String namespace) { MultipleSourcesContainer data = MultipleSourcesContainer.empty(); try { - Set profiles = Set.of(); - if (profileSources) { - profiles = Arrays.stream(activeProfiles).collect(Collectors.toSet()); - } - data = dataSupplier(labels, profiles); + data = dataSupplier(labels); // need this check because when there is no data, the name of the property // source @@ -91,6 +85,6 @@ public final SourceData compute(Map labels, ConfigUtils.Prefix p * @return a container that holds the names of the source that were found and their * data */ - public abstract MultipleSourcesContainer dataSupplier(Map labels, Set profiles); + public abstract MultipleSourcesContainer dataSupplier(Map labels); } diff --git a/spring-cloud-kubernetes-commons/src/main/java/org/springframework/cloud/kubernetes/commons/config/SecretsConfigProperties.java b/spring-cloud-kubernetes-commons/src/main/java/org/springframework/cloud/kubernetes/commons/config/SecretsConfigProperties.java index c8ddda3def..4c94d37018 100644 --- a/spring-cloud-kubernetes-commons/src/main/java/org/springframework/cloud/kubernetes/commons/config/SecretsConfigProperties.java +++ b/spring-cloud-kubernetes-commons/src/main/java/org/springframework/cloud/kubernetes/commons/config/SecretsConfigProperties.java @@ -59,7 +59,7 @@ public List determineSources(Environment environment) { if (!labels.isEmpty()) { result.add(new LabeledSecretNormalizedSource(this.namespace, this.labels, this.failFast, - ConfigUtils.Prefix.DEFAULT, false)); + ConfigUtils.Prefix.DEFAULT)); } return result; } @@ -105,7 +105,7 @@ private Stream normalize(String defaultName, String defaultNam if (!normalizedLabels.isEmpty()) { NormalizedSource labeledBasedSource = new LabeledSecretNormalizedSource(normalizedNamespace, labels, - failFast, prefix, includeProfileSpecificSources); + failFast, prefix); normalizedSources.add(labeledBasedSource); } diff --git a/spring-cloud-kubernetes-commons/src/test/java/org/springframework/cloud/kubernetes/commons/config/LabeledSecretNormalizedSourceTests.java b/spring-cloud-kubernetes-commons/src/test/java/org/springframework/cloud/kubernetes/commons/config/LabeledSecretNormalizedSourceTests.java index 9f0774f35a..a30120d2e2 100644 --- a/spring-cloud-kubernetes-commons/src/test/java/org/springframework/cloud/kubernetes/commons/config/LabeledSecretNormalizedSourceTests.java +++ b/spring-cloud-kubernetes-commons/src/test/java/org/springframework/cloud/kubernetes/commons/config/LabeledSecretNormalizedSourceTests.java @@ -31,8 +31,8 @@ class LabeledSecretNormalizedSourceTests { @Test void testEqualsAndHashCode() { - LabeledSecretNormalizedSource left = new LabeledSecretNormalizedSource("namespace", labels, false, false); - LabeledSecretNormalizedSource right = new LabeledSecretNormalizedSource("namespace", labels, true, false); + LabeledSecretNormalizedSource left = new LabeledSecretNormalizedSource("namespace", labels, false); + LabeledSecretNormalizedSource right = new LabeledSecretNormalizedSource("namespace", labels, true); Assertions.assertEquals(left.hashCode(), right.hashCode()); Assertions.assertEquals(left, right); @@ -48,10 +48,8 @@ void testEqualsAndHashCodePrefixDoesNotMatter() { ConfigUtils.Prefix knownLeft = ConfigUtils.findPrefix("left", false, false, "some"); ConfigUtils.Prefix knownRight = ConfigUtils.findPrefix("right", false, false, "some"); - LabeledSecretNormalizedSource left = new LabeledSecretNormalizedSource("namespace", labels, true, knownLeft, - false); - LabeledSecretNormalizedSource right = new LabeledSecretNormalizedSource("namespace", labels, true, knownRight, - false); + LabeledSecretNormalizedSource left = new LabeledSecretNormalizedSource("namespace", labels, true, knownLeft); + LabeledSecretNormalizedSource right = new LabeledSecretNormalizedSource("namespace", labels, true, knownRight); Assertions.assertEquals(left.hashCode(), right.hashCode()); Assertions.assertEquals(left, right); @@ -59,40 +57,37 @@ void testEqualsAndHashCodePrefixDoesNotMatter() { @Test void testType() { - LabeledSecretNormalizedSource source = new LabeledSecretNormalizedSource("namespace", labels, false, false); + LabeledSecretNormalizedSource source = new LabeledSecretNormalizedSource("namespace", labels, false); Assertions.assertSame(source.type(), NormalizedSourceType.LABELED_SECRET); } @Test void testImmutableGetLabels() { - LabeledSecretNormalizedSource source = new LabeledSecretNormalizedSource("namespace", labels, false, false); + LabeledSecretNormalizedSource source = new LabeledSecretNormalizedSource("namespace", labels, false); Assertions.assertThrows(RuntimeException.class, () -> source.labels().put("c", "d")); } @Test void testTarget() { - LabeledSecretNormalizedSource source = new LabeledSecretNormalizedSource("namespace", labels, false, false); + LabeledSecretNormalizedSource source = new LabeledSecretNormalizedSource("namespace", labels, false); Assertions.assertEquals(source.target(), "secret"); } @Test void testConstructorFields() { ConfigUtils.Prefix prefix = ConfigUtils.findPrefix("prefix", false, false, "some"); - LabeledSecretNormalizedSource source = new LabeledSecretNormalizedSource("namespace", labels, false, prefix, - true); + LabeledSecretNormalizedSource source = new LabeledSecretNormalizedSource("namespace", labels, false, prefix); Assertions.assertTrue(source.name().isEmpty()); Assertions.assertEquals(source.namespace().get(), "namespace"); Assertions.assertFalse(source.failFast()); - Assertions.assertTrue(source.profileSpecificSources()); } @Test void testConstructorWithoutPrefixFields() { - LabeledSecretNormalizedSource source = new LabeledSecretNormalizedSource("namespace", labels, true, true); + LabeledSecretNormalizedSource source = new LabeledSecretNormalizedSource("namespace", labels, true); Assertions.assertEquals(source.namespace().get(), "namespace"); Assertions.assertTrue(source.failFast()); Assertions.assertSame(ConfigUtils.Prefix.DEFAULT, source.prefix()); - Assertions.assertTrue(source.profileSpecificSources()); } } diff --git a/spring-cloud-kubernetes-commons/src/test/java/org/springframework/cloud/kubernetes/commons/config/SecretsConfigPropertiesTests.java b/spring-cloud-kubernetes-commons/src/test/java/org/springframework/cloud/kubernetes/commons/config/SecretsConfigPropertiesTests.java index cb3cd6439f..75731be1e6 100644 --- a/spring-cloud-kubernetes-commons/src/test/java/org/springframework/cloud/kubernetes/commons/config/SecretsConfigPropertiesTests.java +++ b/spring-cloud-kubernetes-commons/src/test/java/org/springframework/cloud/kubernetes/commons/config/SecretsConfigPropertiesTests.java @@ -307,7 +307,6 @@ void testMultipleCases() { * - labels: * - name: second-label * value: secret-two - * includeProfileSpecificSources: true * useNameAsPrefix: true * explicitPrefix: two * - labels: @@ -348,19 +347,15 @@ void testLabelsMultipleCases() { LabeledSecretNormalizedSource labeled1 = (LabeledSecretNormalizedSource) sources.get(1); Assertions.assertEquals(labeled1.prefix().prefixProvider().get(), "one"); - Assertions.assertFalse(labeled1.profileSpecificSources()); LabeledSecretNormalizedSource labeled3 = (LabeledSecretNormalizedSource) sources.get(3); Assertions.assertEquals(labeled3.prefix().prefixProvider().get(), "two"); - Assertions.assertTrue(labeled3.profileSpecificSources()); LabeledSecretNormalizedSource labeled5 = (LabeledSecretNormalizedSource) sources.get(5); Assertions.assertEquals(labeled5.prefix().prefixProvider().get(), "three"); - Assertions.assertFalse(labeled5.profileSpecificSources()); LabeledSecretNormalizedSource labeled7 = (LabeledSecretNormalizedSource) sources.get(7); Assertions.assertSame(labeled7.prefix(), ConfigUtils.Prefix.DEFAULT); - Assertions.assertFalse(labeled7.profileSpecificSources()); Set set = new LinkedHashSet<>(sources); Assertions.assertEquals(5, set.size()); diff --git a/spring-cloud-kubernetes-fabric8-config/src/main/java/org/springframework/cloud/kubernetes/fabric8/config/Fabric8ConfigUtils.java b/spring-cloud-kubernetes-fabric8-config/src/main/java/org/springframework/cloud/kubernetes/fabric8/config/Fabric8ConfigUtils.java index 9c533d473e..9d0e59e07c 100644 --- a/spring-cloud-kubernetes-fabric8-config/src/main/java/org/springframework/cloud/kubernetes/fabric8/config/Fabric8ConfigUtils.java +++ b/spring-cloud-kubernetes-fabric8-config/src/main/java/org/springframework/cloud/kubernetes/fabric8/config/Fabric8ConfigUtils.java @@ -62,39 +62,35 @@ public static Set namespaces(KubernetesClient client, KubernetesNamespac *
 	 *     1. read all secrets in the provided namespace
 	 *     2. from the above, filter the ones that we care about (filter by labels)
-	 *     3. with secret names from (2), find out if there are any profile based secrets (if profiles is not empty)
-	 *     4. concat (2) and (3) and these are the secrets we are interested in
-	 *     5. see if any of the secrets from (4) has a single yaml/properties file
-	 *     6. gather all the names of the secrets (from 4) + data they hold
+	 *     3. see if any of the secrets from (2) has a single yaml/properties file
+	 *     4. gather all the names of the secrets + data they hold
 	 * 
*/ static MultipleSourcesContainer secretsDataByLabels(KubernetesClient client, String namespace, - Map labels, Environment environment, Set profiles) { + Map labels, Environment environment) { List strippedSecrets = strippedSecrets(client, namespace); if (strippedSecrets.isEmpty()) { return MultipleSourcesContainer.empty(); } - return ConfigUtils.processLabeledData(strippedSecrets, environment, labels, namespace, profiles, true); + return ConfigUtils.processLabeledData(strippedSecrets, environment, labels, namespace, true); } /** *
 	 *     1. read all config maps in the provided namespace
 	 *     2. from the above, filter the ones that we care about (filter by labels)
-	 *     3. with config maps names from (2), find out if there are any profile based ones (if profiles is not empty)
-	 *     4. concat (2) and (3) and these are the config maps we are interested in
-	 *     5. see if any from (4) has a single yaml/properties file
-	 *     6. gather all the names of the config maps (from 4) + data they hold
+	 *     3. see if any from (2) has a single yaml/properties file
+	 *     4. gather all the names of the config maps + data they hold
 	 * 
*/ static MultipleSourcesContainer configMapsDataByLabels(KubernetesClient client, String namespace, - Map labels, Environment environment, Set profiles) { + Map labels, Environment environment) { List strippedConfigMaps = strippedConfigMaps(client, namespace); if (strippedConfigMaps.isEmpty()) { return MultipleSourcesContainer.empty(); } - return ConfigUtils.processLabeledData(strippedConfigMaps, environment, labels, namespace, profiles, false); + return ConfigUtils.processLabeledData(strippedConfigMaps, environment, labels, namespace, false); } /** diff --git a/spring-cloud-kubernetes-fabric8-config/src/main/java/org/springframework/cloud/kubernetes/fabric8/config/LabeledConfigMapContextToSourceDataProvider.java b/spring-cloud-kubernetes-fabric8-config/src/main/java/org/springframework/cloud/kubernetes/fabric8/config/LabeledConfigMapContextToSourceDataProvider.java index 8058f041dd..16199d3e0b 100644 --- a/spring-cloud-kubernetes-fabric8-config/src/main/java/org/springframework/cloud/kubernetes/fabric8/config/LabeledConfigMapContextToSourceDataProvider.java +++ b/spring-cloud-kubernetes-fabric8-config/src/main/java/org/springframework/cloud/kubernetes/fabric8/config/LabeledConfigMapContextToSourceDataProvider.java @@ -17,7 +17,6 @@ package org.springframework.cloud.kubernetes.fabric8.config; import java.util.Map; -import java.util.Set; import java.util.function.Supplier; import org.springframework.cloud.kubernetes.commons.config.LabeledConfigMapNormalizedSource; @@ -55,13 +54,12 @@ public Fabric8ContextToSourceData get() { return new LabeledSourceData() { @Override - public MultipleSourcesContainer dataSupplier(Map labels, Set profiles) { + public MultipleSourcesContainer dataSupplier(Map labels) { return Fabric8ConfigUtils.configMapsDataByLabels(context.client(), context.namespace(), labels, - context.environment(), profiles); + context.environment()); } - }.compute(source.labels(), source.prefix(), source.target(), source.profileSpecificSources(), - source.failFast(), context.namespace(), context.environment().getActiveProfiles()); + }.compute(source.labels(), source.prefix(), source.target(), source.failFast(), context.namespace()); }; } diff --git a/spring-cloud-kubernetes-fabric8-config/src/main/java/org/springframework/cloud/kubernetes/fabric8/config/LabeledSecretContextToSourceDataProvider.java b/spring-cloud-kubernetes-fabric8-config/src/main/java/org/springframework/cloud/kubernetes/fabric8/config/LabeledSecretContextToSourceDataProvider.java index efad27a7ba..cea86c6092 100644 --- a/spring-cloud-kubernetes-fabric8-config/src/main/java/org/springframework/cloud/kubernetes/fabric8/config/LabeledSecretContextToSourceDataProvider.java +++ b/spring-cloud-kubernetes-fabric8-config/src/main/java/org/springframework/cloud/kubernetes/fabric8/config/LabeledSecretContextToSourceDataProvider.java @@ -17,7 +17,6 @@ package org.springframework.cloud.kubernetes.fabric8.config; import java.util.Map; -import java.util.Set; import java.util.function.Supplier; import org.springframework.cloud.kubernetes.commons.config.LabeledSecretNormalizedSource; @@ -54,13 +53,12 @@ public Fabric8ContextToSourceData get() { return new LabeledSourceData() { @Override - public MultipleSourcesContainer dataSupplier(Map labels, Set profiles) { + public MultipleSourcesContainer dataSupplier(Map labels) { return Fabric8ConfigUtils.secretsDataByLabels(context.client(), context.namespace(), labels, - context.environment(), profiles); + context.environment()); } - }.compute(source.labels(), source.prefix(), source.target(), source.profileSpecificSources(), - source.failFast(), context.namespace(), context.environment().getActiveProfiles()); + }.compute(source.labels(), source.prefix(), source.target(), source.failFast(), context.namespace()); }; } diff --git a/spring-cloud-kubernetes-fabric8-config/src/test/java/org/springframework/cloud/kubernetes/fabric8/config/Fabric8ConfigUtilsTests.java b/spring-cloud-kubernetes-fabric8-config/src/test/java/org/springframework/cloud/kubernetes/fabric8/config/Fabric8ConfigUtilsTests.java index 843a2e82ad..938c41e16c 100644 --- a/spring-cloud-kubernetes-fabric8-config/src/test/java/org/springframework/cloud/kubernetes/fabric8/config/Fabric8ConfigUtilsTests.java +++ b/spring-cloud-kubernetes-fabric8-config/src/test/java/org/springframework/cloud/kubernetes/fabric8/config/Fabric8ConfigUtilsTests.java @@ -61,7 +61,7 @@ void testSecretDataByLabelsSecretNotFound() { .resource(new SecretBuilder().withMetadata(new ObjectMetaBuilder().withName("my-secret").build()).build()) .create(); MultipleSourcesContainer result = Fabric8ConfigUtils.secretsDataByLabels(client, "spring-k8s", - Map.of("color", "red"), new MockEnvironment(), Set.of()); + Map.of("color", "red"), new MockEnvironment()); Assertions.assertEquals(Map.of(), result.data()); Assertions.assertTrue(result.names().isEmpty()); } @@ -79,7 +79,7 @@ void testSecretDataByLabelsSecretFound() { .create(); MultipleSourcesContainer result = Fabric8ConfigUtils.secretsDataByLabels(client, "spring-k8s", - Map.of("color", "pink"), new MockEnvironment(), Set.of()); + Map.of("color", "pink"), new MockEnvironment()); Assertions.assertEquals(Set.of("my-secret"), result.names()); Assertions.assertEquals(Map.of("property", "value"), result.data()); } @@ -98,7 +98,7 @@ void testSecretDataByLabelsSecretFoundWithPropertyFile() { .create(); MultipleSourcesContainer result = Fabric8ConfigUtils.secretsDataByLabels(client, "spring-k8s", - Map.of("color", "pink"), new MockEnvironment(), Set.of()); + Map.of("color", "pink"), new MockEnvironment()); Assertions.assertEquals(Set.of("my-secret"), result.names()); Assertions.assertEquals(Map.of("key1", "value1"), result.data()); } @@ -125,7 +125,7 @@ void testSecretDataByLabelsTwoSecretsFound() { .create(); MultipleSourcesContainer result = Fabric8ConfigUtils.secretsDataByLabels(client, "spring-k8s", - Map.of("color", "pink"), new MockEnvironment(), Set.of()); + Map.of("color", "pink"), new MockEnvironment()); Assertions.assertTrue(result.names().contains("my-secret")); Assertions.assertTrue(result.names().contains("my-secret-2")); @@ -141,10 +141,8 @@ void testSecretDataByLabelsTwoSecretsFound() { * - secret deployed with name "blue-triangle-secret" and labels "color=blue, shape=triangle, tag=no-fit" * - secret deployed with name "blue-square-secret-k8s" and labels "color=blue, shape=triangle, tag=no-fit" * - * - we search by labels "color=blue, tag=fits", as such first find two secrets: "blue-circle-secret" + * - we search by labels "color=blue, tag=fits", as such find two secrets: "blue-circle-secret" * and "blue-square-secret". - * - since "k8s" profile is enabled, we also take "blue-square-secret-k8s". Notice that this one does not match - * the initial labels (it has "tag=no-fit"), but it does not matter, we take it anyway. * */ @Test @@ -190,16 +188,14 @@ void testSecretDataByLabelsThreeSecretsFound() { .create(); MultipleSourcesContainer result = Fabric8ConfigUtils.secretsDataByLabels(client, "spring-k8s", - Map.of("tag", "fit", "color", "blue"), new MockEnvironment(), Set.of("k8s")); + Map.of("tag", "fit", "color", "blue"), new MockEnvironment()); Assertions.assertTrue(result.names().contains("blue-circle-secret")); Assertions.assertTrue(result.names().contains("blue-square-secret")); - Assertions.assertTrue(result.names().contains("blue-square-secret-k8s")); - Assertions.assertEquals(3, result.data().size()); + Assertions.assertEquals(2, result.data().size()); Assertions.assertEquals("1", result.data().get("one")); Assertions.assertEquals("2", result.data().get("two")); - Assertions.assertEquals("4", result.data().get("four")); } // secret "my-secret" is deployed; we search for it by name and do not find it. diff --git a/spring-cloud-kubernetes-fabric8-config/src/test/java/org/springframework/cloud/kubernetes/fabric8/config/Fabric8SecretsPropertySourceMockTests.java b/spring-cloud-kubernetes-fabric8-config/src/test/java/org/springframework/cloud/kubernetes/fabric8/config/Fabric8SecretsPropertySourceMockTests.java index c048667484..90f917aa9c 100644 --- a/spring-cloud-kubernetes-fabric8-config/src/test/java/org/springframework/cloud/kubernetes/fabric8/config/Fabric8SecretsPropertySourceMockTests.java +++ b/spring-cloud-kubernetes-fabric8-config/src/test/java/org/springframework/cloud/kubernetes/fabric8/config/Fabric8SecretsPropertySourceMockTests.java @@ -64,7 +64,7 @@ void labeledStrategyShouldThrowExceptionOnFailureWhenFailFastIsEnabled() { final Map labels = Collections.singletonMap("a", "b"); final String path = String.format("/api/v1/namespaces/%s/secrets", namespace); - LabeledSecretNormalizedSource labeled = new LabeledSecretNormalizedSource(namespace, labels, true, false); + LabeledSecretNormalizedSource labeled = new LabeledSecretNormalizedSource(namespace, labels, true); Fabric8ConfigContext context = new Fabric8ConfigContext(client, labeled, "default", new MockEnvironment()); mockServer.expect().withPath(path).andReturn(500, "Internal Server Error").always(); @@ -91,7 +91,7 @@ void labeledStrategyShouldNotThrowExceptionOnFailureWhenFailFastIsDisabled() { final Map labels = Collections.singletonMap("a", "b"); final String path = String.format("/api/v1/namespaces/%s/secrets", namespace); - LabeledSecretNormalizedSource labeled = new LabeledSecretNormalizedSource(namespace, labels, false, false); + LabeledSecretNormalizedSource labeled = new LabeledSecretNormalizedSource(namespace, labels, false); Fabric8ConfigContext context = new Fabric8ConfigContext(client, labeled, "default", new MockEnvironment()); mockServer.expect().withPath(path).andReturn(500, "Internal Server Error").always(); diff --git a/spring-cloud-kubernetes-fabric8-config/src/test/java/org/springframework/cloud/kubernetes/fabric8/config/LabeledConfigMapContextToSourceDataProviderTests.java b/spring-cloud-kubernetes-fabric8-config/src/test/java/org/springframework/cloud/kubernetes/fabric8/config/LabeledConfigMapContextToSourceDataProviderTests.java index 7342c4c568..2227d2038e 100644 --- a/spring-cloud-kubernetes-fabric8-config/src/test/java/org/springframework/cloud/kubernetes/fabric8/config/LabeledConfigMapContextToSourceDataProviderTests.java +++ b/spring-cloud-kubernetes-fabric8-config/src/test/java/org/springframework/cloud/kubernetes/fabric8/config/LabeledConfigMapContextToSourceDataProviderTests.java @@ -301,8 +301,7 @@ void testTwoConfigmapsWithPrefix() { /** * two configmaps are deployed: "color-configmap" with label: "{color:blue}" and * "color-configmap-k8s" with no labels. We search by "{color:red}", do not find - * anything and thus have an empty SourceData. profile based sources are enabled, but - * it has no effect. + * anything and thus have an empty SourceData. */ @Test void searchWithLabelsNoConfigmapsFound() { @@ -322,7 +321,6 @@ void searchWithLabelsNoConfigmapsFound() { mockClient.configMaps().inNamespace(NAMESPACE).resource(colorConfigmap).create(); mockClient.configMaps().inNamespace(NAMESPACE).resource(colorConfigmapK8s).create(); MockEnvironment environment = new MockEnvironment(); - environment.setActiveProfiles("k8s"); NormalizedSource normalizedSource = new LabeledConfigMapNormalizedSource(NAMESPACE, Collections.singletonMap("color", "red"), true, ConfigUtils.Prefix.DEFAULT, true); @@ -339,7 +337,7 @@ void searchWithLabelsNoConfigmapsFound() { /** * two configmaps are deployed: "color-configmap" with label: "{color:blue}" and * "shape-configmap" with label: "{shape:round}". We search by "{color:blue}" and find - * one configmap. profile based sources are enabled, but it has no effect. + * one configmap. */ @Test void searchWithLabelsOneConfigMapFound() { @@ -359,7 +357,6 @@ void searchWithLabelsOneConfigMapFound() { mockClient.configMaps().inNamespace(NAMESPACE).resource(colorConfigmap).create(); mockClient.configMaps().inNamespace(NAMESPACE).resource(shapeConfigmap).create(); MockEnvironment environment = new MockEnvironment(); - environment.setActiveProfiles("k8s"); NormalizedSource normalizedSource = new LabeledConfigMapNormalizedSource(NAMESPACE, Collections.singletonMap("color", "blue"), true, ConfigUtils.Prefix.DEFAULT, true); @@ -374,47 +371,6 @@ void searchWithLabelsOneConfigMapFound() { } - /** - * two configmaps are deployed: "color-configmap" with label: "{color:blue}" and - * "color-configmap-k8s" with label: "{color:red}". We search by "{color:blue}" and - * find one configmap. Since profiles are enabled, we will also be reading - * "color-configmap-k8s", even if its labels do not match provided ones. - */ - @Test - void searchWithLabelsOneConfigMapFoundAndOneFromProfileFound() { - ConfigMap colorConfigmap = new ConfigMapBuilder().withNewMetadata() - .withName("color-configmap") - .withLabels(Collections.singletonMap("color", "blue")) - .endMetadata() - .addToData("one", "1") - .build(); - - ConfigMap colorConfigmapK8s = new ConfigMapBuilder().withNewMetadata() - .withName("color-configmap-k8s") - .withLabels(Collections.singletonMap("color", "red")) - .endMetadata() - .addToData("two", "2") - .build(); - - mockClient.configMaps().inNamespace(NAMESPACE).resource(colorConfigmap).create(); - mockClient.configMaps().inNamespace(NAMESPACE).resource(colorConfigmapK8s).create(); - MockEnvironment environment = new MockEnvironment(); - environment.setActiveProfiles("k8s"); - - NormalizedSource normalizedSource = new LabeledConfigMapNormalizedSource(NAMESPACE, - Collections.singletonMap("color", "blue"), true, ConfigUtils.Prefix.DELAYED, true); - Fabric8ConfigContext context = new Fabric8ConfigContext(mockClient, normalizedSource, NAMESPACE, environment); - - Fabric8ContextToSourceData data = new LabeledConfigMapContextToSourceDataProvider().get(); - SourceData sourceData = data.apply(context); - - Assertions.assertEquals(sourceData.sourceData().size(), 2); - Assertions.assertEquals(sourceData.sourceData().get("color-configmap.color-configmap-k8s.one"), "1"); - Assertions.assertEquals(sourceData.sourceData().get("color-configmap.color-configmap-k8s.two"), "2"); - Assertions.assertEquals(sourceData.sourceName(), "configmap.color-configmap.color-configmap-k8s.default"); - - } - /** *
 	 *     - configmap "color-configmap" with label "{color:blue}"
@@ -425,7 +381,7 @@ void searchWithLabelsOneConfigMapFoundAndOneFromProfileFound() {
 	 * 
*/ @Test - void searchWithLabelsTwoConfigMapsFoundAndOneFromProfileFound() { + void searchWithLabelsTwoConfigMapsFound() { ConfigMap colorConfigMap = new ConfigMapBuilder().withNewMetadata() .withName("color-configmap") .withLabels(Collections.singletonMap("color", "blue")) @@ -468,7 +424,6 @@ void searchWithLabelsTwoConfigMapsFoundAndOneFromProfileFound() { mockClient.configMaps().inNamespace(NAMESPACE).resource(shapeConfigmapK8s).create(); MockEnvironment environment = new MockEnvironment(); - environment.setActiveProfiles("k8s"); NormalizedSource normalizedSource = new LabeledConfigMapNormalizedSource(NAMESPACE, Collections.singletonMap("color", "blue"), true, ConfigUtils.Prefix.DELAYED, true); @@ -477,18 +432,11 @@ void searchWithLabelsTwoConfigMapsFoundAndOneFromProfileFound() { Fabric8ContextToSourceData data = new LabeledConfigMapContextToSourceDataProvider().get(); SourceData sourceData = data.apply(context); - Assertions.assertEquals(sourceData.sourceData().size(), 4); - Assertions.assertEquals(sourceData.sourceData() - .get("color-configmap.color-configmap-k8s.shape-configmap.shape-configmap-k8s.one"), "1"); - Assertions.assertEquals(sourceData.sourceData() - .get("color-configmap.color-configmap-k8s.shape-configmap.shape-configmap-k8s.two"), "2"); - Assertions.assertEquals(sourceData.sourceData() - .get("color-configmap.color-configmap-k8s.shape-configmap.shape-configmap-k8s.four"), "4"); - Assertions.assertEquals(sourceData.sourceData() - .get("color-configmap.color-configmap-k8s.shape-configmap.shape-configmap-k8s.five"), "5"); - - Assertions.assertEquals(sourceData.sourceName(), - "configmap.color-configmap.color-configmap-k8s.shape-configmap.shape-configmap-k8s.default"); + Assertions.assertEquals(sourceData.sourceData().size(), 2); + Assertions.assertEquals(sourceData.sourceData().get("color-configmap.shape-configmap.one"), "1"); + Assertions.assertEquals(sourceData.sourceData().get("color-configmap.shape-configmap.two"), "2"); + + Assertions.assertEquals(sourceData.sourceName(), "configmap.color-configmap.shape-configmap.default"); } diff --git a/spring-cloud-kubernetes-fabric8-config/src/test/java/org/springframework/cloud/kubernetes/fabric8/config/LabeledSecretContextToSourceDataProviderTests.java b/spring-cloud-kubernetes-fabric8-config/src/test/java/org/springframework/cloud/kubernetes/fabric8/config/LabeledSecretContextToSourceDataProviderTests.java index 835e831c89..ff383321fb 100644 --- a/spring-cloud-kubernetes-fabric8-config/src/test/java/org/springframework/cloud/kubernetes/fabric8/config/LabeledSecretContextToSourceDataProviderTests.java +++ b/spring-cloud-kubernetes-fabric8-config/src/test/java/org/springframework/cloud/kubernetes/fabric8/config/LabeledSecretContextToSourceDataProviderTests.java @@ -62,14 +62,12 @@ class LabeledSecretContextToSourceDataProviderTests { private static KubernetesClient mockClient; - static { - LABELS.put("label2", "value2"); - LABELS.put("label1", "value1"); - } - @BeforeAll static void beforeAll() { + LABELS.put("label2", "value2"); + LABELS.put("label1", "value1"); + // Configure the kubernetes master url to point to the mock server System.setProperty(Config.KUBERNETES_MASTER_SYSTEM_PROPERTY, mockClient.getConfiguration().getMasterUrl()); System.setProperty(Config.KUBERNETES_TRUST_CERT_SYSTEM_PROPERTY, "true"); @@ -102,7 +100,7 @@ void singleSecretMatchAgainstLabels() { mockClient.secrets().inNamespace(NAMESPACE).resource(secret).create(); - NormalizedSource normalizedSource = new LabeledSecretNormalizedSource(NAMESPACE, LABELS, true, false); + NormalizedSource normalizedSource = new LabeledSecretNormalizedSource(NAMESPACE, LABELS, true); Fabric8ConfigContext context = new Fabric8ConfigContext(mockClient, normalizedSource, NAMESPACE, new MockEnvironment()); @@ -146,7 +144,7 @@ void twoSecretsMatchAgainstLabels() { mockClient.secrets().inNamespace(NAMESPACE).resource(redTwo).create(); mockClient.secrets().inNamespace(NAMESPACE).resource(blue).create(); - NormalizedSource normalizedSource = new LabeledSecretNormalizedSource(NAMESPACE, RED_LABEL, true, false); + NormalizedSource normalizedSource = new LabeledSecretNormalizedSource(NAMESPACE, RED_LABEL, true); Fabric8ConfigContext context = new Fabric8ConfigContext(mockClient, normalizedSource, NAMESPACE, new MockEnvironment()); @@ -175,7 +173,7 @@ void secretNoMatch() { mockClient.secrets().inNamespace(NAMESPACE).resource(pink).create(); - NormalizedSource normalizedSource = new LabeledSecretNormalizedSource(NAMESPACE, BLUE_LABEL, true, false); + NormalizedSource normalizedSource = new LabeledSecretNormalizedSource(NAMESPACE, BLUE_LABEL, true); Fabric8ConfigContext context = new Fabric8ConfigContext(mockClient, normalizedSource, NAMESPACE, new MockEnvironment()); @@ -205,7 +203,7 @@ void namespaceMatch() { mockClient.secrets().inNamespace(NAMESPACE).resource(secret).create(); // different namespace - NormalizedSource normalizedSource = new LabeledSecretNormalizedSource(NAMESPACE + "nope", LABELS, true, false); + NormalizedSource normalizedSource = new LabeledSecretNormalizedSource(NAMESPACE + "nope", LABELS, true); Fabric8ConfigContext context = new Fabric8ConfigContext(mockClient, normalizedSource, NAMESPACE, new MockEnvironment()); @@ -235,7 +233,7 @@ void testWithPrefix() { ConfigUtils.Prefix mePrefix = ConfigUtils.findPrefix("me", false, false, "irrelevant"); NormalizedSource normalizedSource = new LabeledSecretNormalizedSource(NAMESPACE, - Collections.singletonMap("color", "blue"), true, mePrefix, false); + Collections.singletonMap("color", "blue"), true, mePrefix); Fabric8ConfigContext context = new Fabric8ConfigContext(mockClient, normalizedSource, NAMESPACE, new MockEnvironment()); @@ -275,7 +273,7 @@ void testTwoSecretsWithPrefix() { mockClient.secrets().inNamespace(NAMESPACE).resource(anotherBlue).create(); NormalizedSource normalizedSource = new LabeledSecretNormalizedSource(NAMESPACE, - Collections.singletonMap("color", "blue"), true, ConfigUtils.Prefix.DELAYED, false); + Collections.singletonMap("color", "blue"), true, ConfigUtils.Prefix.DELAYED); Fabric8ConfigContext context = new Fabric8ConfigContext(mockClient, normalizedSource, NAMESPACE, new MockEnvironment()); @@ -302,8 +300,7 @@ void testTwoSecretsWithPrefix() { /** * two secrets are deployed: secret "color-secret" with label: "{color:blue}" and * "color-secret-k8s" with no labels. We search by "{color:red}", do not find anything - * and thus have an empty SourceData. profile based sources are enabled, but it has no - * effect. + * and thus have an empty SourceData. */ @Test void searchWithLabelsNoSecretFound() { @@ -323,10 +320,9 @@ void searchWithLabelsNoSecretFound() { mockClient.secrets().inNamespace(NAMESPACE).resource(colorSecret).create(); mockClient.secrets().inNamespace(NAMESPACE).resource(colorSecretK8s).create(); MockEnvironment environment = new MockEnvironment(); - environment.setActiveProfiles("k8s"); NormalizedSource normalizedSource = new LabeledSecretNormalizedSource(NAMESPACE, - Collections.singletonMap("color", "red"), true, ConfigUtils.Prefix.DEFAULT, true); + Collections.singletonMap("color", "red"), true, ConfigUtils.Prefix.DEFAULT); Fabric8ConfigContext context = new Fabric8ConfigContext(mockClient, normalizedSource, NAMESPACE, environment); Fabric8ContextToSourceData data = new LabeledSecretContextToSourceDataProvider().get(); @@ -340,7 +336,7 @@ void searchWithLabelsNoSecretFound() { /** * two secrets are deployed: secret "color-secret" with label: "{color:blue}" and * "shape-secret" with label: "{shape:round}". We search by "{color:blue}" and find - * one secret. profile based sources are enabled, but it has no effect. + * one secret. */ @Test void searchWithLabelsOneSecretFound() { @@ -360,10 +356,9 @@ void searchWithLabelsOneSecretFound() { mockClient.secrets().inNamespace(NAMESPACE).resource(colorSecret).create(); mockClient.secrets().inNamespace(NAMESPACE).resource(shapeSecret).create(); MockEnvironment environment = new MockEnvironment(); - environment.setActiveProfiles("k8s"); NormalizedSource normalizedSource = new LabeledSecretNormalizedSource(NAMESPACE, - Collections.singletonMap("color", "blue"), true, ConfigUtils.Prefix.DEFAULT, true); + Collections.singletonMap("color", "blue"), true, ConfigUtils.Prefix.DEFAULT); Fabric8ConfigContext context = new Fabric8ConfigContext(mockClient, normalizedSource, NAMESPACE, environment); Fabric8ContextToSourceData data = new LabeledSecretContextToSourceDataProvider().get(); @@ -375,47 +370,6 @@ void searchWithLabelsOneSecretFound() { } - /** - * two secrets are deployed: secret "color-secret" with label: "{color:blue}" and - * "color-secret-k8s" with label: "{color:red}". We search by "{color:blue}" and find - * one secret. Since profiles are enabled, we will also be reading "color-secret-k8s", - * even if its labels do not match provided ones. - */ - @Test - void searchWithLabelsOneSecretFoundAndOneFromProfileFound() { - Secret colorSecret = new SecretBuilder().withNewMetadata() - .withName("color-secret") - .withLabels(Collections.singletonMap("color", "blue")) - .endMetadata() - .addToData("one", Base64.getEncoder().encodeToString("1".getBytes())) - .build(); - - Secret colorSecretK8s = new SecretBuilder().withNewMetadata() - .withName("color-secret-k8s") - .withLabels(Collections.singletonMap("color", "red")) - .endMetadata() - .addToData("two", Base64.getEncoder().encodeToString("2".getBytes())) - .build(); - - mockClient.secrets().inNamespace(NAMESPACE).resource(colorSecret).create(); - mockClient.secrets().inNamespace(NAMESPACE).resource(colorSecretK8s).create(); - MockEnvironment environment = new MockEnvironment(); - environment.setActiveProfiles("k8s"); - - NormalizedSource normalizedSource = new LabeledSecretNormalizedSource(NAMESPACE, - Collections.singletonMap("color", "blue"), true, ConfigUtils.Prefix.DELAYED, true); - Fabric8ConfigContext context = new Fabric8ConfigContext(mockClient, normalizedSource, NAMESPACE, environment); - - Fabric8ContextToSourceData data = new LabeledSecretContextToSourceDataProvider().get(); - SourceData sourceData = data.apply(context); - - Assertions.assertEquals(sourceData.sourceData().size(), 2); - Assertions.assertEquals(sourceData.sourceData().get("color-secret.color-secret-k8s.one"), "1"); - Assertions.assertEquals(sourceData.sourceData().get("color-secret.color-secret-k8s.two"), "2"); - Assertions.assertEquals(sourceData.sourceName(), "secret.color-secret.color-secret-k8s.default"); - - } - /** *
 	 *     - secret "color-secret" with label "{color:blue}"
@@ -426,7 +380,7 @@ void searchWithLabelsOneSecretFoundAndOneFromProfileFound() {
 	 * 
*/ @Test - void searchWithLabelsTwoSecretsFoundAndOneFromProfileFound() { + void searchWithLabelsTwoSecretsFound() { Secret colorSecret = new SecretBuilder().withNewMetadata() .withName("color-secret") .withLabels(Collections.singletonMap("color", "blue")) @@ -469,27 +423,21 @@ void searchWithLabelsTwoSecretsFoundAndOneFromProfileFound() { mockClient.secrets().inNamespace(NAMESPACE).resource(shapeSecretK8s).create(); MockEnvironment environment = new MockEnvironment(); - environment.setActiveProfiles("k8s"); NormalizedSource normalizedSource = new LabeledSecretNormalizedSource(NAMESPACE, - Collections.singletonMap("color", "blue"), true, ConfigUtils.Prefix.DELAYED, true); + Collections.singletonMap("color", "blue"), true, ConfigUtils.Prefix.DELAYED); Fabric8ConfigContext context = new Fabric8ConfigContext(mockClient, normalizedSource, NAMESPACE, environment); Fabric8ContextToSourceData data = new LabeledSecretContextToSourceDataProvider().get(); SourceData sourceData = data.apply(context); - Assertions.assertEquals(sourceData.sourceData().size(), 4); - Assertions.assertEquals( - sourceData.sourceData().get("color-secret.color-secret-k8s.shape-secret.shape-secret-k8s.one"), "1"); - Assertions.assertEquals( - sourceData.sourceData().get("color-secret.color-secret-k8s.shape-secret.shape-secret-k8s.two"), "2"); + Assertions.assertEquals(sourceData.sourceData().size(), 2); Assertions.assertEquals( - sourceData.sourceData().get("color-secret.color-secret-k8s.shape-secret.shape-secret-k8s.four"), "4"); + sourceData.sourceData().get("color-secret.shape-secret.one"), "1"); Assertions.assertEquals( - sourceData.sourceData().get("color-secret.color-secret-k8s.shape-secret.shape-secret-k8s.five"), "5"); + sourceData.sourceData().get("color-secret.shape-secret.two"), "2"); - Assertions.assertEquals(sourceData.sourceName(), - "secret.color-secret.color-secret-k8s.shape-secret.shape-secret-k8s.default"); + Assertions.assertEquals(sourceData.sourceName(), "secret.color-secret.shape-secret.default"); } @@ -508,7 +456,7 @@ void testYaml() { mockClient.secrets().inNamespace(NAMESPACE).resource(colorSecret).create(); NormalizedSource normalizedSource = new LabeledSecretNormalizedSource(NAMESPACE, - Collections.singletonMap("color", "blue"), true, ConfigUtils.Prefix.DEFAULT, true); + Collections.singletonMap("color", "blue"), true, ConfigUtils.Prefix.DEFAULT); Fabric8ConfigContext context = new Fabric8ConfigContext(mockClient, normalizedSource, NAMESPACE, new MockEnvironment()); @@ -550,7 +498,7 @@ void cache(CapturedOutput output) { MockEnvironment environment = new MockEnvironment(); NormalizedSource redNormalizedSource = new LabeledSecretNormalizedSource(NAMESPACE, - Collections.singletonMap("color", "red"), true, ConfigUtils.Prefix.DELAYED, true); + Collections.singletonMap("color", "red"), true, ConfigUtils.Prefix.DELAYED); Fabric8ConfigContext redContext = new Fabric8ConfigContext(mockClient, redNormalizedSource, NAMESPACE, environment); Fabric8ContextToSourceData redData = new LabeledSecretContextToSourceDataProvider().get(); @@ -561,7 +509,7 @@ void cache(CapturedOutput output) { Assertions.assertTrue(output.getAll().contains("Loaded all secrets in namespace '" + NAMESPACE + "'")); NormalizedSource greenNormalizedSource = new LabeledSecretNormalizedSource(NAMESPACE, - Collections.singletonMap("color", "green"), true, ConfigUtils.Prefix.DELAYED, true); + Collections.singletonMap("color", "green"), true, ConfigUtils.Prefix.DELAYED); Fabric8ConfigContext greenContext = new Fabric8ConfigContext(mockClient, greenNormalizedSource, NAMESPACE, environment); Fabric8ContextToSourceData greenData = new LabeledSecretContextToSourceDataProvider().get(); diff --git a/spring-cloud-kubernetes-fabric8-config/src/test/java/org/springframework/cloud/kubernetes/fabric8/config/labeled_config_map_with_profile/LabeledConfigMapWithProfile.java b/spring-cloud-kubernetes-fabric8-config/src/test/java/org/springframework/cloud/kubernetes/fabric8/config/labeled_config_map_with_profile/LabeledConfigMapWithProfile.java index 298f337e8a..0a28020a29 100644 --- a/spring-cloud-kubernetes-fabric8-config/src/test/java/org/springframework/cloud/kubernetes/fabric8/config/labeled_config_map_with_profile/LabeledConfigMapWithProfile.java +++ b/spring-cloud-kubernetes-fabric8-config/src/test/java/org/springframework/cloud/kubernetes/fabric8/config/labeled_config_map_with_profile/LabeledConfigMapWithProfile.java @@ -53,9 +53,6 @@ abstract class LabeledConfigMapWithProfile { * - configmap with name "color-configmap-k8s", with labels : "{color: not-blue}" * - configmap with name "green-configmap-k8s", with labels : "{color: green-k8s}" * - configmap with name "green-configmap-prod", with labels : "{color: green-prod}" - * - * # a test that proves order: first read non-profile based configmaps, thus profile based - * # configmaps override non-profile ones. * - configmap with name "green-purple-configmap", labels "{color: green, shape: round}", data: "{eight: 8}" * - configmap with name "green-purple-configmap-k8s", labels "{color: black}", data: "{eight: eight-ish}" * @@ -74,7 +71,7 @@ static void setUpBeforeClass(KubernetesClient mockClient) { Map colorConfigMap = Collections.singletonMap("one", "1"); createConfigMap("color-configmap", colorConfigMap, Collections.singletonMap("color", "blue")); - // is not taken, since "profileSpecificSources=false" for the above + // is not taken Map colorConfigMapK8s = Collections.singletonMap("five", "5"); createConfigMap("color-configmap-k8s", colorConfigMapK8s, Collections.singletonMap("color", "not-blue")); @@ -82,13 +79,13 @@ static void setUpBeforeClass(KubernetesClient mockClient) { Map greenConfigMap = Collections.singletonMap("two", "2"); createConfigMap("green-configmap", greenConfigMap, Collections.singletonMap("color", "green")); - // is taken because k8s profile is active and "profileSpecificSources=true" + // is taken Map greenConfigMapK8s = Collections.singletonMap("six", "6"); - createConfigMap("green-configmap-k8s", greenConfigMapK8s, Collections.singletonMap("color", "green-k8s")); + createConfigMap("green-configmap-k8s", greenConfigMapK8s, Collections.singletonMap("color", "green")); - // is taken because prod profile is active and "profileSpecificSources=true" + // is taken Map greenConfigMapProd = Collections.singletonMap("seven", "7"); - createConfigMap("green-configmap-prod", greenConfigMapProd, Collections.singletonMap("color", "green-prod")); + createConfigMap("green-configmap-prod", greenConfigMapProd, Collections.singletonMap("color", "green")); // not taken Map redConfigMap = Collections.singletonMap("three", "3"); @@ -104,7 +101,7 @@ static void setUpBeforeClass(KubernetesClient mockClient) { // is taken and thus overrides the above Map greenPurpleK8s = Collections.singletonMap("eight", "eight-ish"); - createConfigMap("green-purple-configmap-k8s", greenPurpleK8s, Map.of("color", "black")); + createConfigMap("green-purple-configmap-k8s", greenPurpleK8s, Map.of("color", "green")); } @@ -123,7 +120,7 @@ private static void createConfigMap(String name, Map data, Map * this one is taken from : "blue.one". We find "color-configmap" by labels, and - * "color-configmap-k8s" exists, but "includeProfileSpecificSources=false", thus not taken. + * "color-configmap-k8s" exists, but not taken. * Since "explicitPrefix=blue", we take "blue.one" * */ @@ -141,9 +138,8 @@ void testBlue() { /** *
 	 *   this one is taken from : "green-configmap.green-configmap-k8s.green-configmap-prod.green-purple-configmap.green-purple-configmap-k8s".
-	 *   We find "green-configmap" by labels, also "green-configmap-k8s" and "green-configmap-prod" exists,
-	 *   because "includeProfileSpecificSources=true" is set. Also "green-purple-configmap" and "green-purple-configmap-k8s"
-	 * 	 are found.
+	 *   We find "green-configmap", "green-configmap-k8s", "green-configmap-prod"  by labels.
+	 *   Also "green-purple-configmap" and "green-purple-configmap-k8s" are found.
 	 * 
*/ @Test diff --git a/spring-cloud-kubernetes-fabric8-config/src/test/java/org/springframework/cloud/kubernetes/fabric8/config/labeled_secret_with_profile/LabeledSecretWithProfile.java b/spring-cloud-kubernetes-fabric8-config/src/test/java/org/springframework/cloud/kubernetes/fabric8/config/labeled_secret_with_profile/LabeledSecretWithProfile.java index 184a8242bf..73212476b6 100644 --- a/spring-cloud-kubernetes-fabric8-config/src/test/java/org/springframework/cloud/kubernetes/fabric8/config/labeled_secret_with_profile/LabeledSecretWithProfile.java +++ b/spring-cloud-kubernetes-fabric8-config/src/test/java/org/springframework/cloud/kubernetes/fabric8/config/labeled_secret_with_profile/LabeledSecretWithProfile.java @@ -54,9 +54,6 @@ abstract class LabeledSecretWithProfile { * - secret with name "color-secret-k8s", with labels : "{color: not-blue}" * - secret with name "green-secret-k8s", with labels : "{color: green-k8s}" * - secret with name "green-secret-prod", with labels : "{color: green-prod}" - * - * # a test that proves order: first read non-profile based secrets, thus profile based - * # secrets override non-profile ones. * - secret with name "green-purple-secret", labels "{color: green, shape: round}", data: "{eight: 8}" * - secret with name "green-purple-secret-k8s", labels "{color: black}", data: "{eight: eight-ish}" * @@ -76,7 +73,7 @@ static void setUpBeforeClass(KubernetesClient mockClient) { Base64.getEncoder().encodeToString("1".getBytes(StandardCharsets.UTF_8))); createSecret("color-secret", colorSecret, Collections.singletonMap("color", "blue")); - // is not taken, since "profileSpecificSources=false" for the above + // is not taken Map colorSecretK8s = Collections.singletonMap("five", Base64.getEncoder().encodeToString("5".getBytes(StandardCharsets.UTF_8))); createSecret("color-secret-k8s", colorSecretK8s, Collections.singletonMap("color", "not-blue")); @@ -86,15 +83,15 @@ static void setUpBeforeClass(KubernetesClient mockClient) { Base64.getEncoder().encodeToString("2".getBytes(StandardCharsets.UTF_8))); createSecret("green-secret", greenSecret, Collections.singletonMap("color", "green")); - // is taken because k8s profile is active and "profileSpecificSources=true" + // is taken Map shapeSecretK8s = Collections.singletonMap("six", Base64.getEncoder().encodeToString("6".getBytes(StandardCharsets.UTF_8))); - createSecret("green-secret-k8s", shapeSecretK8s, Collections.singletonMap("color", "green-k8s")); + createSecret("green-secret-k8s", shapeSecretK8s, Collections.singletonMap("color", "green")); - // // is taken because prod profile is active and "profileSpecificSources=true" + // // is taken Map shapeSecretProd = Collections.singletonMap("seven", Base64.getEncoder().encodeToString("7".getBytes(StandardCharsets.UTF_8))); - createSecret("green-secret-prod", shapeSecretProd, Collections.singletonMap("color", "green-prod")); + createSecret("green-secret-prod", shapeSecretProd, Collections.singletonMap("color", "green")); // not taken Map redSecret = Collections.singletonMap("three", @@ -114,7 +111,7 @@ static void setUpBeforeClass(KubernetesClient mockClient) { // is taken and thus overrides the above Map greenPurpleK8s = Collections.singletonMap("eight", Base64.getEncoder().encodeToString("eight-ish".getBytes(StandardCharsets.UTF_8))); - createSecret("green-purple-secret-k8s", greenPurpleK8s, Map.of("color", "black")); + createSecret("green-purple-secret-k8s", greenPurpleK8s, Map.of("color", "green")); } @@ -133,7 +130,7 @@ private static void createSecret(String name, Map data, Map * this one is taken from : "blue.one". We find "color-secret" by labels, and - * "color-secrets-k8s" exists, but "includeProfileSpecificSources=false", thus not taken. + * "color-secrets-k8s". * Since "explicitPrefix=blue", we take "blue.one" * */ @@ -151,9 +148,8 @@ void testBlue() { /** *
 	 *   this one is taken from : "green-purple-secret.green-purple-secret-k8s.green-secret.green-secret-k8s.green-secret-prod".
-	 *   We find "green-secret" by labels, also "green-secrets-k8s" and "green-secrets-prod" exists,
-	 *   because "includeProfileSpecificSources=true" is set. Also "green-purple-secret" and "green-purple-secret-k8s"
-	 * 	 are found.
+	 *   We find "green-secret", "green-secrets-k8s" and "green-secrets-prod" by labels.
+	 *   Also "green-purple-secret" and "green-purple-secret-k8s" are found.
 	 * 
*/ @Test diff --git a/spring-cloud-kubernetes-fabric8-config/src/test/resources/labeled-configmap-with-profile.yaml b/spring-cloud-kubernetes-fabric8-config/src/test/resources/labeled-configmap-with-profile.yaml index 8aa88d2deb..5cde6f0742 100644 --- a/spring-cloud-kubernetes-fabric8-config/src/test/resources/labeled-configmap-with-profile.yaml +++ b/spring-cloud-kubernetes-fabric8-config/src/test/resources/labeled-configmap-with-profile.yaml @@ -7,12 +7,10 @@ spring: enableApi: true useNameAsPrefix: true namespace: spring-k8s - includeProfileSpecificSources: true sources: - labels: color: blue explicitPrefix: blue - includeProfileSpecificSources: false - labels: color: green - labels: diff --git a/spring-cloud-kubernetes-fabric8-config/src/test/resources/labeled-secret-with-profile.yaml b/spring-cloud-kubernetes-fabric8-config/src/test/resources/labeled-secret-with-profile.yaml index 89255f0810..b22cac4bf9 100644 --- a/spring-cloud-kubernetes-fabric8-config/src/test/resources/labeled-secret-with-profile.yaml +++ b/spring-cloud-kubernetes-fabric8-config/src/test/resources/labeled-secret-with-profile.yaml @@ -7,12 +7,10 @@ spring: enableApi: true useNameAsPrefix: true namespace: spring-k8s - includeProfileSpecificSources: true sources: - labels: color: blue explicitPrefix: blue - includeProfileSpecificSources: false - labels: color: green - labels: From 750716210bea123a067efafea2e8cd209d5fc4b8 Mon Sep 17 00:00:00 2001 From: wind57 Date: Sun, 22 Sep 2024 00:05:10 +0300 Subject: [PATCH 11/21] implemented fabric8 --- ...c8ConfigUtilsNamespacedBatchReadTests.java | 383 +++++++++++++ ...onfigUtilsNonNamespacedBatchReadTests.java | 383 +++++++++++++ .../config/Fabric8ConfigUtilsTests.java | 336 +---------- ...DataProviderNamespacedBatchReadTests.java} | 30 +- ...taProviderNonNamespacedBatchReadTests.java | 510 +++++++++++++++++ ...DataProviderNamespacedBatchReadTests.java} | 32 +- ...taProviderNonNamespacedBatchReadTests.java | 534 ++++++++++++++++++ ...DataProviderNamespacedBatchReadTests.java} | 31 +- ...taProviderNonNamespacedBatchReadTests.java | 454 +++++++++++++++ ...DataProviderNamespacedBatchReadTests.java} | 31 +- ...taProviderNonNamespacedBatchReadTests.java | 420 ++++++++++++++ 11 files changed, 2758 insertions(+), 386 deletions(-) create mode 100644 spring-cloud-kubernetes-fabric8-config/src/test/java/org/springframework/cloud/kubernetes/fabric8/config/Fabric8ConfigUtilsNamespacedBatchReadTests.java create mode 100644 spring-cloud-kubernetes-fabric8-config/src/test/java/org/springframework/cloud/kubernetes/fabric8/config/Fabric8ConfigUtilsNonNamespacedBatchReadTests.java rename spring-cloud-kubernetes-fabric8-config/src/test/java/org/springframework/cloud/kubernetes/fabric8/config/{LabeledConfigMapContextToSourceDataProviderTests.java => LabeledConfigMapContextToSourceDataProviderNamespacedBatchReadTests.java} (96%) create mode 100644 spring-cloud-kubernetes-fabric8-config/src/test/java/org/springframework/cloud/kubernetes/fabric8/config/LabeledConfigMapContextToSourceDataProviderNonNamespacedBatchReadTests.java rename spring-cloud-kubernetes-fabric8-config/src/test/java/org/springframework/cloud/kubernetes/fabric8/config/{LabeledSecretContextToSourceDataProviderTests.java => LabeledSecretContextToSourceDataProviderNamespacedBatchReadTests.java} (96%) create mode 100644 spring-cloud-kubernetes-fabric8-config/src/test/java/org/springframework/cloud/kubernetes/fabric8/config/LabeledSecretContextToSourceDataProviderNonNamespacedBatchReadTests.java rename spring-cloud-kubernetes-fabric8-config/src/test/java/org/springframework/cloud/kubernetes/fabric8/config/{NamedConfigMapContextToSourceDataProviderTests.java => NamedConfigMapContextToSourceDataProviderNamespacedBatchReadTests.java} (95%) create mode 100644 spring-cloud-kubernetes-fabric8-config/src/test/java/org/springframework/cloud/kubernetes/fabric8/config/NamedConfigMapContextToSourceDataProviderNonNamespacedBatchReadTests.java rename spring-cloud-kubernetes-fabric8-config/src/test/java/org/springframework/cloud/kubernetes/fabric8/config/{NamedSecretContextToSourceDataProviderTests.java => NamedSecretContextToSourceDataProviderNamespacedBatchReadTests.java} (95%) create mode 100644 spring-cloud-kubernetes-fabric8-config/src/test/java/org/springframework/cloud/kubernetes/fabric8/config/NamedSecretContextToSourceDataProviderNonNamespacedBatchReadTests.java diff --git a/spring-cloud-kubernetes-fabric8-config/src/test/java/org/springframework/cloud/kubernetes/fabric8/config/Fabric8ConfigUtilsNamespacedBatchReadTests.java b/spring-cloud-kubernetes-fabric8-config/src/test/java/org/springframework/cloud/kubernetes/fabric8/config/Fabric8ConfigUtilsNamespacedBatchReadTests.java new file mode 100644 index 0000000000..c971226281 --- /dev/null +++ b/spring-cloud-kubernetes-fabric8-config/src/test/java/org/springframework/cloud/kubernetes/fabric8/config/Fabric8ConfigUtilsNamespacedBatchReadTests.java @@ -0,0 +1,383 @@ +/* + * Copyright 2013-2024 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.fabric8.config; + +import java.util.Base64; +import java.util.LinkedHashSet; +import java.util.Map; +import java.util.Set; + +import io.fabric8.kubernetes.api.model.ConfigMapBuilder; +import io.fabric8.kubernetes.api.model.ObjectMetaBuilder; +import io.fabric8.kubernetes.api.model.SecretBuilder; +import io.fabric8.kubernetes.client.KubernetesClient; +import io.fabric8.kubernetes.client.server.mock.EnableKubernetesMockClient; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +import org.springframework.cloud.kubernetes.commons.config.MultipleSourcesContainer; +import org.springframework.mock.env.MockEnvironment; + +import static org.springframework.cloud.kubernetes.commons.config.Constants.APPLICATION_YAML; + +/** + * @author wind57 + */ +@EnableKubernetesMockClient(crud = true, https = false) +class Fabric8ConfigUtilsNamespacedBatchReadTests { + + private static final boolean NAMESPACED_BATCH_READ = true; + + private KubernetesClient client; + + @AfterEach + void afterEach() { + new Fabric8ConfigMapsNamespaceBatched().discardAll(); + new Fabric8SecretsNamespaceBatched().discardAll(); + } + + /** + *
+	 *  	- secret 'my-secret' is deployed without any labels
+	 *  	- we search for it by labels 'color=red' and do not find it.
+	 * 
+ */ + @Test + void testSecretDataByLabelsSecretNotFound() { + client.secrets() + .inNamespace("spring-k8s") + .resource(new SecretBuilder().withMetadata(new ObjectMetaBuilder().withName("my-secret").build()).build()) + .create(); + MultipleSourcesContainer result = Fabric8ConfigUtils.secretsDataByLabels(client, "spring-k8s", + Map.of("color", "red"), new MockEnvironment(), NAMESPACED_BATCH_READ); + Assertions.assertEquals(Map.of(), result.data()); + Assertions.assertTrue(result.names().isEmpty()); + } + + /** + *
+	 *		- secret 'my-secret' is deployed with label '{color:pink}'
+	 *		- we search for it by same label and find it.
+	 * 
+ */ + @Test + void testSecretDataByLabelsSecretFound() { + client.secrets() + .inNamespace("spring-k8s") + .resource(new SecretBuilder() + .withMetadata(new ObjectMetaBuilder().withName("my-secret").withLabels(Map.of("color", "pink")).build()) + .addToData(Map.of("property", Base64.getEncoder().encodeToString("value".getBytes()))) + .build()) + .create(); + + MultipleSourcesContainer result = Fabric8ConfigUtils.secretsDataByLabels(client, "spring-k8s", + Map.of("color", "pink"), new MockEnvironment(), NAMESPACED_BATCH_READ); + Assertions.assertEquals(Set.of("my-secret"), result.names()); + Assertions.assertEquals(Map.of("property", "value"), result.data()); + } + + /** + *
+	 * 		- secret 'my-secret' is deployed with label '{color:pink}'
+	 * 		- we search for it by same label and find it.
+	 * 		- This secret contains a single .yaml property, as such, it gets some special treatment.
+	 * 
+ */ + @Test + void testSecretDataByLabelsSecretFoundWithPropertyFile() { + client.secrets() + .inNamespace("spring-k8s") + .resource(new SecretBuilder() + .withMetadata(new ObjectMetaBuilder().withName("my-secret").withLabels(Map.of("color", "pink")).build()) + .addToData(Map.of(APPLICATION_YAML, Base64.getEncoder().encodeToString("key1: value1".getBytes()))) + .build()) + .create(); + + MultipleSourcesContainer result = Fabric8ConfigUtils.secretsDataByLabels(client, "spring-k8s", + Map.of("color", "pink"), new MockEnvironment(), NAMESPACED_BATCH_READ); + Assertions.assertEquals(Set.of("my-secret"), result.names()); + Assertions.assertEquals(Map.of("key1", "value1"), result.data()); + } + + /** + *
+	 * 		- secrets 'my-secret' and 'my-secret-2' are deployed with label {color:pink}
+	 * 		- we search for them by same label and find them.
+	 * 
+ */ + @Test + void testSecretDataByLabelsTwoSecretsFound() { + client.secrets() + .inNamespace("spring-k8s") + .resource(new SecretBuilder() + .withMetadata(new ObjectMetaBuilder().withName("my-secret").withLabels(Map.of("color", "pink")).build()) + .addToData(Map.of("property", Base64.getEncoder().encodeToString("value".getBytes()))) + .build()) + .create(); + + client.secrets() + .inNamespace("spring-k8s") + .resource(new SecretBuilder() + .withMetadata( + new ObjectMetaBuilder().withName("my-secret-2").withLabels(Map.of("color", "pink")).build()) + .addToData(Map.of("property-2", Base64.getEncoder().encodeToString("value-2".getBytes()))) + .build()) + .create(); + + MultipleSourcesContainer result = Fabric8ConfigUtils.secretsDataByLabels(client, "spring-k8s", + Map.of("color", "pink"), new MockEnvironment(), NAMESPACED_BATCH_READ); + Assertions.assertTrue(result.names().contains("my-secret")); + Assertions.assertTrue(result.names().contains("my-secret-2")); + + Assertions.assertEquals(2, result.data().size()); + Assertions.assertEquals("value", result.data().get("property")); + Assertions.assertEquals("value-2", result.data().get("property-2")); + } + + /** + *
+	 *     - secret deployed with name "blue-circle-secret" and labels "color=blue, shape=circle, tag=fit"
+	 *     - secret deployed with name "blue-square-secret" and labels "color=blue, shape=square, tag=fit"
+	 *     - secret deployed with name "blue-triangle-secret" and labels "color=blue, shape=triangle, tag=no-fit"
+	 *     - secret deployed with name "blue-square-secret-k8s" and labels "color=blue, shape=triangle, tag=no-fit"
+	 *
+	 *     - we search by labels "color=blue, tag=fits", as such find two secrets: "blue-circle-secret"
+	 *       and "blue-square-secret".
+	 * 
+ */ + @Test + void testSecretDataByLabelsThreeSecretsFound() { + client.secrets() + .inNamespace("spring-k8s") + .resource(new SecretBuilder() + .withMetadata(new ObjectMetaBuilder().withName("blue-circle-secret") + .withLabels(Map.of("color", "blue", "shape", "circle", "tag", "fit")) + .build()) + .addToData(Map.of("one", Base64.getEncoder().encodeToString("1".getBytes()))) + .build()) + .create(); + + client.secrets() + .inNamespace("spring-k8s") + .resource(new SecretBuilder() + .withMetadata(new ObjectMetaBuilder().withName("blue-square-secret") + .withLabels(Map.of("color", "blue", "shape", "square", "tag", "fit")) + .build()) + .addToData(Map.of("two", Base64.getEncoder().encodeToString("2".getBytes()))) + .build()) + .create(); + + client.secrets() + .inNamespace("spring-k8s") + .resource(new SecretBuilder() + .withMetadata(new ObjectMetaBuilder().withName("blue-triangle-secret") + .withLabels(Map.of("color", "blue", "shape", "triangle", "tag", "no-fit")) + .build()) + .addToData(Map.of("three", Base64.getEncoder().encodeToString("3".getBytes()))) + .build()) + .create(); + + client.secrets() + .inNamespace("spring-k8s") + .resource(new SecretBuilder() + .withMetadata(new ObjectMetaBuilder().withName("blue-square-secret-k8s") + .withLabels(Map.of("color", "blue", "shape", "triangle", "tag", "no-fit")) + .build()) + .addToData(Map.of("four", Base64.getEncoder().encodeToString("4".getBytes()))) + .build()) + .create(); + + MultipleSourcesContainer result = Fabric8ConfigUtils.secretsDataByLabels(client, "spring-k8s", + Map.of("tag", "fit", "color", "blue"), new MockEnvironment(), NAMESPACED_BATCH_READ); + + Assertions.assertTrue(result.names().contains("blue-circle-secret")); + Assertions.assertTrue(result.names().contains("blue-square-secret")); + + Assertions.assertEquals(2, result.data().size()); + Assertions.assertEquals("1", result.data().get("one")); + Assertions.assertEquals("2", result.data().get("two")); + } + + /** + *
+	 * 		- secret 'my-secret' is deployed; we search for it by name and do not find it.
+	 * 
+ */ + @Test + void testSecretDataByNameSecretNotFound() { + client.secrets() + .inNamespace("spring-k8s") + .resource(new SecretBuilder().withMetadata(new ObjectMetaBuilder().withName("my-secret").build()).build()) + .create(); + LinkedHashSet names = new LinkedHashSet<>(); + names.add("nope"); + MultipleSourcesContainer result = Fabric8ConfigUtils.secretsDataByName(client, "spring-k8s", names, + new MockEnvironment(), NAMESPACED_BATCH_READ); + Assertions.assertEquals(0, result.names().size()); + Assertions.assertEquals(0, result.data().size()); + } + + /** + *
+	 * 		- secret "my-secret" is deployed; we search for it by name and find it.
+	 * 
+ */ + @Test + void testSecretDataByNameSecretFound() { + client.secrets() + .inNamespace("spring-k8s") + .resource(new SecretBuilder().withMetadata(new ObjectMetaBuilder().withName("my-secret").build()) + .addToData(Map.of("property", Base64.getEncoder().encodeToString("value".getBytes()))) + .build()) + .create(); + LinkedHashSet names = new LinkedHashSet<>(); + names.add("my-secret"); + + MultipleSourcesContainer result = Fabric8ConfigUtils.secretsDataByName(client, "spring-k8s", names, + new MockEnvironment(), NAMESPACED_BATCH_READ); + Assertions.assertEquals(1, result.names().size()); + Assertions.assertEquals("value", result.data().get("property")); + } + + /** + *
+	 * 		- config-map "my-config-map" is deployed without any data
+	 * 		- we search for it by name and find it; but it has no data.
+	 * 
+ */ + @Test + void testConfigMapsDataByNameFoundNoData() { + client.configMaps() + .inNamespace("spring-k8s") + .resource(new ConfigMapBuilder().withMetadata(new ObjectMetaBuilder().withName("my-config-map").build()) + .build()) + .create(); + LinkedHashSet names = new LinkedHashSet<>(); + names.add("my-config-map"); + + MultipleSourcesContainer result = Fabric8ConfigUtils.configMapsDataByName(client, "spring-k8s", names, + new MockEnvironment(), NAMESPACED_BATCH_READ); + Assertions.assertEquals(Set.of("my-config-map"), result.names()); + Assertions.assertTrue(result.data().isEmpty()); + } + + /** + *
+	 *     	- config-map "my-config-map" is deployed; we search for it and do not find it.
+	 * 
+ */ + @Test + void testConfigMapsDataByNameNotFound() { + client.configMaps() + .inNamespace("spring-k8s") + .resource(new ConfigMapBuilder().withMetadata(new ObjectMetaBuilder().withName("my-config-map").build()) + .build()) + .create(); + LinkedHashSet names = new LinkedHashSet<>(); + names.add("my-config-map-not-found"); + MultipleSourcesContainer result = Fabric8ConfigUtils.configMapsDataByName(client, "spring-k8s", names, + new MockEnvironment(), NAMESPACED_BATCH_READ); + Assertions.assertEquals(Set.of(), result.names()); + Assertions.assertTrue(result.data().isEmpty()); + } + + /** + *
+	 *     - config-map "my-config-map" is deployed; we search for it and find it
+	 * 
+ */ + @Test + void testConfigMapDataByNameFound() { + client.configMaps() + .inNamespace("spring-k8s") + .resource(new ConfigMapBuilder().withMetadata(new ObjectMetaBuilder().withName("my-config-map").build()) + .addToData(Map.of("property", "value")) + .build()) + .create(); + + LinkedHashSet names = new LinkedHashSet<>(); + names.add("my-config-map"); + + MultipleSourcesContainer result = Fabric8ConfigUtils.configMapsDataByName(client, "spring-k8s", names, + new MockEnvironment(), NAMESPACED_BATCH_READ); + Assertions.assertEquals(Set.of("my-config-map"), result.names()); + Assertions.assertEquals(Map.of("property", "value"), result.data()); + } + + /** + *
+	 *     - config-map "my-config-map" is deployed
+	 *     - we search for it and find it
+	 *     - it contains a single .yaml property, as such it gets some special treatment
+	 * 
+ */ + @Test + void testConfigMapDataByNameFoundWithPropertyFile() { + client.configMaps() + .inNamespace("spring-k8s") + .resource(new ConfigMapBuilder().withMetadata(new ObjectMetaBuilder().withName("my-config-map").build()) + .addToData(Map.of(APPLICATION_YAML, "key1: value1")) + .build()) + .create(); + + LinkedHashSet names = new LinkedHashSet<>(); + names.add("my-config-map"); + + MultipleSourcesContainer result = Fabric8ConfigUtils.configMapsDataByName(client, "spring-k8s", names, + new MockEnvironment(), NAMESPACED_BATCH_READ); + Assertions.assertEquals(Set.of("my-config-map"), result.names()); + Assertions.assertEquals(Map.of("key1", "value1"), result.data()); + } + + /** + *
+	 *     - config-map "my-config-map" and "my-config-map-2" are deployed
+	 *     - we search and find them.
+	 * 
+ */ + @Test + void testConfigMapDataByNameTwoFound() { + client.configMaps() + .inNamespace("spring-k8s") + .resource(new ConfigMapBuilder().withMetadata(new ObjectMetaBuilder().withName("my-config-map").build()) + .addToData(Map.of("property", "value")) + .build()) + .create(); + + client.configMaps() + .inNamespace("spring-k8s") + .resource(new ConfigMapBuilder().withMetadata(new ObjectMetaBuilder().withName("my-config-map-2").build()) + .addToData(Map.of("property-2", "value-2")) + .build()) + .create(); + + LinkedHashSet names = new LinkedHashSet<>(); + names.add("my-config-map"); + names.add("my-config-map-2"); + + MultipleSourcesContainer result = Fabric8ConfigUtils.configMapsDataByName(client, "spring-k8s", names, + new MockEnvironment(), NAMESPACED_BATCH_READ); + Assertions.assertTrue(result.names().contains("my-config-map")); + Assertions.assertTrue(result.names().contains("my-config-map-2")); + + Assertions.assertEquals(2, result.data().size()); + Assertions.assertEquals("value", result.data().get("property")); + Assertions.assertEquals("value-2", result.data().get("property-2")); + } + +} diff --git a/spring-cloud-kubernetes-fabric8-config/src/test/java/org/springframework/cloud/kubernetes/fabric8/config/Fabric8ConfigUtilsNonNamespacedBatchReadTests.java b/spring-cloud-kubernetes-fabric8-config/src/test/java/org/springframework/cloud/kubernetes/fabric8/config/Fabric8ConfigUtilsNonNamespacedBatchReadTests.java new file mode 100644 index 0000000000..5169a13684 --- /dev/null +++ b/spring-cloud-kubernetes-fabric8-config/src/test/java/org/springframework/cloud/kubernetes/fabric8/config/Fabric8ConfigUtilsNonNamespacedBatchReadTests.java @@ -0,0 +1,383 @@ +/* + * Copyright 2013-2024 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.fabric8.config; + +import java.util.Base64; +import java.util.LinkedHashSet; +import java.util.Map; +import java.util.Set; + +import io.fabric8.kubernetes.api.model.ConfigMapBuilder; +import io.fabric8.kubernetes.api.model.ObjectMetaBuilder; +import io.fabric8.kubernetes.api.model.SecretBuilder; +import io.fabric8.kubernetes.client.KubernetesClient; +import io.fabric8.kubernetes.client.server.mock.EnableKubernetesMockClient; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +import org.springframework.cloud.kubernetes.commons.config.MultipleSourcesContainer; +import org.springframework.mock.env.MockEnvironment; + +import static org.springframework.cloud.kubernetes.commons.config.Constants.APPLICATION_YAML; + +/** + * @author wind57 + */ +@EnableKubernetesMockClient(crud = true, https = false) +class Fabric8ConfigUtilsNonNamespacedBatchReadTests { + + private static final boolean NAMESPACED_BATCH_READ = false; + + private KubernetesClient client; + + @AfterEach + void afterEach() { + new Fabric8ConfigMapsNamespaceBatched().discardAll(); + new Fabric8SecretsNamespaceBatched().discardAll(); + } + + /** + *
+	 *  	- secret 'my-secret' is deployed without any labels
+	 *  	- we search for it by labels 'color=red' and do not find it.
+	 * 
+ */ + @Test + void testSecretDataByLabelsSecretNotFound() { + client.secrets() + .inNamespace("spring-k8s") + .resource(new SecretBuilder().withMetadata(new ObjectMetaBuilder().withName("my-secret").build()).build()) + .create(); + MultipleSourcesContainer result = Fabric8ConfigUtils.secretsDataByLabels(client, "spring-k8s", + Map.of("color", "red"), new MockEnvironment(), NAMESPACED_BATCH_READ); + Assertions.assertEquals(Map.of(), result.data()); + Assertions.assertTrue(result.names().isEmpty()); + } + + /** + *
+	 *		- secret 'my-secret' is deployed with label '{color:pink}'
+	 *		- we search for it by same label and find it.
+	 * 
+ */ + @Test + void testSecretDataByLabelsSecretFound() { + client.secrets() + .inNamespace("spring-k8s") + .resource(new SecretBuilder() + .withMetadata(new ObjectMetaBuilder().withName("my-secret").withLabels(Map.of("color", "pink")).build()) + .addToData(Map.of("property", Base64.getEncoder().encodeToString("value".getBytes()))) + .build()) + .create(); + + MultipleSourcesContainer result = Fabric8ConfigUtils.secretsDataByLabels(client, "spring-k8s", + Map.of("color", "pink"), new MockEnvironment(), NAMESPACED_BATCH_READ); + Assertions.assertEquals(Set.of("my-secret"), result.names()); + Assertions.assertEquals(Map.of("property", "value"), result.data()); + } + + /** + *
+	 * 		- secret 'my-secret' is deployed with label '{color:pink}'
+	 * 		- we search for it by same label and find it.
+	 * 		- This secret contains a single .yaml property, as such, it gets some special treatment.
+	 * 
+ */ + @Test + void testSecretDataByLabelsSecretFoundWithPropertyFile() { + client.secrets() + .inNamespace("spring-k8s") + .resource(new SecretBuilder() + .withMetadata(new ObjectMetaBuilder().withName("my-secret").withLabels(Map.of("color", "pink")).build()) + .addToData(Map.of(APPLICATION_YAML, Base64.getEncoder().encodeToString("key1: value1".getBytes()))) + .build()) + .create(); + + MultipleSourcesContainer result = Fabric8ConfigUtils.secretsDataByLabels(client, "spring-k8s", + Map.of("color", "pink"), new MockEnvironment(), NAMESPACED_BATCH_READ); + Assertions.assertEquals(Set.of("my-secret"), result.names()); + Assertions.assertEquals(Map.of("key1", "value1"), result.data()); + } + + /** + *
+	 * 		- secrets 'my-secret' and 'my-secret-2' are deployed with label {color:pink}
+	 * 		- we search for them by same label and find them.
+	 * 
+ */ + @Test + void testSecretDataByLabelsTwoSecretsFound() { + client.secrets() + .inNamespace("spring-k8s") + .resource(new SecretBuilder() + .withMetadata(new ObjectMetaBuilder().withName("my-secret").withLabels(Map.of("color", "pink")).build()) + .addToData(Map.of("property", Base64.getEncoder().encodeToString("value".getBytes()))) + .build()) + .create(); + + client.secrets() + .inNamespace("spring-k8s") + .resource(new SecretBuilder() + .withMetadata( + new ObjectMetaBuilder().withName("my-secret-2").withLabels(Map.of("color", "pink")).build()) + .addToData(Map.of("property-2", Base64.getEncoder().encodeToString("value-2".getBytes()))) + .build()) + .create(); + + MultipleSourcesContainer result = Fabric8ConfigUtils.secretsDataByLabels(client, "spring-k8s", + Map.of("color", "pink"), new MockEnvironment(), NAMESPACED_BATCH_READ); + Assertions.assertTrue(result.names().contains("my-secret")); + Assertions.assertTrue(result.names().contains("my-secret-2")); + + Assertions.assertEquals(2, result.data().size()); + Assertions.assertEquals("value", result.data().get("property")); + Assertions.assertEquals("value-2", result.data().get("property-2")); + } + + /** + *
+	 *     - secret deployed with name "blue-circle-secret" and labels "color=blue, shape=circle, tag=fit"
+	 *     - secret deployed with name "blue-square-secret" and labels "color=blue, shape=square, tag=fit"
+	 *     - secret deployed with name "blue-triangle-secret" and labels "color=blue, shape=triangle, tag=no-fit"
+	 *     - secret deployed with name "blue-square-secret-k8s" and labels "color=blue, shape=triangle, tag=no-fit"
+	 *
+	 *     - we search by labels "color=blue, tag=fits", as such find two secrets: "blue-circle-secret"
+	 *       and "blue-square-secret".
+	 * 
+ */ + @Test + void testSecretDataByLabelsThreeSecretsFound() { + client.secrets() + .inNamespace("spring-k8s") + .resource(new SecretBuilder() + .withMetadata(new ObjectMetaBuilder().withName("blue-circle-secret") + .withLabels(Map.of("color", "blue", "shape", "circle", "tag", "fit")) + .build()) + .addToData(Map.of("one", Base64.getEncoder().encodeToString("1".getBytes()))) + .build()) + .create(); + + client.secrets() + .inNamespace("spring-k8s") + .resource(new SecretBuilder() + .withMetadata(new ObjectMetaBuilder().withName("blue-square-secret") + .withLabels(Map.of("color", "blue", "shape", "square", "tag", "fit")) + .build()) + .addToData(Map.of("two", Base64.getEncoder().encodeToString("2".getBytes()))) + .build()) + .create(); + + client.secrets() + .inNamespace("spring-k8s") + .resource(new SecretBuilder() + .withMetadata(new ObjectMetaBuilder().withName("blue-triangle-secret") + .withLabels(Map.of("color", "blue", "shape", "triangle", "tag", "no-fit")) + .build()) + .addToData(Map.of("three", Base64.getEncoder().encodeToString("3".getBytes()))) + .build()) + .create(); + + client.secrets() + .inNamespace("spring-k8s") + .resource(new SecretBuilder() + .withMetadata(new ObjectMetaBuilder().withName("blue-square-secret-k8s") + .withLabels(Map.of("color", "blue", "shape", "triangle", "tag", "no-fit")) + .build()) + .addToData(Map.of("four", Base64.getEncoder().encodeToString("4".getBytes()))) + .build()) + .create(); + + MultipleSourcesContainer result = Fabric8ConfigUtils.secretsDataByLabels(client, "spring-k8s", + Map.of("tag", "fit", "color", "blue"), new MockEnvironment(), NAMESPACED_BATCH_READ); + + Assertions.assertTrue(result.names().contains("blue-circle-secret")); + Assertions.assertTrue(result.names().contains("blue-square-secret")); + + Assertions.assertEquals(2, result.data().size()); + Assertions.assertEquals("1", result.data().get("one")); + Assertions.assertEquals("2", result.data().get("two")); + } + + /** + *
+	 * 		- secret 'my-secret' is deployed; we search for it by name and do not find it.
+	 * 
+ */ + @Test + void testSecretDataByNameSecretNotFound() { + client.secrets() + .inNamespace("spring-k8s") + .resource(new SecretBuilder().withMetadata(new ObjectMetaBuilder().withName("my-secret").build()).build()) + .create(); + LinkedHashSet names = new LinkedHashSet<>(); + names.add("nope"); + MultipleSourcesContainer result = Fabric8ConfigUtils.secretsDataByName(client, "spring-k8s", names, + new MockEnvironment(), NAMESPACED_BATCH_READ); + Assertions.assertEquals(0, result.names().size()); + Assertions.assertEquals(0, result.data().size()); + } + + /** + *
+	 * 		- secret "my-secret" is deployed; we search for it by name and find it.
+	 * 
+ */ + @Test + void testSecretDataByNameSecretFound() { + client.secrets() + .inNamespace("spring-k8s") + .resource(new SecretBuilder().withMetadata(new ObjectMetaBuilder().withName("my-secret").build()) + .addToData(Map.of("property", Base64.getEncoder().encodeToString("value".getBytes()))) + .build()) + .create(); + LinkedHashSet names = new LinkedHashSet<>(); + names.add("my-secret"); + + MultipleSourcesContainer result = Fabric8ConfigUtils.secretsDataByName(client, "spring-k8s", names, + new MockEnvironment(), NAMESPACED_BATCH_READ); + Assertions.assertEquals(1, result.names().size()); + Assertions.assertEquals("value", result.data().get("property")); + } + + /** + *
+	 * 		- config-map "my-config-map" is deployed without any data
+	 * 		- we search for it by name and find it; but it has no data.
+	 * 
+ */ + @Test + void testConfigMapsDataByNameFoundNoData() { + client.configMaps() + .inNamespace("spring-k8s") + .resource(new ConfigMapBuilder().withMetadata(new ObjectMetaBuilder().withName("my-config-map").build()) + .build()) + .create(); + LinkedHashSet names = new LinkedHashSet<>(); + names.add("my-config-map"); + + MultipleSourcesContainer result = Fabric8ConfigUtils.configMapsDataByName(client, "spring-k8s", names, + new MockEnvironment(), NAMESPACED_BATCH_READ); + Assertions.assertEquals(Set.of("my-config-map"), result.names()); + Assertions.assertTrue(result.data().isEmpty()); + } + + /** + *
+	 *     	- config-map "my-config-map" is deployed; we search for it and do not find it.
+	 * 
+ */ + @Test + void testConfigMapsDataByNameNotFound() { + client.configMaps() + .inNamespace("spring-k8s") + .resource(new ConfigMapBuilder().withMetadata(new ObjectMetaBuilder().withName("my-config-map").build()) + .build()) + .create(); + LinkedHashSet names = new LinkedHashSet<>(); + names.add("my-config-map-not-found"); + MultipleSourcesContainer result = Fabric8ConfigUtils.configMapsDataByName(client, "spring-k8s", names, + new MockEnvironment(), NAMESPACED_BATCH_READ); + Assertions.assertEquals(Set.of(), result.names()); + Assertions.assertTrue(result.data().isEmpty()); + } + + /** + *
+	 *     - config-map "my-config-map" is deployed; we search for it and find it
+	 * 
+ */ + @Test + void testConfigMapDataByNameFound() { + client.configMaps() + .inNamespace("spring-k8s") + .resource(new ConfigMapBuilder().withMetadata(new ObjectMetaBuilder().withName("my-config-map").build()) + .addToData(Map.of("property", "value")) + .build()) + .create(); + + LinkedHashSet names = new LinkedHashSet<>(); + names.add("my-config-map"); + + MultipleSourcesContainer result = Fabric8ConfigUtils.configMapsDataByName(client, "spring-k8s", names, + new MockEnvironment(), NAMESPACED_BATCH_READ); + Assertions.assertEquals(Set.of("my-config-map"), result.names()); + Assertions.assertEquals(Map.of("property", "value"), result.data()); + } + + /** + *
+	 *     - config-map "my-config-map" is deployed
+	 *     - we search for it and find it
+	 *     - it contains a single .yaml property, as such it gets some special treatment
+	 * 
+ */ + @Test + void testConfigMapDataByNameFoundWithPropertyFile() { + client.configMaps() + .inNamespace("spring-k8s") + .resource(new ConfigMapBuilder().withMetadata(new ObjectMetaBuilder().withName("my-config-map").build()) + .addToData(Map.of(APPLICATION_YAML, "key1: value1")) + .build()) + .create(); + + LinkedHashSet names = new LinkedHashSet<>(); + names.add("my-config-map"); + + MultipleSourcesContainer result = Fabric8ConfigUtils.configMapsDataByName(client, "spring-k8s", names, + new MockEnvironment(), NAMESPACED_BATCH_READ); + Assertions.assertEquals(Set.of("my-config-map"), result.names()); + Assertions.assertEquals(Map.of("key1", "value1"), result.data()); + } + + /** + *
+	 *     - config-map "my-config-map" and "my-config-map-2" are deployed
+	 *     - we search and find them.
+	 * 
+ */ + @Test + void testConfigMapDataByNameTwoFound() { + client.configMaps() + .inNamespace("spring-k8s") + .resource(new ConfigMapBuilder().withMetadata(new ObjectMetaBuilder().withName("my-config-map").build()) + .addToData(Map.of("property", "value")) + .build()) + .create(); + + client.configMaps() + .inNamespace("spring-k8s") + .resource(new ConfigMapBuilder().withMetadata(new ObjectMetaBuilder().withName("my-config-map-2").build()) + .addToData(Map.of("property-2", "value-2")) + .build()) + .create(); + + LinkedHashSet names = new LinkedHashSet<>(); + names.add("my-config-map"); + names.add("my-config-map-2"); + + MultipleSourcesContainer result = Fabric8ConfigUtils.configMapsDataByName(client, "spring-k8s", names, + new MockEnvironment(), NAMESPACED_BATCH_READ); + Assertions.assertTrue(result.names().contains("my-config-map")); + Assertions.assertTrue(result.names().contains("my-config-map-2")); + + Assertions.assertEquals(2, result.data().size()); + Assertions.assertEquals("value", result.data().get("property")); + Assertions.assertEquals("value-2", result.data().get("property-2")); + } + +} diff --git a/spring-cloud-kubernetes-fabric8-config/src/test/java/org/springframework/cloud/kubernetes/fabric8/config/Fabric8ConfigUtilsTests.java b/spring-cloud-kubernetes-fabric8-config/src/test/java/org/springframework/cloud/kubernetes/fabric8/config/Fabric8ConfigUtilsTests.java index cf9ad6ea56..0113ed3924 100644 --- a/spring-cloud-kubernetes-fabric8-config/src/test/java/org/springframework/cloud/kubernetes/fabric8/config/Fabric8ConfigUtilsTests.java +++ b/spring-cloud-kubernetes-fabric8-config/src/test/java/org/springframework/cloud/kubernetes/fabric8/config/Fabric8ConfigUtilsTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2022 the original author or authors. + * Copyright 2013-2024 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. @@ -17,356 +17,22 @@ package org.springframework.cloud.kubernetes.fabric8.config; import java.time.Duration; -import java.util.Base64; -import java.util.LinkedHashSet; -import java.util.Map; import java.util.Set; -import io.fabric8.kubernetes.api.model.ConfigMapBuilder; -import io.fabric8.kubernetes.api.model.ObjectMetaBuilder; -import io.fabric8.kubernetes.api.model.SecretBuilder; -import io.fabric8.kubernetes.client.KubernetesClient; import io.fabric8.kubernetes.client.server.mock.EnableKubernetesMockClient; -import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import org.springframework.cloud.kubernetes.commons.KubernetesNamespaceProvider; -import org.springframework.cloud.kubernetes.commons.config.MultipleSourcesContainer; import org.springframework.cloud.kubernetes.commons.config.reload.ConfigReloadProperties; import org.springframework.mock.env.MockEnvironment; -import static org.springframework.cloud.kubernetes.commons.config.Constants.APPLICATION_YAML; - /** * @author wind57 */ @EnableKubernetesMockClient(crud = true, https = false) class Fabric8ConfigUtilsTests { - private KubernetesClient client; - - @AfterEach - void afterEach() { - new Fabric8ConfigMapsNamespaceBatched().discardAll(); - new Fabric8SecretsNamespaceBatched().discardAll(); - } - - // secret "my-secret" is deployed without any labels; we search for it by labels - // "color=red" and do not find it. - @Test - void testSecretDataByLabelsSecretNotFound() { - client.secrets() - .inNamespace("spring-k8s") - .resource(new SecretBuilder().withMetadata(new ObjectMetaBuilder().withName("my-secret").build()).build()) - .create(); - MultipleSourcesContainer result = Fabric8ConfigUtils.secretsDataByLabels(client, "spring-k8s", - Map.of("color", "red"), new MockEnvironment(), true); - Assertions.assertEquals(Map.of(), result.data()); - Assertions.assertTrue(result.names().isEmpty()); - } - - // secret "my-secret" is deployed with label {color:pink}; we search for it by same - // label and find it. - @Test - void testSecretDataByLabelsSecretFound() { - client.secrets() - .inNamespace("spring-k8s") - .resource(new SecretBuilder() - .withMetadata(new ObjectMetaBuilder().withName("my-secret").withLabels(Map.of("color", "pink")).build()) - .addToData(Map.of("property", Base64.getEncoder().encodeToString("value".getBytes()))) - .build()) - .create(); - - MultipleSourcesContainer result = Fabric8ConfigUtils.secretsDataByLabels(client, "spring-k8s", - Map.of("color", "pink"), new MockEnvironment(), true); - Assertions.assertEquals(Set.of("my-secret"), result.names()); - Assertions.assertEquals(Map.of("property", "value"), result.data()); - } - - // secret "my-secret" is deployed with label {color:pink}; we search for it by same - // label and find it. This secret contains a single .yaml property, as such - // it gets some special treatment. - @Test - void testSecretDataByLabelsSecretFoundWithPropertyFile() { - client.secrets() - .inNamespace("spring-k8s") - .resource(new SecretBuilder() - .withMetadata(new ObjectMetaBuilder().withName("my-secret").withLabels(Map.of("color", "pink")).build()) - .addToData(Map.of(APPLICATION_YAML, Base64.getEncoder().encodeToString("key1: value1".getBytes()))) - .build()) - .create(); - - MultipleSourcesContainer result = Fabric8ConfigUtils.secretsDataByLabels(client, "spring-k8s", - Map.of("color", "pink"), new MockEnvironment(), true); - Assertions.assertEquals(Set.of("my-secret"), result.names()); - Assertions.assertEquals(Map.of("key1", "value1"), result.data()); - } - - // secrets "my-secret" and "my-secret-2" are deployed with label {color:pink}; - // we search for them by same label and find them. - @Test - void testSecretDataByLabelsTwoSecretsFound() { - client.secrets() - .inNamespace("spring-k8s") - .resource(new SecretBuilder() - .withMetadata(new ObjectMetaBuilder().withName("my-secret").withLabels(Map.of("color", "pink")).build()) - .addToData(Map.of("property", Base64.getEncoder().encodeToString("value".getBytes()))) - .build()) - .create(); - - client.secrets() - .inNamespace("spring-k8s") - .resource(new SecretBuilder() - .withMetadata( - new ObjectMetaBuilder().withName("my-secret-2").withLabels(Map.of("color", "pink")).build()) - .addToData(Map.of("property-2", Base64.getEncoder().encodeToString("value-2".getBytes()))) - .build()) - .create(); - - MultipleSourcesContainer result = Fabric8ConfigUtils.secretsDataByLabels(client, "spring-k8s", - Map.of("color", "pink"), new MockEnvironment(), true); - Assertions.assertTrue(result.names().contains("my-secret")); - Assertions.assertTrue(result.names().contains("my-secret-2")); - - Assertions.assertEquals(2, result.data().size()); - Assertions.assertEquals("value", result.data().get("property")); - Assertions.assertEquals("value-2", result.data().get("property-2")); - } - - /** - *
-	 *     - secret deployed with name "blue-circle-secret" and labels "color=blue, shape=circle, tag=fit"
-	 *     - secret deployed with name "blue-square-secret" and labels "color=blue, shape=square, tag=fit"
-	 *     - secret deployed with name "blue-triangle-secret" and labels "color=blue, shape=triangle, tag=no-fit"
-	 *     - secret deployed with name "blue-square-secret-k8s" and labels "color=blue, shape=triangle, tag=no-fit"
-	 *
-	 *     - we search by labels "color=blue, tag=fits", as such find two secrets: "blue-circle-secret"
-	 *       and "blue-square-secret".
-	 * 
- */ - @Test - void testSecretDataByLabelsThreeSecretsFound() { - client.secrets() - .inNamespace("spring-k8s") - .resource(new SecretBuilder() - .withMetadata(new ObjectMetaBuilder().withName("blue-circle-secret") - .withLabels(Map.of("color", "blue", "shape", "circle", "tag", "fit")) - .build()) - .addToData(Map.of("one", Base64.getEncoder().encodeToString("1".getBytes()))) - .build()) - .create(); - - client.secrets() - .inNamespace("spring-k8s") - .resource(new SecretBuilder() - .withMetadata(new ObjectMetaBuilder().withName("blue-square-secret") - .withLabels(Map.of("color", "blue", "shape", "square", "tag", "fit")) - .build()) - .addToData(Map.of("two", Base64.getEncoder().encodeToString("2".getBytes()))) - .build()) - .create(); - - client.secrets() - .inNamespace("spring-k8s") - .resource(new SecretBuilder() - .withMetadata(new ObjectMetaBuilder().withName("blue-triangle-secret") - .withLabels(Map.of("color", "blue", "shape", "triangle", "tag", "no-fit")) - .build()) - .addToData(Map.of("three", Base64.getEncoder().encodeToString("3".getBytes()))) - .build()) - .create(); - - client.secrets() - .inNamespace("spring-k8s") - .resource(new SecretBuilder() - .withMetadata(new ObjectMetaBuilder().withName("blue-square-secret-k8s") - .withLabels(Map.of("color", "blue", "shape", "triangle", "tag", "no-fit")) - .build()) - .addToData(Map.of("four", Base64.getEncoder().encodeToString("4".getBytes()))) - .build()) - .create(); - - MultipleSourcesContainer result = Fabric8ConfigUtils.secretsDataByLabels(client, "spring-k8s", - Map.of("tag", "fit", "color", "blue"), new MockEnvironment(), true); - - Assertions.assertTrue(result.names().contains("blue-circle-secret")); - Assertions.assertTrue(result.names().contains("blue-square-secret")); - - Assertions.assertEquals(2, result.data().size()); - Assertions.assertEquals("1", result.data().get("one")); - Assertions.assertEquals("2", result.data().get("two")); - } - - // secret "my-secret" is deployed; we search for it by name and do not find it. - @Test - void testSecretDataByNameSecretNotFound() { - client.secrets() - .inNamespace("spring-k8s") - .resource(new SecretBuilder().withMetadata(new ObjectMetaBuilder().withName("my-secret").build()).build()) - .create(); - LinkedHashSet names = new LinkedHashSet<>(); - names.add("nope"); - MultipleSourcesContainer result = Fabric8ConfigUtils.secretsDataByName(client, "spring-k8s", names, - new MockEnvironment(), true); - Assertions.assertEquals(0, result.names().size()); - Assertions.assertEquals(0, result.data().size()); - } - - // secret "my-secret" is deployed; we search for it by name and find it. - @Test - void testSecretDataByNameSecretFound() { - client.secrets() - .inNamespace("spring-k8s") - .resource(new SecretBuilder().withMetadata(new ObjectMetaBuilder().withName("my-secret").build()) - .addToData(Map.of("property", Base64.getEncoder().encodeToString("value".getBytes()))) - .build()) - .create(); - LinkedHashSet names = new LinkedHashSet<>(); - names.add("my-secret"); - - MultipleSourcesContainer result = Fabric8ConfigUtils.secretsDataByName(client, "spring-k8s", names, - new MockEnvironment(), true); - Assertions.assertEquals(1, result.names().size()); - Assertions.assertEquals("value", result.data().get("property")); - } - - // secrets "my-secret" and "my-secret-2" are deployed; - // we search for them by name label and find them. - @Test - void testSecretDataByNameTwoSecretsFound() { - client.secrets() - .inNamespace("spring-k8s") - .resource(new SecretBuilder().withMetadata(new ObjectMetaBuilder().withName("my-secret").build()) - .addToData(Map.of("property", Base64.getEncoder().encodeToString("value".getBytes()))) - .build()) - .create(); - - client.secrets() - .inNamespace("spring-k8s") - .resource(new SecretBuilder().withMetadata(new ObjectMetaBuilder().withName("my-secret-2").build()) - .addToData(Map.of("property-2", Base64.getEncoder().encodeToString("value-2".getBytes()))) - .build()) - .create(); - LinkedHashSet names = new LinkedHashSet<>(); - names.add("my-secret"); - names.add("my-secret-2"); - - MultipleSourcesContainer result = Fabric8ConfigUtils.secretsDataByName(client, "spring-k8s", names, - new MockEnvironment(), true); - Assertions.assertTrue(result.names().contains("my-secret")); - Assertions.assertTrue(result.names().contains("my-secret-2")); - - Assertions.assertEquals(2, result.data().size()); - Assertions.assertEquals("value", result.data().get("property")); - Assertions.assertEquals("value-2", result.data().get("property-2")); - } - - // config-map "my-config-map" is deployed without any data; we search for it by name - // and find it; but it has no data. - @Test - void testConfigMapsDataByNameFoundNoData() { - client.configMaps() - .inNamespace("spring-k8s") - .resource(new ConfigMapBuilder().withMetadata(new ObjectMetaBuilder().withName("my-config-map").build()) - .build()) - .create(); - LinkedHashSet names = new LinkedHashSet<>(); - names.add("my-config-map"); - - MultipleSourcesContainer result = Fabric8ConfigUtils.configMapsDataByName(client, "spring-k8s", names, - new MockEnvironment(), true); - Assertions.assertEquals(Set.of("my-config-map"), result.names()); - Assertions.assertTrue(result.data().isEmpty()); - } - - // config-map "my-config-map" is deployed; we search for it and do not find it. - @Test - void testConfigMapsDataByNameNotFound() { - client.configMaps() - .inNamespace("spring-k8s") - .resource(new ConfigMapBuilder().withMetadata(new ObjectMetaBuilder().withName("my-config-map").build()) - .build()) - .create(); - LinkedHashSet names = new LinkedHashSet<>(); - names.add("my-config-map-not-found"); - MultipleSourcesContainer result = Fabric8ConfigUtils.configMapsDataByName(client, "spring-k8s", names, - new MockEnvironment(), true); - Assertions.assertEquals(Set.of(), result.names()); - Assertions.assertTrue(result.data().isEmpty()); - } - - // config-map "my-config-map" is deployed; we search for it and find it - @Test - void testConfigMapDataByNameFound() { - client.configMaps() - .inNamespace("spring-k8s") - .resource(new ConfigMapBuilder().withMetadata(new ObjectMetaBuilder().withName("my-config-map").build()) - .addToData(Map.of("property", "value")) - .build()) - .create(); - - LinkedHashSet names = new LinkedHashSet<>(); - names.add("my-config-map"); - - MultipleSourcesContainer result = Fabric8ConfigUtils.configMapsDataByName(client, "spring-k8s", names, - new MockEnvironment(), true); - Assertions.assertEquals(Set.of("my-config-map"), result.names()); - Assertions.assertEquals(Map.of("property", "value"), result.data()); - } - - // config-map "my-config-map" is deployed; we search for it and find it. - // It contains a single .yaml property, as such it gets some special treatment. - @Test - void testConfigMapDataByNameFoundWithPropertyFile() { - client.configMaps() - .inNamespace("spring-k8s") - .resource(new ConfigMapBuilder().withMetadata(new ObjectMetaBuilder().withName("my-config-map").build()) - .addToData(Map.of(APPLICATION_YAML, "key1: value1")) - .build()) - .create(); - - LinkedHashSet names = new LinkedHashSet<>(); - names.add("my-config-map"); - - MultipleSourcesContainer result = Fabric8ConfigUtils.configMapsDataByName(client, "spring-k8s", names, - new MockEnvironment(), true); - Assertions.assertEquals(Set.of("my-config-map"), result.names()); - Assertions.assertEquals(Map.of("key1", "value1"), result.data()); - } - - // config-map "my-config-map" and "my-config-map-2" are deployed; - // we search and find them. - @Test - void testConfigMapDataByNameTwoFound() { - client.configMaps() - .inNamespace("spring-k8s") - .resource(new ConfigMapBuilder().withMetadata(new ObjectMetaBuilder().withName("my-config-map").build()) - .addToData(Map.of("property", "value")) - .build()) - .create(); - - client.configMaps() - .inNamespace("spring-k8s") - .resource(new ConfigMapBuilder().withMetadata(new ObjectMetaBuilder().withName("my-config-map-2").build()) - .addToData(Map.of("property-2", "value-2")) - .build()) - .create(); - - LinkedHashSet names = new LinkedHashSet<>(); - names.add("my-config-map"); - names.add("my-config-map-2"); - - MultipleSourcesContainer result = Fabric8ConfigUtils.configMapsDataByName(client, "spring-k8s", names, - new MockEnvironment(), true); - Assertions.assertTrue(result.names().contains("my-config-map")); - Assertions.assertTrue(result.names().contains("my-config-map-2")); - - Assertions.assertEquals(2, result.data().size()); - Assertions.assertEquals("value", result.data().get("property")); - Assertions.assertEquals("value-2", result.data().get("property-2")); - } - @Test void testNamespacesFromProperties() { ConfigReloadProperties configReloadProperties = new ConfigReloadProperties(false, true, false, diff --git a/spring-cloud-kubernetes-fabric8-config/src/test/java/org/springframework/cloud/kubernetes/fabric8/config/LabeledConfigMapContextToSourceDataProviderTests.java b/spring-cloud-kubernetes-fabric8-config/src/test/java/org/springframework/cloud/kubernetes/fabric8/config/LabeledConfigMapContextToSourceDataProviderNamespacedBatchReadTests.java similarity index 96% rename from spring-cloud-kubernetes-fabric8-config/src/test/java/org/springframework/cloud/kubernetes/fabric8/config/LabeledConfigMapContextToSourceDataProviderTests.java rename to spring-cloud-kubernetes-fabric8-config/src/test/java/org/springframework/cloud/kubernetes/fabric8/config/LabeledConfigMapContextToSourceDataProviderNamespacedBatchReadTests.java index 0ee6abae68..f6479811d7 100644 --- a/spring-cloud-kubernetes-fabric8-config/src/test/java/org/springframework/cloud/kubernetes/fabric8/config/LabeledConfigMapContextToSourceDataProviderTests.java +++ b/spring-cloud-kubernetes-fabric8-config/src/test/java/org/springframework/cloud/kubernetes/fabric8/config/LabeledConfigMapContextToSourceDataProviderNamespacedBatchReadTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2022 the original author or authors. + * Copyright 2012-2024 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. @@ -45,7 +45,9 @@ */ @EnableKubernetesMockClient(crud = true, https = false) @ExtendWith(OutputCaptureExtension.class) -class LabeledConfigMapContextToSourceDataProviderTests { +class LabeledConfigMapContextToSourceDataProviderNamespacedBatchReadTests { + + private static final boolean NAMESPACED_BATCH_READ = true; private static final String NAMESPACE = "default"; @@ -101,7 +103,7 @@ void singleConfigMapMatchAgainstLabels() { NormalizedSource normalizedSource = new LabeledConfigMapNormalizedSource(NAMESPACE, LABELS, true, false); Fabric8ConfigContext context = new Fabric8ConfigContext(mockClient, normalizedSource, NAMESPACE, - new MockEnvironment(), true); + new MockEnvironment(), NAMESPACED_BATCH_READ); Fabric8ContextToSourceData data = new LabeledConfigMapContextToSourceDataProvider().get(); SourceData sourceData = data.apply(context); @@ -145,7 +147,7 @@ void twoConfigMapsMatchAgainstLabels() { NormalizedSource normalizedSource = new LabeledConfigMapNormalizedSource(NAMESPACE, RED_LABEL, true, false); Fabric8ConfigContext context = new Fabric8ConfigContext(mockClient, normalizedSource, NAMESPACE, - new MockEnvironment(), true); + new MockEnvironment(), NAMESPACED_BATCH_READ); Fabric8ContextToSourceData data = new LabeledConfigMapContextToSourceDataProvider().get(); SourceData sourceData = data.apply(context); @@ -174,7 +176,7 @@ void configMapNoMatch() { NormalizedSource normalizedSource = new LabeledConfigMapNormalizedSource(NAMESPACE, BLUE_LABEL, true, false); Fabric8ConfigContext context = new Fabric8ConfigContext(mockClient, normalizedSource, NAMESPACE, - new MockEnvironment(), true); + new MockEnvironment(), NAMESPACED_BATCH_READ); Fabric8ContextToSourceData data = new LabeledConfigMapContextToSourceDataProvider().get(); SourceData sourceData = data.apply(context); @@ -205,7 +207,7 @@ void namespaceMatch() { NormalizedSource normalizedSource = new LabeledConfigMapNormalizedSource(NAMESPACE + "nope", LABELS, true, false); Fabric8ConfigContext context = new Fabric8ConfigContext(mockClient, normalizedSource, NAMESPACE, - new MockEnvironment(), true); + new MockEnvironment(), NAMESPACED_BATCH_READ); Fabric8ContextToSourceData data = new LabeledConfigMapContextToSourceDataProvider().get(); SourceData sourceData = data.apply(context); @@ -235,7 +237,7 @@ void testWithPrefix() { NormalizedSource normalizedSource = new LabeledConfigMapNormalizedSource(NAMESPACE, Collections.singletonMap("color", "blue"), true, mePrefix, false); Fabric8ConfigContext context = new Fabric8ConfigContext(mockClient, normalizedSource, NAMESPACE, - new MockEnvironment(), true); + new MockEnvironment(), NAMESPACED_BATCH_READ); Fabric8ContextToSourceData data = new LabeledConfigMapContextToSourceDataProvider().get(); SourceData sourceData = data.apply(context); @@ -276,7 +278,7 @@ void testTwoConfigmapsWithPrefix() { NormalizedSource normalizedSource = new LabeledConfigMapNormalizedSource(NAMESPACE, Collections.singletonMap("color", "blue"), true, ConfigUtils.Prefix.DELAYED, false); Fabric8ConfigContext context = new Fabric8ConfigContext(mockClient, normalizedSource, NAMESPACE, - new MockEnvironment(), true); + new MockEnvironment(), NAMESPACED_BATCH_READ); Fabric8ContextToSourceData data = new LabeledConfigMapContextToSourceDataProvider().get(); SourceData sourceData = data.apply(context); @@ -325,7 +327,7 @@ void searchWithLabelsNoConfigmapsFound() { NormalizedSource normalizedSource = new LabeledConfigMapNormalizedSource(NAMESPACE, Collections.singletonMap("color", "red"), true, ConfigUtils.Prefix.DEFAULT, true); Fabric8ConfigContext context = new Fabric8ConfigContext(mockClient, normalizedSource, NAMESPACE, environment, - true); + NAMESPACED_BATCH_READ); Fabric8ContextToSourceData data = new LabeledConfigMapContextToSourceDataProvider().get(); SourceData sourceData = data.apply(context); @@ -362,7 +364,7 @@ void searchWithLabelsOneConfigMapFound() { NormalizedSource normalizedSource = new LabeledConfigMapNormalizedSource(NAMESPACE, Collections.singletonMap("color", "blue"), true, ConfigUtils.Prefix.DEFAULT, true); Fabric8ConfigContext context = new Fabric8ConfigContext(mockClient, normalizedSource, NAMESPACE, environment, - true); + NAMESPACED_BATCH_READ); Fabric8ContextToSourceData data = new LabeledConfigMapContextToSourceDataProvider().get(); SourceData sourceData = data.apply(context); @@ -430,7 +432,7 @@ void searchWithLabelsTwoConfigMapsFound() { NormalizedSource normalizedSource = new LabeledConfigMapNormalizedSource(NAMESPACE, Collections.singletonMap("color", "blue"), true, ConfigUtils.Prefix.DELAYED, true); Fabric8ConfigContext context = new Fabric8ConfigContext(mockClient, normalizedSource, NAMESPACE, environment, - true); + NAMESPACED_BATCH_READ); Fabric8ContextToSourceData data = new LabeledConfigMapContextToSourceDataProvider().get(); SourceData sourceData = data.apply(context); @@ -475,18 +477,20 @@ void cache(CapturedOutput output) { NormalizedSource redNormalizedSource = new LabeledConfigMapNormalizedSource(NAMESPACE, Collections.singletonMap("color", "red"), true, ConfigUtils.Prefix.DELAYED, true); Fabric8ConfigContext redContext = new Fabric8ConfigContext(mockClient, redNormalizedSource, NAMESPACE, - environment, true); + environment, NAMESPACED_BATCH_READ); Fabric8ContextToSourceData redData = new LabeledConfigMapContextToSourceDataProvider().get(); SourceData redSourceData = redData.apply(redContext); Assertions.assertEquals(redSourceData.sourceData().size(), 1); Assertions.assertEquals(redSourceData.sourceData().get("red-configmap.one"), "1"); + Assertions.assertTrue(output.getAll().contains("Loaded all config maps in namespace '" + NAMESPACE + "'")); + Assertions.assertFalse(output.getOut().contains("Will read individual configmaps in namespace")); NormalizedSource greenNormalizedSource = new LabeledConfigMapNormalizedSource(NAMESPACE, Collections.singletonMap("color", "green"), true, ConfigUtils.Prefix.DELAYED, true); Fabric8ConfigContext greenContext = new Fabric8ConfigContext(mockClient, greenNormalizedSource, NAMESPACE, - environment, true); + environment, NAMESPACED_BATCH_READ); Fabric8ContextToSourceData greenData = new LabeledConfigMapContextToSourceDataProvider().get(); SourceData greenSourceData = greenData.apply(greenContext); diff --git a/spring-cloud-kubernetes-fabric8-config/src/test/java/org/springframework/cloud/kubernetes/fabric8/config/LabeledConfigMapContextToSourceDataProviderNonNamespacedBatchReadTests.java b/spring-cloud-kubernetes-fabric8-config/src/test/java/org/springframework/cloud/kubernetes/fabric8/config/LabeledConfigMapContextToSourceDataProviderNonNamespacedBatchReadTests.java new file mode 100644 index 0000000000..1027a28ed5 --- /dev/null +++ b/spring-cloud-kubernetes-fabric8-config/src/test/java/org/springframework/cloud/kubernetes/fabric8/config/LabeledConfigMapContextToSourceDataProviderNonNamespacedBatchReadTests.java @@ -0,0 +1,510 @@ +/* + * Copyright 2012-2024 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.fabric8.config; + +import java.util.Collections; +import java.util.Iterator; +import java.util.LinkedHashMap; +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.AfterEach; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; + +import org.springframework.boot.test.system.CapturedOutput; +import org.springframework.boot.test.system.OutputCaptureExtension; +import org.springframework.cloud.kubernetes.commons.config.ConfigUtils; +import org.springframework.cloud.kubernetes.commons.config.LabeledConfigMapNormalizedSource; +import org.springframework.cloud.kubernetes.commons.config.NormalizedSource; +import org.springframework.cloud.kubernetes.commons.config.SourceData; +import org.springframework.mock.env.MockEnvironment; + +/** + * @author wind57 + */ +@EnableKubernetesMockClient(crud = true, https = false) +@ExtendWith(OutputCaptureExtension.class) +class LabeledConfigMapContextToSourceDataProviderNonNamespacedBatchReadTests { + + private static final boolean NAMESPACED_BATCH_READ = false; + + private static final String NAMESPACE = "default"; + + private static final Map LABELS = new LinkedHashMap<>(); + + private static final Map RED_LABEL = Map.of("color", "red"); + + private static final Map PINK_LABEL = Map.of("color", "pink"); + + private static final Map BLUE_LABEL = Map.of("color", "blue"); + + private static KubernetesClient mockClient; + + static { + LABELS.put("label2", "value2"); + LABELS.put("label1", "value1"); + } + + @BeforeAll + static void beforeAll() { + + // Configure the kubernetes master url to point to the mock server + System.setProperty(Config.KUBERNETES_MASTER_SYSTEM_PROPERTY, mockClient.getConfiguration().getMasterUrl()); + System.setProperty(Config.KUBERNETES_TRUST_CERT_SYSTEM_PROPERTY, "true"); + System.setProperty(Config.KUBERNETES_AUTH_TRYKUBECONFIG_SYSTEM_PROPERTY, "false"); + System.setProperty(Config.KUBERNETES_AUTH_TRYSERVICEACCOUNT_SYSTEM_PROPERTY, "false"); + System.setProperty(Config.KUBERNETES_NAMESPACE_SYSTEM_PROPERTY, NAMESPACE); + System.setProperty(Config.KUBERNETES_HTTP2_DISABLE, "true"); + + } + + @AfterEach + void afterEach() { + mockClient.configMaps().inNamespace(NAMESPACE).delete(); + new Fabric8ConfigMapsNamespaceBatched().discardAll(); + } + + /** + * we have a single config map deployed. it has two labels and these match against our + * queries. + */ + @Test + void singleConfigMapMatchAgainstLabels() { + + ConfigMap configMap = new ConfigMapBuilder().withNewMetadata() + .withName("test-configmap") + .withLabels(LABELS) + .endMetadata() + .addToData("name", "value") + .build(); + + mockClient.configMaps().inNamespace(NAMESPACE).resource(configMap).create(); + + NormalizedSource normalizedSource = new LabeledConfigMapNormalizedSource(NAMESPACE, LABELS, true, false); + Fabric8ConfigContext context = new Fabric8ConfigContext(mockClient, normalizedSource, NAMESPACE, + new MockEnvironment(), NAMESPACED_BATCH_READ); + + Fabric8ContextToSourceData data = new LabeledConfigMapContextToSourceDataProvider().get(); + SourceData sourceData = data.apply(context); + + Assertions.assertEquals("configmap.test-configmap.default", sourceData.sourceName()); + Assertions.assertEquals(Map.of("name", "value"), sourceData.sourceData()); + + } + + /** + * we have three configmaps deployed. two of them have labels that match (color=red), + * one does not (color=blue). + */ + @Test + void twoConfigMapsMatchAgainstLabels() { + + ConfigMap redOne = new ConfigMapBuilder().withNewMetadata() + .withName("red-configmap") + .withLabels(RED_LABEL) + .endMetadata() + .addToData("colorOne", "really-red") + .build(); + + ConfigMap redTwo = new ConfigMapBuilder().withNewMetadata() + .withName("red-configmap-again") + .withLabels(RED_LABEL) + .endMetadata() + .addToData("colorTwo", "really-red-again") + .build(); + + ConfigMap blue = new ConfigMapBuilder().withNewMetadata() + .withName("blue-configmap") + .withLabels(BLUE_LABEL) + .endMetadata() + .addToData("color", "blue") + .build(); + + mockClient.configMaps().inNamespace(NAMESPACE).resource(redOne).create(); + mockClient.configMaps().inNamespace(NAMESPACE).resource(redTwo).create(); + mockClient.configMaps().inNamespace(NAMESPACE).resource(blue).create(); + + NormalizedSource normalizedSource = new LabeledConfigMapNormalizedSource(NAMESPACE, RED_LABEL, true, false); + Fabric8ConfigContext context = new Fabric8ConfigContext(mockClient, normalizedSource, NAMESPACE, + new MockEnvironment(), NAMESPACED_BATCH_READ); + + Fabric8ContextToSourceData data = new LabeledConfigMapContextToSourceDataProvider().get(); + SourceData sourceData = data.apply(context); + + Assertions.assertEquals(sourceData.sourceName(), "configmap.red-configmap.red-configmap-again.default"); + Assertions.assertEquals(sourceData.sourceData().size(), 2); + Assertions.assertEquals(sourceData.sourceData().get("colorOne"), "really-red"); + Assertions.assertEquals(sourceData.sourceData().get("colorTwo"), "really-red-again"); + + } + + /** + * one configmap deployed (pink), does not match our query (blue). + */ + @Test + void configMapNoMatch() { + + ConfigMap pink = new ConfigMapBuilder().withNewMetadata() + .withName("pink-configmap") + .withLabels(PINK_LABEL) + .endMetadata() + .addToData("color", "pink") + .build(); + + mockClient.configMaps().inNamespace(NAMESPACE).resource(pink).create(); + + NormalizedSource normalizedSource = new LabeledConfigMapNormalizedSource(NAMESPACE, BLUE_LABEL, true, false); + Fabric8ConfigContext context = new Fabric8ConfigContext(mockClient, normalizedSource, NAMESPACE, + new MockEnvironment(), NAMESPACED_BATCH_READ); + + Fabric8ContextToSourceData data = new LabeledConfigMapContextToSourceDataProvider().get(); + SourceData sourceData = data.apply(context); + + Assertions.assertEquals(sourceData.sourceName(), "configmap.color.default"); + Assertions.assertEquals(sourceData.sourceData(), Collections.emptyMap()); + } + + /** + * LabeledConfigMapContextToSourceDataProvider gets as input a Fabric8ConfigContext. + * This context has a namespace as well as a NormalizedSource, that has a namespace + * too. It is easy to get confused in code on which namespace to use. This test makes + * sure that we use the proper one. + */ + @Test + void namespaceMatch() { + + ConfigMap configMap = new ConfigMapBuilder().withNewMetadata() + .withName("test-configmap") + .withLabels(LABELS) + .endMetadata() + .addToData("name", "value") + .build(); + + mockClient.configMaps().inNamespace(NAMESPACE).resource(configMap).create(); + + // different namespace + NormalizedSource normalizedSource = new LabeledConfigMapNormalizedSource(NAMESPACE + "nope", LABELS, true, + false); + Fabric8ConfigContext context = new Fabric8ConfigContext(mockClient, normalizedSource, NAMESPACE, + new MockEnvironment(), NAMESPACED_BATCH_READ); + + Fabric8ContextToSourceData data = new LabeledConfigMapContextToSourceDataProvider().get(); + SourceData sourceData = data.apply(context); + + Assertions.assertEquals("configmap.test-configmap.default", sourceData.sourceName()); + Assertions.assertEquals(Map.of("name", "value"), sourceData.sourceData()); + } + + /** + * one configmap with name : "blue-configmap" and labels "color=blue" is deployed. we + * search it with the same labels, find it, and assert that name of the SourceData (it + * must use its name, not its labels) and values in the SourceData must be prefixed + * (since we have provided an explicit prefix). + */ + @Test + void testWithPrefix() { + ConfigMap configMap = new ConfigMapBuilder().withNewMetadata() + .withName("blue-configmap") + .withLabels(Collections.singletonMap("color", "blue")) + .endMetadata() + .addToData("what-color", "blue-color") + .build(); + + mockClient.configMaps().inNamespace(NAMESPACE).resource(configMap).create(); + + ConfigUtils.Prefix mePrefix = ConfigUtils.findPrefix("me", false, false, "irrelevant"); + NormalizedSource normalizedSource = new LabeledConfigMapNormalizedSource(NAMESPACE, + Collections.singletonMap("color", "blue"), true, mePrefix, false); + Fabric8ConfigContext context = new Fabric8ConfigContext(mockClient, normalizedSource, NAMESPACE, + new MockEnvironment(), NAMESPACED_BATCH_READ); + + Fabric8ContextToSourceData data = new LabeledConfigMapContextToSourceDataProvider().get(); + SourceData sourceData = data.apply(context); + + Assertions.assertEquals("configmap.blue-configmap.default", sourceData.sourceName()); + Assertions.assertEquals(Map.of("me.what-color", "blue-color"), sourceData.sourceData()); + } + + /** + * two configmaps are deployed (name:blue-configmap, name:another-blue-configmap) and + * labels "color=blue" (on both). we search with the same labels, find them, and + * assert that name of the SourceData (it must use its name, not its labels) and + * values in the SourceData must be prefixed (since we have provided a delayed + * prefix). + * + * Also notice that the prefix is made up from both configmap names. + * + */ + @Test + void testTwoConfigmapsWithPrefix() { + ConfigMap blueConfigMap = new ConfigMapBuilder().withNewMetadata() + .withName("blue-configmap") + .withLabels(Collections.singletonMap("color", "blue")) + .endMetadata() + .addToData("first", "blue") + .build(); + + ConfigMap anotherBlue = new ConfigMapBuilder().withNewMetadata() + .withName("another-blue-configmap") + .withLabels(Collections.singletonMap("color", "blue")) + .endMetadata() + .addToData("second", "blue") + .build(); + + mockClient.configMaps().inNamespace(NAMESPACE).resource(blueConfigMap).create(); + mockClient.configMaps().inNamespace(NAMESPACE).resource(anotherBlue).create(); + + NormalizedSource normalizedSource = new LabeledConfigMapNormalizedSource(NAMESPACE, + Collections.singletonMap("color", "blue"), true, ConfigUtils.Prefix.DELAYED, false); + Fabric8ConfigContext context = new Fabric8ConfigContext(mockClient, normalizedSource, NAMESPACE, + new MockEnvironment(), NAMESPACED_BATCH_READ); + + Fabric8ContextToSourceData data = new LabeledConfigMapContextToSourceDataProvider().get(); + SourceData sourceData = data.apply(context); + + Assertions.assertEquals(sourceData.sourceName(), "configmap.another-blue-configmap.blue-configmap.default"); + + Map properties = sourceData.sourceData(); + Assertions.assertEquals(2, properties.size()); + Iterator keys = properties.keySet().iterator(); + String firstKey = keys.next(); + String secondKey = keys.next(); + + if (firstKey.contains("first")) { + Assertions.assertEquals(firstKey, "another-blue-configmap.blue-configmap.first"); + } + + Assertions.assertEquals(secondKey, "another-blue-configmap.blue-configmap.second"); + Assertions.assertEquals(properties.get(firstKey), "blue"); + Assertions.assertEquals(properties.get(secondKey), "blue"); + } + + /** + * two configmaps are deployed: "color-configmap" with label: "{color:blue}" and + * "color-configmap-k8s" with no labels. We search by "{color:red}", do not find + * anything and thus have an empty SourceData. + */ + @Test + void searchWithLabelsNoConfigmapsFound() { + ConfigMap colorConfigmap = new ConfigMapBuilder().withNewMetadata() + .withName("color-configmap") + .withLabels(Collections.singletonMap("color", "blue")) + .endMetadata() + .addToData("one", "1") + .build(); + + ConfigMap colorConfigmapK8s = new ConfigMapBuilder().withNewMetadata() + .withName("color-configmap-k8s") + .endMetadata() + .addToData("two", "2") + .build(); + + mockClient.configMaps().inNamespace(NAMESPACE).resource(colorConfigmap).create(); + mockClient.configMaps().inNamespace(NAMESPACE).resource(colorConfigmapK8s).create(); + MockEnvironment environment = new MockEnvironment(); + + NormalizedSource normalizedSource = new LabeledConfigMapNormalizedSource(NAMESPACE, + Collections.singletonMap("color", "red"), true, ConfigUtils.Prefix.DEFAULT, true); + Fabric8ConfigContext context = new Fabric8ConfigContext(mockClient, normalizedSource, NAMESPACE, environment, + NAMESPACED_BATCH_READ); + + Fabric8ContextToSourceData data = new LabeledConfigMapContextToSourceDataProvider().get(); + SourceData sourceData = data.apply(context); + + Assertions.assertTrue(sourceData.sourceData().isEmpty()); + Assertions.assertEquals(sourceData.sourceName(), "configmap.color.default"); + + } + + /** + * two configmaps are deployed: "color-configmap" with label: "{color:blue}" and + * "shape-configmap" with label: "{shape:round}". We search by "{color:blue}" and find + * one configmap. + */ + @Test + void searchWithLabelsOneConfigMapFound() { + ConfigMap colorConfigmap = new ConfigMapBuilder().withNewMetadata() + .withName("color-configmap") + .withLabels(Collections.singletonMap("color", "blue")) + .endMetadata() + .addToData("one", "1") + .build(); + + ConfigMap shapeConfigmap = new ConfigMapBuilder().withNewMetadata() + .withName("shape-configmap") + .endMetadata() + .addToData("two", "2") + .build(); + + mockClient.configMaps().inNamespace(NAMESPACE).resource(colorConfigmap).create(); + mockClient.configMaps().inNamespace(NAMESPACE).resource(shapeConfigmap).create(); + MockEnvironment environment = new MockEnvironment(); + + NormalizedSource normalizedSource = new LabeledConfigMapNormalizedSource(NAMESPACE, + Collections.singletonMap("color", "blue"), true, ConfigUtils.Prefix.DEFAULT, true); + Fabric8ConfigContext context = new Fabric8ConfigContext(mockClient, normalizedSource, NAMESPACE, environment, + NAMESPACED_BATCH_READ); + + Fabric8ContextToSourceData data = new LabeledConfigMapContextToSourceDataProvider().get(); + SourceData sourceData = data.apply(context); + + Assertions.assertEquals(sourceData.sourceData().size(), 1); + Assertions.assertEquals(sourceData.sourceData().get("one"), "1"); + Assertions.assertEquals(sourceData.sourceName(), "configmap.color-configmap.default"); + + } + + /** + *
+	 *     - configmap "color-configmap" with label "{color:blue}"
+	 *     - configmap "shape-configmap" with labels "{color:blue, shape:round}"
+	 *     - configmap "no-fit" with labels "{tag:no-fit}"
+	 *     - configmap "color-configmap-k8s" with label "{color:red}"
+	 *     - configmap "shape-configmap-k8s" with label "{shape:triangle}"
+	 * 
+ */ + @Test + void searchWithLabelsTwoConfigMapsFound() { + ConfigMap colorConfigMap = new ConfigMapBuilder().withNewMetadata() + .withName("color-configmap") + .withLabels(Collections.singletonMap("color", "blue")) + .endMetadata() + .addToData("one", "1") + .build(); + + ConfigMap shapeConfigmap = new ConfigMapBuilder().withNewMetadata() + .withName("shape-configmap") + .withLabels(Map.of("color", "blue", "shape", "round")) + .endMetadata() + .addToData("two", "2") + .build(); + + ConfigMap noFit = new ConfigMapBuilder().withNewMetadata() + .withName("no-fit") + .withLabels(Map.of("tag", "no-fit")) + .endMetadata() + .addToData("three", "3") + .build(); + + ConfigMap colorConfigmapK8s = new ConfigMapBuilder().withNewMetadata() + .withName("color-configmap-k8s") + .withLabels(Map.of("color", "red")) + .endMetadata() + .addToData("four", "4") + .build(); + + ConfigMap shapeConfigmapK8s = new ConfigMapBuilder().withNewMetadata() + .withName("shape-configmap-k8s") + .withLabels(Map.of("shape", "triangle")) + .endMetadata() + .addToData("five", "5") + .build(); + + mockClient.configMaps().inNamespace(NAMESPACE).resource(colorConfigMap).create(); + mockClient.configMaps().inNamespace(NAMESPACE).resource(shapeConfigmap).create(); + mockClient.configMaps().inNamespace(NAMESPACE).resource(noFit).create(); + mockClient.configMaps().inNamespace(NAMESPACE).resource(colorConfigmapK8s).create(); + mockClient.configMaps().inNamespace(NAMESPACE).resource(shapeConfigmapK8s).create(); + + MockEnvironment environment = new MockEnvironment(); + + NormalizedSource normalizedSource = new LabeledConfigMapNormalizedSource(NAMESPACE, + Collections.singletonMap("color", "blue"), true, ConfigUtils.Prefix.DELAYED, true); + Fabric8ConfigContext context = new Fabric8ConfigContext(mockClient, normalizedSource, NAMESPACE, environment, + NAMESPACED_BATCH_READ); + + Fabric8ContextToSourceData data = new LabeledConfigMapContextToSourceDataProvider().get(); + SourceData sourceData = data.apply(context); + + Assertions.assertEquals(sourceData.sourceData().size(), 2); + Assertions.assertEquals(sourceData.sourceData().get("color-configmap.shape-configmap.one"), "1"); + Assertions.assertEquals(sourceData.sourceData().get("color-configmap.shape-configmap.two"), "2"); + + Assertions.assertEquals(sourceData.sourceName(), "configmap.color-configmap.shape-configmap.default"); + + } + + /** + *
+	 *     - configmap "red-configmap" with label "{color:red}"
+	 *     - configmap "green-configmap" with labels "{color:green}"
+	 *     - we first search for "red" and find it, and it is retrieved from the cluster via the client.
+	 * 	   - we then search for the "green" one, and it is not retrieved from the cache.
+	 * 
+ */ + @Test + void nonCache(CapturedOutput output) { + ConfigMap redConfigMap = new ConfigMapBuilder().withNewMetadata() + .withName("red-configmap") + .withLabels(Collections.singletonMap("color", "red")) + .endMetadata() + .addToData("one", "1") + .build(); + + ConfigMap greenConfigmap = new ConfigMapBuilder().withNewMetadata() + .withName("green-configmap") + .withLabels(Map.of("color", "green")) + .endMetadata() + .addToData("two", "2") + .build(); + + mockClient.configMaps().inNamespace(NAMESPACE).resource(redConfigMap).create(); + mockClient.configMaps().inNamespace(NAMESPACE).resource(greenConfigmap).create(); + + MockEnvironment environment = new MockEnvironment(); + + NormalizedSource redNormalizedSource = new LabeledConfigMapNormalizedSource(NAMESPACE, + Collections.singletonMap("color", "red"), true, ConfigUtils.Prefix.DELAYED, true); + Fabric8ConfigContext redContext = new Fabric8ConfigContext(mockClient, redNormalizedSource, NAMESPACE, + environment, NAMESPACED_BATCH_READ); + Fabric8ContextToSourceData redData = new LabeledConfigMapContextToSourceDataProvider().get(); + SourceData redSourceData = redData.apply(redContext); + + Assertions.assertEquals(redSourceData.sourceData().size(), 1); + Assertions.assertEquals(redSourceData.sourceData().get("red-configmap.one"), "1"); + + Assertions.assertFalse(output.getAll().contains("Loaded all config maps in namespace '" + NAMESPACE + "'")); + Assertions.assertTrue(output.getOut().contains("Will read individual configmaps in namespace")); + + NormalizedSource greenNormalizedSource = new LabeledConfigMapNormalizedSource(NAMESPACE, + Collections.singletonMap("color", "green"), true, ConfigUtils.Prefix.DELAYED, true); + Fabric8ConfigContext greenContext = new Fabric8ConfigContext(mockClient, greenNormalizedSource, NAMESPACE, + environment, NAMESPACED_BATCH_READ); + Fabric8ContextToSourceData greenData = new LabeledConfigMapContextToSourceDataProvider().get(); + SourceData greenSourceData = greenData.apply(greenContext); + + Assertions.assertEquals(greenSourceData.sourceData().size(), 1); + Assertions.assertEquals(greenSourceData.sourceData().get("green-configmap.two"), "2"); + + // meaning there is a single entry with such a log statement + String[] out = output.getAll().split("Loaded all config maps in namespace"); + Assertions.assertEquals(out.length, 1); + + // meaning that both reads were non cached + out = output.getAll().split("Will read individual configmaps in namespace"); + Assertions.assertEquals(out.length, 3); + + } + +} diff --git a/spring-cloud-kubernetes-fabric8-config/src/test/java/org/springframework/cloud/kubernetes/fabric8/config/LabeledSecretContextToSourceDataProviderTests.java b/spring-cloud-kubernetes-fabric8-config/src/test/java/org/springframework/cloud/kubernetes/fabric8/config/LabeledSecretContextToSourceDataProviderNamespacedBatchReadTests.java similarity index 96% rename from spring-cloud-kubernetes-fabric8-config/src/test/java/org/springframework/cloud/kubernetes/fabric8/config/LabeledSecretContextToSourceDataProviderTests.java rename to spring-cloud-kubernetes-fabric8-config/src/test/java/org/springframework/cloud/kubernetes/fabric8/config/LabeledSecretContextToSourceDataProviderNamespacedBatchReadTests.java index 2b759f0171..a53736e412 100644 --- a/spring-cloud-kubernetes-fabric8-config/src/test/java/org/springframework/cloud/kubernetes/fabric8/config/LabeledSecretContextToSourceDataProviderTests.java +++ b/spring-cloud-kubernetes-fabric8-config/src/test/java/org/springframework/cloud/kubernetes/fabric8/config/LabeledSecretContextToSourceDataProviderNamespacedBatchReadTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2022 the original author or authors. + * Copyright 2012-2024 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. @@ -48,7 +48,9 @@ */ @EnableKubernetesMockClient(crud = true, https = false) @ExtendWith(OutputCaptureExtension.class) -class LabeledSecretContextToSourceDataProviderTests { +class LabeledSecretContextToSourceDataProviderNamespacedBatchReadTests { + + private static final boolean NAMESPACED_BATCH_READ = true; private static final String NAMESPACE = "default"; @@ -102,7 +104,7 @@ void singleSecretMatchAgainstLabels() { NormalizedSource normalizedSource = new LabeledSecretNormalizedSource(NAMESPACE, LABELS, true); Fabric8ConfigContext context = new Fabric8ConfigContext(mockClient, normalizedSource, NAMESPACE, - new MockEnvironment(), true); + new MockEnvironment(), NAMESPACED_BATCH_READ); Fabric8ContextToSourceData data = new LabeledSecretContextToSourceDataProvider().get(); SourceData sourceData = data.apply(context); @@ -146,7 +148,7 @@ void twoSecretsMatchAgainstLabels() { NormalizedSource normalizedSource = new LabeledSecretNormalizedSource(NAMESPACE, RED_LABEL, true); Fabric8ConfigContext context = new Fabric8ConfigContext(mockClient, normalizedSource, NAMESPACE, - new MockEnvironment(), true); + new MockEnvironment(), NAMESPACED_BATCH_READ); Fabric8ContextToSourceData data = new LabeledSecretContextToSourceDataProvider().get(); SourceData sourceData = data.apply(context); @@ -175,7 +177,7 @@ void secretNoMatch() { NormalizedSource normalizedSource = new LabeledSecretNormalizedSource(NAMESPACE, BLUE_LABEL, true); Fabric8ConfigContext context = new Fabric8ConfigContext(mockClient, normalizedSource, NAMESPACE, - new MockEnvironment(), true); + new MockEnvironment(), NAMESPACED_BATCH_READ); Fabric8ContextToSourceData data = new LabeledSecretContextToSourceDataProvider().get(); SourceData sourceData = data.apply(context); @@ -205,7 +207,7 @@ void namespaceMatch() { // different namespace NormalizedSource normalizedSource = new LabeledSecretNormalizedSource(NAMESPACE + "nope", LABELS, true); Fabric8ConfigContext context = new Fabric8ConfigContext(mockClient, normalizedSource, NAMESPACE, - new MockEnvironment(), true); + new MockEnvironment(), NAMESPACED_BATCH_READ); Fabric8ContextToSourceData data = new LabeledSecretContextToSourceDataProvider().get(); SourceData sourceData = data.apply(context); @@ -235,7 +237,7 @@ void testWithPrefix() { NormalizedSource normalizedSource = new LabeledSecretNormalizedSource(NAMESPACE, Collections.singletonMap("color", "blue"), true, mePrefix); Fabric8ConfigContext context = new Fabric8ConfigContext(mockClient, normalizedSource, NAMESPACE, - new MockEnvironment(), true); + new MockEnvironment(), NAMESPACED_BATCH_READ); Fabric8ContextToSourceData data = new LabeledSecretContextToSourceDataProvider().get(); SourceData sourceData = data.apply(context); @@ -275,7 +277,7 @@ void testTwoSecretsWithPrefix() { NormalizedSource normalizedSource = new LabeledSecretNormalizedSource(NAMESPACE, Collections.singletonMap("color", "blue"), true, ConfigUtils.Prefix.DELAYED); Fabric8ConfigContext context = new Fabric8ConfigContext(mockClient, normalizedSource, NAMESPACE, - new MockEnvironment(), true); + new MockEnvironment(), NAMESPACED_BATCH_READ); Fabric8ContextToSourceData data = new LabeledSecretContextToSourceDataProvider().get(); SourceData sourceData = data.apply(context); @@ -324,7 +326,7 @@ void searchWithLabelsNoSecretFound() { NormalizedSource normalizedSource = new LabeledSecretNormalizedSource(NAMESPACE, Collections.singletonMap("color", "red"), true, ConfigUtils.Prefix.DEFAULT); Fabric8ConfigContext context = new Fabric8ConfigContext(mockClient, normalizedSource, NAMESPACE, environment, - true); + NAMESPACED_BATCH_READ); Fabric8ContextToSourceData data = new LabeledSecretContextToSourceDataProvider().get(); SourceData sourceData = data.apply(context); @@ -361,7 +363,7 @@ void searchWithLabelsOneSecretFound() { NormalizedSource normalizedSource = new LabeledSecretNormalizedSource(NAMESPACE, Collections.singletonMap("color", "blue"), true, ConfigUtils.Prefix.DEFAULT); Fabric8ConfigContext context = new Fabric8ConfigContext(mockClient, normalizedSource, NAMESPACE, environment, - true); + NAMESPACED_BATCH_READ); Fabric8ContextToSourceData data = new LabeledSecretContextToSourceDataProvider().get(); SourceData sourceData = data.apply(context); @@ -429,7 +431,7 @@ void searchWithLabelsTwoSecretsFound() { NormalizedSource normalizedSource = new LabeledSecretNormalizedSource(NAMESPACE, Collections.singletonMap("color", "blue"), true, ConfigUtils.Prefix.DELAYED); Fabric8ConfigContext context = new Fabric8ConfigContext(mockClient, normalizedSource, NAMESPACE, environment, - true); + NAMESPACED_BATCH_READ); Fabric8ContextToSourceData data = new LabeledSecretContextToSourceDataProvider().get(); SourceData sourceData = data.apply(context); @@ -459,7 +461,7 @@ void testYaml() { NormalizedSource normalizedSource = new LabeledSecretNormalizedSource(NAMESPACE, Collections.singletonMap("color", "blue"), true, ConfigUtils.Prefix.DEFAULT); Fabric8ConfigContext context = new Fabric8ConfigContext(mockClient, normalizedSource, NAMESPACE, - new MockEnvironment(), true); + new MockEnvironment(), NAMESPACED_BATCH_READ); Fabric8ContextToSourceData data = new LabeledSecretContextToSourceDataProvider().get(); SourceData sourceData = data.apply(context); @@ -501,18 +503,20 @@ void cache(CapturedOutput output) { NormalizedSource redNormalizedSource = new LabeledSecretNormalizedSource(NAMESPACE, Collections.singletonMap("color", "red"), true, ConfigUtils.Prefix.DELAYED); Fabric8ConfigContext redContext = new Fabric8ConfigContext(mockClient, redNormalizedSource, NAMESPACE, - environment, true); + environment, NAMESPACED_BATCH_READ); Fabric8ContextToSourceData redData = new LabeledSecretContextToSourceDataProvider().get(); SourceData redSourceData = redData.apply(redContext); Assertions.assertEquals(redSourceData.sourceData().size(), 1); Assertions.assertEquals(redSourceData.sourceData().get("red.one"), "1"); + Assertions.assertTrue(output.getAll().contains("Loaded all secrets in namespace '" + NAMESPACE + "'")); + Assertions.assertFalse(output.getAll().contains("Will read individual secrets in namespace")); NormalizedSource greenNormalizedSource = new LabeledSecretNormalizedSource(NAMESPACE, Collections.singletonMap("color", "green"), true, ConfigUtils.Prefix.DELAYED); Fabric8ConfigContext greenContext = new Fabric8ConfigContext(mockClient, greenNormalizedSource, NAMESPACE, - environment, true); + environment, NAMESPACED_BATCH_READ); Fabric8ContextToSourceData greenData = new LabeledSecretContextToSourceDataProvider().get(); SourceData greenSourceData = greenData.apply(greenContext); diff --git a/spring-cloud-kubernetes-fabric8-config/src/test/java/org/springframework/cloud/kubernetes/fabric8/config/LabeledSecretContextToSourceDataProviderNonNamespacedBatchReadTests.java b/spring-cloud-kubernetes-fabric8-config/src/test/java/org/springframework/cloud/kubernetes/fabric8/config/LabeledSecretContextToSourceDataProviderNonNamespacedBatchReadTests.java new file mode 100644 index 0000000000..f097c199d5 --- /dev/null +++ b/spring-cloud-kubernetes-fabric8-config/src/test/java/org/springframework/cloud/kubernetes/fabric8/config/LabeledSecretContextToSourceDataProviderNonNamespacedBatchReadTests.java @@ -0,0 +1,534 @@ +/* + * Copyright 2012-2024 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.fabric8.config; + +import java.util.Base64; +import java.util.Collections; +import java.util.Iterator; +import java.util.LinkedHashMap; +import java.util.Map; + +import io.fabric8.kubernetes.api.model.Secret; +import io.fabric8.kubernetes.api.model.SecretBuilder; +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.AfterEach; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; + +import org.springframework.boot.test.system.CapturedOutput; +import org.springframework.boot.test.system.OutputCaptureExtension; +import org.springframework.cloud.kubernetes.commons.config.ConfigUtils; +import org.springframework.cloud.kubernetes.commons.config.LabeledSecretNormalizedSource; +import org.springframework.cloud.kubernetes.commons.config.NormalizedSource; +import org.springframework.cloud.kubernetes.commons.config.SourceData; +import org.springframework.mock.env.MockEnvironment; + +/** + * @author wind57 + */ +@EnableKubernetesMockClient(crud = true, https = false) +@ExtendWith(OutputCaptureExtension.class) +class LabeledSecretContextToSourceDataProviderNonNamespacedBatchReadTests { + + private static final boolean NAMESPACED_BATCH_READ = false; + + private static final String NAMESPACE = "default"; + + private static final Map LABELS = new LinkedHashMap<>(); + + private static final Map RED_LABEL = Map.of("color", "red"); + + private static final Map PINK_LABEL = Map.of("color", "pink"); + + private static final Map BLUE_LABEL = Map.of("color", "blue"); + + private static KubernetesClient mockClient; + + @BeforeAll + static void beforeAll() { + + LABELS.put("label2", "value2"); + LABELS.put("label1", "value1"); + + // Configure the kubernetes master url to point to the mock server + System.setProperty(Config.KUBERNETES_MASTER_SYSTEM_PROPERTY, mockClient.getConfiguration().getMasterUrl()); + System.setProperty(Config.KUBERNETES_TRUST_CERT_SYSTEM_PROPERTY, "true"); + System.setProperty(Config.KUBERNETES_AUTH_TRYKUBECONFIG_SYSTEM_PROPERTY, "false"); + System.setProperty(Config.KUBERNETES_AUTH_TRYSERVICEACCOUNT_SYSTEM_PROPERTY, "false"); + System.setProperty(Config.KUBERNETES_NAMESPACE_SYSTEM_PROPERTY, NAMESPACE); + System.setProperty(Config.KUBERNETES_HTTP2_DISABLE, "true"); + + } + + @AfterEach + void afterEach() { + mockClient.secrets().inNamespace(NAMESPACE).delete(); + new Fabric8SecretsNamespaceBatched().discardAll(); + } + + /** + * we have a single secret deployed. it has two labels and these match against our + * queries. + */ + @Test + void singleSecretMatchAgainstLabels() { + + Secret secret = new SecretBuilder().withNewMetadata() + .withName("test-secret") + .withLabels(LABELS) + .endMetadata() + .addToData("secretName", Base64.getEncoder().encodeToString("secretValue".getBytes())) + .build(); + + mockClient.secrets().inNamespace(NAMESPACE).resource(secret).create(); + + NormalizedSource normalizedSource = new LabeledSecretNormalizedSource(NAMESPACE, LABELS, true); + Fabric8ConfigContext context = new Fabric8ConfigContext(mockClient, normalizedSource, NAMESPACE, + new MockEnvironment(), NAMESPACED_BATCH_READ); + + Fabric8ContextToSourceData data = new LabeledSecretContextToSourceDataProvider().get(); + SourceData sourceData = data.apply(context); + + Assertions.assertEquals("secret.test-secret.default", sourceData.sourceName()); + Assertions.assertEquals(Map.of("secretName", "secretValue"), sourceData.sourceData()); + + } + + /** + * we have three secrets deployed. two of them have labels that match (color=red), one + * does not (color=blue). + */ + @Test + void twoSecretsMatchAgainstLabels() { + + Secret redOne = new SecretBuilder().withNewMetadata() + .withName("red-secret") + .withLabels(RED_LABEL) + .endMetadata() + .addToData("colorOne", Base64.getEncoder().encodeToString("really-red".getBytes())) + .build(); + + Secret redTwo = new SecretBuilder().withNewMetadata() + .withName("red-secret-again") + .withLabels(RED_LABEL) + .endMetadata() + .addToData("colorTwo", Base64.getEncoder().encodeToString("really-red-again".getBytes())) + .build(); + + Secret blue = new SecretBuilder().withNewMetadata() + .withName("blue-secret") + .withLabels(BLUE_LABEL) + .endMetadata() + .addToData("color", Base64.getEncoder().encodeToString("blue".getBytes())) + .build(); + + mockClient.secrets().inNamespace(NAMESPACE).resource(redOne).create(); + mockClient.secrets().inNamespace(NAMESPACE).resource(redTwo).create(); + mockClient.secrets().inNamespace(NAMESPACE).resource(blue).create(); + + NormalizedSource normalizedSource = new LabeledSecretNormalizedSource(NAMESPACE, RED_LABEL, true); + Fabric8ConfigContext context = new Fabric8ConfigContext(mockClient, normalizedSource, NAMESPACE, + new MockEnvironment(), NAMESPACED_BATCH_READ); + + Fabric8ContextToSourceData data = new LabeledSecretContextToSourceDataProvider().get(); + SourceData sourceData = data.apply(context); + + Assertions.assertEquals(sourceData.sourceName(), "secret.red-secret.red-secret-again.default"); + Assertions.assertEquals(sourceData.sourceData().size(), 2); + Assertions.assertEquals(sourceData.sourceData().get("colorOne"), "really-red"); + Assertions.assertEquals(sourceData.sourceData().get("colorTwo"), "really-red-again"); + + } + + /** + * one secret deployed (pink), does not match our query (blue). + */ + @Test + void secretNoMatch() { + + Secret pink = new SecretBuilder().withNewMetadata() + .withName("pink-secret") + .withLabels(PINK_LABEL) + .endMetadata() + .addToData("color", Base64.getEncoder().encodeToString("pink".getBytes())) + .build(); + + mockClient.secrets().inNamespace(NAMESPACE).resource(pink).create(); + + NormalizedSource normalizedSource = new LabeledSecretNormalizedSource(NAMESPACE, BLUE_LABEL, true); + Fabric8ConfigContext context = new Fabric8ConfigContext(mockClient, normalizedSource, NAMESPACE, + new MockEnvironment(), NAMESPACED_BATCH_READ); + + Fabric8ContextToSourceData data = new LabeledSecretContextToSourceDataProvider().get(); + SourceData sourceData = data.apply(context); + + Assertions.assertEquals(sourceData.sourceName(), "secret.color.default"); + Assertions.assertEquals(sourceData.sourceData(), Collections.emptyMap()); + } + + /** + * LabeledSecretContextToSourceDataProvider gets as input a Fabric8ConfigContext. This + * context has a namespace as well as a NormalizedSource, that has a namespace too. It + * is easy to get confused in code on which namespace to use. This test makes sure + * that we use the proper one. + */ + @Test + void namespaceMatch() { + + Secret secret = new SecretBuilder().withNewMetadata() + .withName("test-secret") + .withLabels(LABELS) + .endMetadata() + .addToData("secretName", Base64.getEncoder().encodeToString("secretValue".getBytes())) + .build(); + + mockClient.secrets().inNamespace(NAMESPACE).resource(secret).create(); + + // different namespace + NormalizedSource normalizedSource = new LabeledSecretNormalizedSource(NAMESPACE + "nope", LABELS, true); + Fabric8ConfigContext context = new Fabric8ConfigContext(mockClient, normalizedSource, NAMESPACE, + new MockEnvironment(), NAMESPACED_BATCH_READ); + + Fabric8ContextToSourceData data = new LabeledSecretContextToSourceDataProvider().get(); + SourceData sourceData = data.apply(context); + + Assertions.assertEquals("secret.test-secret.default", sourceData.sourceName()); + Assertions.assertEquals(Map.of("secretName", "secretValue"), sourceData.sourceData()); + } + + /** + * one secret with name : "blue-secret" and labels "color=blue" is deployed. we search + * it with the same labels, find it, and assert that name of the SourceData (it must + * use its name, not its labels) and values in the SourceData must be prefixed (since + * we have provided an explicit prefix). + */ + @Test + void testWithPrefix() { + Secret secret = new SecretBuilder().withNewMetadata() + .withName("blue-secret") + .withLabels(Collections.singletonMap("color", "blue")) + .endMetadata() + .addToData("what-color", Base64.getEncoder().encodeToString("blue-color".getBytes())) + .build(); + + mockClient.secrets().inNamespace(NAMESPACE).resource(secret).create(); + + ConfigUtils.Prefix mePrefix = ConfigUtils.findPrefix("me", false, false, "irrelevant"); + NormalizedSource normalizedSource = new LabeledSecretNormalizedSource(NAMESPACE, + Collections.singletonMap("color", "blue"), true, mePrefix); + Fabric8ConfigContext context = new Fabric8ConfigContext(mockClient, normalizedSource, NAMESPACE, + new MockEnvironment(), NAMESPACED_BATCH_READ); + + Fabric8ContextToSourceData data = new LabeledSecretContextToSourceDataProvider().get(); + SourceData sourceData = data.apply(context); + + Assertions.assertEquals("secret.blue-secret.default", sourceData.sourceName()); + Assertions.assertEquals(Map.of("me.what-color", "blue-color"), sourceData.sourceData()); + } + + /** + * two secrets are deployed (name:blue-secret, name:another-blue-secret) and labels + * "color=blue" (on both). we search with the same labels, find them, and assert that + * name of the SourceData (it must use its name, not its labels) and values in the + * SourceData must be prefixed (since we have provided a delayed prefix). + * + * Also notice that the prefix is made up from both secret names. + * + */ + @Test + void testTwoSecretsWithPrefix() { + Secret blueSecret = new SecretBuilder().withNewMetadata() + .withName("blue-secret") + .withLabels(Collections.singletonMap("color", "blue")) + .endMetadata() + .addToData("first", Base64.getEncoder().encodeToString("blue".getBytes())) + .build(); + + Secret anotherBlue = new SecretBuilder().withNewMetadata() + .withName("another-blue-secret") + .withLabels(Collections.singletonMap("color", "blue")) + .endMetadata() + .addToData("second", Base64.getEncoder().encodeToString("blue".getBytes())) + .build(); + + mockClient.secrets().inNamespace(NAMESPACE).resource(blueSecret).create(); + mockClient.secrets().inNamespace(NAMESPACE).resource(anotherBlue).create(); + + NormalizedSource normalizedSource = new LabeledSecretNormalizedSource(NAMESPACE, + Collections.singletonMap("color", "blue"), true, ConfigUtils.Prefix.DELAYED); + Fabric8ConfigContext context = new Fabric8ConfigContext(mockClient, normalizedSource, NAMESPACE, + new MockEnvironment(), NAMESPACED_BATCH_READ); + + Fabric8ContextToSourceData data = new LabeledSecretContextToSourceDataProvider().get(); + SourceData sourceData = data.apply(context); + + Assertions.assertEquals(sourceData.sourceName(), "secret.another-blue-secret.blue-secret.default"); + + Map properties = sourceData.sourceData(); + Assertions.assertEquals(2, properties.size()); + Iterator keys = properties.keySet().iterator(); + String firstKey = keys.next(); + String secondKey = keys.next(); + + if (firstKey.contains("first")) { + Assertions.assertEquals(firstKey, "another-blue-secret.blue-secret.first"); + } + + Assertions.assertEquals(secondKey, "another-blue-secret.blue-secret.second"); + Assertions.assertEquals(properties.get(firstKey), "blue"); + Assertions.assertEquals(properties.get(secondKey), "blue"); + } + + /** + * two secrets are deployed: secret "color-secret" with label: "{color:blue}" and + * "color-secret-k8s" with no labels. We search by "{color:red}", do not find anything + * and thus have an empty SourceData. + */ + @Test + void searchWithLabelsNoSecretFound() { + Secret colorSecret = new SecretBuilder().withNewMetadata() + .withName("color-secret") + .withLabels(Collections.singletonMap("color", "blue")) + .endMetadata() + .addToData("one", Base64.getEncoder().encodeToString("1".getBytes())) + .build(); + + Secret colorSecretK8s = new SecretBuilder().withNewMetadata() + .withName("color-secret-k8s") + .endMetadata() + .addToData("two", Base64.getEncoder().encodeToString("2".getBytes())) + .build(); + + mockClient.secrets().inNamespace(NAMESPACE).resource(colorSecret).create(); + mockClient.secrets().inNamespace(NAMESPACE).resource(colorSecretK8s).create(); + MockEnvironment environment = new MockEnvironment(); + + NormalizedSource normalizedSource = new LabeledSecretNormalizedSource(NAMESPACE, + Collections.singletonMap("color", "red"), true, ConfigUtils.Prefix.DEFAULT); + Fabric8ConfigContext context = new Fabric8ConfigContext(mockClient, normalizedSource, NAMESPACE, environment, + NAMESPACED_BATCH_READ); + + Fabric8ContextToSourceData data = new LabeledSecretContextToSourceDataProvider().get(); + SourceData sourceData = data.apply(context); + + Assertions.assertTrue(sourceData.sourceData().isEmpty()); + Assertions.assertEquals(sourceData.sourceName(), "secret.color.default"); + + } + + /** + * two secrets are deployed: secret "color-secret" with label: "{color:blue}" and + * "shape-secret" with label: "{shape:round}". We search by "{color:blue}" and find + * one secret. + */ + @Test + void searchWithLabelsOneSecretFound() { + Secret colorSecret = new SecretBuilder().withNewMetadata() + .withName("color-secret") + .withLabels(Collections.singletonMap("color", "blue")) + .endMetadata() + .addToData("one", Base64.getEncoder().encodeToString("1".getBytes())) + .build(); + + Secret shapeSecret = new SecretBuilder().withNewMetadata() + .withName("shape-secret") + .endMetadata() + .addToData("two", Base64.getEncoder().encodeToString("2".getBytes())) + .build(); + + mockClient.secrets().inNamespace(NAMESPACE).resource(colorSecret).create(); + mockClient.secrets().inNamespace(NAMESPACE).resource(shapeSecret).create(); + MockEnvironment environment = new MockEnvironment(); + + NormalizedSource normalizedSource = new LabeledSecretNormalizedSource(NAMESPACE, + Collections.singletonMap("color", "blue"), true, ConfigUtils.Prefix.DEFAULT); + Fabric8ConfigContext context = new Fabric8ConfigContext(mockClient, normalizedSource, NAMESPACE, environment, + NAMESPACED_BATCH_READ); + + Fabric8ContextToSourceData data = new LabeledSecretContextToSourceDataProvider().get(); + SourceData sourceData = data.apply(context); + + Assertions.assertEquals(sourceData.sourceData().size(), 1); + Assertions.assertEquals(sourceData.sourceData().get("one"), "1"); + Assertions.assertEquals(sourceData.sourceName(), "secret.color-secret.default"); + + } + + /** + *
+	 *     - secret "color-secret" with label "{color:blue}"
+	 *     - secret "shape-secret" with labels "{color:blue, shape:round}"
+	 *     - secret "no-fit" with labels "{tag:no-fit}"
+	 *     - secret "color-secret-k8s" with label "{color:red}"
+	 *     - secret "shape-secret-k8s" with label "{shape:triangle}"
+	 * 
+ */ + @Test + void searchWithLabelsTwoSecretsFound() { + Secret colorSecret = new SecretBuilder().withNewMetadata() + .withName("color-secret") + .withLabels(Collections.singletonMap("color", "blue")) + .endMetadata() + .addToData("one", Base64.getEncoder().encodeToString("1".getBytes())) + .build(); + + Secret shapeSecret = new SecretBuilder().withNewMetadata() + .withName("shape-secret") + .withLabels(Map.of("color", "blue", "shape", "round")) + .endMetadata() + .addToData("two", Base64.getEncoder().encodeToString("2".getBytes())) + .build(); + + Secret noFit = new SecretBuilder().withNewMetadata() + .withName("no-fit") + .withLabels(Map.of("tag", "no-fit")) + .endMetadata() + .addToData("three", Base64.getEncoder().encodeToString("3".getBytes())) + .build(); + + Secret colorSecretK8s = new SecretBuilder().withNewMetadata() + .withName("color-secret-k8s") + .withLabels(Map.of("color", "red")) + .endMetadata() + .addToData("four", Base64.getEncoder().encodeToString("4".getBytes())) + .build(); + + Secret shapeSecretK8s = new SecretBuilder().withNewMetadata() + .withName("shape-secret-k8s") + .withLabels(Map.of("shape", "triangle")) + .endMetadata() + .addToData("five", Base64.getEncoder().encodeToString("5".getBytes())) + .build(); + + mockClient.secrets().inNamespace(NAMESPACE).resource(colorSecret).create(); + mockClient.secrets().inNamespace(NAMESPACE).resource(shapeSecret).create(); + mockClient.secrets().inNamespace(NAMESPACE).resource(noFit).create(); + mockClient.secrets().inNamespace(NAMESPACE).resource(colorSecretK8s).create(); + mockClient.secrets().inNamespace(NAMESPACE).resource(shapeSecretK8s).create(); + + MockEnvironment environment = new MockEnvironment(); + + NormalizedSource normalizedSource = new LabeledSecretNormalizedSource(NAMESPACE, + Collections.singletonMap("color", "blue"), true, ConfigUtils.Prefix.DELAYED); + Fabric8ConfigContext context = new Fabric8ConfigContext(mockClient, normalizedSource, NAMESPACE, environment, + NAMESPACED_BATCH_READ); + + Fabric8ContextToSourceData data = new LabeledSecretContextToSourceDataProvider().get(); + SourceData sourceData = data.apply(context); + + Assertions.assertEquals(sourceData.sourceData().size(), 2); + Assertions.assertEquals(sourceData.sourceData().get("color-secret.shape-secret.one"), "1"); + Assertions.assertEquals(sourceData.sourceData().get("color-secret.shape-secret.two"), "2"); + + Assertions.assertEquals(sourceData.sourceName(), "secret.color-secret.shape-secret.default"); + + } + + /** + * yaml/properties gets special treatment + */ + @Test + void testYaml() { + Secret colorSecret = new SecretBuilder().withNewMetadata() + .withName("color-secret") + .withLabels(Collections.singletonMap("color", "blue")) + .endMetadata() + .addToData("test.yaml", Base64.getEncoder().encodeToString("color: blue".getBytes())) + .build(); + + mockClient.secrets().inNamespace(NAMESPACE).resource(colorSecret).create(); + + NormalizedSource normalizedSource = new LabeledSecretNormalizedSource(NAMESPACE, + Collections.singletonMap("color", "blue"), true, ConfigUtils.Prefix.DEFAULT); + Fabric8ConfigContext context = new Fabric8ConfigContext(mockClient, normalizedSource, NAMESPACE, + new MockEnvironment(), NAMESPACED_BATCH_READ); + + Fabric8ContextToSourceData data = new LabeledSecretContextToSourceDataProvider().get(); + SourceData sourceData = data.apply(context); + + Assertions.assertEquals(sourceData.sourceData().size(), 1); + Assertions.assertEquals(sourceData.sourceData().get("color"), "blue"); + Assertions.assertEquals(sourceData.sourceName(), "secret.color-secret.default"); + } + + /** + *
+	 *     - secret "red" with label "{color:red}"
+	 *     - secret "green" with labels "{color:green}"
+	 *     - we first search for "red" and find it, and it is retrieved from the cluster via the client.
+	 * 	   - we then search for the "green" one, and it is retrieved from the cache this time.
+	 * 
+ */ + @Test + void cache(CapturedOutput output) { + Secret red = new SecretBuilder().withNewMetadata() + .withName("red") + .withLabels(Collections.singletonMap("color", "red")) + .endMetadata() + .addToData("one", Base64.getEncoder().encodeToString("1".getBytes())) + .build(); + + Secret green = new SecretBuilder().withNewMetadata() + .withName("green") + .withLabels(Map.of("color", "green")) + .endMetadata() + .addToData("two", Base64.getEncoder().encodeToString("2".getBytes())) + .build(); + + mockClient.secrets().inNamespace(NAMESPACE).resource(red).create(); + mockClient.secrets().inNamespace(NAMESPACE).resource(green).create(); + + MockEnvironment environment = new MockEnvironment(); + + NormalizedSource redNormalizedSource = new LabeledSecretNormalizedSource(NAMESPACE, + Collections.singletonMap("color", "red"), true, ConfigUtils.Prefix.DELAYED); + Fabric8ConfigContext redContext = new Fabric8ConfigContext(mockClient, redNormalizedSource, NAMESPACE, + environment, NAMESPACED_BATCH_READ); + Fabric8ContextToSourceData redData = new LabeledSecretContextToSourceDataProvider().get(); + SourceData redSourceData = redData.apply(redContext); + + Assertions.assertEquals(redSourceData.sourceData().size(), 1); + Assertions.assertEquals(redSourceData.sourceData().get("red.one"), "1"); + + Assertions.assertFalse(output.getAll().contains("Loaded all secrets in namespace '" + NAMESPACE + "'")); + Assertions.assertTrue(output.getAll().contains("Will read individual secrets in namespace")); + + NormalizedSource greenNormalizedSource = new LabeledSecretNormalizedSource(NAMESPACE, + Collections.singletonMap("color", "green"), true, ConfigUtils.Prefix.DELAYED); + Fabric8ConfigContext greenContext = new Fabric8ConfigContext(mockClient, greenNormalizedSource, NAMESPACE, + environment, NAMESPACED_BATCH_READ); + Fabric8ContextToSourceData greenData = new LabeledSecretContextToSourceDataProvider().get(); + SourceData greenSourceData = greenData.apply(greenContext); + + Assertions.assertEquals(greenSourceData.sourceData().size(), 1); + Assertions.assertEquals(greenSourceData.sourceData().get("green.two"), "2"); + + // meaning there is a single entry with such a log statement + String[] out = output.getAll().split("Loaded all secrets in namespace"); + Assertions.assertEquals(out.length, 1); + + // meaning that the second read was done from the cache + out = output.getAll().split("Will read individual secrets in namespace"); + Assertions.assertEquals(out.length, 3); + + } + +} diff --git a/spring-cloud-kubernetes-fabric8-config/src/test/java/org/springframework/cloud/kubernetes/fabric8/config/NamedConfigMapContextToSourceDataProviderTests.java b/spring-cloud-kubernetes-fabric8-config/src/test/java/org/springframework/cloud/kubernetes/fabric8/config/NamedConfigMapContextToSourceDataProviderNamespacedBatchReadTests.java similarity index 95% rename from spring-cloud-kubernetes-fabric8-config/src/test/java/org/springframework/cloud/kubernetes/fabric8/config/NamedConfigMapContextToSourceDataProviderTests.java rename to spring-cloud-kubernetes-fabric8-config/src/test/java/org/springframework/cloud/kubernetes/fabric8/config/NamedConfigMapContextToSourceDataProviderNamespacedBatchReadTests.java index c572204296..13c023cffe 100644 --- a/spring-cloud-kubernetes-fabric8-config/src/test/java/org/springframework/cloud/kubernetes/fabric8/config/NamedConfigMapContextToSourceDataProviderTests.java +++ b/spring-cloud-kubernetes-fabric8-config/src/test/java/org/springframework/cloud/kubernetes/fabric8/config/NamedConfigMapContextToSourceDataProviderNamespacedBatchReadTests.java @@ -45,7 +45,9 @@ */ @EnableKubernetesMockClient(crud = true, https = false) @ExtendWith(OutputCaptureExtension.class) -class NamedConfigMapContextToSourceDataProviderTests { +class NamedConfigMapContextToSourceDataProviderNamespacedBatchReadTests { + + private static final boolean NAMESPACED_BATCH_READ = true; private static final String NAMESPACE = "default"; @@ -93,7 +95,7 @@ void noMatch() { NormalizedSource normalizedSource = new NamedConfigMapNormalizedSource("blue", NAMESPACE, true, false); Fabric8ConfigContext context = new Fabric8ConfigContext(mockClient, normalizedSource, NAMESPACE, - new MockEnvironment(), true); + new MockEnvironment(), NAMESPACED_BATCH_READ); Fabric8ContextToSourceData data = new NamedConfigMapContextToSourceDataProvider().get(); SourceData sourceData = data.apply(context); @@ -122,7 +124,7 @@ void match() { NormalizedSource normalizedSource = new NamedConfigMapNormalizedSource("red", NAMESPACE, true, false); Fabric8ConfigContext context = new Fabric8ConfigContext(mockClient, normalizedSource, NAMESPACE, - new MockEnvironment(), true); + new MockEnvironment(), NAMESPACED_BATCH_READ); Fabric8ContextToSourceData data = new NamedConfigMapContextToSourceDataProvider().get(); SourceData sourceData = data.apply(context); @@ -163,7 +165,8 @@ void matchIncludeSingleProfile() { NormalizedSource normalizedSource = new NamedConfigMapNormalizedSource("red", NAMESPACE, true, ConfigUtils.Prefix.DEFAULT, true, true); - Fabric8ConfigContext context = new Fabric8ConfigContext(mockClient, normalizedSource, NAMESPACE, env, true); + Fabric8ConfigContext context = new Fabric8ConfigContext(mockClient, normalizedSource, NAMESPACE, env, + NAMESPACED_BATCH_READ); Fabric8ContextToSourceData data = new NamedConfigMapContextToSourceDataProvider().get(); SourceData sourceData = data.apply(context); @@ -208,7 +211,8 @@ void matchIncludeSingleProfileWithPrefix() { env.setActiveProfiles("with-profile"); NormalizedSource normalizedSource = new NamedConfigMapNormalizedSource("red", NAMESPACE, true, PREFIX, true); - Fabric8ConfigContext context = new Fabric8ConfigContext(mockClient, normalizedSource, NAMESPACE, env, true); + Fabric8ConfigContext context = new Fabric8ConfigContext(mockClient, normalizedSource, NAMESPACE, env, + NAMESPACED_BATCH_READ); Fabric8ContextToSourceData data = new NamedConfigMapContextToSourceDataProvider().get(); SourceData sourceData = data.apply(context); @@ -259,7 +263,8 @@ void matchIncludeTwoProfilesWithPrefix() { env.setActiveProfiles("with-taste", "with-shape"); NormalizedSource normalizedSource = new NamedConfigMapNormalizedSource("red", NAMESPACE, true, PREFIX, true); - Fabric8ConfigContext context = new Fabric8ConfigContext(mockClient, normalizedSource, NAMESPACE, env, true); + Fabric8ConfigContext context = new Fabric8ConfigContext(mockClient, normalizedSource, NAMESPACE, env, + NAMESPACED_BATCH_READ); Fabric8ContextToSourceData data = new NamedConfigMapContextToSourceDataProvider().get(); SourceData sourceData = data.apply(context); @@ -290,7 +295,7 @@ void matchWithName() { NormalizedSource normalizedSource = new NamedConfigMapNormalizedSource("application", NAMESPACE, true, false); Fabric8ConfigContext context = new Fabric8ConfigContext(mockClient, normalizedSource, NAMESPACE, - new MockEnvironment(), true); + new MockEnvironment(), NAMESPACED_BATCH_READ); Fabric8ContextToSourceData data = new NamedConfigMapContextToSourceDataProvider().get(); SourceData sourceData = data.apply(context); @@ -320,7 +325,7 @@ void namespaceMatch() { String wrongNamespace = NAMESPACE + "nope"; NormalizedSource normalizedSource = new NamedConfigMapNormalizedSource("red", wrongNamespace, true, false); Fabric8ConfigContext context = new Fabric8ConfigContext(mockClient, normalizedSource, NAMESPACE, - new MockEnvironment(), true); + new MockEnvironment(), NAMESPACED_BATCH_READ); Fabric8ContextToSourceData data = new NamedConfigMapContextToSourceDataProvider().get(); SourceData sourceData = data.apply(context); @@ -346,7 +351,7 @@ void testSingleYaml() { NormalizedSource normalizedSource = new NamedConfigMapNormalizedSource("red", NAMESPACE, true, false); Fabric8ConfigContext context = new Fabric8ConfigContext(mockClient, normalizedSource, NAMESPACE, - new MockEnvironment(), true); + new MockEnvironment(), NAMESPACED_BATCH_READ); Fabric8ContextToSourceData data = new NamedConfigMapContextToSourceDataProvider().get(); SourceData sourceData = data.apply(context); @@ -377,7 +382,7 @@ void testCorrectNameWithProfile() { NormalizedSource normalizedSource = new NamedConfigMapNormalizedSource("one", NAMESPACE, true, true); Fabric8ConfigContext context = new Fabric8ConfigContext(mockClient, normalizedSource, NAMESPACE, environment, - true); + NAMESPACED_BATCH_READ); Fabric8ContextToSourceData data = new NamedConfigMapContextToSourceDataProvider().get(); SourceData sourceData = data.apply(context); @@ -415,19 +420,21 @@ void cache(CapturedOutput output) { NormalizedSource redNormalizedSource = new NamedConfigMapNormalizedSource("red", NAMESPACE, true, PREFIX, false); Fabric8ConfigContext redContext = new Fabric8ConfigContext(mockClient, redNormalizedSource, NAMESPACE, env, - true); + NAMESPACED_BATCH_READ); Fabric8ContextToSourceData redData = new NamedConfigMapContextToSourceDataProvider().get(); SourceData redSourceData = redData.apply(redContext); Assertions.assertEquals(redSourceData.sourceName(), "configmap.red.default"); Assertions.assertEquals(redSourceData.sourceData().size(), 1); Assertions.assertEquals(redSourceData.sourceData().get("some.color"), "really-red"); + Assertions.assertTrue(output.getAll().contains("Loaded all config maps in namespace '" + NAMESPACE + "'")); + Assertions.assertFalse(output.getOut().contains("Will read individual configmaps in namespace")); NormalizedSource greenNormalizedSource = new NamedConfigMapNormalizedSource("green", NAMESPACE, true, PREFIX, false); Fabric8ConfigContext greenContext = new Fabric8ConfigContext(mockClient, greenNormalizedSource, NAMESPACE, env, - true); + NAMESPACED_BATCH_READ); Fabric8ContextToSourceData greenData = new NamedConfigMapContextToSourceDataProvider().get(); SourceData greenSourceData = greenData.apply(greenContext); diff --git a/spring-cloud-kubernetes-fabric8-config/src/test/java/org/springframework/cloud/kubernetes/fabric8/config/NamedConfigMapContextToSourceDataProviderNonNamespacedBatchReadTests.java b/spring-cloud-kubernetes-fabric8-config/src/test/java/org/springframework/cloud/kubernetes/fabric8/config/NamedConfigMapContextToSourceDataProviderNonNamespacedBatchReadTests.java new file mode 100644 index 0000000000..341893a1d8 --- /dev/null +++ b/spring-cloud-kubernetes-fabric8-config/src/test/java/org/springframework/cloud/kubernetes/fabric8/config/NamedConfigMapContextToSourceDataProviderNonNamespacedBatchReadTests.java @@ -0,0 +1,454 @@ +/* + * Copyright 2012-2024 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.fabric8.config; + +import java.util.Collections; +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.AfterEach; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; + +import org.springframework.boot.test.system.CapturedOutput; +import org.springframework.boot.test.system.OutputCaptureExtension; +import org.springframework.cloud.kubernetes.commons.config.ConfigUtils; +import org.springframework.cloud.kubernetes.commons.config.NamedConfigMapNormalizedSource; +import org.springframework.cloud.kubernetes.commons.config.NormalizedSource; +import org.springframework.cloud.kubernetes.commons.config.SourceData; +import org.springframework.mock.env.MockEnvironment; + +/** + * @author wind57 + */ +@EnableKubernetesMockClient(crud = true, https = false) +@ExtendWith(OutputCaptureExtension.class) +class NamedConfigMapContextToSourceDataProviderNonNamespacedBatchReadTests { + + private static final boolean NAMESPACED_BATCH_READ = false; + + private static final String NAMESPACE = "default"; + + private static KubernetesClient mockClient; + + private static final ConfigUtils.Prefix PREFIX = ConfigUtils.findPrefix("some", false, false, "irrelevant"); + + private static final Map COLOR_REALLY_RED = Map.of("color", "really-red"); + + @BeforeAll + static void beforeAll() { + + // Configure the kubernetes master url to point to the mock server + System.setProperty(Config.KUBERNETES_MASTER_SYSTEM_PROPERTY, mockClient.getConfiguration().getMasterUrl()); + System.setProperty(Config.KUBERNETES_TRUST_CERT_SYSTEM_PROPERTY, "true"); + System.setProperty(Config.KUBERNETES_AUTH_TRYKUBECONFIG_SYSTEM_PROPERTY, "false"); + System.setProperty(Config.KUBERNETES_AUTH_TRYSERVICEACCOUNT_SYSTEM_PROPERTY, "false"); + System.setProperty(Config.KUBERNETES_NAMESPACE_SYSTEM_PROPERTY, NAMESPACE); + System.setProperty(Config.KUBERNETES_HTTP2_DISABLE, "true"); + + } + + @AfterEach + void afterEach() { + mockClient.configMaps().inNamespace(NAMESPACE).delete(); + new Fabric8ConfigMapsNamespaceBatched().discardAll(); + } + + /** + *
+	 *     one configmap deployed with name "red"
+	 *     we search by name, but for the "blue" one, as such not find it
+	 * 
+ */ + @Test + void noMatch() { + + ConfigMap configMap = new ConfigMapBuilder().withNewMetadata() + .withName("red") + .endMetadata() + .addToData(COLOR_REALLY_RED) + .build(); + + mockClient.configMaps().inNamespace(NAMESPACE).resource(configMap).create(); + + NormalizedSource normalizedSource = new NamedConfigMapNormalizedSource("blue", NAMESPACE, true, false); + Fabric8ConfigContext context = new Fabric8ConfigContext(mockClient, normalizedSource, NAMESPACE, + new MockEnvironment(), NAMESPACED_BATCH_READ); + + Fabric8ContextToSourceData data = new NamedConfigMapContextToSourceDataProvider().get(); + SourceData sourceData = data.apply(context); + + Assertions.assertEquals(sourceData.sourceName(), "configmap.blue.default"); + Assertions.assertEquals(sourceData.sourceData(), Collections.emptyMap()); + + } + + /** + *
+	 *     one configmap deployed with name "red"
+	 *     we search by name, for the "red" one, as such we find it
+	 * 
+ */ + @Test + void match() { + + ConfigMap configMap = new ConfigMapBuilder().withNewMetadata() + .withName("red") + .endMetadata() + .addToData(COLOR_REALLY_RED) + .build(); + + mockClient.configMaps().inNamespace(NAMESPACE).resource(configMap).create(); + + NormalizedSource normalizedSource = new NamedConfigMapNormalizedSource("red", NAMESPACE, true, false); + Fabric8ConfigContext context = new Fabric8ConfigContext(mockClient, normalizedSource, NAMESPACE, + new MockEnvironment(), NAMESPACED_BATCH_READ); + + Fabric8ContextToSourceData data = new NamedConfigMapContextToSourceDataProvider().get(); + SourceData sourceData = data.apply(context); + + Assertions.assertEquals(sourceData.sourceName(), "configmap.red.default"); + Assertions.assertEquals(sourceData.sourceData(), COLOR_REALLY_RED); + + } + + /** + *
+	 *     - two configmaps deployed : "red" and "red-with-profile".
+	 *     - "red" is matched directly, "red-with-profile" is matched because we have an active profile
+	 *       "active-profile"
+	 * 
+ */ + @Test + void matchIncludeSingleProfile() { + + ConfigMap red = new ConfigMapBuilder().withNewMetadata() + .withName("red") + .endMetadata() + .addToData(COLOR_REALLY_RED) + .build(); + + ConfigMap redWithProfile = new ConfigMapBuilder().withNewMetadata() + .withName("red-with-profile") + .endMetadata() + .addToData("taste", "mango") + .build(); + + mockClient.configMaps().inNamespace(NAMESPACE).resource(red).create(); + mockClient.configMaps().inNamespace(NAMESPACE).resource(redWithProfile).create(); + + // add one more profile and specify that we want profile based config maps + MockEnvironment env = new MockEnvironment(); + env.setActiveProfiles("with-profile"); + NormalizedSource normalizedSource = new NamedConfigMapNormalizedSource("red", NAMESPACE, true, + ConfigUtils.Prefix.DEFAULT, true, true); + + Fabric8ConfigContext context = new Fabric8ConfigContext(mockClient, normalizedSource, NAMESPACE, env, + NAMESPACED_BATCH_READ); + + Fabric8ContextToSourceData data = new NamedConfigMapContextToSourceDataProvider().get(); + SourceData sourceData = data.apply(context); + + Assertions.assertEquals(sourceData.sourceName(), "configmap.red.red-with-profile.default.with-profile"); + Assertions.assertEquals(sourceData.sourceData().size(), 2); + Assertions.assertEquals(sourceData.sourceData().get("color"), "really-red"); + Assertions.assertEquals(sourceData.sourceData().get("taste"), "mango"); + + } + + /** + *
+	 *     - two configmaps deployed : "red" and "red-with-profile".
+	 *     - "red" is matched directly, "red-with-profile" is matched because we have an active profile
+	 *       "active-profile"
+	 *     -  This takes into consideration the prefix, that we explicitly specify.
+	 *        Notice that prefix works for profile based config maps as well.
+	 * 
+ */ + @Test + void matchIncludeSingleProfileWithPrefix() { + + ConfigMap red = new ConfigMapBuilder().withNewMetadata() + .withName("red") + .endMetadata() + .addToData(COLOR_REALLY_RED) + .build(); + + ConfigMap redWithProfile = new ConfigMapBuilder().withNewMetadata() + .withName("red-with-profile") + .endMetadata() + .addToData("taste", "mango") + .build(); + + mockClient.configMaps().inNamespace(NAMESPACE).resource(red).create(); + mockClient.configMaps().inNamespace(NAMESPACE).resource(redWithProfile).create(); + + // add one more profile and specify that we want profile based config maps + // also append prefix + MockEnvironment env = new MockEnvironment(); + env.setActiveProfiles("with-profile"); + NormalizedSource normalizedSource = new NamedConfigMapNormalizedSource("red", NAMESPACE, true, PREFIX, true); + + Fabric8ConfigContext context = new Fabric8ConfigContext(mockClient, normalizedSource, NAMESPACE, env, + NAMESPACED_BATCH_READ); + + Fabric8ContextToSourceData data = new NamedConfigMapContextToSourceDataProvider().get(); + SourceData sourceData = data.apply(context); + + Assertions.assertEquals(sourceData.sourceName(), "configmap.red.red-with-profile.default"); + Assertions.assertEquals(sourceData.sourceData().size(), 2); + Assertions.assertEquals(sourceData.sourceData().get("some.color"), "really-red"); + Assertions.assertEquals(sourceData.sourceData().get("some.taste"), "mango"); + + } + + /** + *
+	 *     - three configmaps deployed : "red", "red-with-taste" and "red-with-shape"
+	 *     - "red" is matched directly, the other two are matched because of active profiles
+	 *     -  This takes into consideration the prefix, that we explicitly specify.
+	 *        Notice that prefix works for profile based config maps as well.
+	 * 
+ */ + @Test + void matchIncludeTwoProfilesWithPrefix() { + + ConfigMap red = new ConfigMapBuilder().withNewMetadata() + .withName("red") + .endMetadata() + .addToData(COLOR_REALLY_RED) + .build(); + + ConfigMap redWithTaste = new ConfigMapBuilder().withNewMetadata() + .withName("red-with-taste") + .endMetadata() + .addToData("taste", "mango") + .build(); + + ConfigMap redWithShape = new ConfigMapBuilder().withNewMetadata() + .withName("red-with-shape") + .endMetadata() + .addToData("shape", "round") + .build(); + + mockClient.configMaps().inNamespace(NAMESPACE).resource(red).create(); + mockClient.configMaps().inNamespace(NAMESPACE).resource(redWithTaste).create(); + mockClient.configMaps().inNamespace(NAMESPACE).resource(redWithShape).create(); + + // add one more profile and specify that we want profile based config maps + // also append prefix + MockEnvironment env = new MockEnvironment(); + env.setActiveProfiles("with-taste", "with-shape"); + NormalizedSource normalizedSource = new NamedConfigMapNormalizedSource("red", NAMESPACE, true, PREFIX, true); + + Fabric8ConfigContext context = new Fabric8ConfigContext(mockClient, normalizedSource, NAMESPACE, env, + NAMESPACED_BATCH_READ); + + Fabric8ContextToSourceData data = new NamedConfigMapContextToSourceDataProvider().get(); + SourceData sourceData = data.apply(context); + + Assertions.assertEquals(sourceData.sourceName(), "configmap.red.red-with-shape.red-with-taste.default"); + Assertions.assertEquals(sourceData.sourceData().size(), 3); + Assertions.assertEquals(sourceData.sourceData().get("some.color"), "really-red"); + Assertions.assertEquals(sourceData.sourceData().get("some.taste"), "mango"); + Assertions.assertEquals(sourceData.sourceData().get("some.shape"), "round"); + + } + + /** + *
+	 * 		proves that an implicit configmap is going to be generated and read, even if
+	 * 	    we did not provide one
+	 * 
+ */ + @Test + void matchWithName() { + ConfigMap configMap = new ConfigMapBuilder().withNewMetadata() + .withName("application") + .endMetadata() + .addToData("color", "red") + .build(); + + mockClient.configMaps().inNamespace(NAMESPACE).resource(configMap).create(); + + NormalizedSource normalizedSource = new NamedConfigMapNormalizedSource("application", NAMESPACE, true, false); + Fabric8ConfigContext context = new Fabric8ConfigContext(mockClient, normalizedSource, NAMESPACE, + new MockEnvironment(), NAMESPACED_BATCH_READ); + + Fabric8ContextToSourceData data = new NamedConfigMapContextToSourceDataProvider().get(); + SourceData sourceData = data.apply(context); + + Assertions.assertEquals(sourceData.sourceName(), "configmap.application.default"); + Assertions.assertEquals(sourceData.sourceData(), Collections.singletonMap("color", "red")); + } + + /** + *
+	 *     - NamedSecretContextToSourceDataProvider gets as input a KubernetesClientConfigContext
+	 *     - This context has a namespace as well as a NormalizedSource, that has a namespace too.
+	 *     - This test makes sure that we use the proper one.
+	 * 
+ */ + @Test + void namespaceMatch() { + + ConfigMap configMap = new ConfigMapBuilder().withNewMetadata() + .withName("red") + .endMetadata() + .addToData(COLOR_REALLY_RED) + .build(); + + mockClient.configMaps().inNamespace(NAMESPACE).resource(configMap).create(); + + String wrongNamespace = NAMESPACE + "nope"; + NormalizedSource normalizedSource = new NamedConfigMapNormalizedSource("red", wrongNamespace, true, false); + Fabric8ConfigContext context = new Fabric8ConfigContext(mockClient, normalizedSource, NAMESPACE, + new MockEnvironment(), NAMESPACED_BATCH_READ); + + Fabric8ContextToSourceData data = new NamedConfigMapContextToSourceDataProvider().get(); + SourceData sourceData = data.apply(context); + + Assertions.assertEquals(sourceData.sourceName(), "configmap.red.default"); + Assertions.assertEquals(sourceData.sourceData(), Collections.singletonMap("color", "really-red")); + } + + /** + *
+	 *     - proves that single yaml file gets special treatment
+	 * 
+ */ + @Test + void testSingleYaml() { + ConfigMap configMap = new ConfigMapBuilder().withNewMetadata() + .withName("red") + .endMetadata() + .addToData("single.yaml", "key: value") + .build(); + + mockClient.configMaps().inNamespace(NAMESPACE).resource(configMap).create(); + + NormalizedSource normalizedSource = new NamedConfigMapNormalizedSource("red", NAMESPACE, true, false); + Fabric8ConfigContext context = new Fabric8ConfigContext(mockClient, normalizedSource, NAMESPACE, + new MockEnvironment(), NAMESPACED_BATCH_READ); + + Fabric8ContextToSourceData data = new NamedConfigMapContextToSourceDataProvider().get(); + SourceData sourceData = data.apply(context); + + Assertions.assertEquals(sourceData.sourceName(), "configmap.red.default"); + Assertions.assertEquals(sourceData.sourceData(), Collections.singletonMap("key", "value")); + } + + /** + *
+	 *     - one configmap is deployed with name "one"
+	 *     - profile is enabled with name "k8s"
+	 *
+	 *     we assert that the name of the source is "one" and does not contain "one-dev"
+	 * 
+ */ + @Test + void testCorrectNameWithProfile() { + ConfigMap configMap = new ConfigMapBuilder().withNewMetadata() + .withName("one") + .endMetadata() + .addToData("key", "value") + .build(); + + mockClient.configMaps().inNamespace(NAMESPACE).resource(configMap).create(); + MockEnvironment environment = new MockEnvironment(); + environment.setActiveProfiles("k8s"); + + NormalizedSource normalizedSource = new NamedConfigMapNormalizedSource("one", NAMESPACE, true, true); + Fabric8ConfigContext context = new Fabric8ConfigContext(mockClient, normalizedSource, NAMESPACE, environment, + NAMESPACED_BATCH_READ); + + Fabric8ContextToSourceData data = new NamedConfigMapContextToSourceDataProvider().get(); + SourceData sourceData = data.apply(context); + + Assertions.assertEquals(sourceData.sourceName(), "configmap.one.default"); + Assertions.assertEquals(sourceData.sourceData(), Collections.singletonMap("key", "value")); + } + + /** + *
+	 *     - two configmaps are deployed : "red", "green", in the same namespace.
+	 *     - we first search for "red" and find it, and it is retrieved from the cluster via the client.
+	 *     - we then search for the "green" one and it is not retrieved from the cache.
+	 * 
+ */ + @Test + void nonCache(CapturedOutput output) { + + ConfigMap red = new ConfigMapBuilder().withNewMetadata() + .withName("red") + .endMetadata() + .addToData(COLOR_REALLY_RED) + .build(); + + ConfigMap green = new ConfigMapBuilder().withNewMetadata() + .withName("green") + .endMetadata() + .addToData("taste", "mango") + .build(); + + mockClient.configMaps().inNamespace(NAMESPACE).resource(red).create(); + mockClient.configMaps().inNamespace(NAMESPACE).resource(green).create(); + + MockEnvironment env = new MockEnvironment(); + NormalizedSource redNormalizedSource = new NamedConfigMapNormalizedSource("red", NAMESPACE, true, PREFIX, + false); + Fabric8ConfigContext redContext = new Fabric8ConfigContext(mockClient, redNormalizedSource, NAMESPACE, env, + NAMESPACED_BATCH_READ); + Fabric8ContextToSourceData redData = new NamedConfigMapContextToSourceDataProvider().get(); + SourceData redSourceData = redData.apply(redContext); + + Assertions.assertEquals(redSourceData.sourceName(), "configmap.red.default"); + Assertions.assertEquals(redSourceData.sourceData().size(), 1); + Assertions.assertEquals(redSourceData.sourceData().get("some.color"), "really-red"); + + Assertions.assertFalse(output.getAll().contains("Loaded all config maps in namespace '" + NAMESPACE + "'")); + Assertions.assertTrue( + output.getOut().contains("Will read individual configmaps in namespace : default with names : [red]")); + + NormalizedSource greenNormalizedSource = new NamedConfigMapNormalizedSource("green", NAMESPACE, true, PREFIX, + false); + Fabric8ConfigContext greenContext = new Fabric8ConfigContext(mockClient, greenNormalizedSource, NAMESPACE, env, + NAMESPACED_BATCH_READ); + Fabric8ContextToSourceData greenData = new NamedConfigMapContextToSourceDataProvider().get(); + SourceData greenSourceData = greenData.apply(greenContext); + + Assertions.assertEquals(greenSourceData.sourceName(), "configmap.green.default"); + Assertions.assertEquals(greenSourceData.sourceData().size(), 1); + Assertions.assertEquals(greenSourceData.sourceData().get("some.taste"), "mango"); + + // meaning there is a no such entry with such a log statement + String[] out = output.getAll().split("Loaded all config maps in namespace"); + Assertions.assertEquals(out.length, 1); + + // meaning that the second read was not from the cache + out = output.getAll().split("Will read individual configmaps in namespace : default"); + Assertions.assertEquals(out.length, 3); + + } + +} diff --git a/spring-cloud-kubernetes-fabric8-config/src/test/java/org/springframework/cloud/kubernetes/fabric8/config/NamedSecretContextToSourceDataProviderTests.java b/spring-cloud-kubernetes-fabric8-config/src/test/java/org/springframework/cloud/kubernetes/fabric8/config/NamedSecretContextToSourceDataProviderNamespacedBatchReadTests.java similarity index 95% rename from spring-cloud-kubernetes-fabric8-config/src/test/java/org/springframework/cloud/kubernetes/fabric8/config/NamedSecretContextToSourceDataProviderTests.java rename to spring-cloud-kubernetes-fabric8-config/src/test/java/org/springframework/cloud/kubernetes/fabric8/config/NamedSecretContextToSourceDataProviderNamespacedBatchReadTests.java index c354344940..2ac91ee5ff 100644 --- a/spring-cloud-kubernetes-fabric8-config/src/test/java/org/springframework/cloud/kubernetes/fabric8/config/NamedSecretContextToSourceDataProviderTests.java +++ b/spring-cloud-kubernetes-fabric8-config/src/test/java/org/springframework/cloud/kubernetes/fabric8/config/NamedSecretContextToSourceDataProviderNamespacedBatchReadTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2022 the original author or authors. + * Copyright 2012-2024 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. @@ -46,7 +46,9 @@ */ @EnableKubernetesMockClient(crud = true, https = false) @ExtendWith(OutputCaptureExtension.class) -class NamedSecretContextToSourceDataProviderTests { +class NamedSecretContextToSourceDataProviderNamespacedBatchReadTests { + + private static final boolean NAMESPACED_BATCH_READ = true; private static final String NAMESPACE = "default"; @@ -89,7 +91,7 @@ void singleSecretMatchAgainstLabels() { NormalizedSource normalizedSource = new NamedSecretNormalizedSource("red", NAMESPACE, true, false); Fabric8ConfigContext context = new Fabric8ConfigContext(mockClient, normalizedSource, NAMESPACE, - new MockEnvironment(), true); + new MockEnvironment(), NAMESPACED_BATCH_READ); Fabric8ContextToSourceData data = new NamedSecretContextToSourceDataProvider().get(); SourceData sourceData = data.apply(context); @@ -130,7 +132,7 @@ void twoSecretMatchAgainstLabels() { NormalizedSource normalizedSource = new NamedSecretNormalizedSource("red", NAMESPACE, true, false); Fabric8ConfigContext context = new Fabric8ConfigContext(mockClient, normalizedSource, NAMESPACE, - new MockEnvironment(), true); + new MockEnvironment(), NAMESPACED_BATCH_READ); Fabric8ContextToSourceData data = new NamedSecretContextToSourceDataProvider().get(); SourceData sourceData = data.apply(context); @@ -157,7 +159,7 @@ void testSecretNoMatch() { NormalizedSource normalizedSource = new NamedSecretNormalizedSource("blue", NAMESPACE, true, false); Fabric8ConfigContext context = new Fabric8ConfigContext(mockClient, normalizedSource, NAMESPACE, - new MockEnvironment(), true); + new MockEnvironment(), NAMESPACED_BATCH_READ); Fabric8ContextToSourceData data = new NamedSecretContextToSourceDataProvider().get(); SourceData sourceData = data.apply(context); @@ -186,7 +188,7 @@ void namespaceMatch() { // different namespace NormalizedSource normalizedSource = new NamedSecretNormalizedSource("red", NAMESPACE + "nope", true, false); Fabric8ConfigContext context = new Fabric8ConfigContext(mockClient, normalizedSource, NAMESPACE, - new MockEnvironment(), true); + new MockEnvironment(), NAMESPACED_BATCH_READ); Fabric8ContextToSourceData data = new NamedSecretContextToSourceDataProvider().get(); SourceData sourceData = data.apply(context); @@ -222,7 +224,8 @@ void matchIncludeSingleProfile() { env.setActiveProfiles("with-profile"); NormalizedSource normalizedSource = new NamedSecretNormalizedSource("red", NAMESPACE, true, ConfigUtils.Prefix.DEFAULT, true, true); - Fabric8ConfigContext context = new Fabric8ConfigContext(mockClient, normalizedSource, NAMESPACE, env, true); + Fabric8ConfigContext context = new Fabric8ConfigContext(mockClient, normalizedSource, NAMESPACE, env, + NAMESPACED_BATCH_READ); Fabric8ContextToSourceData data = new NamedSecretContextToSourceDataProvider().get(); SourceData sourceData = data.apply(context); @@ -264,7 +267,8 @@ void matchIncludeSingleProfileWithPrefix() { env.setActiveProfiles("with-profile"); NormalizedSource normalizedSource = new NamedSecretNormalizedSource("red", NAMESPACE, true, PREFIX, true); - Fabric8ConfigContext context = new Fabric8ConfigContext(mockClient, normalizedSource, NAMESPACE, env, true); + Fabric8ConfigContext context = new Fabric8ConfigContext(mockClient, normalizedSource, NAMESPACE, env, + NAMESPACED_BATCH_READ); Fabric8ContextToSourceData data = new NamedSecretContextToSourceDataProvider().get(); SourceData sourceData = data.apply(context); @@ -313,7 +317,8 @@ void matchIncludeTwoProfilesWithPrefix() { env.setActiveProfiles("with-taste", "with-shape"); NormalizedSource normalizedSource = new NamedSecretNormalizedSource("red", NAMESPACE, true, PREFIX, true); - Fabric8ConfigContext context = new Fabric8ConfigContext(mockClient, normalizedSource, NAMESPACE, env, true); + Fabric8ConfigContext context = new Fabric8ConfigContext(mockClient, normalizedSource, NAMESPACE, env, + NAMESPACED_BATCH_READ); Fabric8ContextToSourceData data = new NamedSecretContextToSourceDataProvider().get(); SourceData sourceData = data.apply(context); @@ -345,7 +350,7 @@ void testSingleYaml() { // different namespace NormalizedSource normalizedSource = new NamedSecretNormalizedSource("single-yaml", NAMESPACE, true, false); Fabric8ConfigContext context = new Fabric8ConfigContext(mockClient, normalizedSource, NAMESPACE, - new MockEnvironment(), true); + new MockEnvironment(), NAMESPACED_BATCH_READ); Fabric8ContextToSourceData data = new NamedSecretContextToSourceDataProvider().get(); SourceData sourceData = data.apply(context); @@ -382,19 +387,21 @@ void cache(CapturedOutput output) { MockEnvironment env = new MockEnvironment(); NormalizedSource redNormalizedSource = new NamedSecretNormalizedSource("red", NAMESPACE, true, PREFIX, false); Fabric8ConfigContext redContext = new Fabric8ConfigContext(mockClient, redNormalizedSource, NAMESPACE, env, - true); + NAMESPACED_BATCH_READ); Fabric8ContextToSourceData redData = new NamedSecretContextToSourceDataProvider().get(); SourceData redSourceData = redData.apply(redContext); Assertions.assertEquals(redSourceData.sourceName(), "secret.red.default"); Assertions.assertEquals(redSourceData.sourceData().size(), 1); Assertions.assertEquals(redSourceData.sourceData().get("some.color"), "red"); + Assertions.assertTrue(output.getAll().contains("Loaded all secrets in namespace '" + NAMESPACE + "'")); + Assertions.assertFalse(output.getOut().contains("Will read individual secrets in namespace")); NormalizedSource greenNormalizedSource = new NamedSecretNormalizedSource("green", NAMESPACE, true, PREFIX, false); Fabric8ConfigContext greenContext = new Fabric8ConfigContext(mockClient, greenNormalizedSource, NAMESPACE, env, - true); + NAMESPACED_BATCH_READ); Fabric8ContextToSourceData greenData = new NamedSecretContextToSourceDataProvider().get(); SourceData greenSourceData = greenData.apply(greenContext); diff --git a/spring-cloud-kubernetes-fabric8-config/src/test/java/org/springframework/cloud/kubernetes/fabric8/config/NamedSecretContextToSourceDataProviderNonNamespacedBatchReadTests.java b/spring-cloud-kubernetes-fabric8-config/src/test/java/org/springframework/cloud/kubernetes/fabric8/config/NamedSecretContextToSourceDataProviderNonNamespacedBatchReadTests.java new file mode 100644 index 0000000000..4e8b91bc63 --- /dev/null +++ b/spring-cloud-kubernetes-fabric8-config/src/test/java/org/springframework/cloud/kubernetes/fabric8/config/NamedSecretContextToSourceDataProviderNonNamespacedBatchReadTests.java @@ -0,0 +1,420 @@ +/* + * Copyright 2012-2024 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.fabric8.config; + +import java.util.Base64; +import java.util.Collections; +import java.util.Map; + +import io.fabric8.kubernetes.api.model.Secret; +import io.fabric8.kubernetes.api.model.SecretBuilder; +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.AfterEach; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; + +import org.springframework.boot.test.system.CapturedOutput; +import org.springframework.boot.test.system.OutputCaptureExtension; +import org.springframework.cloud.kubernetes.commons.config.ConfigUtils; +import org.springframework.cloud.kubernetes.commons.config.NamedSecretNormalizedSource; +import org.springframework.cloud.kubernetes.commons.config.NormalizedSource; +import org.springframework.cloud.kubernetes.commons.config.SourceData; +import org.springframework.mock.env.MockEnvironment; + +/** + * @author wind57 + */ +@EnableKubernetesMockClient(crud = true, https = false) +@ExtendWith(OutputCaptureExtension.class) +class NamedSecretContextToSourceDataProviderNonNamespacedBatchReadTests { + + private static final boolean NAMESPACED_BATCH_READ = false; + + private static final String NAMESPACE = "default"; + + private static KubernetesClient mockClient; + + private static final ConfigUtils.Prefix PREFIX = ConfigUtils.findPrefix("some", false, false, "irrelevant"); + + @BeforeAll + static void beforeAll() { + + // Configure the kubernetes master url to point to the mock server + System.setProperty(Config.KUBERNETES_MASTER_SYSTEM_PROPERTY, mockClient.getConfiguration().getMasterUrl()); + System.setProperty(Config.KUBERNETES_TRUST_CERT_SYSTEM_PROPERTY, "true"); + System.setProperty(Config.KUBERNETES_AUTH_TRYKUBECONFIG_SYSTEM_PROPERTY, "false"); + System.setProperty(Config.KUBERNETES_AUTH_TRYSERVICEACCOUNT_SYSTEM_PROPERTY, "false"); + System.setProperty(Config.KUBERNETES_NAMESPACE_SYSTEM_PROPERTY, NAMESPACE); + System.setProperty(Config.KUBERNETES_HTTP2_DISABLE, "true"); + + } + + @AfterEach + void afterEach() { + mockClient.secrets().inNamespace(NAMESPACE).delete(); + new Fabric8SecretsNamespaceBatched().discardAll(); + } + + /** + * we have a single secret deployed. it matched the name in our queries + */ + @Test + void singleSecretMatchAgainstLabels() { + + Secret secret = new SecretBuilder().withNewMetadata() + .withName("red") + .endMetadata() + .addToData("color", Base64.getEncoder().encodeToString("really-red".getBytes())) + .build(); + + mockClient.secrets().inNamespace(NAMESPACE).resource(secret).create(); + + NormalizedSource normalizedSource = new NamedSecretNormalizedSource("red", NAMESPACE, true, false); + Fabric8ConfigContext context = new Fabric8ConfigContext(mockClient, normalizedSource, NAMESPACE, + new MockEnvironment(), NAMESPACED_BATCH_READ); + + Fabric8ContextToSourceData data = new NamedSecretContextToSourceDataProvider().get(); + SourceData sourceData = data.apply(context); + + Assertions.assertEquals(sourceData.sourceName(), "secret.red.default"); + Assertions.assertEquals(sourceData.sourceData(), Map.of("color", "really-red")); + + } + + /** + * we have three secret deployed. one of them has a name that matches (red), the other + * two have different names, thus no match. + */ + @Test + void twoSecretMatchAgainstLabels() { + + Secret red = new SecretBuilder().withNewMetadata() + .withName("red") + .endMetadata() + .addToData("color", Base64.getEncoder().encodeToString("really-red".getBytes())) + .build(); + + Secret blue = new SecretBuilder().withNewMetadata() + .withName("blue") + .endMetadata() + .addToData("color", Base64.getEncoder().encodeToString("really-blue".getBytes())) + .build(); + + Secret yellow = new SecretBuilder().withNewMetadata() + .withName("yellow") + .endMetadata() + .addToData("color", Base64.getEncoder().encodeToString("really-yellow".getBytes())) + .build(); + + mockClient.secrets().inNamespace(NAMESPACE).resource(red).create(); + mockClient.secrets().inNamespace(NAMESPACE).resource(blue).create(); + mockClient.secrets().inNamespace(NAMESPACE).resource(yellow).create(); + + NormalizedSource normalizedSource = new NamedSecretNormalizedSource("red", NAMESPACE, true, false); + Fabric8ConfigContext context = new Fabric8ConfigContext(mockClient, normalizedSource, NAMESPACE, + new MockEnvironment(), NAMESPACED_BATCH_READ); + + Fabric8ContextToSourceData data = new NamedSecretContextToSourceDataProvider().get(); + SourceData sourceData = data.apply(context); + + Assertions.assertEquals(sourceData.sourceName(), "secret.red.default"); + Assertions.assertEquals(sourceData.sourceData().size(), 1); + Assertions.assertEquals(sourceData.sourceData().get("color"), "really-red"); + + } + + /** + * one secret deployed (pink), does not match our query (blue). + */ + @Test + void testSecretNoMatch() { + + Secret pink = new SecretBuilder().withNewMetadata() + .withName("pink") + .endMetadata() + .addToData("color", Base64.getEncoder().encodeToString("pink".getBytes())) + .build(); + + mockClient.secrets().inNamespace(NAMESPACE).resource(pink).create(); + + NormalizedSource normalizedSource = new NamedSecretNormalizedSource("blue", NAMESPACE, true, false); + Fabric8ConfigContext context = new Fabric8ConfigContext(mockClient, normalizedSource, NAMESPACE, + new MockEnvironment(), NAMESPACED_BATCH_READ); + + Fabric8ContextToSourceData data = new NamedSecretContextToSourceDataProvider().get(); + SourceData sourceData = data.apply(context); + + Assertions.assertEquals(sourceData.sourceName(), "secret.blue.default"); + Assertions.assertEquals(sourceData.sourceData(), Collections.emptyMap()); + } + + /** + * NamedSecretContextToSourceDataProvider gets as input a Fabric8ConfigContext. This + * context has a namespace as well as a NormalizedSource, that has a namespace too. It + * is easy to get confused in code on which namespace to use. This test makes sure + * that we use the proper one. + */ + @Test + void namespaceMatch() { + + Secret secret = new SecretBuilder().withNewMetadata() + .withName("red") + .endMetadata() + .addToData("color", Base64.getEncoder().encodeToString("really-red".getBytes())) + .build(); + + mockClient.secrets().inNamespace(NAMESPACE).resource(secret).create(); + + // different namespace + NormalizedSource normalizedSource = new NamedSecretNormalizedSource("red", NAMESPACE + "nope", true, false); + Fabric8ConfigContext context = new Fabric8ConfigContext(mockClient, normalizedSource, NAMESPACE, + new MockEnvironment(), NAMESPACED_BATCH_READ); + + Fabric8ContextToSourceData data = new NamedSecretContextToSourceDataProvider().get(); + SourceData sourceData = data.apply(context); + + Assertions.assertEquals(sourceData.sourceName(), "secret.red.default"); + Assertions.assertEquals(sourceData.sourceData(), Map.of("color", "really-red")); + } + + /** + * we have two secrets deployed. one matches the query name. the other matches the + * active profile + name, thus is taken also. + */ + @Test + void matchIncludeSingleProfile() { + + Secret red = new SecretBuilder().withNewMetadata() + .withName("red") + .endMetadata() + .addToData("color", Base64.getEncoder().encodeToString("really-red".getBytes())) + .build(); + + Secret redWithProfile = new SecretBuilder().withNewMetadata() + .withName("red-with-profile") + .endMetadata() + .addToData("taste", Base64.getEncoder().encodeToString("mango".getBytes())) + .build(); + + mockClient.secrets().inNamespace(NAMESPACE).resource(red).create(); + mockClient.secrets().inNamespace(NAMESPACE).resource(redWithProfile).create(); + + // add one more profile and specify that we want profile based config maps + MockEnvironment env = new MockEnvironment(); + env.setActiveProfiles("with-profile"); + NormalizedSource normalizedSource = new NamedSecretNormalizedSource("red", NAMESPACE, true, + ConfigUtils.Prefix.DEFAULT, true, true); + Fabric8ConfigContext context = new Fabric8ConfigContext(mockClient, normalizedSource, NAMESPACE, env, + NAMESPACED_BATCH_READ); + + Fabric8ContextToSourceData data = new NamedSecretContextToSourceDataProvider().get(); + SourceData sourceData = data.apply(context); + + Assertions.assertEquals(sourceData.sourceName(), "secret.red.red-with-profile.default.with-profile"); + Assertions.assertEquals(sourceData.sourceData().size(), 2); + Assertions.assertEquals(sourceData.sourceData().get("color"), "really-red"); + Assertions.assertEquals(sourceData.sourceData().get("taste"), "mango"); + + } + + /** + * we have two secrets deployed. one matches the query name. the other matches the + * active profile + name, thus is taken also. This takes into consideration the + * prefix, that we explicitly specify. Notice that prefix works for profile based + * secrets as well. + */ + @Test + void matchIncludeSingleProfileWithPrefix() { + + Secret red = new SecretBuilder().withNewMetadata() + .withName("red") + .endMetadata() + .addToData("color", Base64.getEncoder().encodeToString("really-red".getBytes())) + .build(); + + Secret redWithProfile = new SecretBuilder().withNewMetadata() + .withName("red-with-profile") + .endMetadata() + .addToData("taste", Base64.getEncoder().encodeToString("mango".getBytes())) + .build(); + + mockClient.secrets().inNamespace(NAMESPACE).resource(red).create(); + mockClient.secrets().inNamespace(NAMESPACE).resource(redWithProfile).create(); + + // add one more profile and specify that we want profile based config maps + // also append prefix + MockEnvironment env = new MockEnvironment(); + env.setActiveProfiles("with-profile"); + + NormalizedSource normalizedSource = new NamedSecretNormalizedSource("red", NAMESPACE, true, PREFIX, true); + Fabric8ConfigContext context = new Fabric8ConfigContext(mockClient, normalizedSource, NAMESPACE, env, + NAMESPACED_BATCH_READ); + + Fabric8ContextToSourceData data = new NamedSecretContextToSourceDataProvider().get(); + SourceData sourceData = data.apply(context); + + Assertions.assertEquals(sourceData.sourceName(), "secret.red.red-with-profile.default"); + Assertions.assertEquals(sourceData.sourceData().size(), 2); + Assertions.assertEquals(sourceData.sourceData().get("some.color"), "really-red"); + Assertions.assertEquals(sourceData.sourceData().get("some.taste"), "mango"); + + } + + /** + * we have three secrets deployed. one matches the query name. the other two match the + * active profile + name, thus are taken also. This takes into consideration the + * prefix, that we explicitly specify. Notice that prefix works for profile based + * config maps as well. + */ + @Test + void matchIncludeTwoProfilesWithPrefix() { + + Secret red = new SecretBuilder().withNewMetadata() + .withName("red") + .endMetadata() + .addToData("color", Base64.getEncoder().encodeToString("really-red".getBytes())) + .build(); + + Secret redWithTaste = new SecretBuilder().withNewMetadata() + .withName("red-with-taste") + .endMetadata() + .addToData("taste", Base64.getEncoder().encodeToString("mango".getBytes())) + .build(); + + Secret redWithShape = new SecretBuilder().withNewMetadata() + .withName("red-with-shape") + .endMetadata() + .addToData("shape", Base64.getEncoder().encodeToString("round".getBytes())) + .build(); + + mockClient.secrets().inNamespace(NAMESPACE).resource(red).create(); + mockClient.secrets().inNamespace(NAMESPACE).resource(redWithTaste).create(); + mockClient.secrets().inNamespace(NAMESPACE).resource(redWithShape).create(); + + // add one more profile and specify that we want profile based config maps + // also append prefix + MockEnvironment env = new MockEnvironment(); + env.setActiveProfiles("with-taste", "with-shape"); + NormalizedSource normalizedSource = new NamedSecretNormalizedSource("red", NAMESPACE, true, PREFIX, true); + + Fabric8ConfigContext context = new Fabric8ConfigContext(mockClient, normalizedSource, NAMESPACE, env, + NAMESPACED_BATCH_READ); + + Fabric8ContextToSourceData data = new NamedSecretContextToSourceDataProvider().get(); + SourceData sourceData = data.apply(context); + + Assertions.assertEquals(sourceData.sourceName(), "secret.red.red-with-shape.red-with-taste.default"); + + Assertions.assertEquals(sourceData.sourceData().size(), 3); + Assertions.assertEquals(sourceData.sourceData().get("some.color"), "really-red"); + Assertions.assertEquals(sourceData.sourceData().get("some.taste"), "mango"); + Assertions.assertEquals(sourceData.sourceData().get("some.shape"), "round"); + + } + + /** + *
+	 *     - proves that single yaml file gets special treatment
+	 * 
+ */ + @Test + void testSingleYaml() { + Secret secret = new SecretBuilder().withNewMetadata() + .withName("single-yaml") + .endMetadata() + .addToData("single.yaml", Base64.getEncoder().encodeToString("key: value".getBytes())) + .build(); + + mockClient.secrets().inNamespace(NAMESPACE).resource(secret).create(); + + // different namespace + NormalizedSource normalizedSource = new NamedSecretNormalizedSource("single-yaml", NAMESPACE, true, false); + Fabric8ConfigContext context = new Fabric8ConfigContext(mockClient, normalizedSource, NAMESPACE, + new MockEnvironment(), NAMESPACED_BATCH_READ); + + Fabric8ContextToSourceData data = new NamedSecretContextToSourceDataProvider().get(); + SourceData sourceData = data.apply(context); + + Assertions.assertEquals(sourceData.sourceName(), "secret.single-yaml.default"); + Assertions.assertEquals(sourceData.sourceData(), Collections.singletonMap("key", "value")); + } + + /** + *
+	 *     - two secrets are deployed : "red", "green", in the same namespace.
+	 *     - we first search for "red" and find it, and it is retrieved from the cluster via the client.
+	 *     - we then search for the "green" one, and it is not retrieved from the cache.
+	 * 
+ */ + @Test + void nonCache(CapturedOutput output) { + + Secret red = new SecretBuilder().withNewMetadata() + .withName("red") + .endMetadata() + .addToData("color", Base64.getEncoder().encodeToString("red".getBytes())) + .build(); + + Secret green = new SecretBuilder().withNewMetadata() + .withName("green") + .endMetadata() + .addToData("taste", Base64.getEncoder().encodeToString("mango".getBytes())) + .build(); + + mockClient.secrets().inNamespace(NAMESPACE).resource(red).create(); + mockClient.secrets().inNamespace(NAMESPACE).resource(green).create(); + + MockEnvironment env = new MockEnvironment(); + NormalizedSource redNormalizedSource = new NamedSecretNormalizedSource("red", NAMESPACE, true, PREFIX, false); + Fabric8ConfigContext redContext = new Fabric8ConfigContext(mockClient, redNormalizedSource, NAMESPACE, env, + NAMESPACED_BATCH_READ); + Fabric8ContextToSourceData redData = new NamedSecretContextToSourceDataProvider().get(); + SourceData redSourceData = redData.apply(redContext); + + Assertions.assertEquals(redSourceData.sourceName(), "secret.red.default"); + Assertions.assertEquals(redSourceData.sourceData().size(), 1); + Assertions.assertEquals(redSourceData.sourceData().get("some.color"), "red"); + + Assertions.assertFalse(output.getAll().contains("Loaded all secrets in namespace '" + NAMESPACE + "'")); + Assertions.assertTrue(output.getOut().contains("Will read individual secrets in namespace")); + + NormalizedSource greenNormalizedSource = new NamedSecretNormalizedSource("green", NAMESPACE, true, PREFIX, + false); + Fabric8ConfigContext greenContext = new Fabric8ConfigContext(mockClient, greenNormalizedSource, NAMESPACE, env, + NAMESPACED_BATCH_READ); + Fabric8ContextToSourceData greenData = new NamedSecretContextToSourceDataProvider().get(); + SourceData greenSourceData = greenData.apply(greenContext); + + Assertions.assertEquals(greenSourceData.sourceName(), "secret.green.default"); + Assertions.assertEquals(greenSourceData.sourceData().size(), 1); + Assertions.assertEquals(greenSourceData.sourceData().get("some.taste"), "mango"); + + // meaning there is a single entry with such a log statement + String[] out = output.getAll().split("Loaded all secrets in namespace"); + Assertions.assertEquals(out.length, 1); + + // meaning that the second read was done from the cache + out = output.getAll().split("Will read individual secrets in namespace"); + Assertions.assertEquals(out.length, 3); + + } + +} From 6b230b6edfdeca102da7a823b3eca458a4e64490 Mon Sep 17 00:00:00 2001 From: wind57 Date: Sun, 22 Sep 2024 17:43:45 +0300 Subject: [PATCH 12/21] dirty --- .../config/KubernetesClientConfigContext.java | 9 +- ...netesClientConfigDataLocationResolver.java | 5 +- ...sClientConfigMapPropertySourceLocator.java | 7 +- .../config/KubernetesClientConfigUtils.java | 125 +++++++------ .../config/KubernetesClientSecretsCache.java | 91 --------- ...tesClientSecretsPropertySourceLocator.java | 7 +- ...ernetesClientSourcesNamespaceBatched.java} | 58 ++++-- ...netesClientSourcesNonNamespaceBatched.java | 167 +++++++++++++++++ .../KubernetesClientSourcesStripper.java | 53 ++++++ ...dConfigMapContextToSourceDataProvider.java | 2 +- ...eledSecretContextToSourceDataProvider.java | 2 +- ...dConfigMapContextToSourceDataProvider.java | 7 +- ...amedSecretContextToSourceDataProvider.java | 8 +- ...ntConfigMapPropertySourceLocatorTests.java | 20 +- ...tesClientConfigMapPropertySourceTests.java | 16 +- ...ientSecretsPropertySourceLocatorTests.java | 16 +- ...netesClientSecretsPropertySourceTests.java | 14 +- ...igMapContextToSourceDataProviderTests.java | 30 +-- ...ecretContextToSourceDataProviderTests.java | 30 +-- ...igMapContextToSourceDataProviderTests.java | 31 +-- ...ecretContextToSourceDataProviderTests.java | 26 +-- .../commons/config/ConfigMapCache.java | 4 +- .../ConfigMapPropertySourceLocator.java | 2 +- .../commons/config/ConfigUtils.java | 3 + .../commons/config/SecretsCache.java | 4 +- .../config/SecretsPropertySourceLocator.java | 2 +- ...bernetesConfigServerAutoConfiguration.java | 4 +- .../Fabric8ConfigDataLocationResolver.java | 4 +- ...Fabric8ConfigMapPropertySourceLocator.java | 2 +- .../fabric8/config/Fabric8ConfigUtils.java | 177 ++++-------------- .../Fabric8SecretsNamespaceBatched.java | 69 ------- .../Fabric8SecretsPropertySourceLocator.java | 2 +- ...va => Fabric8SourcesNamespaceBatched.java} | 44 ++++- ...=> Fabric8SourcesNonNamespaceBatched.java} | 53 ++++-- .../config/Fabric8SourcesStripper.java | 45 +++++ .../fabric8/config/ConfigMapsTest.java | 2 +- .../Fabric8ConfigMapPropertySourceTests.java | 2 +- ...c8ConfigUtilsNamespacedBatchReadTests.java | 4 +- ...onfigUtilsNonNamespacedBatchReadTests.java | 4 +- ...eDataProviderNamespacedBatchReadTests.java | 2 +- ...taProviderNonNamespacedBatchReadTests.java | 2 +- ...eDataProviderNamespacedBatchReadTests.java | 2 +- ...taProviderNonNamespacedBatchReadTests.java | 2 +- ...eDataProviderNamespacedBatchReadTests.java | 2 +- ...taProviderNonNamespacedBatchReadTests.java | 2 +- ...eDataProviderNamespacedBatchReadTests.java | 2 +- ...taProviderNonNamespacedBatchReadTests.java | 2 +- 47 files changed, 644 insertions(+), 523 deletions(-) delete mode 100644 spring-cloud-kubernetes-client-config/src/main/java/org/springframework/cloud/kubernetes/client/config/KubernetesClientSecretsCache.java rename spring-cloud-kubernetes-client-config/src/main/java/org/springframework/cloud/kubernetes/client/config/{KubernetesClientConfigMapsCache.java => KubernetesClientSourcesNamespaceBatched.java} (51%) create mode 100644 spring-cloud-kubernetes-client-config/src/main/java/org/springframework/cloud/kubernetes/client/config/KubernetesClientSourcesNonNamespaceBatched.java create mode 100644 spring-cloud-kubernetes-client-config/src/main/java/org/springframework/cloud/kubernetes/client/config/KubernetesClientSourcesStripper.java delete mode 100644 spring-cloud-kubernetes-fabric8-config/src/main/java/org/springframework/cloud/kubernetes/fabric8/config/Fabric8SecretsNamespaceBatched.java rename spring-cloud-kubernetes-fabric8-config/src/main/java/org/springframework/cloud/kubernetes/fabric8/config/{Fabric8ConfigMapsNamespaceBatched.java => Fabric8SourcesNamespaceBatched.java} (55%) rename spring-cloud-kubernetes-fabric8-config/src/main/java/org/springframework/cloud/kubernetes/fabric8/config/{Fabric8SourcesNonBatched.java => Fabric8SourcesNonNamespaceBatched.java} (59%) create mode 100644 spring-cloud-kubernetes-fabric8-config/src/main/java/org/springframework/cloud/kubernetes/fabric8/config/Fabric8SourcesStripper.java diff --git a/spring-cloud-kubernetes-client-config/src/main/java/org/springframework/cloud/kubernetes/client/config/KubernetesClientConfigContext.java b/spring-cloud-kubernetes-client-config/src/main/java/org/springframework/cloud/kubernetes/client/config/KubernetesClientConfigContext.java index 0af0002412..74ca27d417 100644 --- a/spring-cloud-kubernetes-client-config/src/main/java/org/springframework/cloud/kubernetes/client/config/KubernetesClientConfigContext.java +++ b/spring-cloud-kubernetes-client-config/src/main/java/org/springframework/cloud/kubernetes/client/config/KubernetesClientConfigContext.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2022 the original author or authors. + * Copyright 2013-2024 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. @@ -27,10 +27,5 @@ * @author wind57 */ public record KubernetesClientConfigContext(CoreV1Api client, NormalizedSource normalizedSource, String namespace, - Environment environment, boolean includeDefaultProfileData) { - - public KubernetesClientConfigContext(CoreV1Api client, NormalizedSource normalizedSource, String namespace, - Environment environment) { - this(client, normalizedSource, namespace, environment, true); - } + Environment environment, boolean includeDefaultProfileData, boolean namespacedBatchRead) { } diff --git a/spring-cloud-kubernetes-client-config/src/main/java/org/springframework/cloud/kubernetes/client/config/KubernetesClientConfigDataLocationResolver.java b/spring-cloud-kubernetes-client-config/src/main/java/org/springframework/cloud/kubernetes/client/config/KubernetesClientConfigDataLocationResolver.java index 19d7f14ff9..3dbf5f70eb 100644 --- a/spring-cloud-kubernetes-client-config/src/main/java/org/springframework/cloud/kubernetes/client/config/KubernetesClientConfigDataLocationResolver.java +++ b/spring-cloud-kubernetes-client-config/src/main/java/org/springframework/cloud/kubernetes/client/config/KubernetesClientConfigDataLocationResolver.java @@ -64,7 +64,8 @@ protected void registerBeans(ConfigDataLocationResolverContext resolverContext, coreV1Api, configMapProperties, namespaceProvider); if (isRetryEnabledForConfigMap(configMapProperties)) { configMapPropertySourceLocator = new ConfigDataRetryableConfigMapPropertySourceLocator( - configMapPropertySourceLocator, configMapProperties, new KubernetesClientConfigMapsCache()); + configMapPropertySourceLocator, configMapProperties, + new KubernetesClientSourcesNamespaceBatched()); } registerSingle(bootstrapContext, ConfigMapPropertySourceLocator.class, configMapPropertySourceLocator, @@ -76,7 +77,7 @@ protected void registerBeans(ConfigDataLocationResolverContext resolverContext, coreV1Api, namespaceProvider, secretsProperties); if (isRetryEnabledForSecrets(secretsProperties)) { secretsPropertySourceLocator = new ConfigDataRetryableSecretsPropertySourceLocator( - secretsPropertySourceLocator, secretsProperties, new KubernetesClientSecretsCache()); + secretsPropertySourceLocator, secretsProperties, new KubernetesClientSourcesNamespaceBatched()); } registerSingle(bootstrapContext, SecretsPropertySourceLocator.class, secretsPropertySourceLocator, diff --git a/spring-cloud-kubernetes-client-config/src/main/java/org/springframework/cloud/kubernetes/client/config/KubernetesClientConfigMapPropertySourceLocator.java b/spring-cloud-kubernetes-client-config/src/main/java/org/springframework/cloud/kubernetes/client/config/KubernetesClientConfigMapPropertySourceLocator.java index 56a17e6a7e..9015db29a6 100644 --- a/spring-cloud-kubernetes-client-config/src/main/java/org/springframework/cloud/kubernetes/client/config/KubernetesClientConfigMapPropertySourceLocator.java +++ b/spring-cloud-kubernetes-client-config/src/main/java/org/springframework/cloud/kubernetes/client/config/KubernetesClientConfigMapPropertySourceLocator.java @@ -41,19 +41,20 @@ public class KubernetesClientConfigMapPropertySourceLocator extends ConfigMapPro public KubernetesClientConfigMapPropertySourceLocator(CoreV1Api coreV1Api, ConfigMapConfigProperties properties, KubernetesNamespaceProvider kubernetesNamespaceProvider) { - super(properties, new KubernetesClientConfigMapsCache()); + super(properties, new KubernetesClientSourcesNamespaceBatched()); this.coreV1Api = coreV1Api; this.kubernetesNamespaceProvider = kubernetesNamespaceProvider; } @Override - protected MapPropertySource getMapPropertySource(NormalizedSource source, ConfigurableEnvironment environment) { + protected MapPropertySource getMapPropertySource(NormalizedSource source, ConfigurableEnvironment environment, + boolean namespacedBatchRead) { String normalizedNamespace = source.namespace().orElse(null); String namespace = getApplicationNamespace(normalizedNamespace, source.target(), kubernetesNamespaceProvider); KubernetesClientConfigContext context = new KubernetesClientConfigContext(coreV1Api, source, namespace, - environment); + environment, true, namespacedBatchRead); return new KubernetesClientConfigMapPropertySource(context); } diff --git a/spring-cloud-kubernetes-client-config/src/main/java/org/springframework/cloud/kubernetes/client/config/KubernetesClientConfigUtils.java b/spring-cloud-kubernetes-client-config/src/main/java/org/springframework/cloud/kubernetes/client/config/KubernetesClientConfigUtils.java index 0dc869b322..d53926fb7e 100644 --- a/spring-cloud-kubernetes-client-config/src/main/java/org/springframework/cloud/kubernetes/client/config/KubernetesClientConfigUtils.java +++ b/spring-cloud-kubernetes-client-config/src/main/java/org/springframework/cloud/kubernetes/client/config/KubernetesClientConfigUtils.java @@ -33,6 +33,10 @@ import org.springframework.core.env.Environment; import static org.springframework.cloud.kubernetes.client.KubernetesClientUtils.getApplicationNamespace; +import static org.springframework.cloud.kubernetes.client.config.KubernetesClientSourcesNamespaceBatched.strippedConfigMapsBatchRead; +import static org.springframework.cloud.kubernetes.client.config.KubernetesClientSourcesNamespaceBatched.strippedSecretsBatchRead; +import static org.springframework.cloud.kubernetes.client.config.KubernetesClientSourcesNonNamespaceBatched.strippedConfigMapsNonBatchRead; +import static org.springframework.cloud.kubernetes.client.config.KubernetesClientSourcesNonNamespaceBatched.strippedSecretsNonBatchRead; /** * @author Ryan Baxter @@ -61,38 +65,29 @@ public static Set namespaces(KubernetesNamespaceProvider provider, Confi return namespaces; } - /** - *
-	 *     1. read all secrets in the provided namespace
-	 *     2. from the above, filter the ones that we care about (filter by labels)
-	 *     3. see if any of the secrets from (2) has a single yaml/properties file
-	 *     4. gather all the names of the secrets + data they hold
-	 * 
- */ - static MultipleSourcesContainer secretsDataByLabels(CoreV1Api coreV1Api, String namespace, - Map labels, Environment environment) { - List strippedSecrets = strippedSecrets(coreV1Api, namespace); - if (strippedSecrets.isEmpty()) { - return MultipleSourcesContainer.empty(); - } - return ConfigUtils.processLabeledData(strippedSecrets, environment, labels, namespace, DECODE); - } - /** *
 	 *     1. read all config maps in the provided namespace
-	 *     2. from the above, filter the ones that we care about (filter by labels)
-	 *     3. see if any from (2) has a single yaml/properties file
+	 *     2. from the above, filter the ones that we care about (by name)
+	 *     3. see if any of the config maps has a single yaml/properties file
 	 *     4. gather all the names of the config maps + data they hold
 	 * 
*/ - static MultipleSourcesContainer configMapsDataByLabels(CoreV1Api coreV1Api, String namespace, - Map labels, Environment environment) { - List strippedConfigMaps = strippedConfigMaps(coreV1Api, namespace); - if (strippedConfigMaps.isEmpty()) { - return MultipleSourcesContainer.empty(); + static MultipleSourcesContainer configMapsDataByName(CoreV1Api client, String namespace, + LinkedHashSet sourceNames, Environment environment, boolean namespacedBatchRead) { + + List strippedConfigMaps; + + if (namespacedBatchRead) { + LOG.debug("Will read all configmaps in namespace : " + namespace); + strippedConfigMaps = strippedConfigMapsBatchRead(client, namespace); } - return ConfigUtils.processLabeledData(strippedConfigMaps, environment, labels, namespace, DECODE); + else { + LOG.debug("Will read individual configmaps in namespace : " + namespace + " with names : " + sourceNames); + strippedConfigMaps = strippedConfigMapsNonBatchRead(client, namespace, sourceNames); + } + + return ConfigUtils.processNamedData(strippedConfigMaps, environment, sourceNames, namespace, false); } /** @@ -103,49 +98,71 @@ static MultipleSourcesContainer configMapsDataByLabels(CoreV1Api coreV1Api, Stri * 4. gather all the names of the secrets + decoded data they hold * */ - static MultipleSourcesContainer secretsDataByName(CoreV1Api coreV1Api, String namespace, - LinkedHashSet sourceNames, Environment environment, boolean includeDefaultProfileData) { - List strippedSecrets = strippedSecrets(coreV1Api, namespace); - if (strippedSecrets.isEmpty()) { - return MultipleSourcesContainer.empty(); + static MultipleSourcesContainer secretsDataByName(CoreV1Api client, String namespace, + LinkedHashSet sourceNames, Environment environment, boolean namespacedBatchRead) { + + List strippedSecrets; + + if (namespacedBatchRead) { + LOG.debug("Will read all secrets in namespace : " + namespace); + strippedSecrets = strippedSecretsBatchRead(client, namespace); + } + else { + LOG.debug("Will read individual secrets in namespace : " + namespace + " with names : " + sourceNames); + strippedSecrets = strippedSecretsNonBatchRead(client, namespace, sourceNames); } - return ConfigUtils.processNamedData(strippedSecrets, environment, sourceNames, namespace, DECODE, - includeDefaultProfileData); + + return ConfigUtils.processNamedData(strippedSecrets, environment, sourceNames, namespace, false); } /** *
 	 *     1. read all config maps in the provided namespace
-	 *     2. from the above, filter the ones that we care about (by name)
-	 *     3. see if any of the config maps has a single yaml/properties file
+	 *     2. from the above, filter the ones that we care about (filter by labels)
+	 *     3. see if any from (2) has a single yaml/properties file
 	 *     4. gather all the names of the config maps + data they hold
 	 * 
*/ - static MultipleSourcesContainer configMapsDataByName(CoreV1Api coreV1Api, String namespace, - LinkedHashSet sourceNames, Environment environment, boolean includeDefaultProfileData) { - List strippedConfigMaps = strippedConfigMaps(coreV1Api, namespace); - if (strippedConfigMaps.isEmpty()) { - return MultipleSourcesContainer.empty(); - } - return ConfigUtils.processNamedData(strippedConfigMaps, environment, sourceNames, namespace, DECODE, - includeDefaultProfileData); - } + static MultipleSourcesContainer configMapsDataByLabels(CoreV1Api client, String namespace, + Map labels, Environment environment, boolean namespacedBatchRead) { + + List strippedConfigMaps; - private static List strippedConfigMaps(CoreV1Api coreV1Api, String namespace) { - List strippedConfigMaps = KubernetesClientConfigMapsCache.byNamespace(coreV1Api, - namespace); - if (strippedConfigMaps.isEmpty()) { - LOG.debug("No configmaps in namespace '" + namespace + "'"); + if (namespacedBatchRead) { + LOG.debug("Will read all configmaps in namespace : " + namespace); + strippedConfigMaps = strippedConfigMapsBatchRead(client, namespace); + } + else { + LOG.debug("Will read individual configmaps in namespace : " + namespace + " with labels : " + labels); + strippedConfigMaps = strippedConfigMapsNonBatchRead(client, namespace, labels); } - return strippedConfigMaps; + + return ConfigUtils.processLabeledData(strippedConfigMaps, environment, labels, namespace, false); } - private static List strippedSecrets(CoreV1Api coreV1Api, String namespace) { - List strippedSecrets = KubernetesClientSecretsCache.byNamespace(coreV1Api, namespace); - if (strippedSecrets.isEmpty()) { - LOG.debug("No configmaps in namespace '" + namespace + "'"); + /** + *
+	 *     1. read all secrets in the provided namespace
+	 *     2. from the above, filter the ones that we care about (filter by labels)
+	 *     3. see if any of the secrets from (2) has a single yaml/properties file
+	 *     4. gather all the names of the secrets + data they hold
+	 * 
+ */ + static MultipleSourcesContainer secretsDataByLabels(CoreV1Api client, String namespace, Map labels, + Environment environment, boolean namespacedBatchRead) { + + List strippedSecrets; + + if (namespacedBatchRead) { + LOG.debug("Will read all secrets in namespace : " + namespace); + strippedSecrets = strippedSecretsBatchRead(client, namespace); } - return strippedSecrets; + else { + LOG.debug("Will read individual secrets in namespace : " + namespace + " with labels : " + labels); + strippedSecrets = strippedSecretsNonBatchRead(client, namespace, labels); + } + + return ConfigUtils.processLabeledData(strippedSecrets, environment, labels, namespace, false); } } diff --git a/spring-cloud-kubernetes-client-config/src/main/java/org/springframework/cloud/kubernetes/client/config/KubernetesClientSecretsCache.java b/spring-cloud-kubernetes-client-config/src/main/java/org/springframework/cloud/kubernetes/client/config/KubernetesClientSecretsCache.java deleted file mode 100644 index 49df6f8306..0000000000 --- a/spring-cloud-kubernetes-client-config/src/main/java/org/springframework/cloud/kubernetes/client/config/KubernetesClientSecretsCache.java +++ /dev/null @@ -1,91 +0,0 @@ -/* - * Copyright 2013-2022 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.client.config; - -import java.util.List; -import java.util.Map; -import java.util.concurrent.ConcurrentHashMap; -import java.util.stream.Collectors; - -import io.kubernetes.client.openapi.ApiException; -import io.kubernetes.client.openapi.apis.CoreV1Api; -import io.kubernetes.client.openapi.models.V1Secret; -import org.apache.commons.logging.LogFactory; - -import org.springframework.cloud.kubernetes.commons.config.SecretsCache; -import org.springframework.cloud.kubernetes.commons.config.StrippedSourceContainer; -import org.springframework.core.log.LogAccessor; -import org.springframework.util.ObjectUtils; - -/** - * A cache of V1ConfigMap(s) per namespace. Makes sure we read config maps only once from - * a namespace. - * - * @author wind57 - */ -public class KubernetesClientSecretsCache implements SecretsCache { - - private static final LogAccessor LOG = new LogAccessor(LogFactory.getLog(KubernetesClientConfigMapsCache.class)); - - /** - * at the moment our loading of config maps is using a single thread, but might change - * in the future, thus a thread safe structure. - */ - private static final ConcurrentHashMap> CACHE = new ConcurrentHashMap<>(); - - @Override - public void discardAll() { - CACHE.clear(); - } - - static List byNamespace(CoreV1Api coreV1Api, String namespace) { - boolean[] b = new boolean[1]; - List result = CACHE.computeIfAbsent(namespace, x -> { - try { - b[0] = true; - return strippedSecrets(coreV1Api - .listNamespacedSecret(namespace, null, null, null, null, null, null, null, null, null, null, null) - .getItems()); - } - catch (ApiException apiException) { - throw new RuntimeException(apiException.getResponseBody(), apiException); - } - }); - - if (b[0]) { - LOG.debug(() -> "Loaded all secrets in namespace '" + namespace + "'"); - } - else { - LOG.debug(() -> "Loaded (from cache) all secrets in namespace '" + namespace + "'"); - } - - return result; - } - - private static List strippedSecrets(List secrets) { - return secrets.stream() - .map(secret -> new StrippedSourceContainer(secret.getMetadata().getLabels(), secret.getMetadata().getName(), - transform(secret.getData()))) - .toList(); - } - - private static Map transform(Map in) { - return ObjectUtils.isEmpty(in) ? Map.of() - : in.entrySet().stream().collect(Collectors.toMap(Map.Entry::getKey, en -> new String(en.getValue()))); - } - -} diff --git a/spring-cloud-kubernetes-client-config/src/main/java/org/springframework/cloud/kubernetes/client/config/KubernetesClientSecretsPropertySourceLocator.java b/spring-cloud-kubernetes-client-config/src/main/java/org/springframework/cloud/kubernetes/client/config/KubernetesClientSecretsPropertySourceLocator.java index 057a43d90f..d9440b4258 100644 --- a/spring-cloud-kubernetes-client-config/src/main/java/org/springframework/cloud/kubernetes/client/config/KubernetesClientSecretsPropertySourceLocator.java +++ b/spring-cloud-kubernetes-client-config/src/main/java/org/springframework/cloud/kubernetes/client/config/KubernetesClientSecretsPropertySourceLocator.java @@ -41,19 +41,20 @@ public class KubernetesClientSecretsPropertySourceLocator extends SecretsPropert public KubernetesClientSecretsPropertySourceLocator(CoreV1Api coreV1Api, KubernetesNamespaceProvider kubernetesNamespaceProvider, SecretsConfigProperties secretsConfigProperties) { - super(secretsConfigProperties, new KubernetesClientSecretsCache()); + super(secretsConfigProperties, new KubernetesClientSourcesNamespaceBatched()); this.coreV1Api = coreV1Api; this.kubernetesNamespaceProvider = kubernetesNamespaceProvider; } @Override - protected SecretsPropertySource getPropertySource(ConfigurableEnvironment environment, NormalizedSource source) { + protected SecretsPropertySource getPropertySource(ConfigurableEnvironment environment, NormalizedSource source, + boolean namespacedBatchRead) { String normalizedNamespace = source.namespace().orElse(null); String namespace = getApplicationNamespace(normalizedNamespace, source.target(), kubernetesNamespaceProvider); KubernetesClientConfigContext context = new KubernetesClientConfigContext(coreV1Api, source, namespace, - environment); + environment, true, namespacedBatchRead); return new KubernetesClientSecretsPropertySource(context); } diff --git a/spring-cloud-kubernetes-client-config/src/main/java/org/springframework/cloud/kubernetes/client/config/KubernetesClientConfigMapsCache.java b/spring-cloud-kubernetes-client-config/src/main/java/org/springframework/cloud/kubernetes/client/config/KubernetesClientSourcesNamespaceBatched.java similarity index 51% rename from spring-cloud-kubernetes-client-config/src/main/java/org/springframework/cloud/kubernetes/client/config/KubernetesClientConfigMapsCache.java rename to spring-cloud-kubernetes-client-config/src/main/java/org/springframework/cloud/kubernetes/client/config/KubernetesClientSourcesNamespaceBatched.java index 3a0d046db6..9aac395802 100644 --- a/spring-cloud-kubernetes-client-config/src/main/java/org/springframework/cloud/kubernetes/client/config/KubernetesClientConfigMapsCache.java +++ b/spring-cloud-kubernetes-client-config/src/main/java/org/springframework/cloud/kubernetes/client/config/KubernetesClientSourcesNamespaceBatched.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2022 the original author or authors. + * Copyright 2013-2024 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. @@ -21,40 +21,45 @@ import io.kubernetes.client.openapi.ApiException; import io.kubernetes.client.openapi.apis.CoreV1Api; -import io.kubernetes.client.openapi.models.V1ConfigMap; import org.apache.commons.logging.LogFactory; import org.springframework.cloud.kubernetes.commons.config.ConfigMapCache; +import org.springframework.cloud.kubernetes.commons.config.SecretsCache; import org.springframework.cloud.kubernetes.commons.config.StrippedSourceContainer; import org.springframework.core.log.LogAccessor; /** - * A cache of V1ConfigMap(s) per namespace. Makes sure we read config maps only once from - * a namespace. - * * @author wind57 */ -public final class KubernetesClientConfigMapsCache implements ConfigMapCache { +final class KubernetesClientSourcesNamespaceBatched implements SecretsCache, ConfigMapCache { - private static final LogAccessor LOG = new LogAccessor(LogFactory.getLog(KubernetesClientConfigMapsCache.class)); + private static final LogAccessor LOG = new LogAccessor( + LogFactory.getLog(KubernetesClientSourcesNamespaceBatched.class)); /** * at the moment our loading of config maps is using a single thread, but might change * in the future, thus a thread safe structure. */ - private static final ConcurrentHashMap> CACHE = new ConcurrentHashMap<>(); + private static final ConcurrentHashMap> SECRETS_CACHE = new ConcurrentHashMap<>(); + + private static final ConcurrentHashMap> CONFIG_MAPS_CACHE = new ConcurrentHashMap<>(); + + @Override + public void discardSecrets() { + SECRETS_CACHE.clear(); + } @Override - public void discardAll() { - CACHE.clear(); + public void discardConfigMaps() { + CONFIG_MAPS_CACHE.clear(); } - static List byNamespace(CoreV1Api coreV1Api, String namespace) { + static List strippedConfigMapsBatchRead(CoreV1Api coreV1Api, String namespace) { boolean[] b = new boolean[1]; - List result = CACHE.computeIfAbsent(namespace, x -> { + List result = CONFIG_MAPS_CACHE.computeIfAbsent(namespace, x -> { try { b[0] = true; - return strippedConfigMaps(coreV1Api + return KubernetesClientSourcesStripper.strippedConfigMaps(coreV1Api .listNamespacedConfigMap(namespace, null, null, null, null, null, null, null, null, null, null, null) .getItems()); @@ -74,11 +79,28 @@ static List byNamespace(CoreV1Api coreV1Api, String nam return result; } - private static List strippedConfigMaps(List configMaps) { - return configMaps.stream() - .map(configMap -> new StrippedSourceContainer(configMap.getMetadata().getLabels(), - configMap.getMetadata().getName(), configMap.getData())) - .toList(); + static List strippedSecretsBatchRead(CoreV1Api coreV1Api, String namespace) { + boolean[] b = new boolean[1]; + List result = SECRETS_CACHE.computeIfAbsent(namespace, x -> { + try { + b[0] = true; + return KubernetesClientSourcesStripper.strippedSecrets(coreV1Api + .listNamespacedSecret(namespace, null, null, null, null, null, null, null, null, null, null, null) + .getItems()); + } + catch (ApiException apiException) { + throw new RuntimeException(apiException.getResponseBody(), apiException); + } + }); + + if (b[0]) { + LOG.debug(() -> "Loaded all secrets in namespace '" + namespace + "'"); + } + else { + LOG.debug(() -> "Loaded (from cache) all secrets in namespace '" + namespace + "'"); + } + + return result; } } diff --git a/spring-cloud-kubernetes-client-config/src/main/java/org/springframework/cloud/kubernetes/client/config/KubernetesClientSourcesNonNamespaceBatched.java b/spring-cloud-kubernetes-client-config/src/main/java/org/springframework/cloud/kubernetes/client/config/KubernetesClientSourcesNonNamespaceBatched.java new file mode 100644 index 0000000000..bb8d6cc5ea --- /dev/null +++ b/spring-cloud-kubernetes-client-config/src/main/java/org/springframework/cloud/kubernetes/client/config/KubernetesClientSourcesNonNamespaceBatched.java @@ -0,0 +1,167 @@ +/* + * Copyright 2013-2024 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.client.config; + +import java.util.ArrayList; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + +import io.kubernetes.client.openapi.ApiException; +import io.kubernetes.client.openapi.apis.CoreV1Api; +import io.kubernetes.client.openapi.models.V1ConfigMap; +import io.kubernetes.client.openapi.models.V1Secret; +import org.apache.commons.logging.LogFactory; + +import org.springframework.cloud.kubernetes.commons.config.StrippedSourceContainer; +import org.springframework.core.log.LogAccessor; + +final class KubernetesClientSourcesNonNamespaceBatched { + + private static final LogAccessor LOG = new LogAccessor( + LogFactory.getLog(KubernetesClientSourcesNonNamespaceBatched.class)); + + private KubernetesClientSourcesNonNamespaceBatched() { + + } + + /** + * read configmaps by name, one by one, without caching them. + */ + static List strippedConfigMapsNonBatchRead(CoreV1Api client, String namespace, + LinkedHashSet sourceNames) { + + List configMaps = new ArrayList<>(sourceNames.size()); + + for (String sourceName : sourceNames) { + V1ConfigMap configMap; + try { + configMap = client.readNamespacedConfigMap(namespace, sourceName, null); + } + catch (ApiException e) { + throw new RuntimeException(e.getResponseBody(), e); + } + if (configMap != null) { + LOG.debug("Loaded config map '" + sourceName + "'"); + configMaps.add(configMap); + } + } + + List strippedConfigMaps = KubernetesClientSourcesStripper + .strippedConfigMaps(configMaps); + + if (strippedConfigMaps.isEmpty()) { + LOG.debug("No configmaps in namespace '" + namespace + "'"); + } + + return strippedConfigMaps; + } + + /** + * read secrets by name, one by one, without caching them. + */ + static List strippedSecretsNonBatchRead(CoreV1Api client, String namespace, + LinkedHashSet sourceNames) { + + List secrets = new ArrayList<>(sourceNames.size()); + + for (String sourceName : sourceNames) { + V1Secret secret; + try { + secret = client.readNamespacedSecret(sourceName, namespace, null); + } + catch (ApiException e) { + throw new RuntimeException(e.getResponseBody(), e); + } + if (secret != null) { + LOG.debug("Loaded config map '" + sourceName + "'"); + secrets.add(secret); + } + } + + List strippedSecrets = KubernetesClientSourcesStripper.strippedSecrets(secrets); + + if (strippedSecrets.isEmpty()) { + LOG.debug("No secrets in namespace '" + namespace + "'"); + } + + return strippedSecrets; + } + + /** + * read configmaps by labels, without caching them. + */ + static List strippedConfigMapsNonBatchRead(CoreV1Api client, String namespace, + Map labels) { + + List configMaps; + try { + configMaps = client + .listNamespacedConfigMap(namespace, null, null, null, null, labelSelector(labels), null, null, null, + null, null, null) + .getItems(); + } + catch (ApiException e) { + throw new RuntimeException(e.getResponseBody(), e); + } + for (V1ConfigMap configMap : configMaps) { + LOG.debug("Loaded config map '" + configMap.getMetadata().getName() + "'"); + } + + List strippedConfigMaps = KubernetesClientSourcesStripper + .strippedConfigMaps(configMaps); + if (strippedConfigMaps.isEmpty()) { + LOG.debug("No configmaps in namespace '" + namespace + "'"); + } + + return strippedConfigMaps; + } + + /** + * read secrets by labels, without caching them. + */ + static List strippedSecretsNonBatchRead(CoreV1Api client, String namespace, + Map labels) { + + List secrets; + try { + secrets = client + .listNamespacedSecret(namespace, null, null, null, null, labelSelector(labels), null, null, null, null, + null, null) + .getItems(); + } + catch (ApiException e) { + throw new RuntimeException(e.getResponseBody(), e); + } + for (V1Secret secret : secrets) { + LOG.debug("Loaded secret '" + secret.getMetadata().getName() + "'"); + } + + List strippedSecrets = KubernetesClientSourcesStripper.strippedSecrets(secrets); + if (strippedSecrets.isEmpty()) { + LOG.debug("No secrets in namespace '" + namespace + "'"); + } + + return strippedSecrets; + } + + private static String labelSelector(Map labels) { + return labels.entrySet().stream().map(en -> en.getKey() + "=" + en.getValue()).collect(Collectors.joining("&")); + } + +} diff --git a/spring-cloud-kubernetes-client-config/src/main/java/org/springframework/cloud/kubernetes/client/config/KubernetesClientSourcesStripper.java b/spring-cloud-kubernetes-client-config/src/main/java/org/springframework/cloud/kubernetes/client/config/KubernetesClientSourcesStripper.java new file mode 100644 index 0000000000..6bbbe3c350 --- /dev/null +++ b/spring-cloud-kubernetes-client-config/src/main/java/org/springframework/cloud/kubernetes/client/config/KubernetesClientSourcesStripper.java @@ -0,0 +1,53 @@ +/* + * Copyright 2013-2024 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.client.config; + +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + +import io.kubernetes.client.openapi.models.V1ConfigMap; +import io.kubernetes.client.openapi.models.V1Secret; + +import org.springframework.cloud.kubernetes.commons.config.StrippedSourceContainer; +import org.springframework.util.ObjectUtils; + +/** + * @author wind57 + */ +interface KubernetesClientSourcesStripper { + + static List strippedSecrets(List secrets) { + return secrets.stream() + .map(secret -> new StrippedSourceContainer(secret.getMetadata().getLabels(), secret.getMetadata().getName(), + transform(secret.getData()))) + .toList(); + } + + static List strippedConfigMaps(List configMaps) { + return configMaps.stream() + .map(configMap -> new StrippedSourceContainer(configMap.getMetadata().getLabels(), + configMap.getMetadata().getName(), configMap.getData())) + .toList(); + } + + private static Map transform(Map in) { + return ObjectUtils.isEmpty(in) ? Map.of() + : in.entrySet().stream().collect(Collectors.toMap(Map.Entry::getKey, en -> new String(en.getValue()))); + } + +} diff --git a/spring-cloud-kubernetes-client-config/src/main/java/org/springframework/cloud/kubernetes/client/config/LabeledConfigMapContextToSourceDataProvider.java b/spring-cloud-kubernetes-client-config/src/main/java/org/springframework/cloud/kubernetes/client/config/LabeledConfigMapContextToSourceDataProvider.java index ee135b3835..98b26edd58 100644 --- a/spring-cloud-kubernetes-client-config/src/main/java/org/springframework/cloud/kubernetes/client/config/LabeledConfigMapContextToSourceDataProvider.java +++ b/spring-cloud-kubernetes-client-config/src/main/java/org/springframework/cloud/kubernetes/client/config/LabeledConfigMapContextToSourceDataProvider.java @@ -50,7 +50,7 @@ public KubernetesClientContextToSourceData get() { @Override public MultipleSourcesContainer dataSupplier(Map labels) { return KubernetesClientConfigUtils.configMapsDataByLabels(context.client(), context.namespace(), - labels, context.environment()); + labels, context.environment(), context.namespacedBatchRead()); } }.compute(source.labels(), source.prefix(), source.target(), source.failFast(), context.namespace()); diff --git a/spring-cloud-kubernetes-client-config/src/main/java/org/springframework/cloud/kubernetes/client/config/LabeledSecretContextToSourceDataProvider.java b/spring-cloud-kubernetes-client-config/src/main/java/org/springframework/cloud/kubernetes/client/config/LabeledSecretContextToSourceDataProvider.java index 723e680ebd..a20905aefb 100644 --- a/spring-cloud-kubernetes-client-config/src/main/java/org/springframework/cloud/kubernetes/client/config/LabeledSecretContextToSourceDataProvider.java +++ b/spring-cloud-kubernetes-client-config/src/main/java/org/springframework/cloud/kubernetes/client/config/LabeledSecretContextToSourceDataProvider.java @@ -56,7 +56,7 @@ public KubernetesClientContextToSourceData get() { @Override public MultipleSourcesContainer dataSupplier(Map labels) { return KubernetesClientConfigUtils.secretsDataByLabels(context.client(), context.namespace(), - labels, context.environment()); + labels, context.environment(), context.namespacedBatchRead()); } }.compute(source.labels(), source.prefix(), source.target(), source.failFast(), context.namespace()); diff --git a/spring-cloud-kubernetes-client-config/src/main/java/org/springframework/cloud/kubernetes/client/config/NamedConfigMapContextToSourceDataProvider.java b/spring-cloud-kubernetes-client-config/src/main/java/org/springframework/cloud/kubernetes/client/config/NamedConfigMapContextToSourceDataProvider.java index 18c1668d96..7a1ee2d677 100644 --- a/spring-cloud-kubernetes-client-config/src/main/java/org/springframework/cloud/kubernetes/client/config/NamedConfigMapContextToSourceDataProvider.java +++ b/spring-cloud-kubernetes-client-config/src/main/java/org/springframework/cloud/kubernetes/client/config/NamedConfigMapContextToSourceDataProvider.java @@ -55,12 +55,11 @@ protected String generateSourceName(String target, String sourceName, String nam @Override public MultipleSourcesContainer dataSupplier(LinkedHashSet sourceNames) { return KubernetesClientConfigUtils.configMapsDataByName(context.client(), context.namespace(), - sourceNames, context.environment(), context.includeDefaultProfileData()); + sourceNames, context.environment(), context.namespacedBatchRead()); } }.compute(source.name().orElseThrow(), source.prefix(), source.target(), source.profileSpecificSources(), - source.failFast(), context.namespace(), context.environment().getActiveProfiles(), - context.si - ); + source.failFast(), context.namespace(), context.environment().getActiveProfiles(), + context.namespacedBatchRead()); }; } diff --git a/spring-cloud-kubernetes-client-config/src/main/java/org/springframework/cloud/kubernetes/client/config/NamedSecretContextToSourceDataProvider.java b/spring-cloud-kubernetes-client-config/src/main/java/org/springframework/cloud/kubernetes/client/config/NamedSecretContextToSourceDataProvider.java index bfde140060..d8de61aa9a 100644 --- a/spring-cloud-kubernetes-client-config/src/main/java/org/springframework/cloud/kubernetes/client/config/NamedSecretContextToSourceDataProvider.java +++ b/spring-cloud-kubernetes-client-config/src/main/java/org/springframework/cloud/kubernetes/client/config/NamedSecretContextToSourceDataProvider.java @@ -44,11 +44,12 @@ public KubernetesClientContextToSourceData get() { return new NamedSourceData() { @Override protected String generateSourceName(String target, String sourceName, String namespace, - String[] activeProfiles) { + String[] activeProfiles, boolean namesspacedBatchRead) { if (source.appendProfileToName()) { return ConfigUtils.sourceName(target, sourceName, namespace, activeProfiles); } - return super.generateSourceName(target, sourceName, namespace, activeProfiles); + return super.generateSourceName(target, sourceName, namespace, activeProfiles, + namesspacedBatchRead); } @Override @@ -57,7 +58,8 @@ public MultipleSourcesContainer dataSupplier(LinkedHashSet sourceNames) sourceNames, context.environment(), context.includeDefaultProfileData()); } }.compute(source.name().orElseThrow(), source.prefix(), source.target(), source.profileSpecificSources(), - source.failFast(), context.namespace(), context.environment().getActiveProfiles()); + source.failFast(), context.namespace(), context.environment().getActiveProfiles(), + context.namespacedBatchRead()); }; } diff --git a/spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/KubernetesClientConfigMapPropertySourceLocatorTests.java b/spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/KubernetesClientConfigMapPropertySourceLocatorTests.java index 1266b00f70..6e2204d17c 100644 --- a/spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/KubernetesClientConfigMapPropertySourceLocatorTests.java +++ b/spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/KubernetesClientConfigMapPropertySourceLocatorTests.java @@ -57,6 +57,8 @@ */ class KubernetesClientConfigMapPropertySourceLocatorTests { + private static final boolean NAMESPACED_BATCH_READ = true; + private static final V1ConfigMapList PROPERTIES_CONFIGMAP_LIST = new V1ConfigMapList() .addItemsItem(new V1ConfigMapBuilder() .withMetadata(new V1ObjectMetaBuilder().withName("bootstrap-640") @@ -100,7 +102,8 @@ void locateWithoutSources() { stubFor(get("/api/v1/namespaces/default/configmaps") .willReturn(aResponse().withStatus(200).withBody(new JSON().serialize(PROPERTIES_CONFIGMAP_LIST)))); ConfigMapConfigProperties configMapConfigProperties = new ConfigMapConfigProperties(true, List.of(), List.of(), - Map.of(), true, "bootstrap-640", null, false, false, false, RetryProperties.DEFAULT); + Map.of(), true, "bootstrap-640", null, false, false, false, RetryProperties.DEFAULT, + NAMESPACED_BATCH_READ); MockEnvironment mockEnvironment = new MockEnvironment(); mockEnvironment.setProperty("spring.cloud.kubernetes.client.namespace", "default"); PropertySource propertySource = new KubernetesClientConfigMapPropertySourceLocator(api, @@ -119,7 +122,8 @@ void locateWithSources() { ConfigMapConfigProperties.Source source = new ConfigMapConfigProperties.Source("bootstrap-640", "default", Collections.emptyMap(), null, null, null); ConfigMapConfigProperties configMapConfigProperties = new ConfigMapConfigProperties(true, List.of(), - List.of(source), Map.of(), true, "fake-name", null, false, false, false, RetryProperties.DEFAULT); + List.of(source), Map.of(), true, "fake-name", null, false, false, false, RetryProperties.DEFAULT, + NAMESPACED_BATCH_READ); PropertySource propertySource = new KubernetesClientConfigMapPropertySourceLocator(api, configMapConfigProperties, new KubernetesNamespaceProvider(new MockEnvironment())) @@ -142,7 +146,8 @@ void testLocateWithoutNamespaceConstructor() { .willReturn(aResponse().withStatus(200).withBody(new JSON().serialize(PROPERTIES_CONFIGMAP_LIST)))); ConfigMapConfigProperties configMapConfigProperties = new ConfigMapConfigProperties(true, List.of(), List.of(), - Map.of(), true, "bootstrap-640", null, false, false, false, RetryProperties.DEFAULT); + Map.of(), true, "bootstrap-640", null, false, false, false, RetryProperties.DEFAULT, + NAMESPACED_BATCH_READ); assertThatThrownBy(() -> new KubernetesClientConfigMapPropertySourceLocator(api, configMapConfigProperties, new KubernetesNamespaceProvider(new MockEnvironment())) @@ -162,7 +167,8 @@ void testLocateWithoutNamespace() { stubFor(get("/api/v1/namespaces/default/configmaps") .willReturn(aResponse().withStatus(200).withBody(new JSON().serialize(PROPERTIES_CONFIGMAP_LIST)))); ConfigMapConfigProperties configMapConfigProperties = new ConfigMapConfigProperties(true, List.of(), List.of(), - Map.of(), true, "bootstrap-640", null, false, false, false, RetryProperties.DEFAULT); + Map.of(), true, "bootstrap-640", null, false, false, false, RetryProperties.DEFAULT, + NAMESPACED_BATCH_READ); assertThatThrownBy(() -> new KubernetesClientConfigMapPropertySourceLocator(api, configMapConfigProperties, new KubernetesNamespaceProvider(ENV)) .locate(ENV)).isInstanceOf(NamespaceResolutionFailedException.class); @@ -175,7 +181,8 @@ public void locateShouldThrowExceptionOnFailureWhenFailFastIsEnabled() { .willReturn(aResponse().withStatus(500).withBody("Internal Server Error"))); ConfigMapConfigProperties configMapConfigProperties = new ConfigMapConfigProperties(true, List.of(), List.of(), - Map.of(), true, "bootstrap-640", "default", false, false, true, RetryProperties.DEFAULT); + Map.of(), true, "bootstrap-640", "default", false, false, true, RetryProperties.DEFAULT, + NAMESPACED_BATCH_READ); KubernetesClientConfigMapPropertySourceLocator locator = new KubernetesClientConfigMapPropertySourceLocator(api, configMapConfigProperties, new KubernetesNamespaceProvider(new MockEnvironment())); @@ -191,7 +198,8 @@ public void locateShouldNotThrowExceptionOnFailureWhenFailFastIsDisabled() { .willReturn(aResponse().withStatus(500).withBody("Internal Server Error"))); ConfigMapConfigProperties configMapConfigProperties = new ConfigMapConfigProperties(true, List.of(), List.of(), - Map.of(), true, "bootstrap-640", "default", false, false, false, RetryProperties.DEFAULT); + Map.of(), true, "bootstrap-640", "default", false, false, false, RetryProperties.DEFAULT, + NAMESPACED_BATCH_READ); KubernetesClientConfigMapPropertySourceLocator locator = new KubernetesClientConfigMapPropertySourceLocator(api, configMapConfigProperties, new KubernetesNamespaceProvider(new MockEnvironment())); diff --git a/spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/KubernetesClientConfigMapPropertySourceTests.java b/spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/KubernetesClientConfigMapPropertySourceTests.java index 51916d24c9..df63c7dbce 100644 --- a/spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/KubernetesClientConfigMapPropertySourceTests.java +++ b/spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/KubernetesClientConfigMapPropertySourceTests.java @@ -54,6 +54,8 @@ */ class KubernetesClientConfigMapPropertySourceTests { + private static final boolean NAMESPACED_BATCH_READ = true; + private static final V1ConfigMapList PROPERTIES_CONFIGMAP_LIST = new V1ConfigMapList() .addItemsItem(new V1ConfigMapBuilder() .withMetadata(new V1ObjectMetaBuilder().withName("bootstrap-640") @@ -97,7 +99,7 @@ public static void after() { @AfterEach public void afterEach() { WireMock.reset(); - new KubernetesClientConfigMapsCache().discardAll(); + new KubernetesClientSourcesNamespaceBatched().discardConfigMaps(); } @Test @@ -108,7 +110,7 @@ public void propertiesFile() { NormalizedSource source = new NamedConfigMapNormalizedSource("bootstrap-640", "default", false, true); KubernetesClientConfigContext context = new KubernetesClientConfigContext(api, source, "default", - new MockEnvironment()); + new MockEnvironment(), false, NAMESPACED_BATCH_READ); KubernetesClientConfigMapPropertySource propertySource = new KubernetesClientConfigMapPropertySource(context); verify(getRequestedFor(urlEqualTo("/api/v1/namespaces/default/configmaps"))); @@ -129,7 +131,7 @@ public void yamlFile() { NormalizedSource source = new NamedConfigMapNormalizedSource("bootstrap-641", "default", false, true); KubernetesClientConfigContext context = new KubernetesClientConfigContext(api, source, "default", - new MockEnvironment()); + new MockEnvironment(), false, NAMESPACED_BATCH_READ); KubernetesClientConfigMapPropertySource propertySource = new KubernetesClientConfigMapPropertySource(context); verify(getRequestedFor(urlEqualTo("/api/v1/namespaces/default/configmaps"))); @@ -151,7 +153,7 @@ public void propertiesFileWithPrefix() { ConfigUtils.Prefix prefix = ConfigUtils.findPrefix("prefix", false, false, null); NormalizedSource source = new NamedConfigMapNormalizedSource("bootstrap-640", "default", false, prefix, true); KubernetesClientConfigContext context = new KubernetesClientConfigContext(api, source, "default", - new MockEnvironment()); + new MockEnvironment(), false, NAMESPACED_BATCH_READ); KubernetesClientConfigMapPropertySource propertySource = new KubernetesClientConfigMapPropertySource(context); verify(getRequestedFor(urlEqualTo("/api/v1/namespaces/default/configmaps"))); @@ -171,7 +173,7 @@ void constructorWithNamespaceMustNotFail() { ConfigUtils.Prefix prefix = ConfigUtils.findPrefix("prefix", false, false, null); NormalizedSource source = new NamedConfigMapNormalizedSource("bootstrap-640", "default", false, prefix, true); KubernetesClientConfigContext context = new KubernetesClientConfigContext(new CoreV1Api(), source, "default", - new MockEnvironment()); + new MockEnvironment(), false, NAMESPACED_BATCH_READ); assertThat(new KubernetesClientConfigMapPropertySource(context)).isNotNull(); } @@ -184,7 +186,7 @@ public void constructorShouldThrowExceptionOnFailureWhenFailFastIsEnabled() { ConfigUtils.Prefix prefix = ConfigUtils.findPrefix("prefix", false, false, null); NormalizedSource source = new NamedConfigMapNormalizedSource("my-config", "default", true, prefix, true); KubernetesClientConfigContext context = new KubernetesClientConfigContext(new CoreV1Api(), source, "default", - new MockEnvironment()); + new MockEnvironment(), false, NAMESPACED_BATCH_READ); assertThatThrownBy(() -> new KubernetesClientConfigMapPropertySource(context)) .isInstanceOf(IllegalStateException.class) @@ -200,7 +202,7 @@ public void constructorShouldNotThrowExceptionOnFailureWhenFailFastIsDisabled() ConfigUtils.Prefix prefix = ConfigUtils.findPrefix("prefix", false, false, null); NormalizedSource source = new NamedConfigMapNormalizedSource("my-config", "default", false, prefix, true); KubernetesClientConfigContext context = new KubernetesClientConfigContext(new CoreV1Api(), source, "default", - new MockEnvironment()); + new MockEnvironment(), false, NAMESPACED_BATCH_READ); assertThatNoException().isThrownBy((() -> new KubernetesClientConfigMapPropertySource(context))); verify(getRequestedFor(urlEqualTo("/api/v1/namespaces/default/configmaps"))); diff --git a/spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/KubernetesClientSecretsPropertySourceLocatorTests.java b/spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/KubernetesClientSecretsPropertySourceLocatorTests.java index b9ecb61cc0..c7a2ecb91b 100644 --- a/spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/KubernetesClientSecretsPropertySourceLocatorTests.java +++ b/spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/KubernetesClientSecretsPropertySourceLocatorTests.java @@ -52,6 +52,8 @@ */ class KubernetesClientSecretsPropertySourceLocatorTests { + private static final boolean NAMESPACED_BATCH_READ = true; + private static final String LIST_API = "/api/v1/namespaces/default/secrets"; private static final String LIST_BODY = "{\n" + "\t\"kind\": \"SecretList\",\n" + "\t\"apiVersion\": \"v1\",\n" @@ -115,7 +117,8 @@ void getLocateWithSources() { Collections.emptyMap(), null, null, null); SecretsConfigProperties secretsConfigProperties = new SecretsConfigProperties(true, Map.of(), List.of(), - List.of(source1, source2), true, "app", "default", false, true, false, RetryProperties.DEFAULT); + List.of(source1, source2), true, "app", "default", false, true, false, RetryProperties.DEFAULT, + NAMESPACED_BATCH_READ); PropertySource propertySource = new KubernetesClientSecretsPropertySourceLocator(api, new KubernetesNamespaceProvider(new MockEnvironment()), secretsConfigProperties) @@ -129,7 +132,8 @@ void getLocateWithOutSources() { CoreV1Api api = new CoreV1Api(); stubFor(get(LIST_API).willReturn(aResponse().withStatus(200).withBody(LIST_BODY))); SecretsConfigProperties secretsConfigProperties = new SecretsConfigProperties(true, Map.of(), List.of(), - List.of(), true, "db-secret", "default", false, true, false, RetryProperties.DEFAULT); + List.of(), true, "db-secret", "default", false, true, false, RetryProperties.DEFAULT, + NAMESPACED_BATCH_READ); PropertySource propertySource = new KubernetesClientSecretsPropertySourceLocator(api, new KubernetesNamespaceProvider(new MockEnvironment()), secretsConfigProperties) @@ -151,7 +155,7 @@ void testLocateWithoutNamespaceConstructor() { stubFor(get(LIST_API).willReturn(aResponse().withStatus(200).withBody(LIST_BODY))); SecretsConfigProperties secretsConfigProperties = new SecretsConfigProperties(true, Map.of(), List.of(), - List.of(), true, "db-secret", "", false, true, false, RetryProperties.DEFAULT); + List.of(), true, "db-secret", "", false, true, false, RetryProperties.DEFAULT, NAMESPACED_BATCH_READ); assertThatThrownBy(() -> new KubernetesClientSecretsPropertySourceLocator(api, new KubernetesNamespaceProvider(new MockEnvironment()), secretsConfigProperties) @@ -164,7 +168,8 @@ void locateShouldThrowExceptionOnFailureWhenFailFastIsEnabled() { stubFor(get(LIST_API).willReturn(aResponse().withStatus(500).withBody("Internal Server Error"))); SecretsConfigProperties secretsConfigProperties = new SecretsConfigProperties(true, Map.of(), List.of(), - List.of(), true, "db-secret", "default", false, true, true, RetryProperties.DEFAULT); + List.of(), true, "db-secret", "default", false, true, true, RetryProperties.DEFAULT, + NAMESPACED_BATCH_READ); KubernetesClientSecretsPropertySourceLocator locator = new KubernetesClientSecretsPropertySourceLocator(api, new KubernetesNamespaceProvider(new MockEnvironment()), secretsConfigProperties); @@ -179,7 +184,8 @@ void locateShouldNotThrowExceptionOnFailureWhenFailFastIsDisabled() { stubFor(get(LIST_API).willReturn(aResponse().withStatus(500).withBody("Internal Server Error"))); SecretsConfigProperties secretsConfigProperties = new SecretsConfigProperties(true, Map.of(), List.of(), - List.of(), true, "db-secret", "default", false, true, false, RetryProperties.DEFAULT); + List.of(), true, "db-secret", "default", false, true, false, RetryProperties.DEFAULT, + NAMESPACED_BATCH_READ); KubernetesClientSecretsPropertySourceLocator locator = new KubernetesClientSecretsPropertySourceLocator(api, new KubernetesNamespaceProvider(new MockEnvironment()), secretsConfigProperties); diff --git a/spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/KubernetesClientSecretsPropertySourceTests.java b/spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/KubernetesClientSecretsPropertySourceTests.java index 90b792337c..3b87f55959 100644 --- a/spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/KubernetesClientSecretsPropertySourceTests.java +++ b/spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/KubernetesClientSecretsPropertySourceTests.java @@ -57,6 +57,8 @@ */ class KubernetesClientSecretsPropertySourceTests { + private static final boolean NAMESPACED_BATCH_READ = true; + private static final String API = "/api/v1/namespaces/default/secrets"; private static final V1SecretList SECRET_LIST = new V1SecretListBuilder() @@ -126,7 +128,7 @@ static void after() { @AfterEach void afterEach() { WireMock.reset(); - new KubernetesClientSecretsCache().discardAll(); + new KubernetesClientSourcesNamespaceBatched().discardSecrets(); } @Test @@ -137,7 +139,7 @@ void emptyDataSecretTest() { NormalizedSource source = new NamedSecretNormalizedSource("db-secret", "default", false, false); KubernetesClientConfigContext context = new KubernetesClientConfigContext(api, source, "default", - new MockEnvironment()); + new MockEnvironment(), true, NAMESPACED_BATCH_READ); KubernetesClientSecretsPropertySource propertySource = new KubernetesClientSecretsPropertySource(context); assertThat(propertySource.getName()).isEqualTo("secret.db-secret.default"); @@ -151,7 +153,7 @@ void secretsTest() { NormalizedSource source = new NamedSecretNormalizedSource("db-secret", "default", false, false); KubernetesClientConfigContext context = new KubernetesClientConfigContext(api, source, "default", - new MockEnvironment()); + new MockEnvironment(), true, NAMESPACED_BATCH_READ); KubernetesClientSecretsPropertySource propertySource = new KubernetesClientSecretsPropertySource(context); assertThat(propertySource.containsProperty("password")).isTrue(); @@ -169,7 +171,7 @@ void secretLabelsTest() { NormalizedSource source = new LabeledSecretNormalizedSource("default", labels, false); KubernetesClientConfigContext context = new KubernetesClientConfigContext(api, source, "default", - new MockEnvironment()); + new MockEnvironment(), false, NAMESPACED_BATCH_READ); KubernetesClientSecretsPropertySource propertySource = new KubernetesClientSecretsPropertySource(context); assertThat(propertySource.containsProperty("spring.rabbitmq.password")).isTrue(); @@ -183,7 +185,7 @@ void constructorShouldThrowExceptionOnFailureWhenFailFastIsEnabled() { NormalizedSource source = new NamedSecretNormalizedSource("secret", "default", true, false); KubernetesClientConfigContext context = new KubernetesClientConfigContext(api, source, "default", - new MockEnvironment()); + new MockEnvironment(), true, NAMESPACED_BATCH_READ); assertThatThrownBy(() -> new KubernetesClientSecretsPropertySource(context)) .isInstanceOf(IllegalStateException.class) @@ -198,7 +200,7 @@ void constructorShouldNotThrowExceptionOnFailureWhenFailFastIsDisabled() { NormalizedSource source = new NamedSecretNormalizedSource("secret", "db-secret", false, false); KubernetesClientConfigContext context = new KubernetesClientConfigContext(api, source, "default", - new MockEnvironment()); + new MockEnvironment(), true, NAMESPACED_BATCH_READ); assertThatNoException().isThrownBy((() -> new KubernetesClientSecretsPropertySource(context))); verify(getRequestedFor(urlEqualTo(API))); diff --git a/spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/LabeledConfigMapContextToSourceDataProviderTests.java b/spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/LabeledConfigMapContextToSourceDataProviderTests.java index d84280bb0e..c3e5bba059 100644 --- a/spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/LabeledConfigMapContextToSourceDataProviderTests.java +++ b/spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/LabeledConfigMapContextToSourceDataProviderTests.java @@ -57,6 +57,8 @@ @ExtendWith(OutputCaptureExtension.class) class LabeledConfigMapContextToSourceDataProviderTests { + private static final boolean NAMESPACED_BATCH_READ = true; + private static final Map LABELS = new LinkedHashMap<>(); private static final Map RED_LABEL = Map.of("color", "red"); @@ -87,7 +89,7 @@ static void setup() { @AfterEach void afterEach() { WireMock.reset(); - new KubernetesClientConfigMapsCache().discardAll(); + new KubernetesClientSourcesNamespaceBatched().discardConfigMaps(); } /** @@ -111,7 +113,7 @@ void singleConfigMapMatchAgainstLabels() { NormalizedSource source = new LabeledConfigMapNormalizedSource(NAMESPACE, LABELS, true, false); KubernetesClientConfigContext context = new KubernetesClientConfigContext(api, source, NAMESPACE, - new MockEnvironment()); + new MockEnvironment(), false, NAMESPACED_BATCH_READ); KubernetesClientContextToSourceData data = new LabeledConfigMapContextToSourceDataProvider().get(); SourceData sourceData = data.apply(context); @@ -161,7 +163,7 @@ void twoConfigMapsMatchAgainstLabels() { NormalizedSource source = new LabeledConfigMapNormalizedSource(NAMESPACE, RED_LABEL, true, false); KubernetesClientConfigContext context = new KubernetesClientConfigContext(api, source, NAMESPACE, - new MockEnvironment()); + new MockEnvironment(), false, NAMESPACED_BATCH_READ); KubernetesClientContextToSourceData data = new LabeledConfigMapContextToSourceDataProvider().get(); SourceData sourceData = data.apply(context); @@ -193,7 +195,7 @@ void configMapNoMatch() { NormalizedSource source = new LabeledConfigMapNormalizedSource(NAMESPACE, BLUE_LABEL, true, false); KubernetesClientConfigContext context = new KubernetesClientConfigContext(api, source, NAMESPACE, - new MockEnvironment()); + new MockEnvironment(), false, NAMESPACED_BATCH_READ); KubernetesClientContextToSourceData data = new LabeledConfigMapContextToSourceDataProvider().get(); SourceData sourceData = data.apply(context); @@ -226,7 +228,7 @@ void namespaceMatch() { String wrongNamespace = NAMESPACE + "nope"; NormalizedSource source = new LabeledConfigMapNormalizedSource(wrongNamespace, LABELS, true, false); KubernetesClientConfigContext context = new KubernetesClientConfigContext(api, source, NAMESPACE, - new MockEnvironment()); + new MockEnvironment(), false, NAMESPACED_BATCH_READ); KubernetesClientContextToSourceData data = new LabeledConfigMapContextToSourceDataProvider().get(); SourceData sourceData = data.apply(context); @@ -258,7 +260,7 @@ void testWithPrefix() { ConfigUtils.Prefix mePrefix = ConfigUtils.findPrefix("me", false, false, "irrelevant"); NormalizedSource source = new LabeledConfigMapNormalizedSource(NAMESPACE, BLUE_LABEL, true, mePrefix, false); KubernetesClientConfigContext context = new KubernetesClientConfigContext(api, source, NAMESPACE, - new MockEnvironment()); + new MockEnvironment(), false, NAMESPACED_BATCH_READ); KubernetesClientContextToSourceData data = new LabeledConfigMapContextToSourceDataProvider().get(); SourceData sourceData = data.apply(context); @@ -304,7 +306,7 @@ void testTwoConfigmapsWithPrefix() { NormalizedSource source = new LabeledConfigMapNormalizedSource(NAMESPACE, BLUE_LABEL, true, ConfigUtils.Prefix.DELAYED, false); KubernetesClientConfigContext context = new KubernetesClientConfigContext(api, source, NAMESPACE, - new MockEnvironment()); + new MockEnvironment(), false, NAMESPACED_BATCH_READ); KubernetesClientContextToSourceData data = new LabeledConfigMapContextToSourceDataProvider().get(); SourceData sourceData = data.apply(context); @@ -356,7 +358,7 @@ void searchWithLabelsNoConfigmapsFound() { NormalizedSource source = new LabeledConfigMapNormalizedSource(NAMESPACE, RED_LABEL, true, ConfigUtils.Prefix.DEFAULT, true); KubernetesClientConfigContext context = new KubernetesClientConfigContext(api, source, NAMESPACE, - new MockEnvironment()); + new MockEnvironment(), false, NAMESPACED_BATCH_READ); KubernetesClientContextToSourceData data = new LabeledConfigMapContextToSourceDataProvider().get(); SourceData sourceData = data.apply(context); @@ -395,7 +397,7 @@ void searchWithLabelsOneConfigMapFound() { NormalizedSource source = new LabeledConfigMapNormalizedSource(NAMESPACE, BLUE_LABEL, true, ConfigUtils.Prefix.DEFAULT, true); KubernetesClientConfigContext context = new KubernetesClientConfigContext(api, source, NAMESPACE, - new MockEnvironment()); + new MockEnvironment(), false, NAMESPACED_BATCH_READ); KubernetesClientContextToSourceData data = new LabeledConfigMapContextToSourceDataProvider().get(); SourceData sourceData = data.apply(context); @@ -438,7 +440,8 @@ void searchWithLabelsOneConfigMapFoundAndOneFromProfileFound() { NormalizedSource source = new LabeledConfigMapNormalizedSource(NAMESPACE, BLUE_LABEL, true, ConfigUtils.Prefix.DELAYED, true); - KubernetesClientConfigContext context = new KubernetesClientConfigContext(api, source, NAMESPACE, environment); + KubernetesClientConfigContext context = new KubernetesClientConfigContext(api, source, NAMESPACE, environment, + false, NAMESPACED_BATCH_READ); KubernetesClientContextToSourceData data = new LabeledConfigMapContextToSourceDataProvider().get(); SourceData sourceData = data.apply(context); @@ -514,7 +517,8 @@ void searchWithLabelsTwoConfigMapsFoundAndOneFromProfileFound() { NormalizedSource source = new LabeledConfigMapNormalizedSource(NAMESPACE, BLUE_LABEL, true, ConfigUtils.Prefix.DELAYED, true); - KubernetesClientConfigContext context = new KubernetesClientConfigContext(api, source, NAMESPACE, environment); + KubernetesClientConfigContext context = new KubernetesClientConfigContext(api, source, NAMESPACE, environment, + false, NAMESPACED_BATCH_READ); KubernetesClientContextToSourceData data = new LabeledConfigMapContextToSourceDataProvider().get(); SourceData sourceData = data.apply(context); @@ -569,7 +573,7 @@ void cache(CapturedOutput output) { NormalizedSource redSource = new LabeledConfigMapNormalizedSource(NAMESPACE, Map.of("color", "red"), false, ConfigUtils.Prefix.DEFAULT, false); KubernetesClientConfigContext redContext = new KubernetesClientConfigContext(api, redSource, NAMESPACE, - new MockEnvironment()); + new MockEnvironment(), false, NAMESPACED_BATCH_READ); KubernetesClientContextToSourceData redData = new LabeledConfigMapContextToSourceDataProvider().get(); SourceData redSourceData = redData.apply(redContext); @@ -581,7 +585,7 @@ void cache(CapturedOutput output) { NormalizedSource greenSource = new LabeledConfigMapNormalizedSource(NAMESPACE, Map.of("color", "green"), false, ConfigUtils.Prefix.DEFAULT, false); KubernetesClientConfigContext greenContext = new KubernetesClientConfigContext(api, greenSource, NAMESPACE, - new MockEnvironment()); + new MockEnvironment(), false, NAMESPACED_BATCH_READ); KubernetesClientContextToSourceData greenData = new LabeledConfigMapContextToSourceDataProvider().get(); SourceData greenSourceData = greenData.apply(greenContext); diff --git a/spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/LabeledSecretContextToSourceDataProviderTests.java b/spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/LabeledSecretContextToSourceDataProviderTests.java index 34a743e828..088ca7e1b7 100644 --- a/spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/LabeledSecretContextToSourceDataProviderTests.java +++ b/spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/LabeledSecretContextToSourceDataProviderTests.java @@ -58,6 +58,8 @@ @ExtendWith(OutputCaptureExtension.class) class LabeledSecretContextToSourceDataProviderTests { + private static final boolean NAMESPACED_BATCH_READ = true; + private static final Map LABELS = new LinkedHashMap<>(); private static final Map RED_LABEL = Map.of("color", "red"); @@ -84,7 +86,7 @@ static void setup() { @AfterEach void afterEach() { WireMock.reset(); - new KubernetesClientSecretsCache().discardAll(); + new KubernetesClientSourcesNamespaceBatched().discardSecrets(); } /** @@ -109,7 +111,7 @@ void noMatch() { NormalizedSource source = new LabeledSecretNormalizedSource(NAMESPACE, Collections.singletonMap("color", "blue"), false); KubernetesClientConfigContext context = new KubernetesClientConfigContext(api, source, NAMESPACE, - new MockEnvironment()); + new MockEnvironment(), false, NAMESPACED_BATCH_READ); KubernetesClientContextToSourceData data = new LabeledSecretContextToSourceDataProvider().get(); SourceData sourceData = data.apply(context); @@ -137,7 +139,7 @@ void singleSecretMatchAgainstLabels() { NormalizedSource source = new LabeledSecretNormalizedSource(NAMESPACE, LABELS, false); KubernetesClientConfigContext context = new KubernetesClientConfigContext(api, source, NAMESPACE, - new MockEnvironment()); + new MockEnvironment(), false, NAMESPACED_BATCH_READ); KubernetesClientContextToSourceData data = new LabeledSecretContextToSourceDataProvider().get(); SourceData sourceData = data.apply(context); @@ -169,7 +171,7 @@ void twoSecretsMatchAgainstLabels() { NormalizedSource source = new LabeledSecretNormalizedSource(NAMESPACE, RED_LABEL, false); KubernetesClientConfigContext context = new KubernetesClientConfigContext(api, source, NAMESPACE, - new MockEnvironment()); + new MockEnvironment(), false, NAMESPACED_BATCH_READ); KubernetesClientContextToSourceData data = new LabeledSecretContextToSourceDataProvider().get(); SourceData sourceData = data.apply(context); @@ -194,7 +196,7 @@ void namespaceMatch() { NormalizedSource source = new LabeledSecretNormalizedSource(NAMESPACE + "nope", LABELS, false); KubernetesClientConfigContext context = new KubernetesClientConfigContext(api, source, NAMESPACE, - new MockEnvironment()); + new MockEnvironment(), false, NAMESPACED_BATCH_READ); KubernetesClientContextToSourceData data = new LabeledSecretContextToSourceDataProvider().get(); SourceData sourceData = data.apply(context); @@ -227,7 +229,7 @@ void testWithPrefix() { ConfigUtils.Prefix prefix = ConfigUtils.findPrefix("me", false, false, null); NormalizedSource source = new LabeledSecretNormalizedSource(NAMESPACE, Map.of("color", "blue"), false, prefix); KubernetesClientConfigContext context = new KubernetesClientConfigContext(api, source, NAMESPACE, - new MockEnvironment()); + new MockEnvironment(), false, NAMESPACED_BATCH_READ); KubernetesClientContextToSourceData data = new LabeledSecretContextToSourceDataProvider().get(); SourceData sourceData = data.apply(context); @@ -272,7 +274,7 @@ void testTwoSecretsWithPrefix() { NormalizedSource source = new LabeledSecretNormalizedSource(NAMESPACE, Map.of("color", "blue"), false, ConfigUtils.Prefix.DELAYED); KubernetesClientConfigContext context = new KubernetesClientConfigContext(api, source, NAMESPACE, - new MockEnvironment()); + new MockEnvironment(), false, NAMESPACED_BATCH_READ); KubernetesClientContextToSourceData data = new LabeledSecretContextToSourceDataProvider().get(); SourceData sourceData = data.apply(context); @@ -331,7 +333,7 @@ void searchWithLabelsOneSecretFound() { NormalizedSource source = new LabeledSecretNormalizedSource(NAMESPACE, Map.of("color", "blue"), false, ConfigUtils.Prefix.DEFAULT); KubernetesClientConfigContext context = new KubernetesClientConfigContext(api, source, NAMESPACE, - new MockEnvironment()); + new MockEnvironment(), false, NAMESPACED_BATCH_READ); KubernetesClientContextToSourceData data = new LabeledSecretContextToSourceDataProvider().get(); SourceData sourceData = data.apply(context); @@ -374,7 +376,8 @@ void searchWithLabelsOneSecretFoundAndOneFromProfileFound() { NormalizedSource source = new LabeledSecretNormalizedSource(NAMESPACE, Map.of("color", "blue"), false, ConfigUtils.Prefix.DELAYED); - KubernetesClientConfigContext context = new KubernetesClientConfigContext(api, source, NAMESPACE, environment); + KubernetesClientConfigContext context = new KubernetesClientConfigContext(api, source, NAMESPACE, environment, + false, NAMESPACED_BATCH_READ); KubernetesClientContextToSourceData data = new LabeledSecretContextToSourceDataProvider().get(); SourceData sourceData = data.apply(context); @@ -450,7 +453,8 @@ void searchWithLabelsTwoSecretsFoundAndOneFromProfileFound() { NormalizedSource source = new LabeledSecretNormalizedSource(NAMESPACE, Map.of("color", "blue"), false, ConfigUtils.Prefix.DELAYED); - KubernetesClientConfigContext context = new KubernetesClientConfigContext(api, source, NAMESPACE, environment); + KubernetesClientConfigContext context = new KubernetesClientConfigContext(api, source, NAMESPACE, environment, + false, NAMESPACED_BATCH_READ); KubernetesClientContextToSourceData data = new LabeledSecretContextToSourceDataProvider().get(); SourceData sourceData = data.apply(context); @@ -491,7 +495,7 @@ void testYaml() { NormalizedSource source = new LabeledSecretNormalizedSource(NAMESPACE, Map.of("color", "blue"), false, ConfigUtils.Prefix.DEFAULT); KubernetesClientConfigContext context = new KubernetesClientConfigContext(api, source, NAMESPACE, - new MockEnvironment()); + new MockEnvironment(), false, NAMESPACED_BATCH_READ); KubernetesClientContextToSourceData data = new LabeledSecretContextToSourceDataProvider().get(); SourceData sourceData = data.apply(context); @@ -536,7 +540,7 @@ void cache(CapturedOutput output) { NormalizedSource redSource = new LabeledSecretNormalizedSource(NAMESPACE, Map.of("color", "red"), false, ConfigUtils.Prefix.DEFAULT); KubernetesClientConfigContext redContext = new KubernetesClientConfigContext(api, redSource, NAMESPACE, - new MockEnvironment()); + new MockEnvironment(), false, NAMESPACED_BATCH_READ); KubernetesClientContextToSourceData redData = new LabeledSecretContextToSourceDataProvider().get(); SourceData redSourceData = redData.apply(redContext); @@ -548,7 +552,7 @@ void cache(CapturedOutput output) { NormalizedSource greenSource = new LabeledSecretNormalizedSource(NAMESPACE, Map.of("color", "green"), false, ConfigUtils.Prefix.DEFAULT); KubernetesClientConfigContext greenContext = new KubernetesClientConfigContext(api, greenSource, NAMESPACE, - new MockEnvironment()); + new MockEnvironment(), false, NAMESPACED_BATCH_READ); KubernetesClientContextToSourceData greenData = new LabeledSecretContextToSourceDataProvider().get(); SourceData greenSourceData = greenData.apply(greenContext); diff --git a/spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/NamedConfigMapContextToSourceDataProviderTests.java b/spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/NamedConfigMapContextToSourceDataProviderTests.java index dcdbda388e..6152bffa52 100644 --- a/spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/NamedConfigMapContextToSourceDataProviderTests.java +++ b/spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/NamedConfigMapContextToSourceDataProviderTests.java @@ -55,6 +55,8 @@ @ExtendWith(OutputCaptureExtension.class) class NamedConfigMapContextToSourceDataProviderTests { + private static final boolean NAMESPACED_BATCH_READ = true; + private static final String NAMESPACE = "default"; private static final String RED_CONFIG_MAP_NAME = "red"; @@ -82,7 +84,7 @@ static void setup() { @AfterEach void afterEach() { WireMock.reset(); - new KubernetesClientConfigMapsCache().discardAll(); + new KubernetesClientSourcesNamespaceBatched().discardConfigMaps(); } /** @@ -104,7 +106,7 @@ void noMatch() { NormalizedSource source = new NamedConfigMapNormalizedSource(BLUE_CONFIG_MAP_NAME, NAMESPACE, true, false); KubernetesClientConfigContext context = new KubernetesClientConfigContext(api, source, NAMESPACE, - new MockEnvironment()); + new MockEnvironment(), true, NAMESPACED_BATCH_READ); KubernetesClientContextToSourceData data = new NamedConfigMapContextToSourceDataProvider().get(); SourceData sourceData = data.apply(context); @@ -134,7 +136,7 @@ void match() { NormalizedSource source = new NamedConfigMapNormalizedSource(RED_CONFIG_MAP_NAME, NAMESPACE, true, false); KubernetesClientConfigContext context = new KubernetesClientConfigContext(api, source, NAMESPACE, - new MockEnvironment()); + new MockEnvironment(), true, NAMESPACED_BATCH_READ); KubernetesClientContextToSourceData data = new NamedConfigMapContextToSourceDataProvider().get(); SourceData sourceData = data.apply(context); @@ -147,8 +149,6 @@ void match() { /** *
 	 *     - two configmaps deployed : "red" and "red-with-profile".
-	 *     - "red" is matched directly, "red-with-profile" is matched because we have an active profile
-	 *       "active-profile"
 	 * 
*/ @Test @@ -173,7 +173,7 @@ void matchIncludeSingleProfile() { MockEnvironment environment = new MockEnvironment(); environment.setActiveProfiles("with-profile"); KubernetesClientConfigContext context = new KubernetesClientConfigContext(api, source, NAMESPACE, environment, - false); + true, NAMESPACED_BATCH_READ); KubernetesClientContextToSourceData data = new NamedConfigMapContextToSourceDataProvider().get(); SourceData sourceData = data.apply(context); @@ -215,7 +215,8 @@ void matchIncludeSingleProfileWithPrefix() { true); MockEnvironment environment = new MockEnvironment(); environment.setActiveProfiles("with-profile"); - KubernetesClientConfigContext context = new KubernetesClientConfigContext(api, source, NAMESPACE, environment); + KubernetesClientConfigContext context = new KubernetesClientConfigContext(api, source, NAMESPACE, environment, + true, NAMESPACED_BATCH_READ); KubernetesClientContextToSourceData data = new NamedConfigMapContextToSourceDataProvider().get(); SourceData sourceData = data.apply(context); @@ -270,7 +271,8 @@ void matchIncludeTwoProfilesWithPrefix() { true); MockEnvironment environment = new MockEnvironment(); environment.setActiveProfiles("with-taste", "with-shape"); - KubernetesClientConfigContext context = new KubernetesClientConfigContext(api, source, NAMESPACE, environment); + KubernetesClientConfigContext context = new KubernetesClientConfigContext(api, source, NAMESPACE, environment, + true, NAMESPACED_BATCH_READ); KubernetesClientContextToSourceData data = new NamedConfigMapContextToSourceDataProvider().get(); SourceData sourceData = data.apply(context); @@ -304,7 +306,7 @@ void matchWithName() { ConfigUtils.Prefix prefix = ConfigUtils.findPrefix("some", false, false, null); NormalizedSource source = new NamedConfigMapNormalizedSource("application", NAMESPACE, true, prefix, false); KubernetesClientConfigContext context = new KubernetesClientConfigContext(api, source, NAMESPACE, - new MockEnvironment()); + new MockEnvironment(), true, NAMESPACED_BATCH_READ); KubernetesClientContextToSourceData data = new NamedConfigMapContextToSourceDataProvider().get(); SourceData sourceData = data.apply(context); @@ -335,7 +337,7 @@ void namespaceMatch() { String wrongNamespace = NAMESPACE + "nope"; NormalizedSource source = new NamedConfigMapNormalizedSource(RED_CONFIG_MAP_NAME, wrongNamespace, true, false); KubernetesClientConfigContext context = new KubernetesClientConfigContext(api, source, NAMESPACE, - new MockEnvironment()); + new MockEnvironment(), true, NAMESPACED_BATCH_READ); KubernetesClientContextToSourceData data = new NamedConfigMapContextToSourceDataProvider().get(); SourceData sourceData = data.apply(context); @@ -362,7 +364,7 @@ void testSingleYaml() { NormalizedSource source = new NamedConfigMapNormalizedSource(RED_CONFIG_MAP_NAME, NAMESPACE, true, false); KubernetesClientConfigContext context = new KubernetesClientConfigContext(api, source, NAMESPACE, - new MockEnvironment()); + new MockEnvironment(), true, NAMESPACED_BATCH_READ); KubernetesClientContextToSourceData data = new NamedConfigMapContextToSourceDataProvider().get(); SourceData sourceData = data.apply(context); @@ -394,7 +396,8 @@ void testCorrectNameWithProfile() { environment.setActiveProfiles("k8s"); NormalizedSource source = new NamedConfigMapNormalizedSource("one", NAMESPACE, true, true); - KubernetesClientConfigContext context = new KubernetesClientConfigContext(api, source, NAMESPACE, environment); + KubernetesClientConfigContext context = new KubernetesClientConfigContext(api, source, NAMESPACE, environment, + true, NAMESPACED_BATCH_READ); KubernetesClientContextToSourceData data = new NamedConfigMapContextToSourceDataProvider().get(); SourceData sourceData = data.apply(context); @@ -433,7 +436,7 @@ void cache(CapturedOutput output) { NormalizedSource redSource = new NamedConfigMapNormalizedSource("red", NAMESPACE, true, false); KubernetesClientConfigContext redContext = new KubernetesClientConfigContext(api, redSource, NAMESPACE, - environment); + environment, true, NAMESPACED_BATCH_READ); KubernetesClientContextToSourceData redData = new NamedConfigMapContextToSourceDataProvider().get(); SourceData redSourceData = redData.apply(redContext); @@ -443,7 +446,7 @@ void cache(CapturedOutput output) { NormalizedSource greenSource = new NamedConfigMapNormalizedSource("green", NAMESPACE, true, true); KubernetesClientConfigContext greenContext = new KubernetesClientConfigContext(api, greenSource, NAMESPACE, - environment); + environment, false, NAMESPACED_BATCH_READ); KubernetesClientContextToSourceData greenData = new NamedConfigMapContextToSourceDataProvider().get(); SourceData greenSourceData = greenData.apply(greenContext); diff --git a/spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/NamedSecretContextToSourceDataProviderTests.java b/spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/NamedSecretContextToSourceDataProviderTests.java index e78b2426d4..4c4e09c3cb 100644 --- a/spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/NamedSecretContextToSourceDataProviderTests.java +++ b/spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/NamedSecretContextToSourceDataProviderTests.java @@ -53,6 +53,8 @@ @ExtendWith(OutputCaptureExtension.class) class NamedSecretContextToSourceDataProviderTests { + private static final boolean NAMESPACED_BATCH_READ = true; + private static final ConfigUtils.Prefix PREFIX = ConfigUtils.findPrefix("some", false, false, "irrelevant"); private static final String NAMESPACE = "default"; @@ -74,7 +76,7 @@ static void setup() { @AfterEach void afterEach() { WireMock.reset(); - new KubernetesClientSecretsCache().discardAll(); + new KubernetesClientSourcesNamespaceBatched().discardSecrets(); } /** @@ -94,7 +96,7 @@ void singleSecretMatchAgainstLabels() { // blue does not match red NormalizedSource source = new NamedSecretNormalizedSource("red", NAMESPACE, false, false); KubernetesClientConfigContext context = new KubernetesClientConfigContext(api, source, NAMESPACE, - new MockEnvironment()); + new MockEnvironment(), false, NAMESPACED_BATCH_READ); KubernetesClientContextToSourceData data = new NamedSecretContextToSourceDataProvider().get(); SourceData sourceData = data.apply(context); @@ -134,7 +136,7 @@ void twoSecretMatchAgainstLabels() { // blue does not match red, nor pink NormalizedSource source = new NamedSecretNormalizedSource("red", NAMESPACE, false, false); KubernetesClientConfigContext context = new KubernetesClientConfigContext(api, source, NAMESPACE, - new MockEnvironment()); + new MockEnvironment(), false, NAMESPACED_BATCH_READ); KubernetesClientContextToSourceData data = new NamedSecretContextToSourceDataProvider().get(); SourceData sourceData = data.apply(context); @@ -163,7 +165,7 @@ void testSecretNoMatch() { // blue does not match red NormalizedSource source = new NamedSecretNormalizedSource("blue", NAMESPACE, false, false); KubernetesClientConfigContext context = new KubernetesClientConfigContext(api, source, NAMESPACE, - new MockEnvironment()); + new MockEnvironment(), false, NAMESPACED_BATCH_READ); KubernetesClientContextToSourceData data = new NamedSecretContextToSourceDataProvider().get(); SourceData sourceData = data.apply(context); @@ -194,7 +196,7 @@ void namespaceMatch() { String wrongNamespace = NAMESPACE + "nope"; NormalizedSource source = new NamedSecretNormalizedSource("red", wrongNamespace, false, false); KubernetesClientConfigContext context = new KubernetesClientConfigContext(api, source, NAMESPACE, - new MockEnvironment()); + new MockEnvironment(), false, NAMESPACED_BATCH_READ); KubernetesClientContextToSourceData data = new NamedSecretContextToSourceDataProvider().get(); SourceData sourceData = data.apply(context); @@ -230,7 +232,7 @@ void matchIncludeSingleProfile() { MockEnvironment environment = new MockEnvironment(); environment.addActiveProfile("with-profile"); KubernetesClientConfigContext context = new KubernetesClientConfigContext(api, source, NAMESPACE, environment, - false); + false, NAMESPACED_BATCH_READ); KubernetesClientContextToSourceData data = new NamedSecretContextToSourceDataProvider().get(); SourceData sourceData = data.apply(context); @@ -268,7 +270,8 @@ void matchIncludeSingleProfileWithPrefix() { NormalizedSource source = new NamedSecretNormalizedSource("red", NAMESPACE, true, PREFIX, true); MockEnvironment environment = new MockEnvironment(); environment.addActiveProfile("with-taste"); - KubernetesClientConfigContext context = new KubernetesClientConfigContext(api, source, NAMESPACE, environment); + KubernetesClientConfigContext context = new KubernetesClientConfigContext(api, source, NAMESPACE, environment, + true, NAMESPACED_BATCH_READ); KubernetesClientContextToSourceData data = new NamedSecretContextToSourceDataProvider().get(); SourceData sourceData = data.apply(context); @@ -312,7 +315,8 @@ void matchIncludeTwoProfilesWithPrefix() { NormalizedSource source = new NamedSecretNormalizedSource("red", NAMESPACE, true, PREFIX, true); MockEnvironment environment = new MockEnvironment(); environment.setActiveProfiles("with-taste", "with-shape"); - KubernetesClientConfigContext context = new KubernetesClientConfigContext(api, source, NAMESPACE, environment); + KubernetesClientConfigContext context = new KubernetesClientConfigContext(api, source, NAMESPACE, environment, + true, NAMESPACED_BATCH_READ); KubernetesClientContextToSourceData data = new NamedSecretContextToSourceDataProvider().get(); SourceData sourceData = data.apply(context); @@ -344,7 +348,7 @@ void testSingleYaml() { NormalizedSource source = new NamedSecretNormalizedSource("single-yaml", NAMESPACE, true, false); KubernetesClientConfigContext context = new KubernetesClientConfigContext(api, source, NAMESPACE, - new MockEnvironment()); + new MockEnvironment(), false, NAMESPACED_BATCH_READ); KubernetesClientContextToSourceData data = new NamedSecretContextToSourceDataProvider().get(); SourceData sourceData = data.apply(context); @@ -383,7 +387,7 @@ void cache(CapturedOutput output) { NormalizedSource redSource = new NamedSecretNormalizedSource("red", NAMESPACE, true, false); KubernetesClientConfigContext redContext = new KubernetesClientConfigContext(api, redSource, NAMESPACE, - environment); + environment, false, NAMESPACED_BATCH_READ); KubernetesClientContextToSourceData redData = new NamedSecretContextToSourceDataProvider().get(); SourceData redSourceData = redData.apply(redContext); @@ -393,7 +397,7 @@ void cache(CapturedOutput output) { NormalizedSource greenSource = new NamedSecretNormalizedSource("green", NAMESPACE, true, true); KubernetesClientConfigContext greenContext = new KubernetesClientConfigContext(api, greenSource, NAMESPACE, - environment); + environment, false, NAMESPACED_BATCH_READ); KubernetesClientContextToSourceData greenData = new NamedSecretContextToSourceDataProvider().get(); SourceData greenSourceData = greenData.apply(greenContext); diff --git a/spring-cloud-kubernetes-commons/src/main/java/org/springframework/cloud/kubernetes/commons/config/ConfigMapCache.java b/spring-cloud-kubernetes-commons/src/main/java/org/springframework/cloud/kubernetes/commons/config/ConfigMapCache.java index 8c55724660..5ffae1b338 100644 --- a/spring-cloud-kubernetes-commons/src/main/java/org/springframework/cloud/kubernetes/commons/config/ConfigMapCache.java +++ b/spring-cloud-kubernetes-commons/src/main/java/org/springframework/cloud/kubernetes/commons/config/ConfigMapCache.java @@ -24,7 +24,7 @@ public interface ConfigMapCache { /** * Discards all stored entries from the cache. */ - void discardAll(); + void discardConfigMaps(); /** * an implementation that does nothing. In the next major release it will become @@ -33,7 +33,7 @@ public interface ConfigMapCache { class NOOPCache implements ConfigMapCache { @Override - public void discardAll() { + public void discardConfigMaps() { } } diff --git a/spring-cloud-kubernetes-commons/src/main/java/org/springframework/cloud/kubernetes/commons/config/ConfigMapPropertySourceLocator.java b/spring-cloud-kubernetes-commons/src/main/java/org/springframework/cloud/kubernetes/commons/config/ConfigMapPropertySourceLocator.java index 9f430675c7..3d0978bcc0 100644 --- a/spring-cloud-kubernetes-commons/src/main/java/org/springframework/cloud/kubernetes/commons/config/ConfigMapPropertySourceLocator.java +++ b/spring-cloud-kubernetes-commons/src/main/java/org/springframework/cloud/kubernetes/commons/config/ConfigMapPropertySourceLocator.java @@ -87,7 +87,7 @@ public PropertySource locate(Environment environment) { addPropertySourcesFromPaths(environment, composite); - cache.discardAll(); + cache.discardConfigMaps(); return composite; } return null; diff --git a/spring-cloud-kubernetes-commons/src/main/java/org/springframework/cloud/kubernetes/commons/config/ConfigUtils.java b/spring-cloud-kubernetes-commons/src/main/java/org/springframework/cloud/kubernetes/commons/config/ConfigUtils.java index eadb08f44e..8f8bed7344 100644 --- a/spring-cloud-kubernetes-commons/src/main/java/org/springframework/cloud/kubernetes/commons/config/ConfigUtils.java +++ b/spring-cloud-kubernetes-commons/src/main/java/org/springframework/cloud/kubernetes/commons/config/ConfigUtils.java @@ -179,6 +179,9 @@ public static String sourceName(String target, String applicationName, String na public static MultipleSourcesContainer processNamedData(List strippedSources, Environment environment, LinkedHashSet sourceNames, String namespace, boolean decode) { + if (strippedSources.isEmpty()) { + return MultipleSourcesContainer.empty(); + } return processNamedData(strippedSources, environment, sourceNames, namespace, decode, true); } diff --git a/spring-cloud-kubernetes-commons/src/main/java/org/springframework/cloud/kubernetes/commons/config/SecretsCache.java b/spring-cloud-kubernetes-commons/src/main/java/org/springframework/cloud/kubernetes/commons/config/SecretsCache.java index 90a35bc264..3c8500c061 100644 --- a/spring-cloud-kubernetes-commons/src/main/java/org/springframework/cloud/kubernetes/commons/config/SecretsCache.java +++ b/spring-cloud-kubernetes-commons/src/main/java/org/springframework/cloud/kubernetes/commons/config/SecretsCache.java @@ -24,7 +24,7 @@ public interface SecretsCache { /** * Discards all stored entries from the cache. */ - void discardAll(); + void discardSecrets(); /** * an implementation that does nothing. In the next major release it will become @@ -33,7 +33,7 @@ public interface SecretsCache { class NOOPCache implements SecretsCache { @Override - public void discardAll() { + public void discardSecrets() { } } diff --git a/spring-cloud-kubernetes-commons/src/main/java/org/springframework/cloud/kubernetes/commons/config/SecretsPropertySourceLocator.java b/spring-cloud-kubernetes-commons/src/main/java/org/springframework/cloud/kubernetes/commons/config/SecretsPropertySourceLocator.java index d02b076f63..a86c4626fe 100644 --- a/spring-cloud-kubernetes-commons/src/main/java/org/springframework/cloud/kubernetes/commons/config/SecretsPropertySourceLocator.java +++ b/spring-cloud-kubernetes-commons/src/main/java/org/springframework/cloud/kubernetes/commons/config/SecretsPropertySourceLocator.java @@ -90,7 +90,7 @@ public PropertySource locate(Environment environment) { getSecretsPropertySourceForSingleSecret(env, s, properties.namespacedBatchRead()))); } - cache.discardAll(); + cache.discardSecrets(); return composite; } return null; diff --git a/spring-cloud-kubernetes-controllers/spring-cloud-kubernetes-configserver/src/main/java/org/springframework/cloud/kubernetes/configserver/KubernetesConfigServerAutoConfiguration.java b/spring-cloud-kubernetes-controllers/spring-cloud-kubernetes-configserver/src/main/java/org/springframework/cloud/kubernetes/configserver/KubernetesConfigServerAutoConfiguration.java index 1028151d1d..d24d19ea51 100644 --- a/spring-cloud-kubernetes-controllers/spring-cloud-kubernetes-configserver/src/main/java/org/springframework/cloud/kubernetes/configserver/KubernetesConfigServerAutoConfiguration.java +++ b/spring-cloud-kubernetes-controllers/spring-cloud-kubernetes-configserver/src/main/java/org/springframework/cloud/kubernetes/configserver/KubernetesConfigServerAutoConfiguration.java @@ -80,7 +80,7 @@ public KubernetesPropertySourceSupplier configMapPropertySourceSupplier( NamedConfigMapNormalizedSource source = new NamedConfigMapNormalizedSource(applicationName, space, false, ConfigUtils.Prefix.DEFAULT, true, true); KubernetesClientConfigContext context = new KubernetesClientConfigContext(coreApi, source, space, - springEnv, false); + springEnv, false, true); propertySources.add(new KubernetesClientConfigMapPropertySource(context)); }); @@ -100,7 +100,7 @@ public KubernetesPropertySourceSupplier secretsPropertySourceSupplier(Kubernetes NormalizedSource source = new NamedSecretNormalizedSource(applicationName, space, false, ConfigUtils.Prefix.DEFAULT, true, true); KubernetesClientConfigContext context = new KubernetesClientConfigContext(coreApi, source, space, - springEnv, false); + springEnv, false, true); propertySources.add(new KubernetesClientSecretsPropertySource(context)); }); diff --git a/spring-cloud-kubernetes-fabric8-config/src/main/java/org/springframework/cloud/kubernetes/fabric8/config/Fabric8ConfigDataLocationResolver.java b/spring-cloud-kubernetes-fabric8-config/src/main/java/org/springframework/cloud/kubernetes/fabric8/config/Fabric8ConfigDataLocationResolver.java index 982266f250..a824605351 100644 --- a/spring-cloud-kubernetes-fabric8-config/src/main/java/org/springframework/cloud/kubernetes/fabric8/config/Fabric8ConfigDataLocationResolver.java +++ b/spring-cloud-kubernetes-fabric8-config/src/main/java/org/springframework/cloud/kubernetes/fabric8/config/Fabric8ConfigDataLocationResolver.java @@ -63,7 +63,7 @@ protected void registerBeans(ConfigDataLocationResolverContext resolverContext, kubernetesClient, configMapProperties, namespaceProvider); if (isRetryEnabledForConfigMap(configMapProperties)) { configMapPropertySourceLocator = new ConfigDataRetryableConfigMapPropertySourceLocator( - configMapPropertySourceLocator, configMapProperties, new Fabric8ConfigMapsNamespaceBatched()); + configMapPropertySourceLocator, configMapProperties, new Fabric8SourcesNamespaceBatched()); } registerSingle(bootstrapContext, ConfigMapPropertySourceLocator.class, configMapPropertySourceLocator, @@ -75,7 +75,7 @@ protected void registerBeans(ConfigDataLocationResolverContext resolverContext, kubernetesClient, secretsProperties, namespaceProvider); if (isRetryEnabledForSecrets(secretsProperties)) { secretsPropertySourceLocator = new ConfigDataRetryableSecretsPropertySourceLocator( - secretsPropertySourceLocator, secretsProperties, new Fabric8SecretsNamespaceBatched()); + secretsPropertySourceLocator, secretsProperties, new Fabric8SourcesNamespaceBatched()); } registerSingle(bootstrapContext, SecretsPropertySourceLocator.class, secretsPropertySourceLocator, diff --git a/spring-cloud-kubernetes-fabric8-config/src/main/java/org/springframework/cloud/kubernetes/fabric8/config/Fabric8ConfigMapPropertySourceLocator.java b/spring-cloud-kubernetes-fabric8-config/src/main/java/org/springframework/cloud/kubernetes/fabric8/config/Fabric8ConfigMapPropertySourceLocator.java index ff131d1af5..bf9a883148 100644 --- a/spring-cloud-kubernetes-fabric8-config/src/main/java/org/springframework/cloud/kubernetes/fabric8/config/Fabric8ConfigMapPropertySourceLocator.java +++ b/spring-cloud-kubernetes-fabric8-config/src/main/java/org/springframework/cloud/kubernetes/fabric8/config/Fabric8ConfigMapPropertySourceLocator.java @@ -45,7 +45,7 @@ public class Fabric8ConfigMapPropertySourceLocator extends ConfigMapPropertySour Fabric8ConfigMapPropertySourceLocator(KubernetesClient client, ConfigMapConfigProperties properties, KubernetesNamespaceProvider provider) { - super(properties, new Fabric8ConfigMapsNamespaceBatched()); + super(properties, new Fabric8SourcesNamespaceBatched()); this.client = client; this.provider = provider; } diff --git a/spring-cloud-kubernetes-fabric8-config/src/main/java/org/springframework/cloud/kubernetes/fabric8/config/Fabric8ConfigUtils.java b/spring-cloud-kubernetes-fabric8-config/src/main/java/org/springframework/cloud/kubernetes/fabric8/config/Fabric8ConfigUtils.java index 73ff9e0650..c77271ac17 100644 --- a/spring-cloud-kubernetes-fabric8-config/src/main/java/org/springframework/cloud/kubernetes/fabric8/config/Fabric8ConfigUtils.java +++ b/spring-cloud-kubernetes-fabric8-config/src/main/java/org/springframework/cloud/kubernetes/fabric8/config/Fabric8ConfigUtils.java @@ -21,8 +21,6 @@ import java.util.Map; import java.util.Set; -import io.fabric8.kubernetes.api.model.ConfigMap; -import io.fabric8.kubernetes.api.model.Secret; import io.fabric8.kubernetes.client.KubernetesClient; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; @@ -35,12 +33,10 @@ import org.springframework.cloud.kubernetes.fabric8.Fabric8Utils; import org.springframework.core.env.Environment; -import static org.springframework.cloud.kubernetes.fabric8.config.Fabric8ConfigMapsNamespaceBatched.configMapsByNamespace; -import static org.springframework.cloud.kubernetes.fabric8.config.Fabric8SecretsNamespaceBatched.secretsByNamespace; -import static org.springframework.cloud.kubernetes.fabric8.config.Fabric8SourcesNonBatched.configMapsByLabels; -import static org.springframework.cloud.kubernetes.fabric8.config.Fabric8SourcesNonBatched.configMapsByName; -import static org.springframework.cloud.kubernetes.fabric8.config.Fabric8SourcesNonBatched.secretsByLabels; -import static org.springframework.cloud.kubernetes.fabric8.config.Fabric8SourcesNonBatched.secretsByName; +import static org.springframework.cloud.kubernetes.fabric8.config.Fabric8SourcesNamespaceBatched.strippedConfigMapsBatchRead; +import static org.springframework.cloud.kubernetes.fabric8.config.Fabric8SourcesNamespaceBatched.strippedSecretsBatchRead; +import static org.springframework.cloud.kubernetes.fabric8.config.Fabric8SourcesNonNamespaceBatched.strippedConfigMapsNonBatchRead; +import static org.springframework.cloud.kubernetes.fabric8.config.Fabric8SourcesNonNamespaceBatched.strippedSecretsNonBatchRead; /** * Utility class that works with configuration properties. @@ -67,41 +63,16 @@ public static Set namespaces(KubernetesClient client, KubernetesNamespac return namespaces; } - /** - *
-	 *     1. read all secrets in the provided namespace
-	 *     2. from the above, filter the ones that we care about (filter by labels)
-	 *     3. see if any of the secrets from (2) has a single yaml/properties file
-	 *     4. gather all the names of the secrets + data they hold
-	 * 
- */ - static MultipleSourcesContainer secretsDataByLabels(KubernetesClient client, String namespace, - Map labels, Environment environment, boolean namespacedBatchRead) { - - List strippedSecrets; - - if (namespacedBatchRead) { - LOG.debug("Will read all secrets in namespace : " + namespace); - strippedSecrets = strippedSecretsBatchRead(client, namespace); - } - else { - LOG.debug("Will read individual secrets in namespace : " + namespace + " with labels : " + labels); - strippedSecrets = strippedSecretsByLabels(client, namespace, labels); - } - - return ConfigUtils.processLabeledData(strippedSecrets, environment, labels, namespace, true); - } - /** *
 	 *     1. read all config maps in the provided namespace
-	 *     2. from the above, filter the ones that we care about (filter by labels)
-	 *     3. see if any from (2) has a single yaml/properties file
+	 *     2. from the above, filter the ones that we care about (by name)
+	 *     3. see if any of the config maps has a single yaml/properties file
 	 *     4. gather all the names of the config maps + data they hold
 	 * 
*/ - static MultipleSourcesContainer configMapsDataByLabels(KubernetesClient client, String namespace, - Map labels, Environment environment, boolean namespacedBatchRead) { + static MultipleSourcesContainer configMapsDataByName(KubernetesClient client, String namespace, + LinkedHashSet sourceNames, Environment environment, boolean namespacedBatchRead) { List strippedConfigMaps; @@ -110,15 +81,11 @@ static MultipleSourcesContainer configMapsDataByLabels(KubernetesClient client, strippedConfigMaps = strippedConfigMapsBatchRead(client, namespace); } else { - LOG.debug("Will read individual configmaps in namespace : " + namespace + " with labels : " + labels); - strippedConfigMaps = strippedConfigMapsByLabels(client, namespace, labels); - } - - if (strippedConfigMaps.isEmpty()) { - return MultipleSourcesContainer.empty(); + LOG.debug("Will read individual configmaps in namespace : " + namespace + " with names : " + sourceNames); + strippedConfigMaps = strippedConfigMapsNonBatchRead(client, namespace, sourceNames); } - return ConfigUtils.processLabeledData(strippedConfigMaps, environment, labels, namespace, false); + return ConfigUtils.processNamedData(strippedConfigMaps, environment, sourceNames, namespace, false); } /** @@ -140,25 +107,22 @@ static MultipleSourcesContainer secretsDataByName(KubernetesClient client, Strin } else { LOG.debug("Will read individual secrets in namespace : " + namespace + " with names : " + sourceNames); - strippedSecrets = strippedSecretsByName(client, namespace, sourceNames); + strippedSecrets = strippedSecretsNonBatchRead(client, namespace, sourceNames); } - if (strippedSecrets.isEmpty()) { - return MultipleSourcesContainer.empty(); - } return ConfigUtils.processNamedData(strippedSecrets, environment, sourceNames, namespace, true); } /** *
 	 *     1. read all config maps in the provided namespace
-	 *     2. from the above, filter the ones that we care about (by name)
-	 *     3. see if any of the config maps has a single yaml/properties file
+	 *     2. from the above, filter the ones that we care about (filter by labels)
+	 *     3. see if any from (2) has a single yaml/properties file
 	 *     4. gather all the names of the config maps + data they hold
 	 * 
*/ - static MultipleSourcesContainer configMapsDataByName(KubernetesClient client, String namespace, - LinkedHashSet sourceNames, Environment environment, boolean namespacedBatchRead) { + static MultipleSourcesContainer configMapsDataByLabels(KubernetesClient client, String namespace, + Map labels, Environment environment, boolean namespacedBatchRead) { List strippedConfigMaps; @@ -167,107 +131,36 @@ static MultipleSourcesContainer configMapsDataByName(KubernetesClient client, St strippedConfigMaps = strippedConfigMapsBatchRead(client, namespace); } else { - LOG.debug("Will read individual configmaps in namespace : " + namespace + " with names : " + sourceNames); - strippedConfigMaps = strippedConfigMapsByName(client, namespace, sourceNames); - } - - if (strippedConfigMaps.isEmpty()) { - return MultipleSourcesContainer.empty(); - } - return ConfigUtils.processNamedData(strippedConfigMaps, environment, sourceNames, namespace, false); - } - - static List strippedConfigMaps(List configMaps) { - return configMaps.stream() - .map(configMap -> new StrippedSourceContainer(configMap.getMetadata().getLabels(), - configMap.getMetadata().getName(), configMap.getData())) - .toList(); - } - - static List strippedSecrets(List secrets) { - return secrets.stream() - .map(secret -> new StrippedSourceContainer(secret.getMetadata().getLabels(), secret.getMetadata().getName(), - secret.getData())) - .toList(); - } - - /** - * read specific configmaps (by name) in the given namespaces, and strip them down to - * only needed fields. - */ - private static List strippedConfigMapsByName(KubernetesClient client, String namespace, - LinkedHashSet sourceNames) { - List strippedConfigMaps = configMapsByName(client, namespace, sourceNames); - if (strippedConfigMaps.isEmpty()) { - LOG.debug("No configmaps in namespace '" + namespace + "'"); - } - - return strippedConfigMaps; - } - - /** - * read specific secrets (by name) in the given namespaces, and strip them down to - * only needed fields. - */ - private static List strippedSecretsByName(KubernetesClient client, String namespace, - LinkedHashSet sourceNames) { - List strippedSecrets = secretsByName(client, namespace, sourceNames); - if (strippedSecrets.isEmpty()) { - LOG.debug("No secrets in namespace '" + namespace + "'"); - } - - return strippedSecrets; - } - - /** - * read specific configmaps (by labels) in the given namespaces, and strip them down - * to only needed fields. - */ - private static List strippedConfigMapsByLabels(KubernetesClient client, String namespace, - Map labels) { - List strippedConfigMaps = configMapsByLabels(client, namespace, labels); - if (strippedConfigMaps.isEmpty()) { - LOG.debug("No configmaps in namespace '" + namespace + "'"); + LOG.debug("Will read individual configmaps in namespace : " + namespace + " with labels : " + labels); + strippedConfigMaps = strippedConfigMapsNonBatchRead(client, namespace, labels); } - return strippedConfigMaps; + return ConfigUtils.processLabeledData(strippedConfigMaps, environment, labels, namespace, false); } /** - * read specific configmaps (by labels) in the given namespaces, and strip them down - * to only needed fields. + *
+	 *     1. read all secrets in the provided namespace
+	 *     2. from the above, filter the ones that we care about (filter by labels)
+	 *     3. see if any of the secrets from (2) has a single yaml/properties file
+	 *     4. gather all the names of the secrets + data they hold
+	 * 
*/ - private static List strippedSecretsByLabels(KubernetesClient client, String namespace, - Map labels) { - List strippedSecrets = secretsByLabels(client, namespace, labels); - if (strippedSecrets.isEmpty()) { - LOG.debug("No secrets in namespace '" + namespace + "'"); - } + static MultipleSourcesContainer secretsDataByLabels(KubernetesClient client, String namespace, + Map labels, Environment environment, boolean namespacedBatchRead) { - return strippedSecrets; - } + List strippedSecrets; - /** - * read all configmaps in the given namespace (batch), and strip them down to only - * needed fields. - */ - private static List strippedConfigMapsBatchRead(KubernetesClient client, - String namespace) { - List strippedConfigMaps = configMapsByNamespace(client, namespace); - if (strippedConfigMaps.isEmpty()) { - LOG.debug("No configmaps in namespace '" + namespace + "'"); + if (namespacedBatchRead) { + LOG.debug("Will read all secrets in namespace : " + namespace); + strippedSecrets = strippedSecretsBatchRead(client, namespace); } - - return strippedConfigMaps; - } - - private static List strippedSecretsBatchRead(KubernetesClient client, String namespace) { - List strippedSecrets = secretsByNamespace(client, namespace); - if (strippedSecrets.isEmpty()) { - LOG.debug("No secrets in namespace '" + namespace + "'"); + else { + LOG.debug("Will read individual secrets in namespace : " + namespace + " with labels : " + labels); + strippedSecrets = strippedSecretsNonBatchRead(client, namespace, labels); } - return strippedSecrets; + return ConfigUtils.processLabeledData(strippedSecrets, environment, labels, namespace, true); } } diff --git a/spring-cloud-kubernetes-fabric8-config/src/main/java/org/springframework/cloud/kubernetes/fabric8/config/Fabric8SecretsNamespaceBatched.java b/spring-cloud-kubernetes-fabric8-config/src/main/java/org/springframework/cloud/kubernetes/fabric8/config/Fabric8SecretsNamespaceBatched.java deleted file mode 100644 index 85f85ca76d..0000000000 --- a/spring-cloud-kubernetes-fabric8-config/src/main/java/org/springframework/cloud/kubernetes/fabric8/config/Fabric8SecretsNamespaceBatched.java +++ /dev/null @@ -1,69 +0,0 @@ -/* - * Copyright 2013-2022 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.fabric8.config; - -import java.util.List; -import java.util.concurrent.ConcurrentHashMap; - -import io.fabric8.kubernetes.client.KubernetesClient; -import org.apache.commons.logging.LogFactory; - -import org.springframework.cloud.kubernetes.commons.config.SecretsCache; -import org.springframework.cloud.kubernetes.commons.config.StrippedSourceContainer; -import org.springframework.core.log.LogAccessor; - -import static org.springframework.cloud.kubernetes.fabric8.config.Fabric8ConfigUtils.strippedSecrets; - -/** - * A cache of ConfigMaps per namespace. Makes sure we read config maps only once from a - * namespace. - * - * @author wind57 - */ -final class Fabric8SecretsNamespaceBatched implements SecretsCache { - - private static final LogAccessor LOG = new LogAccessor(LogFactory.getLog(Fabric8SecretsNamespaceBatched.class)); - - /** - * at the moment our loading of config maps is using a single thread, but might change - * in the future, thus a thread safe structure. - */ - private static final ConcurrentHashMap> CACHE = new ConcurrentHashMap<>(); - - @Override - public void discardAll() { - CACHE.clear(); - } - - static List secretsByNamespace(KubernetesClient client, String namespace) { - boolean[] b = new boolean[1]; - List result = CACHE.computeIfAbsent(namespace, x -> { - b[0] = true; - return strippedSecrets(client.secrets().inNamespace(namespace).list().getItems()); - }); - - if (b[0]) { - LOG.debug(() -> "Loaded all secrets in namespace '" + namespace + "'"); - } - else { - LOG.debug(() -> "Loaded (from cache) all secrets in namespace '" + namespace + "'"); - } - - return result; - } - -} diff --git a/spring-cloud-kubernetes-fabric8-config/src/main/java/org/springframework/cloud/kubernetes/fabric8/config/Fabric8SecretsPropertySourceLocator.java b/spring-cloud-kubernetes-fabric8-config/src/main/java/org/springframework/cloud/kubernetes/fabric8/config/Fabric8SecretsPropertySourceLocator.java index e1f8a3d1ac..f7a1a765ec 100644 --- a/spring-cloud-kubernetes-fabric8-config/src/main/java/org/springframework/cloud/kubernetes/fabric8/config/Fabric8SecretsPropertySourceLocator.java +++ b/spring-cloud-kubernetes-fabric8-config/src/main/java/org/springframework/cloud/kubernetes/fabric8/config/Fabric8SecretsPropertySourceLocator.java @@ -45,7 +45,7 @@ public class Fabric8SecretsPropertySourceLocator extends SecretsPropertySourceLo Fabric8SecretsPropertySourceLocator(KubernetesClient client, SecretsConfigProperties properties, KubernetesNamespaceProvider provider) { - super(properties, new Fabric8SecretsNamespaceBatched()); + super(properties, new Fabric8SourcesNamespaceBatched()); this.client = client; this.provider = provider; } diff --git a/spring-cloud-kubernetes-fabric8-config/src/main/java/org/springframework/cloud/kubernetes/fabric8/config/Fabric8ConfigMapsNamespaceBatched.java b/spring-cloud-kubernetes-fabric8-config/src/main/java/org/springframework/cloud/kubernetes/fabric8/config/Fabric8SourcesNamespaceBatched.java similarity index 55% rename from spring-cloud-kubernetes-fabric8-config/src/main/java/org/springframework/cloud/kubernetes/fabric8/config/Fabric8ConfigMapsNamespaceBatched.java rename to spring-cloud-kubernetes-fabric8-config/src/main/java/org/springframework/cloud/kubernetes/fabric8/config/Fabric8SourcesNamespaceBatched.java index 4ef8d6837d..c5abbafc98 100644 --- a/spring-cloud-kubernetes-fabric8-config/src/main/java/org/springframework/cloud/kubernetes/fabric8/config/Fabric8ConfigMapsNamespaceBatched.java +++ b/spring-cloud-kubernetes-fabric8-config/src/main/java/org/springframework/cloud/kubernetes/fabric8/config/Fabric8SourcesNamespaceBatched.java @@ -23,37 +23,44 @@ import org.apache.commons.logging.LogFactory; import org.springframework.cloud.kubernetes.commons.config.ConfigMapCache; +import org.springframework.cloud.kubernetes.commons.config.SecretsCache; import org.springframework.cloud.kubernetes.commons.config.StrippedSourceContainer; import org.springframework.core.log.LogAccessor; -import static org.springframework.cloud.kubernetes.fabric8.config.Fabric8ConfigUtils.strippedConfigMaps; - /** * A cache of ConfigMaps per namespace. Makes sure we read config maps only once from a * namespace. * * @author wind57 */ -final class Fabric8ConfigMapsNamespaceBatched implements ConfigMapCache { +final class Fabric8SourcesNamespaceBatched implements SecretsCache, ConfigMapCache { - private static final LogAccessor LOG = new LogAccessor(LogFactory.getLog(Fabric8ConfigMapsNamespaceBatched.class)); + private static final LogAccessor LOG = new LogAccessor(LogFactory.getLog(Fabric8SourcesNamespaceBatched.class)); /** * at the moment our loading of config maps is using a single thread, but might change * in the future, thus a thread safe structure. */ - private static final ConcurrentHashMap> CACHE = new ConcurrentHashMap<>(); + private static final ConcurrentHashMap> SECRETS_CACHE = new ConcurrentHashMap<>(); + + private static final ConcurrentHashMap> CONFIG_MAPS_CACHE = new ConcurrentHashMap<>(); + + @Override + public void discardSecrets() { + SECRETS_CACHE.clear(); + } @Override - public void discardAll() { - CACHE.clear(); + public void discardConfigMaps() { + CONFIG_MAPS_CACHE.clear(); } - static List configMapsByNamespace(KubernetesClient client, String namespace) { + static List strippedConfigMapsBatchRead(KubernetesClient client, String namespace) { boolean[] b = new boolean[1]; - List result = CACHE.computeIfAbsent(namespace, x -> { + List result = CONFIG_MAPS_CACHE.computeIfAbsent(namespace, x -> { b[0] = true; - return strippedConfigMaps(client.configMaps().inNamespace(namespace).list().getItems()); + return Fabric8SourcesStripper + .strippedConfigMaps(client.configMaps().inNamespace(namespace).list().getItems()); }); if (b[0]) { @@ -66,4 +73,21 @@ static List configMapsByNamespace(KubernetesClient clie return result; } + static List strippedSecretsBatchRead(KubernetesClient client, String namespace) { + boolean[] b = new boolean[1]; + List result = SECRETS_CACHE.computeIfAbsent(namespace, x -> { + b[0] = true; + return Fabric8SourcesStripper.strippedSecrets(client.secrets().inNamespace(namespace).list().getItems()); + }); + + if (b[0]) { + LOG.debug(() -> "Loaded all secrets in namespace '" + namespace + "'"); + } + else { + LOG.debug(() -> "Loaded (from cache) all secrets in namespace '" + namespace + "'"); + } + + return result; + } + } diff --git a/spring-cloud-kubernetes-fabric8-config/src/main/java/org/springframework/cloud/kubernetes/fabric8/config/Fabric8SourcesNonBatched.java b/spring-cloud-kubernetes-fabric8-config/src/main/java/org/springframework/cloud/kubernetes/fabric8/config/Fabric8SourcesNonNamespaceBatched.java similarity index 59% rename from spring-cloud-kubernetes-fabric8-config/src/main/java/org/springframework/cloud/kubernetes/fabric8/config/Fabric8SourcesNonBatched.java rename to spring-cloud-kubernetes-fabric8-config/src/main/java/org/springframework/cloud/kubernetes/fabric8/config/Fabric8SourcesNonNamespaceBatched.java index 7313dc4812..ad258a2daf 100644 --- a/spring-cloud-kubernetes-fabric8-config/src/main/java/org/springframework/cloud/kubernetes/fabric8/config/Fabric8SourcesNonBatched.java +++ b/spring-cloud-kubernetes-fabric8-config/src/main/java/org/springframework/cloud/kubernetes/fabric8/config/Fabric8SourcesNonNamespaceBatched.java @@ -29,26 +29,23 @@ import org.springframework.cloud.kubernetes.commons.config.StrippedSourceContainer; import org.springframework.core.log.LogAccessor; -import static org.springframework.cloud.kubernetes.fabric8.config.Fabric8ConfigUtils.strippedConfigMaps; -import static org.springframework.cloud.kubernetes.fabric8.config.Fabric8ConfigUtils.strippedSecrets; - /** * non batch reads (not reading in the whole namespace) of configmaps and secrets. * * @author wind57 */ -final class Fabric8SourcesNonBatched { +final class Fabric8SourcesNonNamespaceBatched { - private static final LogAccessor LOG = new LogAccessor(LogFactory.getLog(Fabric8SourcesNonBatched.class)); + private static final LogAccessor LOG = new LogAccessor(LogFactory.getLog(Fabric8SourcesNonNamespaceBatched.class)); - private Fabric8SourcesNonBatched() { + private Fabric8SourcesNonNamespaceBatched() { } /** * read configmaps by name, one by one, without caching them. */ - static List configMapsByName(KubernetesClient client, String namespace, + static List strippedConfigMapsNonBatchRead(KubernetesClient client, String namespace, LinkedHashSet sourceNames) { List configMaps = new ArrayList<>(sourceNames.size()); @@ -61,13 +58,19 @@ static List configMapsByName(KubernetesClient client, S } } - return strippedConfigMaps(configMaps); + List strippedConfigMaps = Fabric8SourcesStripper.strippedConfigMaps(configMaps); + + if (strippedConfigMaps.isEmpty()) { + LOG.debug("No configmaps in namespace '" + namespace + "'"); + } + + return strippedConfigMaps; } /** * read secrets by name, one by one, without caching them. */ - static List secretsByName(KubernetesClient client, String namespace, + static List strippedSecretsNonBatchRead(KubernetesClient client, String namespace, LinkedHashSet sourceNames) { List secrets = new ArrayList<>(sourceNames.size()); @@ -80,29 +83,51 @@ static List secretsByName(KubernetesClient client, Stri } } - return strippedSecrets(secrets); + List strippedSecrets = Fabric8SourcesStripper.strippedSecrets(secrets); + + if (strippedSecrets.isEmpty()) { + LOG.debug("No secrets in namespace '" + namespace + "'"); + } + + return strippedSecrets; } /** * read configmaps by labels, without caching them. */ - static List configMapsByLabels(KubernetesClient client, String namespace, + static List strippedConfigMapsNonBatchRead(KubernetesClient client, String namespace, Map labels) { List configMaps = client.configMaps().inNamespace(namespace).withLabels(labels).list().getItems(); + for (ConfigMap configMap : configMaps) { + LOG.debug("Loaded config map '" + configMap.getMetadata().getName() + "'"); + } + + List strippedConfigMaps = Fabric8SourcesStripper.strippedConfigMaps(configMaps); + if (strippedConfigMaps.isEmpty()) { + LOG.debug("No configmaps in namespace '" + namespace + "'"); + } - return strippedConfigMaps(configMaps); + return strippedConfigMaps; } /** * read secrets by labels, without caching them. */ - static List secretsByLabels(KubernetesClient client, String namespace, + static List strippedSecretsNonBatchRead(KubernetesClient client, String namespace, Map labels) { List secrets = client.secrets().inNamespace(namespace).withLabels(labels).list().getItems(); + for (Secret secret : secrets) { + LOG.debug("Loaded secret '" + secret.getMetadata().getName() + "'"); + } + + List strippedSecrets = Fabric8SourcesStripper.strippedSecrets(secrets); + if (strippedSecrets.isEmpty()) { + LOG.debug("No secrets in namespace '" + namespace + "'"); + } - return strippedSecrets(secrets); + return strippedSecrets; } } diff --git a/spring-cloud-kubernetes-fabric8-config/src/main/java/org/springframework/cloud/kubernetes/fabric8/config/Fabric8SourcesStripper.java b/spring-cloud-kubernetes-fabric8-config/src/main/java/org/springframework/cloud/kubernetes/fabric8/config/Fabric8SourcesStripper.java new file mode 100644 index 0000000000..ceada73543 --- /dev/null +++ b/spring-cloud-kubernetes-fabric8-config/src/main/java/org/springframework/cloud/kubernetes/fabric8/config/Fabric8SourcesStripper.java @@ -0,0 +1,45 @@ +/* + * Copyright 2013-2024 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.fabric8.config; + +import java.util.List; + +import io.fabric8.kubernetes.api.model.ConfigMap; +import io.fabric8.kubernetes.api.model.Secret; + +import org.springframework.cloud.kubernetes.commons.config.StrippedSourceContainer; + +/** + * @author wind57 + */ +interface Fabric8SourcesStripper { + + static List strippedConfigMaps(List configMaps) { + return configMaps.stream() + .map(configMap -> new StrippedSourceContainer(configMap.getMetadata().getLabels(), + configMap.getMetadata().getName(), configMap.getData())) + .toList(); + } + + static List strippedSecrets(List secrets) { + return secrets.stream() + .map(secret -> new StrippedSourceContainer(secret.getMetadata().getLabels(), secret.getMetadata().getName(), + secret.getData())) + .toList(); + } + +} diff --git a/spring-cloud-kubernetes-fabric8-config/src/test/java/org/springframework/cloud/kubernetes/fabric8/config/ConfigMapsTest.java b/spring-cloud-kubernetes-fabric8-config/src/test/java/org/springframework/cloud/kubernetes/fabric8/config/ConfigMapsTest.java index 0e26db0c4b..f72dfcdbfc 100644 --- a/spring-cloud-kubernetes-fabric8-config/src/test/java/org/springframework/cloud/kubernetes/fabric8/config/ConfigMapsTest.java +++ b/spring-cloud-kubernetes-fabric8-config/src/test/java/org/springframework/cloud/kubernetes/fabric8/config/ConfigMapsTest.java @@ -44,7 +44,7 @@ class ConfigMapsTest { @AfterEach void afterEach() { - new Fabric8ConfigMapsNamespaceBatched().discardAll(); + new Fabric8SourcesNamespaceBatched().discardConfigMaps(); } @Test diff --git a/spring-cloud-kubernetes-fabric8-config/src/test/java/org/springframework/cloud/kubernetes/fabric8/config/Fabric8ConfigMapPropertySourceTests.java b/spring-cloud-kubernetes-fabric8-config/src/test/java/org/springframework/cloud/kubernetes/fabric8/config/Fabric8ConfigMapPropertySourceTests.java index 732188ac79..986a364b11 100644 --- a/spring-cloud-kubernetes-fabric8-config/src/test/java/org/springframework/cloud/kubernetes/fabric8/config/Fabric8ConfigMapPropertySourceTests.java +++ b/spring-cloud-kubernetes-fabric8-config/src/test/java/org/springframework/cloud/kubernetes/fabric8/config/Fabric8ConfigMapPropertySourceTests.java @@ -44,7 +44,7 @@ class Fabric8ConfigMapPropertySourceTests { @AfterEach void afterEach() { - new Fabric8ConfigMapsNamespaceBatched().discardAll(); + new Fabric8SourcesNamespaceBatched().discardConfigMaps(); } @Test diff --git a/spring-cloud-kubernetes-fabric8-config/src/test/java/org/springframework/cloud/kubernetes/fabric8/config/Fabric8ConfigUtilsNamespacedBatchReadTests.java b/spring-cloud-kubernetes-fabric8-config/src/test/java/org/springframework/cloud/kubernetes/fabric8/config/Fabric8ConfigUtilsNamespacedBatchReadTests.java index c971226281..3e56fd3346 100644 --- a/spring-cloud-kubernetes-fabric8-config/src/test/java/org/springframework/cloud/kubernetes/fabric8/config/Fabric8ConfigUtilsNamespacedBatchReadTests.java +++ b/spring-cloud-kubernetes-fabric8-config/src/test/java/org/springframework/cloud/kubernetes/fabric8/config/Fabric8ConfigUtilsNamespacedBatchReadTests.java @@ -47,8 +47,8 @@ class Fabric8ConfigUtilsNamespacedBatchReadTests { @AfterEach void afterEach() { - new Fabric8ConfigMapsNamespaceBatched().discardAll(); - new Fabric8SecretsNamespaceBatched().discardAll(); + new Fabric8SourcesNamespaceBatched().discardSecrets(); + new Fabric8SourcesNamespaceBatched().discardConfigMaps(); } /** diff --git a/spring-cloud-kubernetes-fabric8-config/src/test/java/org/springframework/cloud/kubernetes/fabric8/config/Fabric8ConfigUtilsNonNamespacedBatchReadTests.java b/spring-cloud-kubernetes-fabric8-config/src/test/java/org/springframework/cloud/kubernetes/fabric8/config/Fabric8ConfigUtilsNonNamespacedBatchReadTests.java index 5169a13684..f589db42d2 100644 --- a/spring-cloud-kubernetes-fabric8-config/src/test/java/org/springframework/cloud/kubernetes/fabric8/config/Fabric8ConfigUtilsNonNamespacedBatchReadTests.java +++ b/spring-cloud-kubernetes-fabric8-config/src/test/java/org/springframework/cloud/kubernetes/fabric8/config/Fabric8ConfigUtilsNonNamespacedBatchReadTests.java @@ -47,8 +47,8 @@ class Fabric8ConfigUtilsNonNamespacedBatchReadTests { @AfterEach void afterEach() { - new Fabric8ConfigMapsNamespaceBatched().discardAll(); - new Fabric8SecretsNamespaceBatched().discardAll(); + new Fabric8SourcesNamespaceBatched().discardSecrets(); + new Fabric8SourcesNamespaceBatched().discardConfigMaps(); } /** diff --git a/spring-cloud-kubernetes-fabric8-config/src/test/java/org/springframework/cloud/kubernetes/fabric8/config/LabeledConfigMapContextToSourceDataProviderNamespacedBatchReadTests.java b/spring-cloud-kubernetes-fabric8-config/src/test/java/org/springframework/cloud/kubernetes/fabric8/config/LabeledConfigMapContextToSourceDataProviderNamespacedBatchReadTests.java index f6479811d7..694b4be682 100644 --- a/spring-cloud-kubernetes-fabric8-config/src/test/java/org/springframework/cloud/kubernetes/fabric8/config/LabeledConfigMapContextToSourceDataProviderNamespacedBatchReadTests.java +++ b/spring-cloud-kubernetes-fabric8-config/src/test/java/org/springframework/cloud/kubernetes/fabric8/config/LabeledConfigMapContextToSourceDataProviderNamespacedBatchReadTests.java @@ -82,7 +82,7 @@ static void beforeAll() { @AfterEach void afterEach() { mockClient.configMaps().inNamespace(NAMESPACE).delete(); - new Fabric8ConfigMapsNamespaceBatched().discardAll(); + new Fabric8SourcesNamespaceBatched().discardConfigMaps(); } /** diff --git a/spring-cloud-kubernetes-fabric8-config/src/test/java/org/springframework/cloud/kubernetes/fabric8/config/LabeledConfigMapContextToSourceDataProviderNonNamespacedBatchReadTests.java b/spring-cloud-kubernetes-fabric8-config/src/test/java/org/springframework/cloud/kubernetes/fabric8/config/LabeledConfigMapContextToSourceDataProviderNonNamespacedBatchReadTests.java index 1027a28ed5..e086efc79c 100644 --- a/spring-cloud-kubernetes-fabric8-config/src/test/java/org/springframework/cloud/kubernetes/fabric8/config/LabeledConfigMapContextToSourceDataProviderNonNamespacedBatchReadTests.java +++ b/spring-cloud-kubernetes-fabric8-config/src/test/java/org/springframework/cloud/kubernetes/fabric8/config/LabeledConfigMapContextToSourceDataProviderNonNamespacedBatchReadTests.java @@ -82,7 +82,7 @@ static void beforeAll() { @AfterEach void afterEach() { mockClient.configMaps().inNamespace(NAMESPACE).delete(); - new Fabric8ConfigMapsNamespaceBatched().discardAll(); + new Fabric8SourcesNamespaceBatched().discardConfigMaps(); } /** diff --git a/spring-cloud-kubernetes-fabric8-config/src/test/java/org/springframework/cloud/kubernetes/fabric8/config/LabeledSecretContextToSourceDataProviderNamespacedBatchReadTests.java b/spring-cloud-kubernetes-fabric8-config/src/test/java/org/springframework/cloud/kubernetes/fabric8/config/LabeledSecretContextToSourceDataProviderNamespacedBatchReadTests.java index a53736e412..c4831ccb43 100644 --- a/spring-cloud-kubernetes-fabric8-config/src/test/java/org/springframework/cloud/kubernetes/fabric8/config/LabeledSecretContextToSourceDataProviderNamespacedBatchReadTests.java +++ b/spring-cloud-kubernetes-fabric8-config/src/test/java/org/springframework/cloud/kubernetes/fabric8/config/LabeledSecretContextToSourceDataProviderNamespacedBatchReadTests.java @@ -83,7 +83,7 @@ static void beforeAll() { @AfterEach void afterEach() { mockClient.secrets().inNamespace(NAMESPACE).delete(); - new Fabric8SecretsNamespaceBatched().discardAll(); + new Fabric8SourcesNamespaceBatched().discardSecrets(); } /** diff --git a/spring-cloud-kubernetes-fabric8-config/src/test/java/org/springframework/cloud/kubernetes/fabric8/config/LabeledSecretContextToSourceDataProviderNonNamespacedBatchReadTests.java b/spring-cloud-kubernetes-fabric8-config/src/test/java/org/springframework/cloud/kubernetes/fabric8/config/LabeledSecretContextToSourceDataProviderNonNamespacedBatchReadTests.java index f097c199d5..0258c6c3fa 100644 --- a/spring-cloud-kubernetes-fabric8-config/src/test/java/org/springframework/cloud/kubernetes/fabric8/config/LabeledSecretContextToSourceDataProviderNonNamespacedBatchReadTests.java +++ b/spring-cloud-kubernetes-fabric8-config/src/test/java/org/springframework/cloud/kubernetes/fabric8/config/LabeledSecretContextToSourceDataProviderNonNamespacedBatchReadTests.java @@ -81,7 +81,7 @@ static void beforeAll() { @AfterEach void afterEach() { mockClient.secrets().inNamespace(NAMESPACE).delete(); - new Fabric8SecretsNamespaceBatched().discardAll(); + new Fabric8SourcesNamespaceBatched().discardSecrets(); } /** diff --git a/spring-cloud-kubernetes-fabric8-config/src/test/java/org/springframework/cloud/kubernetes/fabric8/config/NamedConfigMapContextToSourceDataProviderNamespacedBatchReadTests.java b/spring-cloud-kubernetes-fabric8-config/src/test/java/org/springframework/cloud/kubernetes/fabric8/config/NamedConfigMapContextToSourceDataProviderNamespacedBatchReadTests.java index 13c023cffe..40522cf6c2 100644 --- a/spring-cloud-kubernetes-fabric8-config/src/test/java/org/springframework/cloud/kubernetes/fabric8/config/NamedConfigMapContextToSourceDataProviderNamespacedBatchReadTests.java +++ b/spring-cloud-kubernetes-fabric8-config/src/test/java/org/springframework/cloud/kubernetes/fabric8/config/NamedConfigMapContextToSourceDataProviderNamespacedBatchReadTests.java @@ -73,7 +73,7 @@ static void beforeAll() { @AfterEach void afterEach() { mockClient.configMaps().inNamespace(NAMESPACE).delete(); - new Fabric8ConfigMapsNamespaceBatched().discardAll(); + new Fabric8SourcesNamespaceBatched().discardConfigMaps(); } /** diff --git a/spring-cloud-kubernetes-fabric8-config/src/test/java/org/springframework/cloud/kubernetes/fabric8/config/NamedConfigMapContextToSourceDataProviderNonNamespacedBatchReadTests.java b/spring-cloud-kubernetes-fabric8-config/src/test/java/org/springframework/cloud/kubernetes/fabric8/config/NamedConfigMapContextToSourceDataProviderNonNamespacedBatchReadTests.java index 341893a1d8..1a45d515f9 100644 --- a/spring-cloud-kubernetes-fabric8-config/src/test/java/org/springframework/cloud/kubernetes/fabric8/config/NamedConfigMapContextToSourceDataProviderNonNamespacedBatchReadTests.java +++ b/spring-cloud-kubernetes-fabric8-config/src/test/java/org/springframework/cloud/kubernetes/fabric8/config/NamedConfigMapContextToSourceDataProviderNonNamespacedBatchReadTests.java @@ -71,7 +71,7 @@ static void beforeAll() { @AfterEach void afterEach() { mockClient.configMaps().inNamespace(NAMESPACE).delete(); - new Fabric8ConfigMapsNamespaceBatched().discardAll(); + new Fabric8SourcesNamespaceBatched().discardConfigMaps(); } /** diff --git a/spring-cloud-kubernetes-fabric8-config/src/test/java/org/springframework/cloud/kubernetes/fabric8/config/NamedSecretContextToSourceDataProviderNamespacedBatchReadTests.java b/spring-cloud-kubernetes-fabric8-config/src/test/java/org/springframework/cloud/kubernetes/fabric8/config/NamedSecretContextToSourceDataProviderNamespacedBatchReadTests.java index 2ac91ee5ff..2dfd2d0fa6 100644 --- a/spring-cloud-kubernetes-fabric8-config/src/test/java/org/springframework/cloud/kubernetes/fabric8/config/NamedSecretContextToSourceDataProviderNamespacedBatchReadTests.java +++ b/spring-cloud-kubernetes-fabric8-config/src/test/java/org/springframework/cloud/kubernetes/fabric8/config/NamedSecretContextToSourceDataProviderNamespacedBatchReadTests.java @@ -72,7 +72,7 @@ static void beforeAll() { @AfterEach void afterEach() { mockClient.secrets().inNamespace(NAMESPACE).delete(); - new Fabric8SecretsNamespaceBatched().discardAll(); + new Fabric8SourcesNamespaceBatched().discardSecrets(); } /** diff --git a/spring-cloud-kubernetes-fabric8-config/src/test/java/org/springframework/cloud/kubernetes/fabric8/config/NamedSecretContextToSourceDataProviderNonNamespacedBatchReadTests.java b/spring-cloud-kubernetes-fabric8-config/src/test/java/org/springframework/cloud/kubernetes/fabric8/config/NamedSecretContextToSourceDataProviderNonNamespacedBatchReadTests.java index 4e8b91bc63..10d7ae672d 100644 --- a/spring-cloud-kubernetes-fabric8-config/src/test/java/org/springframework/cloud/kubernetes/fabric8/config/NamedSecretContextToSourceDataProviderNonNamespacedBatchReadTests.java +++ b/spring-cloud-kubernetes-fabric8-config/src/test/java/org/springframework/cloud/kubernetes/fabric8/config/NamedSecretContextToSourceDataProviderNonNamespacedBatchReadTests.java @@ -70,7 +70,7 @@ static void beforeAll() { @AfterEach void afterEach() { mockClient.secrets().inNamespace(NAMESPACE).delete(); - new Fabric8SecretsNamespaceBatched().discardAll(); + new Fabric8SourcesNamespaceBatched().discardSecrets(); } /** From 2ff03daccd7c3d2311a6bd5c0b742f9bc78c8d7b Mon Sep 17 00:00:00 2001 From: wind57 Date: Mon, 23 Sep 2024 11:00:28 +0300 Subject: [PATCH 13/21] dirty --- .../NamedConfigMapContextToSourceDataProvider.java | 7 +++---- .../config/NamedSecretContextToSourceDataProvider.java | 10 ++++------ ...NamedConfigMapContextToSourceDataProviderTests.java | 2 +- .../kubernetes/commons/config/LabeledSourceData.java | 2 -- .../kubernetes/commons/config/NamedSourceData.java | 8 +++----- .../NamedConfigMapContextToSourceDataProvider.java | 7 +++---- .../config/NamedSecretContextToSourceDataProvider.java | 7 +++---- 7 files changed, 17 insertions(+), 26 deletions(-) diff --git a/spring-cloud-kubernetes-client-config/src/main/java/org/springframework/cloud/kubernetes/client/config/NamedConfigMapContextToSourceDataProvider.java b/spring-cloud-kubernetes-client-config/src/main/java/org/springframework/cloud/kubernetes/client/config/NamedConfigMapContextToSourceDataProvider.java index 7a1ee2d677..2d50bfd294 100644 --- a/spring-cloud-kubernetes-client-config/src/main/java/org/springframework/cloud/kubernetes/client/config/NamedConfigMapContextToSourceDataProvider.java +++ b/spring-cloud-kubernetes-client-config/src/main/java/org/springframework/cloud/kubernetes/client/config/NamedConfigMapContextToSourceDataProvider.java @@ -45,11 +45,11 @@ public KubernetesClientContextToSourceData get() { return new NamedSourceData() { @Override protected String generateSourceName(String target, String sourceName, String namespace, - String[] activeProfiles, boolean namespacedBatchRead) { + String[] activeProfiles) { if (source.appendProfileToName()) { return ConfigUtils.sourceName(target, sourceName, namespace, activeProfiles); } - return super.generateSourceName(target, sourceName, namespace, activeProfiles, namespacedBatchRead); + return super.generateSourceName(target, sourceName, namespace, activeProfiles); } @Override @@ -58,8 +58,7 @@ public MultipleSourcesContainer dataSupplier(LinkedHashSet sourceNames) sourceNames, context.environment(), context.namespacedBatchRead()); } }.compute(source.name().orElseThrow(), source.prefix(), source.target(), source.profileSpecificSources(), - source.failFast(), context.namespace(), context.environment().getActiveProfiles(), - context.namespacedBatchRead()); + source.failFast(), context.namespace(), context.environment().getActiveProfiles()); }; } diff --git a/spring-cloud-kubernetes-client-config/src/main/java/org/springframework/cloud/kubernetes/client/config/NamedSecretContextToSourceDataProvider.java b/spring-cloud-kubernetes-client-config/src/main/java/org/springframework/cloud/kubernetes/client/config/NamedSecretContextToSourceDataProvider.java index d8de61aa9a..34fe0be336 100644 --- a/spring-cloud-kubernetes-client-config/src/main/java/org/springframework/cloud/kubernetes/client/config/NamedSecretContextToSourceDataProvider.java +++ b/spring-cloud-kubernetes-client-config/src/main/java/org/springframework/cloud/kubernetes/client/config/NamedSecretContextToSourceDataProvider.java @@ -44,22 +44,20 @@ public KubernetesClientContextToSourceData get() { return new NamedSourceData() { @Override protected String generateSourceName(String target, String sourceName, String namespace, - String[] activeProfiles, boolean namesspacedBatchRead) { + String[] activeProfiles) { if (source.appendProfileToName()) { return ConfigUtils.sourceName(target, sourceName, namespace, activeProfiles); } - return super.generateSourceName(target, sourceName, namespace, activeProfiles, - namesspacedBatchRead); + return super.generateSourceName(target, sourceName, namespace, activeProfiles); } @Override public MultipleSourcesContainer dataSupplier(LinkedHashSet sourceNames) { return KubernetesClientConfigUtils.secretsDataByName(context.client(), context.namespace(), - sourceNames, context.environment(), context.includeDefaultProfileData()); + sourceNames, context.environment(), context.namespacedBatchRead()); } }.compute(source.name().orElseThrow(), source.prefix(), source.target(), source.profileSpecificSources(), - source.failFast(), context.namespace(), context.environment().getActiveProfiles(), - context.namespacedBatchRead()); + source.failFast(), context.namespace(), context.environment().getActiveProfiles()); }; } diff --git a/spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/NamedConfigMapContextToSourceDataProviderTests.java b/spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/NamedConfigMapContextToSourceDataProviderTests.java index 6152bffa52..07e7066f43 100644 --- a/spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/NamedConfigMapContextToSourceDataProviderTests.java +++ b/spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/NamedConfigMapContextToSourceDataProviderTests.java @@ -173,7 +173,7 @@ void matchIncludeSingleProfile() { MockEnvironment environment = new MockEnvironment(); environment.setActiveProfiles("with-profile"); KubernetesClientConfigContext context = new KubernetesClientConfigContext(api, source, NAMESPACE, environment, - true, NAMESPACED_BATCH_READ); + false, NAMESPACED_BATCH_READ); KubernetesClientContextToSourceData data = new NamedConfigMapContextToSourceDataProvider().get(); SourceData sourceData = data.apply(context); diff --git a/spring-cloud-kubernetes-commons/src/main/java/org/springframework/cloud/kubernetes/commons/config/LabeledSourceData.java b/spring-cloud-kubernetes-commons/src/main/java/org/springframework/cloud/kubernetes/commons/config/LabeledSourceData.java index 40a6005ea0..69e524415b 100644 --- a/spring-cloud-kubernetes-commons/src/main/java/org/springframework/cloud/kubernetes/commons/config/LabeledSourceData.java +++ b/spring-cloud-kubernetes-commons/src/main/java/org/springframework/cloud/kubernetes/commons/config/LabeledSourceData.java @@ -80,8 +80,6 @@ public final SourceData compute(Map labels, ConfigUtils.Prefix p * Implementation specific (fabric8 or k8s-native) way to get the data from then given * source names. * @param labels the ones that have been configured - * @param profiles profiles to taken into account when gathering source data. Can be - * empty. * @return a container that holds the names of the source that were found and their * data */ diff --git a/spring-cloud-kubernetes-commons/src/main/java/org/springframework/cloud/kubernetes/commons/config/NamedSourceData.java b/spring-cloud-kubernetes-commons/src/main/java/org/springframework/cloud/kubernetes/commons/config/NamedSourceData.java index 6729632582..93024062e2 100644 --- a/spring-cloud-kubernetes-commons/src/main/java/org/springframework/cloud/kubernetes/commons/config/NamedSourceData.java +++ b/spring-cloud-kubernetes-commons/src/main/java/org/springframework/cloud/kubernetes/commons/config/NamedSourceData.java @@ -32,7 +32,7 @@ public abstract class NamedSourceData { public final SourceData compute(String sourceName, ConfigUtils.Prefix prefix, String target, boolean profileSources, - boolean failFast, String namespace, String[] activeProfiles, boolean namespacedBatchRead) { + boolean failFast, String namespace, String[] activeProfiles) { LinkedHashSet sourceNames = new LinkedHashSet<>(); // first comes non-profile based source @@ -67,12 +67,10 @@ public final SourceData compute(String sourceName, ConfigUtils.Prefix prefix, St } String names = data.names().stream().sorted().collect(Collectors.joining(PROPERTY_SOURCE_NAME_SEPARATOR)); - return new SourceData(generateSourceName(target, names, namespace, activeProfiles, namespacedBatchRead), - data.data()); + return new SourceData(generateSourceName(target, names, namespace, activeProfiles), data.data()); } - protected String generateSourceName(String target, String sourceName, String namespace, String[] activeProfiles, - boolean namespacedBatchRead) { + protected String generateSourceName(String target, String sourceName, String namespace, String[] activeProfile) { return ConfigUtils.sourceName(target, sourceName, namespace); } diff --git a/spring-cloud-kubernetes-fabric8-config/src/main/java/org/springframework/cloud/kubernetes/fabric8/config/NamedConfigMapContextToSourceDataProvider.java b/spring-cloud-kubernetes-fabric8-config/src/main/java/org/springframework/cloud/kubernetes/fabric8/config/NamedConfigMapContextToSourceDataProvider.java index 2280dae3f2..896d4122eb 100644 --- a/spring-cloud-kubernetes-fabric8-config/src/main/java/org/springframework/cloud/kubernetes/fabric8/config/NamedConfigMapContextToSourceDataProvider.java +++ b/spring-cloud-kubernetes-fabric8-config/src/main/java/org/springframework/cloud/kubernetes/fabric8/config/NamedConfigMapContextToSourceDataProvider.java @@ -53,11 +53,11 @@ public Fabric8ContextToSourceData get() { @Override protected String generateSourceName(String target, String sourceName, String namespace, - String[] activeProfiles, boolean namespacedBatchRead) { + String[] activeProfiles) { if (source.appendProfileToName()) { return ConfigUtils.sourceName(target, sourceName, namespace, activeProfiles); } - return super.generateSourceName(target, sourceName, namespace, activeProfiles, namespacedBatchRead); + return super.generateSourceName(target, sourceName, namespace, activeProfiles); } @Override @@ -66,8 +66,7 @@ public MultipleSourcesContainer dataSupplier(LinkedHashSet sourceNames) context.environment(), context.namespacedBatchRead()); } }.compute(source.name().orElseThrow(), source.prefix(), source.target(), source.profileSpecificSources(), - source.failFast(), context.namespace(), context.environment().getActiveProfiles(), - context.namespacedBatchRead()); + source.failFast(), context.namespace(), context.environment().getActiveProfiles()); }; } diff --git a/spring-cloud-kubernetes-fabric8-config/src/main/java/org/springframework/cloud/kubernetes/fabric8/config/NamedSecretContextToSourceDataProvider.java b/spring-cloud-kubernetes-fabric8-config/src/main/java/org/springframework/cloud/kubernetes/fabric8/config/NamedSecretContextToSourceDataProvider.java index ee48fcd25a..764d3a866b 100644 --- a/spring-cloud-kubernetes-fabric8-config/src/main/java/org/springframework/cloud/kubernetes/fabric8/config/NamedSecretContextToSourceDataProvider.java +++ b/spring-cloud-kubernetes-fabric8-config/src/main/java/org/springframework/cloud/kubernetes/fabric8/config/NamedSecretContextToSourceDataProvider.java @@ -43,11 +43,11 @@ public Fabric8ContextToSourceData get() { return new NamedSourceData() { @Override protected String generateSourceName(String target, String sourceName, String namespace, - String[] activeProfiles, boolean namespacedBatchRead) { + String[] activeProfiles) { if (source.appendProfileToName()) { return ConfigUtils.sourceName(target, sourceName, namespace, activeProfiles); } - return super.generateSourceName(target, sourceName, namespace, activeProfiles, namespacedBatchRead); + return super.generateSourceName(target, sourceName, namespace, activeProfiles); } @Override @@ -56,8 +56,7 @@ public MultipleSourcesContainer dataSupplier(LinkedHashSet sourceNames) context.environment(), context.namespacedBatchRead()); } }.compute(source.name().orElseThrow(), source.prefix(), source.target(), source.profileSpecificSources(), - source.failFast(), context.namespace(), context.environment().getActiveProfiles(), - context.namespacedBatchRead()); + source.failFast(), context.namespace(), context.environment().getActiveProfiles()); }; } From 0d429a74fb71e457b149f8ff69e5a8c1453ee5af Mon Sep 17 00:00:00 2001 From: wind57 Date: Mon, 23 Sep 2024 16:16:53 +0300 Subject: [PATCH 14/21] fix --- .../config/KubernetesClientConfigUtils.java | 12 +- ...netesClientSourcesNonNamespaceBatched.java | 10 +- .../KubernetesClientSourcesStripper.java | 14 + ...dConfigMapContextToSourceDataProvider.java | 3 +- ...amedSecretContextToSourceDataProvider.java | 3 +- ...DataProviderNamespacedBatchReadTests.java} | 12 +- ...taProviderNonNamespacedBatchReadTests.java | 630 ++++++++++++++++++ ...DataProviderNamespacedBatchReadTests.java} | 12 +- ...taProviderNonNamespacedBatchReadTests.java | 588 ++++++++++++++++ ...DataProviderNamespacedBatchReadTests.java} | 12 +- ...taProviderNonNamespacedBatchReadTests.java | 466 +++++++++++++ ...DataProviderNamespacedBatchReadTests.java} | 8 +- ...taProviderNonNamespacedBatchReadTests.java | 420 ++++++++++++ .../sources_order/SourcesOrderTests.java | 19 + .../commons/config/ConfigUtils.java | 12 +- 15 files changed, 2201 insertions(+), 20 deletions(-) rename spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/{LabeledConfigMapContextToSourceDataProviderTests.java => LabeledConfigMapContextToSourceDataProviderNamespacedBatchReadTests.java} (98%) create mode 100644 spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/LabeledConfigMapContextToSourceDataProviderNonNamespacedBatchReadTests.java rename spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/{LabeledSecretContextToSourceDataProviderTests.java => LabeledSecretContextToSourceDataProviderNamespacedBatchReadTests.java} (98%) create mode 100644 spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/LabeledSecretContextToSourceDataProviderNonNamespacedBatchReadTests.java rename spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/{NamedConfigMapContextToSourceDataProviderTests.java => NamedConfigMapContextToSourceDataProviderNamespacedBatchReadTests.java} (98%) create mode 100644 spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/NamedConfigMapContextToSourceDataProviderNonNamespacedBatchReadTests.java rename spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/{NamedSecretContextToSourceDataProviderTests.java => NamedSecretContextToSourceDataProviderNamespacedBatchReadTests.java} (98%) create mode 100644 spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/NamedSecretContextToSourceDataProviderNonNamespacedBatchReadTests.java diff --git a/spring-cloud-kubernetes-client-config/src/main/java/org/springframework/cloud/kubernetes/client/config/KubernetesClientConfigUtils.java b/spring-cloud-kubernetes-client-config/src/main/java/org/springframework/cloud/kubernetes/client/config/KubernetesClientConfigUtils.java index d53926fb7e..09b99a2f0a 100644 --- a/spring-cloud-kubernetes-client-config/src/main/java/org/springframework/cloud/kubernetes/client/config/KubernetesClientConfigUtils.java +++ b/spring-cloud-kubernetes-client-config/src/main/java/org/springframework/cloud/kubernetes/client/config/KubernetesClientConfigUtils.java @@ -74,7 +74,8 @@ public static Set namespaces(KubernetesNamespaceProvider provider, Confi * */ static MultipleSourcesContainer configMapsDataByName(CoreV1Api client, String namespace, - LinkedHashSet sourceNames, Environment environment, boolean namespacedBatchRead) { + LinkedHashSet sourceNames, Environment environment, boolean includeDefaultProfileData, + boolean namespacedBatchRead) { List strippedConfigMaps; @@ -87,7 +88,8 @@ static MultipleSourcesContainer configMapsDataByName(CoreV1Api client, String na strippedConfigMaps = strippedConfigMapsNonBatchRead(client, namespace, sourceNames); } - return ConfigUtils.processNamedData(strippedConfigMaps, environment, sourceNames, namespace, false); + return ConfigUtils.processNamedData(strippedConfigMaps, environment, sourceNames, namespace, false, + includeDefaultProfileData); } /** @@ -99,7 +101,8 @@ static MultipleSourcesContainer configMapsDataByName(CoreV1Api client, String na * */ static MultipleSourcesContainer secretsDataByName(CoreV1Api client, String namespace, - LinkedHashSet sourceNames, Environment environment, boolean namespacedBatchRead) { + LinkedHashSet sourceNames, Environment environment, boolean includeDefaultProfileData, + boolean namespacedBatchRead) { List strippedSecrets; @@ -112,7 +115,8 @@ static MultipleSourcesContainer secretsDataByName(CoreV1Api client, String names strippedSecrets = strippedSecretsNonBatchRead(client, namespace, sourceNames); } - return ConfigUtils.processNamedData(strippedSecrets, environment, sourceNames, namespace, false); + return ConfigUtils.processNamedData(strippedSecrets, environment, sourceNames, namespace, false, + includeDefaultProfileData); } /** diff --git a/spring-cloud-kubernetes-client-config/src/main/java/org/springframework/cloud/kubernetes/client/config/KubernetesClientSourcesNonNamespaceBatched.java b/spring-cloud-kubernetes-client-config/src/main/java/org/springframework/cloud/kubernetes/client/config/KubernetesClientSourcesNonNamespaceBatched.java index bb8d6cc5ea..2665315441 100644 --- a/spring-cloud-kubernetes-client-config/src/main/java/org/springframework/cloud/kubernetes/client/config/KubernetesClientSourcesNonNamespaceBatched.java +++ b/spring-cloud-kubernetes-client-config/src/main/java/org/springframework/cloud/kubernetes/client/config/KubernetesClientSourcesNonNamespaceBatched.java @@ -49,12 +49,12 @@ static List strippedConfigMapsNonBatchRead(CoreV1Api cl List configMaps = new ArrayList<>(sourceNames.size()); for (String sourceName : sourceNames) { - V1ConfigMap configMap; + V1ConfigMap configMap = null; try { - configMap = client.readNamespacedConfigMap(namespace, sourceName, null); + configMap = client.readNamespacedConfigMap(sourceName, namespace, null); } catch (ApiException e) { - throw new RuntimeException(e.getResponseBody(), e); + KubernetesClientSourcesStripper.handleApiException(e, sourceName); } if (configMap != null) { LOG.debug("Loaded config map '" + sourceName + "'"); @@ -81,12 +81,12 @@ static List strippedSecretsNonBatchRead(CoreV1Api clien List secrets = new ArrayList<>(sourceNames.size()); for (String sourceName : sourceNames) { - V1Secret secret; + V1Secret secret = null; try { secret = client.readNamespacedSecret(sourceName, namespace, null); } catch (ApiException e) { - throw new RuntimeException(e.getResponseBody(), e); + KubernetesClientSourcesStripper.handleApiException(e, sourceName); } if (secret != null) { LOG.debug("Loaded config map '" + sourceName + "'"); diff --git a/spring-cloud-kubernetes-client-config/src/main/java/org/springframework/cloud/kubernetes/client/config/KubernetesClientSourcesStripper.java b/spring-cloud-kubernetes-client-config/src/main/java/org/springframework/cloud/kubernetes/client/config/KubernetesClientSourcesStripper.java index 6bbbe3c350..8ac6e78da5 100644 --- a/spring-cloud-kubernetes-client-config/src/main/java/org/springframework/cloud/kubernetes/client/config/KubernetesClientSourcesStripper.java +++ b/spring-cloud-kubernetes-client-config/src/main/java/org/springframework/cloud/kubernetes/client/config/KubernetesClientSourcesStripper.java @@ -20,10 +20,13 @@ import java.util.Map; import java.util.stream.Collectors; +import io.kubernetes.client.openapi.ApiException; import io.kubernetes.client.openapi.models.V1ConfigMap; import io.kubernetes.client.openapi.models.V1Secret; +import org.apache.commons.logging.LogFactory; import org.springframework.cloud.kubernetes.commons.config.StrippedSourceContainer; +import org.springframework.core.log.LogAccessor; import org.springframework.util.ObjectUtils; /** @@ -31,6 +34,8 @@ */ interface KubernetesClientSourcesStripper { + LogAccessor LOG = new LogAccessor(LogFactory.getLog(KubernetesClientSourcesStripper.class)); + static List strippedSecrets(List secrets) { return secrets.stream() .map(secret -> new StrippedSourceContainer(secret.getMetadata().getLabels(), secret.getMetadata().getName(), @@ -45,6 +50,15 @@ static List strippedConfigMaps(List config .toList(); } + static void handleApiException(ApiException e, String sourceName) { + if (e.getCode() == 404) { + LOG.warn("source with name : " + sourceName + " not found. Ignoring"); + } + else { + throw new RuntimeException(e.getResponseBody(), e); + } + } + private static Map transform(Map in) { return ObjectUtils.isEmpty(in) ? Map.of() : in.entrySet().stream().collect(Collectors.toMap(Map.Entry::getKey, en -> new String(en.getValue()))); diff --git a/spring-cloud-kubernetes-client-config/src/main/java/org/springframework/cloud/kubernetes/client/config/NamedConfigMapContextToSourceDataProvider.java b/spring-cloud-kubernetes-client-config/src/main/java/org/springframework/cloud/kubernetes/client/config/NamedConfigMapContextToSourceDataProvider.java index 2d50bfd294..5709791937 100644 --- a/spring-cloud-kubernetes-client-config/src/main/java/org/springframework/cloud/kubernetes/client/config/NamedConfigMapContextToSourceDataProvider.java +++ b/spring-cloud-kubernetes-client-config/src/main/java/org/springframework/cloud/kubernetes/client/config/NamedConfigMapContextToSourceDataProvider.java @@ -55,7 +55,8 @@ protected String generateSourceName(String target, String sourceName, String nam @Override public MultipleSourcesContainer dataSupplier(LinkedHashSet sourceNames) { return KubernetesClientConfigUtils.configMapsDataByName(context.client(), context.namespace(), - sourceNames, context.environment(), context.namespacedBatchRead()); + sourceNames, context.environment(), context.includeDefaultProfileData(), + context.namespacedBatchRead()); } }.compute(source.name().orElseThrow(), source.prefix(), source.target(), source.profileSpecificSources(), source.failFast(), context.namespace(), context.environment().getActiveProfiles()); diff --git a/spring-cloud-kubernetes-client-config/src/main/java/org/springframework/cloud/kubernetes/client/config/NamedSecretContextToSourceDataProvider.java b/spring-cloud-kubernetes-client-config/src/main/java/org/springframework/cloud/kubernetes/client/config/NamedSecretContextToSourceDataProvider.java index 34fe0be336..c77c42cc21 100644 --- a/spring-cloud-kubernetes-client-config/src/main/java/org/springframework/cloud/kubernetes/client/config/NamedSecretContextToSourceDataProvider.java +++ b/spring-cloud-kubernetes-client-config/src/main/java/org/springframework/cloud/kubernetes/client/config/NamedSecretContextToSourceDataProvider.java @@ -54,7 +54,8 @@ protected String generateSourceName(String target, String sourceName, String nam @Override public MultipleSourcesContainer dataSupplier(LinkedHashSet sourceNames) { return KubernetesClientConfigUtils.secretsDataByName(context.client(), context.namespace(), - sourceNames, context.environment(), context.namespacedBatchRead()); + sourceNames, context.environment(), context.includeDefaultProfileData(), + context.namespacedBatchRead()); } }.compute(source.name().orElseThrow(), source.prefix(), source.target(), source.profileSpecificSources(), source.failFast(), context.namespace(), context.environment().getActiveProfiles()); diff --git a/spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/LabeledConfigMapContextToSourceDataProviderTests.java b/spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/LabeledConfigMapContextToSourceDataProviderNamespacedBatchReadTests.java similarity index 98% rename from spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/LabeledConfigMapContextToSourceDataProviderTests.java rename to spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/LabeledConfigMapContextToSourceDataProviderNamespacedBatchReadTests.java index c3e5bba059..f63fa7caf9 100644 --- a/spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/LabeledConfigMapContextToSourceDataProviderTests.java +++ b/spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/LabeledConfigMapContextToSourceDataProviderNamespacedBatchReadTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2022 the original author or authors. + * Copyright 2013-2024 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. @@ -32,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.AfterAll; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeAll; @@ -55,7 +56,7 @@ * @author wind57 */ @ExtendWith(OutputCaptureExtension.class) -class LabeledConfigMapContextToSourceDataProviderTests { +class LabeledConfigMapContextToSourceDataProviderNamespacedBatchReadTests { private static final boolean NAMESPACED_BATCH_READ = true; @@ -92,6 +93,11 @@ void afterEach() { new KubernetesClientSourcesNamespaceBatched().discardConfigMaps(); } + @AfterAll + static void afterAll() { + WireMock.shutdownServer(); + } + /** * we have a single config map deployed. it has two labels and these match against our * queries. @@ -580,7 +586,9 @@ void cache(CapturedOutput output) { Assertions.assertEquals(redSourceData.sourceData().size(), 1); Assertions.assertEquals(redSourceData.sourceData().get("color"), "red"); Assertions.assertEquals(redSourceData.sourceName(), "configmap.red-configmap.default"); + Assertions.assertTrue(output.getAll().contains("Loaded all config maps in namespace '" + NAMESPACE + "'")); + Assertions.assertFalse(output.getAll().contains("Will read individual configmaps in namespace")); NormalizedSource greenSource = new LabeledConfigMapNormalizedSource(NAMESPACE, Map.of("color", "green"), false, ConfigUtils.Prefix.DEFAULT, false); diff --git a/spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/LabeledConfigMapContextToSourceDataProviderNonNamespacedBatchReadTests.java b/spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/LabeledConfigMapContextToSourceDataProviderNonNamespacedBatchReadTests.java new file mode 100644 index 0000000000..fa844f6f4f --- /dev/null +++ b/spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/LabeledConfigMapContextToSourceDataProviderNonNamespacedBatchReadTests.java @@ -0,0 +1,630 @@ +/* + * Copyright 2013-2024 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.client.config; + +import java.util.Collections; +import java.util.Iterator; +import java.util.LinkedHashMap; +import java.util.Map; + +import com.github.tomakehurst.wiremock.WireMockServer; +import com.github.tomakehurst.wiremock.client.WireMock; +import io.kubernetes.client.openapi.ApiClient; +import io.kubernetes.client.openapi.Configuration; +import io.kubernetes.client.openapi.JSON; +import io.kubernetes.client.openapi.apis.CoreV1Api; +import io.kubernetes.client.openapi.models.V1ConfigMap; +import io.kubernetes.client.openapi.models.V1ConfigMapBuilder; +import io.kubernetes.client.openapi.models.V1ConfigMapList; +import io.kubernetes.client.openapi.models.V1ConfigMapListBuilder; +import io.kubernetes.client.openapi.models.V1ObjectMetaBuilder; +import io.kubernetes.client.util.ClientBuilder; +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.junit.jupiter.api.extension.ExtendWith; + +import org.springframework.boot.test.system.CapturedOutput; +import org.springframework.boot.test.system.OutputCaptureExtension; +import org.springframework.cloud.kubernetes.commons.config.ConfigUtils; +import org.springframework.cloud.kubernetes.commons.config.LabeledConfigMapNormalizedSource; +import org.springframework.cloud.kubernetes.commons.config.NormalizedSource; +import org.springframework.cloud.kubernetes.commons.config.SourceData; +import org.springframework.mock.env.MockEnvironment; + +import static com.github.tomakehurst.wiremock.client.WireMock.aResponse; +import static com.github.tomakehurst.wiremock.client.WireMock.get; +import static com.github.tomakehurst.wiremock.client.WireMock.stubFor; +import static com.github.tomakehurst.wiremock.core.WireMockConfiguration.options; + +/** + * @author wind57 + */ +@ExtendWith(OutputCaptureExtension.class) +class LabeledConfigMapContextToSourceDataProviderNonNamespacedBatchReadTests { + + private static final boolean NAMESPACED_BATCH_READ = false; + + private static final Map LABELS = new LinkedHashMap<>(); + + private static final Map RED_LABEL = Map.of("color", "red"); + + private static final Map BLUE_LABEL = Map.of("color", "blue"); + + private static final Map PINK_LABEL = Map.of("color", "pink"); + + private static final String NAMESPACE = "default"; + + static { + LABELS.put("label2", "value2"); + LABELS.put("label1", "value1"); + } + + @BeforeAll + static void setup() { + WireMockServer wireMockServer = new WireMockServer(options().dynamicPort()); + + wireMockServer.start(); + WireMock.configureFor("localhost", wireMockServer.port()); + + ApiClient client = new ClientBuilder().setBasePath("http://localhost:" + wireMockServer.port()).build(); + client.setDebugging(true); + Configuration.setDefaultApiClient(client); + } + + @AfterEach + void afterEach() { + WireMock.reset(); + new KubernetesClientSourcesNamespaceBatched().discardConfigMaps(); + } + + @AfterAll + static void afterAll() { + WireMock.shutdownServer(); + } + + /** + * we have a single config map deployed. it has two labels and these match against our + * queries. + */ + @Test + void singleConfigMapMatchAgainstLabels() { + + V1ConfigMap one = new V1ConfigMapBuilder() + .withMetadata(new V1ObjectMetaBuilder().withName("test-configmap") + .withLabels(LABELS) + .withNamespace(NAMESPACE) + .build()) + .addToData("name", "value") + .build(); + + V1ConfigMapList configMapList = new V1ConfigMapListBuilder().addToItems(one).build(); + stubCall(configMapList, + "/api/v1/namespaces/default/configmaps?labelSelector=label2%3Dvalue2%26label1%3Dvalue1"); + CoreV1Api api = new CoreV1Api(); + + NormalizedSource source = new LabeledConfigMapNormalizedSource(NAMESPACE, LABELS, true, false); + KubernetesClientConfigContext context = new KubernetesClientConfigContext(api, source, NAMESPACE, + new MockEnvironment(), false, NAMESPACED_BATCH_READ); + + KubernetesClientContextToSourceData data = new LabeledConfigMapContextToSourceDataProvider().get(); + SourceData sourceData = data.apply(context); + + Assertions.assertEquals("configmap.test-configmap.default", sourceData.sourceName()); + Assertions.assertEquals(Map.of("name", "value"), sourceData.sourceData()); + + } + + /** + * we have three configmaps deployed. two of them have labels that match (color=red), + * one does not (color=blue). + */ + @Test + void twoConfigMapsMatchAgainstLabels() { + + V1ConfigMap redOne = new V1ConfigMapBuilder() + .withMetadata(new V1ObjectMetaBuilder().withName("red-configmap") + .withLabels(RED_LABEL) + .withNamespace(NAMESPACE) + .build()) + .addToData("colorOne", "really-red") + .build(); + + V1ConfigMap redTwo = new V1ConfigMapBuilder() + .withMetadata(new V1ObjectMetaBuilder().withName("red-configmap-again") + .withLabels(RED_LABEL) + .withNamespace(NAMESPACE) + .build()) + .addToData("colorTwo", "really-red-again") + .build(); + + V1ConfigMap blue = new V1ConfigMapBuilder() + .withMetadata(new V1ObjectMetaBuilder().withName("blue-configmap") + .withLabels(BLUE_LABEL) + .withNamespace(NAMESPACE) + .build()) + .addToData("color", "blue") + .build(); + + V1ConfigMapList configMapList = new V1ConfigMapList().addItemsItem(redOne) + .addItemsItem(redTwo) + .addItemsItem(blue); + + stubCall(configMapList, "/api/v1/namespaces/default/configmaps?labelSelector=color%3Dred"); + CoreV1Api api = new CoreV1Api(); + + NormalizedSource source = new LabeledConfigMapNormalizedSource(NAMESPACE, RED_LABEL, true, false); + KubernetesClientConfigContext context = new KubernetesClientConfigContext(api, source, NAMESPACE, + new MockEnvironment(), false, NAMESPACED_BATCH_READ); + + KubernetesClientContextToSourceData data = new LabeledConfigMapContextToSourceDataProvider().get(); + SourceData sourceData = data.apply(context); + + Assertions.assertEquals(sourceData.sourceName(), "configmap.red-configmap.red-configmap-again.default"); + Assertions.assertEquals(sourceData.sourceData().size(), 2); + Assertions.assertEquals(sourceData.sourceData().get("colorOne"), "really-red"); + Assertions.assertEquals(sourceData.sourceData().get("colorTwo"), "really-red-again"); + + } + + /** + * one configmap deployed (pink), does not match our query (blue). + */ + @Test + void configMapNoMatch() { + + V1ConfigMap one = new V1ConfigMapBuilder() + .withMetadata(new V1ObjectMetaBuilder().withName("pink-configmap") + .withLabels(PINK_LABEL) + .withNamespace(NAMESPACE) + .build()) + .addToData("color", "pink") + .build(); + V1ConfigMapList configMapList = new V1ConfigMapList().addItemsItem(one); + + // pink returns one + stubCall(configMapList, "/api/v1/namespaces/default/configmaps?labelSelector=color%3Dpink"); + + // blue returns none + stubCall(new V1ConfigMapList(), "/api/v1/namespaces/default/configmaps?labelSelector=color%3Dblue"); + CoreV1Api api = new CoreV1Api(); + + NormalizedSource source = new LabeledConfigMapNormalizedSource(NAMESPACE, BLUE_LABEL, true, false); + KubernetesClientConfigContext context = new KubernetesClientConfigContext(api, source, NAMESPACE, + new MockEnvironment(), false, NAMESPACED_BATCH_READ); + + KubernetesClientContextToSourceData data = new LabeledConfigMapContextToSourceDataProvider().get(); + SourceData sourceData = data.apply(context); + + Assertions.assertEquals(sourceData.sourceName(), "configmap.color.default"); + Assertions.assertEquals(sourceData.sourceData(), Collections.emptyMap()); + + } + + /** + * LabeledConfigMapContextToSourceDataProvider gets as input a Fabric8ConfigContext. + * This context has a namespace as well as a NormalizedSource, that has a namespace + * too. It is easy to get confused in code on which namespace to use. This test makes + * sure that we use the proper one. + */ + @Test + void namespaceMatch() { + V1ConfigMap one = new V1ConfigMapBuilder() + .withMetadata(new V1ObjectMetaBuilder().withName("test-configmap") + .withLabels(LABELS) + .withNamespace(NAMESPACE) + .build()) + .addToData("name", "value") + .build(); + V1ConfigMapList configMapList = new V1ConfigMapList().addItemsItem(one); + stubCall(configMapList, + "/api/v1/namespaces/default/configmaps?labelSelector=label2%3Dvalue2%26label1%3Dvalue1"); + + CoreV1Api api = new CoreV1Api(); + + String wrongNamespace = NAMESPACE + "nope"; + NormalizedSource source = new LabeledConfigMapNormalizedSource(wrongNamespace, LABELS, true, false); + KubernetesClientConfigContext context = new KubernetesClientConfigContext(api, source, NAMESPACE, + new MockEnvironment(), false, NAMESPACED_BATCH_READ); + + KubernetesClientContextToSourceData data = new LabeledConfigMapContextToSourceDataProvider().get(); + SourceData sourceData = data.apply(context); + + Assertions.assertEquals("configmap.test-configmap.default", sourceData.sourceName()); + Assertions.assertEquals(Map.of("name", "value"), sourceData.sourceData()); + } + + /** + * one configmap with name : "blue-configmap" and labels "color=blue" is deployed. we + * search it with the same labels, find it, and assert that name of the SourceData (it + * must use its name, not its labels) and values in the SourceData must be prefixed + * (since we have provided an explicit prefix). + */ + @Test + void testWithPrefix() { + V1ConfigMap one = new V1ConfigMapBuilder() + .withMetadata(new V1ObjectMetaBuilder().withName("blue-configmap") + .withLabels(BLUE_LABEL) + .withNamespace(NAMESPACE) + .build()) + .addToData("what-color", "blue-color") + .build(); + V1ConfigMapList configMapList = new V1ConfigMapList().addItemsItem(one); + stubCall(configMapList, "/api/v1/namespaces/default/configmaps?labelSelector=color%3Dblue"); + + CoreV1Api api = new CoreV1Api(); + + ConfigUtils.Prefix mePrefix = ConfigUtils.findPrefix("me", false, false, "irrelevant"); + NormalizedSource source = new LabeledConfigMapNormalizedSource(NAMESPACE, BLUE_LABEL, true, mePrefix, false); + KubernetesClientConfigContext context = new KubernetesClientConfigContext(api, source, NAMESPACE, + new MockEnvironment(), false, NAMESPACED_BATCH_READ); + + KubernetesClientContextToSourceData data = new LabeledConfigMapContextToSourceDataProvider().get(); + SourceData sourceData = data.apply(context); + + Assertions.assertEquals("configmap.blue-configmap.default", sourceData.sourceName()); + Assertions.assertEquals(Map.of("me.what-color", "blue-color"), sourceData.sourceData()); + } + + /** + * two configmaps are deployed (name:blue-configmap, name:another-blue-configmap) and + * labels "color=blue" (on both). we search with the same labels, find them, and + * assert that name of the SourceData (it must use its name, not its labels) and + * values in the SourceData must be prefixed (since we have provided a delayed + * prefix). + * + * Also notice that the prefix is made up from both configmap names. + * + */ + @Test + void testTwoConfigmapsWithPrefix() { + + V1ConfigMap one = new V1ConfigMapBuilder() + .withMetadata(new V1ObjectMetaBuilder().withName("blue-configmap") + .withLabels(BLUE_LABEL) + .withNamespace(NAMESPACE) + .build()) + .addToData("first", "blue") + .build(); + + V1ConfigMap two = new V1ConfigMapBuilder() + .withMetadata(new V1ObjectMetaBuilder().withName("another-blue-configmap") + .withLabels(BLUE_LABEL) + .withNamespace(NAMESPACE) + .build()) + .addToData("second", "blue") + .build(); + + V1ConfigMapList configMapList = new V1ConfigMapList().addItemsItem(one).addItemsItem(two); + + stubCall(configMapList, "/api/v1/namespaces/default/configmaps?labelSelector=color%3Dblue"); + CoreV1Api api = new CoreV1Api(); + + NormalizedSource source = new LabeledConfigMapNormalizedSource(NAMESPACE, BLUE_LABEL, true, + ConfigUtils.Prefix.DELAYED, false); + KubernetesClientConfigContext context = new KubernetesClientConfigContext(api, source, NAMESPACE, + new MockEnvironment(), false, NAMESPACED_BATCH_READ); + + KubernetesClientContextToSourceData data = new LabeledConfigMapContextToSourceDataProvider().get(); + SourceData sourceData = data.apply(context); + + Assertions.assertEquals(sourceData.sourceName(), "configmap.another-blue-configmap.blue-configmap.default"); + + Map properties = sourceData.sourceData(); + Assertions.assertEquals(2, properties.size()); + Iterator keys = properties.keySet().iterator(); + String firstKey = keys.next(); + String secondKey = keys.next(); + + if (firstKey.contains("first")) { + Assertions.assertEquals(firstKey, "another-blue-configmap.blue-configmap.first"); + } + + Assertions.assertEquals(secondKey, "another-blue-configmap.blue-configmap.second"); + Assertions.assertEquals(properties.get(firstKey), "blue"); + Assertions.assertEquals(properties.get(secondKey), "blue"); + } + + /** + * two configmaps are deployed: "color-configmap" with label: "{color:blue}" and + * "color-configmap-k8s" with no labels. We search by "{color:red}", do not find + * anything and thus have an empty SourceData. + */ + @Test + void searchWithLabelsNoConfigmapsFound() { + + V1ConfigMap one = new V1ConfigMapBuilder() + .withMetadata(new V1ObjectMetaBuilder().withName("color-configmap") + .withLabels(BLUE_LABEL) + .withNamespace(NAMESPACE) + .build()) + .addToData("one", "1") + .build(); + + V1ConfigMap two = new V1ConfigMapBuilder() + .withMetadata(new V1ObjectMetaBuilder().withName("color-config-k8s").withNamespace(NAMESPACE).build()) + .addToData("two", "2") + .build(); + + V1ConfigMapList configMapList = new V1ConfigMapList().addItemsItem(one).addItemsItem(two); + + stubCall(configMapList, "/api/v1/namespaces/default/configmaps?labelSelector=color%3Dblue"); + stubCall(new V1ConfigMapList(), "/api/v1/namespaces/default/configmaps?labelSelector=color%3Dred"); + CoreV1Api api = new CoreV1Api(); + + NormalizedSource source = new LabeledConfigMapNormalizedSource(NAMESPACE, RED_LABEL, true, + ConfigUtils.Prefix.DEFAULT, true); + KubernetesClientConfigContext context = new KubernetesClientConfigContext(api, source, NAMESPACE, + new MockEnvironment(), false, NAMESPACED_BATCH_READ); + + KubernetesClientContextToSourceData data = new LabeledConfigMapContextToSourceDataProvider().get(); + SourceData sourceData = data.apply(context); + + Assertions.assertTrue(sourceData.sourceData().isEmpty()); + Assertions.assertEquals(sourceData.sourceName(), "configmap.color.default"); + + } + + /** + * two configmaps are deployed: "color-configmap" with label: "{color:blue}" and + * "shape-configmap" with label: "{shape:round}". We search by "{color:blue}" and find + * one configmap. + */ + @Test + void searchWithLabelsOneConfigMapFound() { + + V1ConfigMap one = new V1ConfigMapBuilder() + .withMetadata(new V1ObjectMetaBuilder().withName("color-configmap") + .withLabels(BLUE_LABEL) + .withNamespace(NAMESPACE) + .build()) + .addToData("one", "1") + .build(); + + V1ConfigMap two = new V1ConfigMapBuilder() + .withMetadata(new V1ObjectMetaBuilder().withName("shape-configmap").withNamespace(NAMESPACE).build()) + .addToData("two", "2") + .build(); + + V1ConfigMapList configMapListOne = new V1ConfigMapList().addItemsItem(one); + stubCall(configMapListOne, "/api/v1/namespaces/default/configmaps?labelSelector=color%3Dblue"); + + V1ConfigMapList configMapListTwo = new V1ConfigMapList().addItemsItem(one).addItemsItem(two); + stubCall(configMapListTwo, "/api/v1/namespaces/default/configmaps?labelSelector=shape%3Dround"); + + CoreV1Api api = new CoreV1Api(); + + NormalizedSource source = new LabeledConfigMapNormalizedSource(NAMESPACE, BLUE_LABEL, true, + ConfigUtils.Prefix.DEFAULT, true); + KubernetesClientConfigContext context = new KubernetesClientConfigContext(api, source, NAMESPACE, + new MockEnvironment(), false, NAMESPACED_BATCH_READ); + + KubernetesClientContextToSourceData data = new LabeledConfigMapContextToSourceDataProvider().get(); + SourceData sourceData = data.apply(context); + + Assertions.assertEquals(sourceData.sourceData().size(), 1); + Assertions.assertEquals(sourceData.sourceData().get("one"), "1"); + Assertions.assertEquals(sourceData.sourceName(), "configmap.color-configmap.default"); + + } + + /** + * two configmaps are deployed: "color-configmap" with label: "{color:blue}" and + * "color-configmap-k8s" with label: "{color:blue}". We search by "{color:blue}" and + * find them both. + */ + @Test + void searchWithLabelsOneConfigMapFoundAndOneFromProfileFound() { + + V1ConfigMap one = new V1ConfigMapBuilder() + .withMetadata(new V1ObjectMetaBuilder().withName("color-configmap") + .withLabels(BLUE_LABEL) + .withNamespace(NAMESPACE) + .build()) + .addToData("one", "1") + .build(); + + V1ConfigMap two = new V1ConfigMapBuilder() + .withMetadata(new V1ObjectMetaBuilder().withName("color-configmap-k8s") + .withLabels(BLUE_LABEL) + .withNamespace(NAMESPACE) + .build()) + .addToData("two", "2") + .build(); + + V1ConfigMapList configMapList = new V1ConfigMapList().addItemsItem(one).addItemsItem(two); + stubCall(configMapList, "/api/v1/namespaces/default/configmaps?labelSelector=color%3Dblue"); + + CoreV1Api api = new CoreV1Api(); + MockEnvironment environment = new MockEnvironment(); + + NormalizedSource source = new LabeledConfigMapNormalizedSource(NAMESPACE, BLUE_LABEL, true, + ConfigUtils.Prefix.DELAYED, true); + KubernetesClientConfigContext context = new KubernetesClientConfigContext(api, source, NAMESPACE, environment, + false, NAMESPACED_BATCH_READ); + + KubernetesClientContextToSourceData data = new LabeledConfigMapContextToSourceDataProvider().get(); + SourceData sourceData = data.apply(context); + + Assertions.assertEquals(sourceData.sourceData().size(), 2); + Assertions.assertEquals(sourceData.sourceData().get("color-configmap.color-configmap-k8s.one"), "1"); + Assertions.assertEquals(sourceData.sourceData().get("color-configmap.color-configmap-k8s.two"), "2"); + Assertions.assertEquals(sourceData.sourceName(), "configmap.color-configmap.color-configmap-k8s.default"); + + } + + /** + *
+	 *     - configmap "color-configmap" with label "{color:blue}"
+	 *     - configmap "shape-configmap" with labels "{color:blue, shape:round}"
+	 *     - configmap "no-fit" with labels "{tag:no-fit}"
+	 *     - configmap "color-configmap-k8s" with label "{color:red}"
+	 *     - configmap "shape-configmap-k8s" with label "{shape:triangle}"
+	 * 
+ */ + @Test + void searchWithLabelsTwoConfigMapsFoundAndOneFromProfileFound() { + + V1ConfigMap colorConfigMap = new V1ConfigMapBuilder() + .withMetadata(new V1ObjectMetaBuilder().withName("color-configmap") + .withLabels(BLUE_LABEL) + .withNamespace(NAMESPACE) + .build()) + .addToData("one", "1") + .build(); + + V1ConfigMap shapeConfigmap = new V1ConfigMapBuilder() + .withMetadata(new V1ObjectMetaBuilder().withName("shape-configmap") + .withLabels(Map.of("color", "blue", "shape", "round")) + .withNamespace(NAMESPACE) + .build()) + .addToData("two", "2") + .build(); + + V1ConfigMap noFit = new V1ConfigMapBuilder() + .withMetadata(new V1ObjectMetaBuilder().withName("no-fit") + .withLabels(Map.of("tag", "no-fit")) + .withNamespace(NAMESPACE) + .build()) + .addToData("three", "3") + .build(); + + V1ConfigMap colorConfigmapK8s = new V1ConfigMapBuilder() + .withMetadata(new V1ObjectMetaBuilder().withName("color-configmap-k8s") + .withLabels(BLUE_LABEL) + .withNamespace(NAMESPACE) + .build()) + .addToData("four", "4") + .build(); + + V1ConfigMap shapeConfigmapK8s = new V1ConfigMapBuilder() + .withMetadata(new V1ObjectMetaBuilder().withName("shape-configmap-k8s") + .withLabels(BLUE_LABEL) + .withNamespace(NAMESPACE) + .build()) + .addToData("five", "5") + .build(); + + V1ConfigMapList configMapList = new V1ConfigMapList().addItemsItem(colorConfigMap) + .addItemsItem(shapeConfigmap) + .addItemsItem(noFit) + .addItemsItem(colorConfigmapK8s) + .addItemsItem(shapeConfigmapK8s); + + stubCall(configMapList, "/api/v1/namespaces/default/configmaps?labelSelector=color%3Dblue"); + CoreV1Api api = new CoreV1Api(); + MockEnvironment environment = new MockEnvironment(); + + NormalizedSource source = new LabeledConfigMapNormalizedSource(NAMESPACE, BLUE_LABEL, true, + ConfigUtils.Prefix.DELAYED, true); + KubernetesClientConfigContext context = new KubernetesClientConfigContext(api, source, NAMESPACE, environment, + false, NAMESPACED_BATCH_READ); + + KubernetesClientContextToSourceData data = new LabeledConfigMapContextToSourceDataProvider().get(); + SourceData sourceData = data.apply(context); + + Assertions.assertEquals(sourceData.sourceData().size(), 4); + Assertions.assertEquals(sourceData.sourceData() + .get("color-configmap.color-configmap-k8s.shape-configmap.shape-configmap-k8s.one"), "1"); + Assertions.assertEquals(sourceData.sourceData() + .get("color-configmap.color-configmap-k8s.shape-configmap.shape-configmap-k8s.two"), "2"); + Assertions.assertEquals(sourceData.sourceData() + .get("color-configmap.color-configmap-k8s.shape-configmap.shape-configmap-k8s.four"), "4"); + Assertions.assertEquals(sourceData.sourceData() + .get("color-configmap.color-configmap-k8s.shape-configmap.shape-configmap-k8s.five"), "5"); + + Assertions.assertEquals(sourceData.sourceName(), + "configmap.color-configmap.color-configmap-k8s.shape-configmap.shape-configmap-k8s.default"); + + } + + /** + *
+	 *     - one configmap is deployed with label {"color", "red"}
+	 *     - one configmap is deployed with label {"color", "green"}
+	 *
+	 *     - we first search for "red" and find it, and it is retrieved from the cluster via the client.
+	 * 	   - we then search for the "green" one, and it is not cached.
+	 * 
+ */ + @Test + void nonCache(CapturedOutput output) { + V1ConfigMap red = new V1ConfigMapBuilder() + .withMetadata(new V1ObjectMetaBuilder().withLabels(Map.of("color", "red")) + .withNamespace(NAMESPACE) + .withName("red-configmap") + .build()) + .addToData("color", "red") + .build(); + + V1ConfigMap green = new V1ConfigMapBuilder() + .withMetadata(new V1ObjectMetaBuilder().withLabels(Map.of("color", "green")) + .withNamespace(NAMESPACE) + .withName("green-configmap") + .build()) + .addToData("color", "green") + .build(); + + V1ConfigMapList configMapListRed = new V1ConfigMapList().addItemsItem(red); + stubCall(configMapListRed, "/api/v1/namespaces/default/configmaps?labelSelector=color%3Dred"); + + V1ConfigMapList configMapListGreen = new V1ConfigMapList().addItemsItem(green); + stubCall(configMapListGreen, "/api/v1/namespaces/default/configmaps?labelSelector=color%3Dgreen"); + + CoreV1Api api = new CoreV1Api(); + + NormalizedSource redSource = new LabeledConfigMapNormalizedSource(NAMESPACE, Map.of("color", "red"), false, + ConfigUtils.Prefix.DEFAULT, false); + KubernetesClientConfigContext redContext = new KubernetesClientConfigContext(api, redSource, NAMESPACE, + new MockEnvironment(), false, NAMESPACED_BATCH_READ); + KubernetesClientContextToSourceData redData = new LabeledConfigMapContextToSourceDataProvider().get(); + SourceData redSourceData = redData.apply(redContext); + + Assertions.assertEquals(redSourceData.sourceData().size(), 1); + Assertions.assertEquals(redSourceData.sourceData().get("color"), "red"); + Assertions.assertEquals(redSourceData.sourceName(), "configmap.red-configmap.default"); + + Assertions.assertFalse(output.getAll().contains("Loaded all config maps in namespace '" + NAMESPACE + "'")); + Assertions.assertTrue(output.getAll().contains("Will read individual configmaps in namespace")); + + NormalizedSource greenSource = new LabeledConfigMapNormalizedSource(NAMESPACE, Map.of("color", "green"), false, + ConfigUtils.Prefix.DEFAULT, false); + KubernetesClientConfigContext greenContext = new KubernetesClientConfigContext(api, greenSource, NAMESPACE, + new MockEnvironment(), false, NAMESPACED_BATCH_READ); + KubernetesClientContextToSourceData greenData = new LabeledConfigMapContextToSourceDataProvider().get(); + SourceData greenSourceData = greenData.apply(greenContext); + + Assertions.assertEquals(greenSourceData.sourceData().size(), 1); + Assertions.assertEquals(greenSourceData.sourceData().get("color"), "green"); + Assertions.assertEquals(greenSourceData.sourceName(), "configmap.green-configmap.default"); + + // meaning there is a single entry with such a log statement + String[] out = output.getAll().split("Loaded all config maps in namespace"); + Assertions.assertEquals(out.length, 1); + + // meaning that the second read was done from the cache + out = output.getAll().split("Will read individual configmaps in namespace"); + Assertions.assertEquals(out.length, 3); + } + + private void stubCall(V1ConfigMapList configMapList, String path) { + stubFor(get(path).willReturn(aResponse().withStatus(200).withBody(new JSON().serialize(configMapList)))); + } + +} diff --git a/spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/LabeledSecretContextToSourceDataProviderTests.java b/spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/LabeledSecretContextToSourceDataProviderNamespacedBatchReadTests.java similarity index 98% rename from spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/LabeledSecretContextToSourceDataProviderTests.java rename to spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/LabeledSecretContextToSourceDataProviderNamespacedBatchReadTests.java index 088ca7e1b7..fc6522419e 100644 --- a/spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/LabeledSecretContextToSourceDataProviderTests.java +++ b/spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/LabeledSecretContextToSourceDataProviderNamespacedBatchReadTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2022 the original author or authors. + * Copyright 2013-2024 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. @@ -33,6 +33,7 @@ import io.kubernetes.client.openapi.models.V1SecretBuilder; import io.kubernetes.client.openapi.models.V1SecretList; import io.kubernetes.client.util.ClientBuilder; +import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeAll; @@ -56,7 +57,7 @@ * @author wind57 */ @ExtendWith(OutputCaptureExtension.class) -class LabeledSecretContextToSourceDataProviderTests { +class LabeledSecretContextToSourceDataProviderNamespacedBatchReadTests { private static final boolean NAMESPACED_BATCH_READ = true; @@ -89,6 +90,11 @@ void afterEach() { new KubernetesClientSourcesNamespaceBatched().discardSecrets(); } + @AfterAll + static void afterAll() { + WireMock.shutdownServer(); + } + /** * we have a single secret deployed. it does not match our query. */ @@ -547,7 +553,9 @@ void cache(CapturedOutput output) { Assertions.assertEquals(redSourceData.sourceData().size(), 1); Assertions.assertEquals(redSourceData.sourceData().get("color"), "red"); Assertions.assertEquals(redSourceData.sourceName(), "secret.red.default"); + Assertions.assertTrue(output.getAll().contains("Loaded all secrets in namespace '" + NAMESPACE + "'")); + Assertions.assertFalse(output.getAll().contains("Will read individual secrets in namespace")); NormalizedSource greenSource = new LabeledSecretNormalizedSource(NAMESPACE, Map.of("color", "green"), false, ConfigUtils.Prefix.DEFAULT); diff --git a/spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/LabeledSecretContextToSourceDataProviderNonNamespacedBatchReadTests.java b/spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/LabeledSecretContextToSourceDataProviderNonNamespacedBatchReadTests.java new file mode 100644 index 0000000000..2c4172f6c2 --- /dev/null +++ b/spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/LabeledSecretContextToSourceDataProviderNonNamespacedBatchReadTests.java @@ -0,0 +1,588 @@ +/* + * Copyright 2013-2024 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.client.config; + +import java.util.Base64; +import java.util.Collections; +import java.util.Iterator; +import java.util.LinkedHashMap; +import java.util.Map; + +import com.github.tomakehurst.wiremock.WireMockServer; +import com.github.tomakehurst.wiremock.client.WireMock; +import io.kubernetes.client.openapi.ApiClient; +import io.kubernetes.client.openapi.Configuration; +import io.kubernetes.client.openapi.JSON; +import io.kubernetes.client.openapi.apis.CoreV1Api; +import io.kubernetes.client.openapi.models.V1ObjectMetaBuilder; +import io.kubernetes.client.openapi.models.V1Secret; +import io.kubernetes.client.openapi.models.V1SecretBuilder; +import io.kubernetes.client.openapi.models.V1SecretList; +import io.kubernetes.client.util.ClientBuilder; +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.junit.jupiter.api.extension.ExtendWith; + +import org.springframework.boot.test.system.CapturedOutput; +import org.springframework.boot.test.system.OutputCaptureExtension; +import org.springframework.cloud.kubernetes.commons.config.ConfigUtils; +import org.springframework.cloud.kubernetes.commons.config.LabeledSecretNormalizedSource; +import org.springframework.cloud.kubernetes.commons.config.NormalizedSource; +import org.springframework.cloud.kubernetes.commons.config.SourceData; +import org.springframework.mock.env.MockEnvironment; + +import static com.github.tomakehurst.wiremock.client.WireMock.aResponse; +import static com.github.tomakehurst.wiremock.client.WireMock.get; +import static com.github.tomakehurst.wiremock.client.WireMock.stubFor; +import static com.github.tomakehurst.wiremock.core.WireMockConfiguration.options; + +/** + * @author wind57 + */ +@ExtendWith(OutputCaptureExtension.class) +public class LabeledSecretContextToSourceDataProviderNonNamespacedBatchReadTests { + + private static final boolean NAMESPACED_BATCH_READ = false; + + private static final Map LABELS = new LinkedHashMap<>(); + + private static final Map RED_LABEL = Map.of("color", "red"); + + private static final String NAMESPACE = "default"; + + static { + LABELS.put("label2", "value2"); + LABELS.put("label1", "value1"); + } + + @BeforeAll + static void setup() { + WireMockServer wireMockServer = new WireMockServer(options().dynamicPort()); + + wireMockServer.start(); + WireMock.configureFor("localhost", wireMockServer.port()); + + ApiClient client = new ClientBuilder().setBasePath("http://localhost:" + wireMockServer.port()).build(); + client.setDebugging(true); + Configuration.setDefaultApiClient(client); + } + + @AfterEach + void afterEach() { + WireMock.reset(); + new KubernetesClientSourcesNamespaceBatched().discardSecrets(); + } + + @AfterAll + static void afterAll() { + WireMock.shutdownServer(); + } + + /** + * we have a single secret deployed. it does not match our query. + */ + @Test + void noMatch() { + + V1Secret red = new V1SecretBuilder() + .withMetadata(new V1ObjectMetaBuilder().withLabels(Collections.singletonMap("color", "red")) + .withNamespace(NAMESPACE) + .withName("red-secret") + .build()) + .addToData("color", Base64.getEncoder().encode("really-red".getBytes())) + .build(); + V1SecretList secretList = new V1SecretList().addItemsItem(red); + + stubCall(secretList, "/api/v1/namespaces/default/secrets?labelSelector=color%3Dred"); + stubCall(new V1SecretList(), "/api/v1/namespaces/default/secrets?labelSelector=color%3Dblue"); + CoreV1Api api = new CoreV1Api(); + + // blue does not match red + NormalizedSource source = new LabeledSecretNormalizedSource(NAMESPACE, + Collections.singletonMap("color", "blue"), false); + KubernetesClientConfigContext context = new KubernetesClientConfigContext(api, source, NAMESPACE, + new MockEnvironment(), false, NAMESPACED_BATCH_READ); + + KubernetesClientContextToSourceData data = new LabeledSecretContextToSourceDataProvider().get(); + SourceData sourceData = data.apply(context); + + Assertions.assertEquals(sourceData.sourceName(), "secret.color.default"); + Assertions.assertEquals(sourceData.sourceData(), Collections.emptyMap()); + + } + + /** + * we have a single secret deployed. it has two labels and these match against our + * queries. + */ + @Test + void singleSecretMatchAgainstLabels() { + + V1Secret red = new V1SecretBuilder().withMetadata( + new V1ObjectMetaBuilder().withLabels(LABELS).withNamespace(NAMESPACE).withName("test-secret").build()) + .addToData("color", "really-red".getBytes()) + .build(); + V1SecretList secretList = new V1SecretList().addItemsItem(red); + + stubCall(secretList, "/api/v1/namespaces/default/secrets?labelSelector=label2%3Dvalue2%26label1%3Dvalue1"); + CoreV1Api api = new CoreV1Api(); + + NormalizedSource source = new LabeledSecretNormalizedSource(NAMESPACE, LABELS, false); + KubernetesClientConfigContext context = new KubernetesClientConfigContext(api, source, NAMESPACE, + new MockEnvironment(), false, NAMESPACED_BATCH_READ); + + KubernetesClientContextToSourceData data = new LabeledSecretContextToSourceDataProvider().get(); + SourceData sourceData = data.apply(context); + + Assertions.assertEquals(sourceData.sourceName(), "secret.test-secret.default"); + Assertions.assertEquals(sourceData.sourceData(), Map.of("color", "really-red")); + + } + + /** + * we have two secrets deployed. both of them have labels that match (color=red). + */ + @Test + void twoSecretsMatchAgainstLabels() { + + V1Secret one = new V1SecretBuilder().withMetadata( + new V1ObjectMetaBuilder().withLabels(RED_LABEL).withNamespace(NAMESPACE).withName("color-one").build()) + .addToData("colorOne", "really-red-one".getBytes()) + .build(); + + V1Secret two = new V1SecretBuilder().withMetadata( + new V1ObjectMetaBuilder().withLabels(RED_LABEL).withNamespace(NAMESPACE).withName("color-two").build()) + .addToData("colorTwo", "really-red-two".getBytes()) + .build(); + + V1SecretList secretList = new V1SecretList().addItemsItem(one).addItemsItem(two); + stubCall(secretList, "/api/v1/namespaces/default/secrets?labelSelector=color%3Dred"); + CoreV1Api api = new CoreV1Api(); + + NormalizedSource source = new LabeledSecretNormalizedSource(NAMESPACE, RED_LABEL, false); + KubernetesClientConfigContext context = new KubernetesClientConfigContext(api, source, NAMESPACE, + new MockEnvironment(), false, NAMESPACED_BATCH_READ); + + KubernetesClientContextToSourceData data = new LabeledSecretContextToSourceDataProvider().get(); + SourceData sourceData = data.apply(context); + + Assertions.assertEquals(sourceData.sourceName(), "secret.color-one.color-two.default"); + Assertions.assertEquals(sourceData.sourceData().size(), 2); + Assertions.assertEquals(sourceData.sourceData().get("colorOne"), "really-red-one"); + Assertions.assertEquals(sourceData.sourceData().get("colorTwo"), "really-red-two"); + + } + + @Test + void namespaceMatch() { + V1Secret one = new V1SecretBuilder().withMetadata( + new V1ObjectMetaBuilder().withLabels(LABELS).withNamespace(NAMESPACE).withName("test-secret").build()) + .addToData("color", "really-red".getBytes()) + .build(); + V1SecretList secretList = new V1SecretList().addItemsItem(one); + + stubCall(secretList, "/api/v1/namespaces/default/secrets?labelSelector=label2%3Dvalue2%26label1%3Dvalue1"); + CoreV1Api api = new CoreV1Api(); + + NormalizedSource source = new LabeledSecretNormalizedSource(NAMESPACE + "nope", LABELS, false); + KubernetesClientConfigContext context = new KubernetesClientConfigContext(api, source, NAMESPACE, + new MockEnvironment(), false, NAMESPACED_BATCH_READ); + + KubernetesClientContextToSourceData data = new LabeledSecretContextToSourceDataProvider().get(); + SourceData sourceData = data.apply(context); + + Assertions.assertEquals(sourceData.sourceName(), "secret.test-secret.default"); + Assertions.assertEquals(sourceData.sourceData(), Map.of("color", "really-red")); + } + + /** + * one secret with name : "blue-secret" and labels "color=blue" is deployed. we search + * it with the same labels, find it, and assert that name of the SourceData (it must + * use its name, not its labels) and values in the SourceData must be prefixed (since + * we have provided an explicit prefix). + */ + @Test + void testWithPrefix() { + + V1Secret one = new V1SecretBuilder() + .withMetadata(new V1ObjectMetaBuilder().withLabels(Map.of("color", "blue")) + .withNamespace(NAMESPACE) + .withName("blue-secret") + .build()) + .addToData("what-color", "blue-color".getBytes()) + .build(); + V1SecretList secretList = new V1SecretList().addItemsItem(one); + + stubCall(secretList, "/api/v1/namespaces/default/secrets?labelSelector=color%3Dblue"); + CoreV1Api api = new CoreV1Api(); + + ConfigUtils.Prefix prefix = ConfigUtils.findPrefix("me", false, false, null); + NormalizedSource source = new LabeledSecretNormalizedSource(NAMESPACE, Map.of("color", "blue"), false, prefix); + KubernetesClientConfigContext context = new KubernetesClientConfigContext(api, source, NAMESPACE, + new MockEnvironment(), false, NAMESPACED_BATCH_READ); + + KubernetesClientContextToSourceData data = new LabeledSecretContextToSourceDataProvider().get(); + SourceData sourceData = data.apply(context); + + Assertions.assertEquals("secret.blue-secret.default", sourceData.sourceName()); + Assertions.assertEquals(Map.of("me.what-color", "blue-color"), sourceData.sourceData()); + } + + /** + * two secrets are deployed (name:blue-secret, name:another-blue-secret) and labels + * "color=blue" (on both). we search with the same labels, find them, and assert that + * name of the SourceData (it must use its name, not its labels) and values in the + * SourceData must be prefixed (since we have provided a delayed prefix). + * + * Also notice that the prefix is made up from both secret names. + * + */ + @Test + void testTwoSecretsWithPrefix() { + + V1Secret one = new V1SecretBuilder() + .withMetadata(new V1ObjectMetaBuilder().withLabels(Map.of("color", "blue")) + .withNamespace(NAMESPACE) + .withName("blue-secret") + .build()) + .addToData("first", "blue".getBytes()) + .build(); + + V1Secret two = new V1SecretBuilder() + .withMetadata(new V1ObjectMetaBuilder().withLabels(Map.of("color", "blue")) + .withNamespace(NAMESPACE) + .withName("another-blue-secret") + .build()) + .addToData("second", "blue".getBytes()) + .build(); + + V1SecretList secretList = new V1SecretList().addItemsItem(one).addItemsItem(two); + + stubCall(secretList, "/api/v1/namespaces/default/secrets?labelSelector=color%3Dblue"); + CoreV1Api api = new CoreV1Api(); + + NormalizedSource source = new LabeledSecretNormalizedSource(NAMESPACE, Map.of("color", "blue"), false, + ConfigUtils.Prefix.DELAYED); + KubernetesClientConfigContext context = new KubernetesClientConfigContext(api, source, NAMESPACE, + new MockEnvironment(), false, NAMESPACED_BATCH_READ); + + KubernetesClientContextToSourceData data = new LabeledSecretContextToSourceDataProvider().get(); + SourceData sourceData = data.apply(context); + + // maps don't have a defined order, so assert components separately + Assertions.assertEquals(46, sourceData.sourceName().length()); + Assertions.assertTrue(sourceData.sourceName().contains("secret")); + Assertions.assertTrue(sourceData.sourceName().contains("blue-secret")); + Assertions.assertTrue(sourceData.sourceName().contains("another-blue-secret")); + Assertions.assertTrue(sourceData.sourceName().contains("default")); + + Map properties = sourceData.sourceData(); + Assertions.assertEquals(2, properties.size()); + Iterator keys = properties.keySet().iterator(); + String firstKey = keys.next(); + String secondKey = keys.next(); + + if (firstKey.contains("first")) { + Assertions.assertEquals(firstKey, "another-blue-secret.blue-secret.first"); + } + + Assertions.assertEquals(secondKey, "another-blue-secret.blue-secret.second"); + Assertions.assertEquals(properties.get(firstKey), "blue"); + Assertions.assertEquals(properties.get(secondKey), "blue"); + } + + /** + * two secrets are deployed: secret "color-secret" with label: "{color:blue}" and + * "shape-secret" with label: "{shape:round}". We search by "{color:blue}" and find + * one secret. + */ + @Test + void searchWithLabelsOneSecretFound() { + + V1Secret colorSecret = new V1SecretBuilder() + .withMetadata(new V1ObjectMetaBuilder().withLabels(Map.of("color", "blue")) + .withNamespace(NAMESPACE) + .withName("color-secret") + .build()) + .addToData("one", "1".getBytes()) + .build(); + + V1Secret shapeSecret = new V1SecretBuilder() + .withMetadata(new V1ObjectMetaBuilder().withLabels(Map.of("shape", "round")) + .withNamespace(NAMESPACE) + .withName("shape-secret") + .build()) + .addToData("two", "2".getBytes()) + .build(); + + V1SecretList secretList = new V1SecretList().addItemsItem(colorSecret).addItemsItem(shapeSecret); + + stubCall(secretList, "/api/v1/namespaces/default/secrets?labelSelector=color%3Dblue"); + CoreV1Api api = new CoreV1Api(); + + NormalizedSource source = new LabeledSecretNormalizedSource(NAMESPACE, Map.of("color", "blue"), false, + ConfigUtils.Prefix.DEFAULT); + KubernetesClientConfigContext context = new KubernetesClientConfigContext(api, source, NAMESPACE, + new MockEnvironment(), false, NAMESPACED_BATCH_READ); + + KubernetesClientContextToSourceData data = new LabeledSecretContextToSourceDataProvider().get(); + SourceData sourceData = data.apply(context); + + Assertions.assertEquals(sourceData.sourceData().size(), 1); + Assertions.assertEquals(sourceData.sourceData().get("one"), "1"); + Assertions.assertEquals(sourceData.sourceName(), "secret.color-secret.default"); + + } + + /** + * two secrets are deployed: secret "color-secret" with label: "{color:blue}" and + * "color-secret-k8s" with label: "{color:blue}". We search by "{color:blue}" and find + * both. + */ + @Test + void searchWithLabelsOneSecretFoundAndOneFromProfileFound() { + + V1Secret colorSecret = new V1SecretBuilder() + .withMetadata(new V1ObjectMetaBuilder().withLabels(Map.of("color", "blue")) + .withNamespace(NAMESPACE) + .withName("color-secret") + .build()) + .addToData("one", "1".getBytes()) + .build(); + + V1Secret shapeSecret = new V1SecretBuilder() + .withMetadata(new V1ObjectMetaBuilder().withLabels(Map.of("color", "blue")) + .withNamespace(NAMESPACE) + .withName("color-secret-k8s") + .build()) + .addToData("two", "2".getBytes()) + .build(); + + V1SecretList secretList = new V1SecretList().addItemsItem(colorSecret).addItemsItem(shapeSecret); + + stubCall(secretList, "/api/v1/namespaces/default/secrets?labelSelector=color%3Dblue"); + CoreV1Api api = new CoreV1Api(); + MockEnvironment environment = new MockEnvironment(); + + NormalizedSource source = new LabeledSecretNormalizedSource(NAMESPACE, Map.of("color", "blue"), false, + ConfigUtils.Prefix.DELAYED); + KubernetesClientConfigContext context = new KubernetesClientConfigContext(api, source, NAMESPACE, environment, + false, NAMESPACED_BATCH_READ); + + KubernetesClientContextToSourceData data = new LabeledSecretContextToSourceDataProvider().get(); + SourceData sourceData = data.apply(context); + + Assertions.assertEquals(sourceData.sourceData().size(), 2); + Assertions.assertEquals(sourceData.sourceData().get("color-secret.color-secret-k8s.one"), "1"); + Assertions.assertEquals(sourceData.sourceData().get("color-secret.color-secret-k8s.two"), "2"); + Assertions.assertEquals(sourceData.sourceName(), "secret.color-secret.color-secret-k8s.default"); + + } + + /** + *
+	 *     - secret "color-secret" with label "{color:blue}"
+	 *     - secret "shape-secret" with labels "{color:blue, shape:round}"
+	 *     - secret "no-fit" with labels "{tag:no-fit}"
+	 *     - secret "color-secret-k8s" with label "{color:red}"
+	 *     - secret "shape-secret-k8s" with label "{shape:triangle}"
+	 * 
+ */ + @Test + void searchWithLabelsTwoSecretsFoundAndOneFromProfileFound() { + + V1Secret colorSecret = new V1SecretBuilder() + .withMetadata(new V1ObjectMetaBuilder().withLabels(Map.of("color", "blue")) + .withNamespace(NAMESPACE) + .withName("color-secret") + .build()) + .addToData("one", "1".getBytes()) + .build(); + + V1Secret shapeSecret = new V1SecretBuilder() + .withMetadata(new V1ObjectMetaBuilder().withLabels(Map.of("color", "blue", "shape", "round")) + .withNamespace(NAMESPACE) + .withName("shape-secret") + .build()) + .addToData("two", "2".getBytes()) + .build(); + + V1Secret noFit = new V1SecretBuilder() + .withMetadata(new V1ObjectMetaBuilder().withLabels(Map.of("tag", "no-fit")) + .withNamespace(NAMESPACE) + .withName("no-fit") + .build()) + .addToData("three", "3".getBytes()) + .build(); + + V1Secret colorSecretK8s = new V1SecretBuilder() + .withMetadata(new V1ObjectMetaBuilder().withLabels(Map.of("color", "blue")) + .withNamespace(NAMESPACE) + .withName("color-secret-k8s") + .build()) + .addToData("four", "4".getBytes()) + .build(); + + V1Secret shapeSecretK8s = new V1SecretBuilder() + .withMetadata(new V1ObjectMetaBuilder().withLabels(Map.of("color", "blue")) + .withNamespace(NAMESPACE) + .withName("shape-secret-k8s") + .build()) + .addToData("five", "5".getBytes()) + .build(); + + V1SecretList secretList = new V1SecretList().addItemsItem(colorSecret) + .addItemsItem(shapeSecret) + .addItemsItem(noFit) + .addItemsItem(colorSecretK8s) + .addItemsItem(shapeSecretK8s); + + stubCall(secretList, "/api/v1/namespaces/default/secrets?labelSelector=color%3Dblue"); + CoreV1Api api = new CoreV1Api(); + MockEnvironment environment = new MockEnvironment(); + + NormalizedSource source = new LabeledSecretNormalizedSource(NAMESPACE, Map.of("color", "blue"), false, + ConfigUtils.Prefix.DELAYED); + KubernetesClientConfigContext context = new KubernetesClientConfigContext(api, source, NAMESPACE, environment, + false, NAMESPACED_BATCH_READ); + + KubernetesClientContextToSourceData data = new LabeledSecretContextToSourceDataProvider().get(); + SourceData sourceData = data.apply(context); + + Assertions.assertEquals(sourceData.sourceData().size(), 4); + Assertions.assertEquals( + sourceData.sourceData().get("color-secret.color-secret-k8s.shape-secret.shape-secret-k8s.one"), "1"); + Assertions.assertEquals( + sourceData.sourceData().get("color-secret.color-secret-k8s.shape-secret.shape-secret-k8s.two"), "2"); + Assertions.assertEquals( + sourceData.sourceData().get("color-secret.color-secret-k8s.shape-secret.shape-secret-k8s.four"), "4"); + Assertions.assertEquals( + sourceData.sourceData().get("color-secret.color-secret-k8s.shape-secret.shape-secret-k8s.five"), "5"); + + Assertions.assertEquals(sourceData.sourceName(), + "secret.color-secret.color-secret-k8s.shape-secret.shape-secret-k8s.default"); + + } + + /** + * yaml/properties gets special treatment + */ + @Test + void testYaml() { + V1Secret colorSecret = new V1SecretBuilder() + .withMetadata(new V1ObjectMetaBuilder().withLabels(Map.of("color", "blue")) + .withNamespace(NAMESPACE) + .withName("color-secret") + .build()) + .addToData("test.yaml", "color: blue".getBytes()) + .build(); + + V1SecretList secretList = new V1SecretList().addItemsItem(colorSecret); + + stubCall(secretList, "/api/v1/namespaces/default/secrets?labelSelector=color%3Dblue"); + CoreV1Api api = new CoreV1Api(); + + NormalizedSource source = new LabeledSecretNormalizedSource(NAMESPACE, Map.of("color", "blue"), false, + ConfigUtils.Prefix.DEFAULT); + KubernetesClientConfigContext context = new KubernetesClientConfigContext(api, source, NAMESPACE, + new MockEnvironment(), false, NAMESPACED_BATCH_READ); + + KubernetesClientContextToSourceData data = new LabeledSecretContextToSourceDataProvider().get(); + SourceData sourceData = data.apply(context); + + Assertions.assertEquals(sourceData.sourceData().size(), 1); + Assertions.assertEquals(sourceData.sourceData().get("color"), "blue"); + Assertions.assertEquals(sourceData.sourceName(), "secret.color-secret.default"); + } + + /** + *
+	 *     - one secret is deployed with label {"color", "red"}
+	 *     - one secret is deployed with label {"color", "green"}
+	 *
+	 *     - we first search for "red" and find it, and it is retrieved from the cluster via the client.
+	 * 	   - we then search for the "green" one, and it is not retrieved from the cache.
+	 * 
+ */ + @Test + void nonCache(CapturedOutput output) { + V1Secret red = new V1SecretBuilder() + .withMetadata(new V1ObjectMetaBuilder().withLabels(Map.of("color", "red")) + .withNamespace(NAMESPACE) + .withName("red") + .build()) + .addToData("color", "red".getBytes()) + .build(); + + V1Secret green = new V1SecretBuilder() + .withMetadata(new V1ObjectMetaBuilder().withLabels(Map.of("color", "green")) + .withNamespace(NAMESPACE) + .withName("green") + .build()) + .addToData("color", "green".getBytes()) + .build(); + + V1SecretList secretListRed = new V1SecretList().addItemsItem(red); + stubCall(secretListRed, "/api/v1/namespaces/default/secrets?labelSelector=color%3Dred"); + + V1SecretList secretListGreen = new V1SecretList().addItemsItem(green); + stubCall(secretListGreen, "/api/v1/namespaces/default/secrets?labelSelector=color%3Dgreen"); + + CoreV1Api api = new CoreV1Api(); + + NormalizedSource redSource = new LabeledSecretNormalizedSource(NAMESPACE, Map.of("color", "red"), false, + ConfigUtils.Prefix.DEFAULT); + KubernetesClientConfigContext redContext = new KubernetesClientConfigContext(api, redSource, NAMESPACE, + new MockEnvironment(), false, NAMESPACED_BATCH_READ); + KubernetesClientContextToSourceData redData = new LabeledSecretContextToSourceDataProvider().get(); + SourceData redSourceData = redData.apply(redContext); + + Assertions.assertEquals(redSourceData.sourceData().size(), 1); + Assertions.assertEquals(redSourceData.sourceData().get("color"), "red"); + Assertions.assertEquals(redSourceData.sourceName(), "secret.red.default"); + + Assertions.assertFalse(output.getAll().contains("Loaded all secrets in namespace '" + NAMESPACE + "'")); + Assertions.assertTrue(output.getAll().contains("Will read individual secrets in namespace")); + + NormalizedSource greenSource = new LabeledSecretNormalizedSource(NAMESPACE, Map.of("color", "green"), false, + ConfigUtils.Prefix.DEFAULT); + KubernetesClientConfigContext greenContext = new KubernetesClientConfigContext(api, greenSource, NAMESPACE, + new MockEnvironment(), false, NAMESPACED_BATCH_READ); + KubernetesClientContextToSourceData greenData = new LabeledSecretContextToSourceDataProvider().get(); + SourceData greenSourceData = greenData.apply(greenContext); + + Assertions.assertEquals(greenSourceData.sourceData().size(), 1); + Assertions.assertEquals(greenSourceData.sourceData().get("color"), "green"); + Assertions.assertEquals(greenSourceData.sourceName(), "secret.green.default"); + + // meaning there is a single entry with such a log statement + String[] out = output.getAll().split("Loaded all secrets in namespace"); + Assertions.assertEquals(out.length, 1); + + // meaning that the second read was done from the cache + out = output.getAll().split("Will read individual secrets in namespace"); + Assertions.assertEquals(out.length, 3); + } + + private void stubCall(V1SecretList configMapList, String path) { + stubFor(get(path).willReturn(aResponse().withStatus(200).withBody(new JSON().serialize(configMapList)))); + } + +} diff --git a/spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/NamedConfigMapContextToSourceDataProviderTests.java b/spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/NamedConfigMapContextToSourceDataProviderNamespacedBatchReadTests.java similarity index 98% rename from spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/NamedConfigMapContextToSourceDataProviderTests.java rename to spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/NamedConfigMapContextToSourceDataProviderNamespacedBatchReadTests.java index 07e7066f43..243b2c34ee 100644 --- a/spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/NamedConfigMapContextToSourceDataProviderTests.java +++ b/spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/NamedConfigMapContextToSourceDataProviderNamespacedBatchReadTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2022 the original author or authors. + * Copyright 2013-2024 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. @@ -30,6 +30,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.AfterAll; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeAll; @@ -53,7 +54,7 @@ * @author wind57 */ @ExtendWith(OutputCaptureExtension.class) -class NamedConfigMapContextToSourceDataProviderTests { +class NamedConfigMapContextToSourceDataProviderNamespacedBatchReadTests { private static final boolean NAMESPACED_BATCH_READ = true; @@ -87,6 +88,11 @@ void afterEach() { new KubernetesClientSourcesNamespaceBatched().discardConfigMaps(); } + @AfterAll + static void afterAll() { + WireMock.shutdownServer(); + } + /** *
 	 *     one configmap deployed with name "red"
@@ -442,7 +448,9 @@ void cache(CapturedOutput output) {
 
 		Assertions.assertEquals(redSourceData.sourceName(), "configmap.red.default");
 		Assertions.assertEquals(redSourceData.sourceData(), Map.of("color", "red"));
+
 		Assertions.assertTrue(output.getAll().contains("Loaded all config maps in namespace '" + NAMESPACE + "'"));
+		Assertions.assertFalse(output.getAll().contains("Will read individual configmaps in namespace"));
 
 		NormalizedSource greenSource = new NamedConfigMapNormalizedSource("green", NAMESPACE, true, true);
 		KubernetesClientConfigContext greenContext = new KubernetesClientConfigContext(api, greenSource, NAMESPACE,
diff --git a/spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/NamedConfigMapContextToSourceDataProviderNonNamespacedBatchReadTests.java b/spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/NamedConfigMapContextToSourceDataProviderNonNamespacedBatchReadTests.java
new file mode 100644
index 0000000000..762d093b44
--- /dev/null
+++ b/spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/NamedConfigMapContextToSourceDataProviderNonNamespacedBatchReadTests.java
@@ -0,0 +1,466 @@
+/*
+ * Copyright 2013-2024 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.client.config;
+
+import java.util.Map;
+
+import com.github.tomakehurst.wiremock.WireMockServer;
+import com.github.tomakehurst.wiremock.client.WireMock;
+import io.kubernetes.client.openapi.ApiClient;
+import io.kubernetes.client.openapi.Configuration;
+import io.kubernetes.client.openapi.JSON;
+import io.kubernetes.client.openapi.apis.CoreV1Api;
+import io.kubernetes.client.openapi.models.V1ConfigMap;
+import io.kubernetes.client.openapi.models.V1ConfigMapBuilder;
+import io.kubernetes.client.openapi.models.V1ObjectMetaBuilder;
+import io.kubernetes.client.util.ClientBuilder;
+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.junit.jupiter.api.extension.ExtendWith;
+
+import org.springframework.boot.test.system.CapturedOutput;
+import org.springframework.boot.test.system.OutputCaptureExtension;
+import org.springframework.cloud.kubernetes.commons.config.ConfigUtils;
+import org.springframework.cloud.kubernetes.commons.config.NamedConfigMapNormalizedSource;
+import org.springframework.cloud.kubernetes.commons.config.NormalizedSource;
+import org.springframework.cloud.kubernetes.commons.config.SourceData;
+import org.springframework.mock.env.MockEnvironment;
+
+import static com.github.tomakehurst.wiremock.client.WireMock.aResponse;
+import static com.github.tomakehurst.wiremock.client.WireMock.get;
+import static com.github.tomakehurst.wiremock.client.WireMock.stubFor;
+import static com.github.tomakehurst.wiremock.core.WireMockConfiguration.options;
+
+/**
+ * @author wind57
+ */
+@ExtendWith(OutputCaptureExtension.class)
+class NamedConfigMapContextToSourceDataProviderNonNamespacedBatchReadTests {
+
+	private static final boolean NAMESPACED_BATCH_READ = false;
+
+	private static final String NAMESPACE = "default";
+
+	private static final String RED_CONFIG_MAP_NAME = "red";
+
+	private static final String RED_WITH_PROFILE_CONFIG_MAP_NAME = RED_CONFIG_MAP_NAME + "-with-profile";
+
+	private static final String BLUE_CONFIG_MAP_NAME = "blue";
+
+	private static final Map COLOR_REALLY_RED = Map.of("color", "really-red");
+
+	private static final Map TASTE_MANGO = Map.of("taste", "mango");
+
+	@BeforeAll
+	static void setup() {
+		WireMockServer wireMockServer = new WireMockServer(options().dynamicPort());
+
+		wireMockServer.start();
+		WireMock.configureFor("localhost", wireMockServer.port());
+
+		ApiClient client = new ClientBuilder().setBasePath("http://localhost:" + wireMockServer.port()).build();
+		client.setDebugging(true);
+		Configuration.setDefaultApiClient(client);
+	}
+
+	@AfterEach
+	void afterEach() {
+		WireMock.reset();
+		new KubernetesClientSourcesNamespaceBatched().discardConfigMaps();
+	}
+
+	@AfterAll
+	static void afterAll() {
+		WireMock.shutdownServer();
+	}
+
+	/**
+	 * 
+	 *     one configmap deployed with name "red"
+	 *     we search by name, but for the "blue" one, as such not find it
+	 * 
+ */ + @Test + void noMatch() { + V1ConfigMap redConfigMap = new V1ConfigMapBuilder() + .withMetadata(new V1ObjectMetaBuilder().withName(RED_CONFIG_MAP_NAME).withNamespace(NAMESPACE).build()) + .addToData(COLOR_REALLY_RED) + .build(); + stubCall(redConfigMap, "/api/v1/namespaces/default/configmaps/red"); + + CoreV1Api api = new CoreV1Api(); + + NormalizedSource source = new NamedConfigMapNormalizedSource(BLUE_CONFIG_MAP_NAME, NAMESPACE, true, false); + KubernetesClientConfigContext context = new KubernetesClientConfigContext(api, source, NAMESPACE, + new MockEnvironment(), true, NAMESPACED_BATCH_READ); + + KubernetesClientContextToSourceData data = new NamedConfigMapContextToSourceDataProvider().get(); + SourceData sourceData = data.apply(context); + + Assertions.assertEquals(sourceData.sourceName(), "configmap.blue.default"); + Assertions.assertEquals(sourceData.sourceData(), Map.of()); + + } + + /** + *
+	 *     one configmap deployed with name "red"
+	 *     we search by name, for the "red" one, as such we find it
+	 * 
+ */ + @Test + void match() { + + V1ConfigMap configMap = new V1ConfigMapBuilder() + .withMetadata(new V1ObjectMetaBuilder().withName(RED_CONFIG_MAP_NAME).withNamespace(NAMESPACE).build()) + .addToData(COLOR_REALLY_RED) + .build(); + stubCall(configMap, "/api/v1/namespaces/default/configmaps/red"); + + CoreV1Api api = new CoreV1Api(); + + NormalizedSource source = new NamedConfigMapNormalizedSource(RED_CONFIG_MAP_NAME, NAMESPACE, true, false); + KubernetesClientConfigContext context = new KubernetesClientConfigContext(api, source, NAMESPACE, + new MockEnvironment(), true, NAMESPACED_BATCH_READ); + + KubernetesClientContextToSourceData data = new NamedConfigMapContextToSourceDataProvider().get(); + SourceData sourceData = data.apply(context); + + Assertions.assertEquals(sourceData.sourceName(), "configmap.red.default"); + Assertions.assertEquals(sourceData.sourceData(), COLOR_REALLY_RED); + + } + + /** + *
+	 *     - two configmaps deployed : "red" and "red-with-profile".
+	 * 
+ */ + @Test + void matchIncludeSingleProfile() { + + V1ConfigMap red = new V1ConfigMapBuilder() + .withMetadata(new V1ObjectMetaBuilder().withName(RED_CONFIG_MAP_NAME).withNamespace(NAMESPACE).build()) + .addToData(COLOR_REALLY_RED) + .build(); + stubCall(red, "/api/v1/namespaces/default/configmaps/red"); + + V1ConfigMap redWithProfile = new V1ConfigMapBuilder().withMetadata( + new V1ObjectMetaBuilder().withName(RED_WITH_PROFILE_CONFIG_MAP_NAME).withNamespace(NAMESPACE).build()) + .addToData(TASTE_MANGO) + .build(); + stubCall(redWithProfile, "/api/v1/namespaces/default/configmaps/red-with-profile"); + + CoreV1Api api = new CoreV1Api(); + + NormalizedSource source = new NamedConfigMapNormalizedSource(RED_CONFIG_MAP_NAME, NAMESPACE, true, + ConfigUtils.Prefix.DEFAULT, true, true); + MockEnvironment environment = new MockEnvironment(); + environment.setActiveProfiles("with-profile"); + KubernetesClientConfigContext context = new KubernetesClientConfigContext(api, source, NAMESPACE, environment, + false, NAMESPACED_BATCH_READ); + + KubernetesClientContextToSourceData data = new NamedConfigMapContextToSourceDataProvider().get(); + SourceData sourceData = data.apply(context); + + Assertions.assertEquals(sourceData.sourceName(), "configmap.red.red-with-profile.default.with-profile"); + Assertions.assertEquals(sourceData.sourceData().size(), 1); + Assertions.assertEquals(sourceData.sourceData().get("taste"), "mango"); + + } + + /** + *
+	 *     - two configmaps deployed : "red" and "red-with-profile".
+	 *     - "red" is matched directly, "red-with-profile" is matched because we have an active profile
+	 *       "active-profile"
+	 *     -  This takes into consideration the prefix, that we explicitly specify.
+	 *        Notice that prefix works for profile based config maps as well.
+	 * 
+ */ + @Test + void matchIncludeSingleProfileWithPrefix() { + + V1ConfigMap red = new V1ConfigMapBuilder() + .withMetadata(new V1ObjectMetaBuilder().withName(RED_CONFIG_MAP_NAME).withNamespace(NAMESPACE).build()) + .addToData(COLOR_REALLY_RED) + .build(); + stubCall(red, "/api/v1/namespaces/default/configmaps/red"); + + V1ConfigMap redWithTaste = new V1ConfigMapBuilder().withMetadata( + new V1ObjectMetaBuilder().withName(RED_WITH_PROFILE_CONFIG_MAP_NAME).withNamespace(NAMESPACE).build()) + .addToData(TASTE_MANGO) + .build(); + stubCall(redWithTaste, "/api/v1/namespaces/default/configmaps/red-with-profile"); + + CoreV1Api api = new CoreV1Api(); + + ConfigUtils.Prefix prefix = ConfigUtils.findPrefix("some", false, false, null); + NormalizedSource source = new NamedConfigMapNormalizedSource(RED_CONFIG_MAP_NAME, NAMESPACE, true, prefix, + true); + MockEnvironment environment = new MockEnvironment(); + environment.setActiveProfiles("with-profile"); + KubernetesClientConfigContext context = new KubernetesClientConfigContext(api, source, NAMESPACE, environment, + true, NAMESPACED_BATCH_READ); + + KubernetesClientContextToSourceData data = new NamedConfigMapContextToSourceDataProvider().get(); + SourceData sourceData = data.apply(context); + + Assertions.assertEquals(sourceData.sourceName(), "configmap.red.red-with-profile.default"); + Assertions.assertEquals(sourceData.sourceData().size(), 2); + Assertions.assertEquals(sourceData.sourceData().get("some.color"), "really-red"); + Assertions.assertEquals(sourceData.sourceData().get("some.taste"), "mango"); + + } + + /** + *
+	 *     - three configmaps deployed : "red", "red-with-taste" and "red-with-shape"
+	 *     - "red" is matched directly, the other two are matched because of active profiles
+	 *     -  This takes into consideration the prefix, that we explicitly specify.
+	 *        Notice that prefix works for profile based config maps as well.
+	 * 
+ */ + @Test + void matchIncludeTwoProfilesWithPrefix() { + + V1ConfigMap red = new V1ConfigMapBuilder() + .withMetadata(new V1ObjectMetaBuilder().withName(RED_CONFIG_MAP_NAME).withNamespace(NAMESPACE).build()) + .addToData(COLOR_REALLY_RED) + .build(); + stubCall(red, "/api/v1/namespaces/default/configmaps/red"); + + V1ConfigMap redWithTaste = new V1ConfigMapBuilder() + .withMetadata(new V1ObjectMetaBuilder().withName(RED_CONFIG_MAP_NAME + "-with-taste") + .withNamespace(NAMESPACE) + .withResourceVersion("1") + .build()) + .addToData(TASTE_MANGO) + .build(); + stubCall(redWithTaste, "/api/v1/namespaces/default/configmaps/red-with-taste"); + + V1ConfigMap redWithShape = new V1ConfigMapBuilder() + .withMetadata(new V1ObjectMetaBuilder().withName(RED_CONFIG_MAP_NAME + "-with-shape") + .withNamespace(NAMESPACE) + .build()) + .addToData("shape", "round") + .build(); + stubCall(redWithShape, "/api/v1/namespaces/default/configmaps/red-with-shape"); + + CoreV1Api api = new CoreV1Api(); + + ConfigUtils.Prefix prefix = ConfigUtils.findPrefix("some", false, false, null); + NormalizedSource source = new NamedConfigMapNormalizedSource(RED_CONFIG_MAP_NAME, NAMESPACE, true, prefix, + true); + MockEnvironment environment = new MockEnvironment(); + environment.setActiveProfiles("with-taste", "with-shape"); + KubernetesClientConfigContext context = new KubernetesClientConfigContext(api, source, NAMESPACE, environment, + true, NAMESPACED_BATCH_READ); + + KubernetesClientContextToSourceData data = new NamedConfigMapContextToSourceDataProvider().get(); + SourceData sourceData = data.apply(context); + + Assertions.assertEquals(sourceData.sourceName(), "configmap.red.red-with-shape.red-with-taste.default"); + Assertions.assertEquals(sourceData.sourceData().size(), 3); + Assertions.assertEquals(sourceData.sourceData().get("some.color"), "really-red"); + Assertions.assertEquals(sourceData.sourceData().get("some.taste"), "mango"); + Assertions.assertEquals(sourceData.sourceData().get("some.shape"), "round"); + + } + + /** + *
+	 * 		proves that an implicit configmap is not going to be generated and read
+	 * 
+ */ + @Test + void matchWithName() { + + V1ConfigMap red = new V1ConfigMapBuilder() + .withMetadata(new V1ObjectMetaBuilder().withName("application").withNamespace(NAMESPACE).build()) + .addToData("color", "red") + .build(); + stubCall(red, "/api/v1/namespaces/default/configmaps/red"); + + CoreV1Api api = new CoreV1Api(); + + ConfigUtils.Prefix prefix = ConfigUtils.findPrefix("some", false, false, null); + NormalizedSource source = new NamedConfigMapNormalizedSource("application", NAMESPACE, true, prefix, false); + KubernetesClientConfigContext context = new KubernetesClientConfigContext(api, source, NAMESPACE, + new MockEnvironment(), true, NAMESPACED_BATCH_READ); + + KubernetesClientContextToSourceData data = new NamedConfigMapContextToSourceDataProvider().get(); + SourceData sourceData = data.apply(context); + + Assertions.assertEquals(sourceData.sourceName(), "configmap.application.default"); + Assertions.assertEquals(sourceData.sourceData(), Map.of()); + } + + /** + *
+	 *     - NamedSecretContextToSourceDataProvider gets as input a KubernetesClientConfigContext.
+	 *     - This context has a namespace as well as a NormalizedSource, that has a namespace too.
+	 *     - This test makes sure that we use the proper one.
+	 * 
+ */ + @Test + void namespaceMatch() { + + V1ConfigMap configMap = new V1ConfigMapBuilder() + .withMetadata(new V1ObjectMetaBuilder().withName(RED_CONFIG_MAP_NAME).withNamespace(NAMESPACE).build()) + .addToData(COLOR_REALLY_RED) + .build(); + stubCall(configMap, "/api/v1/namespaces/default/configmaps/red"); + + CoreV1Api api = new CoreV1Api(); + + String wrongNamespace = NAMESPACE + "nope"; + NormalizedSource source = new NamedConfigMapNormalizedSource(RED_CONFIG_MAP_NAME, wrongNamespace, true, false); + KubernetesClientConfigContext context = new KubernetesClientConfigContext(api, source, NAMESPACE, + new MockEnvironment(), true, NAMESPACED_BATCH_READ); + + KubernetesClientContextToSourceData data = new NamedConfigMapContextToSourceDataProvider().get(); + SourceData sourceData = data.apply(context); + + Assertions.assertEquals(sourceData.sourceName(), "configmap.red.default"); + Assertions.assertEquals(sourceData.sourceData(), COLOR_REALLY_RED); + } + + /** + *
+	 *     - proves that single yaml file gets special treatment
+	 * 
+ */ + @Test + void testSingleYaml() { + V1ConfigMap singleYaml = new V1ConfigMapBuilder() + .withMetadata(new V1ObjectMetaBuilder().withName(RED_CONFIG_MAP_NAME).withNamespace(NAMESPACE).build()) + .addToData("single.yaml", "key: value") + .build(); + stubCall(singleYaml, "/api/v1/namespaces/default/configmaps/red"); + + CoreV1Api api = new CoreV1Api(); + + NormalizedSource source = new NamedConfigMapNormalizedSource(RED_CONFIG_MAP_NAME, NAMESPACE, true, false); + KubernetesClientConfigContext context = new KubernetesClientConfigContext(api, source, NAMESPACE, + new MockEnvironment(), true, NAMESPACED_BATCH_READ); + + KubernetesClientContextToSourceData data = new NamedConfigMapContextToSourceDataProvider().get(); + SourceData sourceData = data.apply(context); + + Assertions.assertEquals(sourceData.sourceName(), "configmap.red.default"); + Assertions.assertEquals(sourceData.sourceData(), Map.of("key", "value")); + } + + /** + *
+	 *     - one configmap is deployed with name "one"
+	 *     - profile is enabled with name "k8s"
+	 *
+	 *     we assert that the name of the source is "one" and does not contain "one-dev"
+	 * 
+ */ + @Test + void testCorrectNameWithProfile() { + V1ConfigMap one = new V1ConfigMapBuilder() + .withMetadata(new V1ObjectMetaBuilder().withName("one").withNamespace(NAMESPACE).build()) + .addToData("key", "value") + .build(); + stubCall(one, "/api/v1/namespaces/default/configmaps/one"); + + CoreV1Api api = new CoreV1Api(); + + MockEnvironment environment = new MockEnvironment(); + environment.setActiveProfiles("k8s"); + + NormalizedSource source = new NamedConfigMapNormalizedSource("one", NAMESPACE, true, true); + KubernetesClientConfigContext context = new KubernetesClientConfigContext(api, source, NAMESPACE, environment, + true, NAMESPACED_BATCH_READ); + + KubernetesClientContextToSourceData data = new NamedConfigMapContextToSourceDataProvider().get(); + SourceData sourceData = data.apply(context); + + Assertions.assertEquals(sourceData.sourceName(), "configmap.one.default"); + Assertions.assertEquals(sourceData.sourceData(), Map.of("key", "value")); + } + + /** + *
+	 *     - one configmap is deployed with name "red"
+	 *     - one configmap is deployed with name "green"
+	 *
+	 *     - we first search for "red" and find it, and it is retrieved from the cluster via the client.
+	 * 	   - we then search for the "green" one, and it is retrieved again from the cluster, non cached.
+	 * 
+ */ + @Test + void nonCache(CapturedOutput output) { + V1ConfigMap red = new V1ConfigMapBuilder() + .withMetadata(new V1ObjectMetaBuilder().withName("red").withNamespace(NAMESPACE).build()) + .addToData("color", "red") + .build(); + stubCall(red, "/api/v1/namespaces/default/configmaps/red"); + + V1ConfigMap green = new V1ConfigMapBuilder() + .withMetadata(new V1ObjectMetaBuilder().withName("green").withNamespace(NAMESPACE).build()) + .addToData("color", "green") + .build(); + stubCall(green, "/api/v1/namespaces/default/configmaps/green"); + + CoreV1Api api = new CoreV1Api(); + + MockEnvironment environment = new MockEnvironment(); + + NormalizedSource redSource = new NamedConfigMapNormalizedSource("red", NAMESPACE, true, false); + KubernetesClientConfigContext redContext = new KubernetesClientConfigContext(api, redSource, NAMESPACE, + environment, true, NAMESPACED_BATCH_READ); + KubernetesClientContextToSourceData redData = new NamedConfigMapContextToSourceDataProvider().get(); + SourceData redSourceData = redData.apply(redContext); + + Assertions.assertEquals(redSourceData.sourceName(), "configmap.red.default"); + Assertions.assertEquals(redSourceData.sourceData(), Map.of("color", "red")); + + Assertions.assertFalse(output.getAll().contains("Loaded all config maps in namespace '" + NAMESPACE + "'")); + Assertions.assertTrue(output.getAll().contains("Will read individual configmaps in namespace")); + + NormalizedSource greenSource = new NamedConfigMapNormalizedSource("green", NAMESPACE, true, true); + KubernetesClientConfigContext greenContext = new KubernetesClientConfigContext(api, greenSource, NAMESPACE, + environment, false, NAMESPACED_BATCH_READ); + KubernetesClientContextToSourceData greenData = new NamedConfigMapContextToSourceDataProvider().get(); + SourceData greenSourceData = greenData.apply(greenContext); + + Assertions.assertEquals(greenSourceData.sourceName(), "configmap.green.default"); + Assertions.assertEquals(greenSourceData.sourceData(), Map.of("color", "green")); + + // meaning there is a single entry with such a log statement + String[] out = output.getAll().split("Loaded all config maps in namespace"); + Assertions.assertEquals(out.length, 1); + + // meaning that the second read was done from the cache + out = output.getAll().split("Will read individual configmaps in namespace"); + Assertions.assertEquals(out.length, 3); + + } + + private void stubCall(V1ConfigMap configMap, String path) { + stubFor(get(path).willReturn(aResponse().withStatus(200).withBody(new JSON().serialize(configMap)))); + } + +} diff --git a/spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/NamedSecretContextToSourceDataProviderTests.java b/spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/NamedSecretContextToSourceDataProviderNamespacedBatchReadTests.java similarity index 98% rename from spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/NamedSecretContextToSourceDataProviderTests.java rename to spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/NamedSecretContextToSourceDataProviderNamespacedBatchReadTests.java index 4c4e09c3cb..a72279c7f8 100644 --- a/spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/NamedSecretContextToSourceDataProviderTests.java +++ b/spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/NamedSecretContextToSourceDataProviderNamespacedBatchReadTests.java @@ -31,6 +31,7 @@ import io.kubernetes.client.openapi.models.V1SecretList; import io.kubernetes.client.openapi.models.V1SecretListBuilder; import io.kubernetes.client.util.ClientBuilder; +import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeAll; @@ -51,7 +52,7 @@ import static com.github.tomakehurst.wiremock.core.WireMockConfiguration.options; @ExtendWith(OutputCaptureExtension.class) -class NamedSecretContextToSourceDataProviderTests { +class NamedSecretContextToSourceDataProviderNamespacedBatchReadTests { private static final boolean NAMESPACED_BATCH_READ = true; @@ -79,6 +80,11 @@ void afterEach() { new KubernetesClientSourcesNamespaceBatched().discardSecrets(); } + @AfterAll + static void afterAll() { + WireMock.shutdownServer(); + } + /** * we have a single secret deployed. it matched the name in our queries */ diff --git a/spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/NamedSecretContextToSourceDataProviderNonNamespacedBatchReadTests.java b/spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/NamedSecretContextToSourceDataProviderNonNamespacedBatchReadTests.java new file mode 100644 index 0000000000..760ebd2ef1 --- /dev/null +++ b/spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/NamedSecretContextToSourceDataProviderNonNamespacedBatchReadTests.java @@ -0,0 +1,420 @@ +/* + * Copyright 2013-2024 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.client.config; + +import java.util.Collections; +import java.util.Map; + +import com.github.tomakehurst.wiremock.WireMockServer; +import com.github.tomakehurst.wiremock.client.WireMock; +import io.kubernetes.client.openapi.ApiClient; +import io.kubernetes.client.openapi.Configuration; +import io.kubernetes.client.openapi.JSON; +import io.kubernetes.client.openapi.apis.CoreV1Api; +import io.kubernetes.client.openapi.models.V1ObjectMetaBuilder; +import io.kubernetes.client.openapi.models.V1Secret; +import io.kubernetes.client.openapi.models.V1SecretBuilder; +import io.kubernetes.client.util.ClientBuilder; +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.junit.jupiter.api.extension.ExtendWith; + +import org.springframework.boot.test.system.CapturedOutput; +import org.springframework.boot.test.system.OutputCaptureExtension; +import org.springframework.cloud.kubernetes.commons.config.ConfigUtils; +import org.springframework.cloud.kubernetes.commons.config.NamedSecretNormalizedSource; +import org.springframework.cloud.kubernetes.commons.config.NormalizedSource; +import org.springframework.cloud.kubernetes.commons.config.SourceData; +import org.springframework.mock.env.MockEnvironment; + +import static com.github.tomakehurst.wiremock.client.WireMock.aResponse; +import static com.github.tomakehurst.wiremock.client.WireMock.get; +import static com.github.tomakehurst.wiremock.client.WireMock.stubFor; +import static com.github.tomakehurst.wiremock.core.WireMockConfiguration.options; + +@ExtendWith(OutputCaptureExtension.class) +class NamedSecretContextToSourceDataProviderNonNamespacedBatchReadTests { + + private static final boolean NAMESPACED_BATCH_READ = false; + + private static final ConfigUtils.Prefix PREFIX = ConfigUtils.findPrefix("some", false, false, "irrelevant"); + + private static final String NAMESPACE = "default"; + + private static final Map COLOR_REALLY_RED = Map.of("color", "really-red".getBytes()); + + @BeforeAll + static void setup() { + WireMockServer wireMockServer = new WireMockServer(options().dynamicPort()); + + wireMockServer.start(); + WireMock.configureFor("localhost", wireMockServer.port()); + + ApiClient client = new ClientBuilder().setBasePath("http://localhost:" + wireMockServer.port()).build(); + client.setDebugging(true); + Configuration.setDefaultApiClient(client); + } + + @AfterEach + void afterEach() { + WireMock.reset(); + new KubernetesClientSourcesNamespaceBatched().discardSecrets(); + } + + @AfterAll + static void afterAll() { + WireMock.shutdownServer(); + } + + /** + * we have a single secret deployed. it matched the name in our queries + */ + @Test + void singleSecretMatchAgainstLabels() { + + V1Secret red = new V1SecretBuilder() + .withMetadata(new V1ObjectMetaBuilder().withNamespace(NAMESPACE).withName("red").build()) + .addToData(COLOR_REALLY_RED) + .build(); + stubCall(red, "/api/v1/namespaces/default/secrets/red"); + CoreV1Api api = new CoreV1Api(); + + // blue does not match red + NormalizedSource source = new NamedSecretNormalizedSource("red", NAMESPACE, false, false); + KubernetesClientConfigContext context = new KubernetesClientConfigContext(api, source, NAMESPACE, + new MockEnvironment(), false, NAMESPACED_BATCH_READ); + + KubernetesClientContextToSourceData data = new NamedSecretContextToSourceDataProvider().get(); + SourceData sourceData = data.apply(context); + + Assertions.assertEquals(sourceData.sourceName(), "secret.red.default"); + Assertions.assertEquals(sourceData.sourceData(), Map.of("color", "really-red")); + + } + + /** + * we have three secrets deployed. one of them has a name that matches (red), the + * other two have different names, thus no match. + */ + @Test + void twoSecretMatchAgainstLabels() { + + V1Secret red = new V1SecretBuilder() + .withMetadata(new V1ObjectMetaBuilder().withNamespace(NAMESPACE).withName("red").build()) + .addToData(COLOR_REALLY_RED) + .build(); + stubCall(red, "/api/v1/namespaces/default/secrets/red"); + + V1Secret blue = new V1SecretBuilder() + .withMetadata(new V1ObjectMetaBuilder().withNamespace(NAMESPACE).withName("blue").build()) + .addToData(COLOR_REALLY_RED) + .build(); + stubCall(blue, "/api/v1/namespaces/default/secrets/blue"); + + V1Secret pink = new V1SecretBuilder() + .withMetadata(new V1ObjectMetaBuilder().withNamespace(NAMESPACE).withName("pink").build()) + .addToData(COLOR_REALLY_RED) + .build(); + stubCall(pink, "/api/v1/namespaces/default/secrets/pink"); + + CoreV1Api api = new CoreV1Api(); + + // blue does not match red, nor pink + NormalizedSource source = new NamedSecretNormalizedSource("red", NAMESPACE, false, false); + KubernetesClientConfigContext context = new KubernetesClientConfigContext(api, source, NAMESPACE, + new MockEnvironment(), false, NAMESPACED_BATCH_READ); + + KubernetesClientContextToSourceData data = new NamedSecretContextToSourceDataProvider().get(); + SourceData sourceData = data.apply(context); + + Assertions.assertEquals(sourceData.sourceName(), "secret.red.default"); + Assertions.assertEquals(sourceData.sourceData().size(), 1); + Assertions.assertEquals(sourceData.sourceData().get("color"), "really-red"); + + } + + /** + * one secret deployed (pink), does not match our query (blue). + */ + @Test + void testSecretNoMatch() { + + V1Secret secret = new V1SecretBuilder() + .withMetadata(new V1ObjectMetaBuilder().withNamespace(NAMESPACE).withName("red").build()) + .addToData(COLOR_REALLY_RED) + .build(); + + stubCall(secret, "/api/v1/namespaces/default/secrets/blue"); + CoreV1Api api = new CoreV1Api(); + + // blue does not match red + NormalizedSource source = new NamedSecretNormalizedSource("blue", NAMESPACE, false, false); + KubernetesClientConfigContext context = new KubernetesClientConfigContext(api, source, NAMESPACE, + new MockEnvironment(), false, NAMESPACED_BATCH_READ); + + KubernetesClientContextToSourceData data = new NamedSecretContextToSourceDataProvider().get(); + SourceData sourceData = data.apply(context); + + Assertions.assertEquals(sourceData.sourceName(), "secret.blue.default"); + Assertions.assertEquals(sourceData.sourceData(), Collections.emptyMap()); + } + + /** + *
+	 *     - LabeledSecretContextToSourceDataProvider gets as input a KubernetesClientConfigContext.
+	 *     - This context has a namespace as well as a NormalizedSource, that has a namespace too.
+	 *     - This test makes sure that we use the proper one.
+	 * 
+ */ + @Test + void namespaceMatch() { + + V1Secret secret = new V1SecretBuilder() + .withMetadata(new V1ObjectMetaBuilder().withNamespace(NAMESPACE).withName("red").build()) + .addToData(COLOR_REALLY_RED) + .build(); + + stubCall(secret, "/api/v1/namespaces/default/secrets/red"); + CoreV1Api api = new CoreV1Api(); + + String wrongNamespace = NAMESPACE + "nope"; + NormalizedSource source = new NamedSecretNormalizedSource("red", wrongNamespace, false, false); + KubernetesClientConfigContext context = new KubernetesClientConfigContext(api, source, NAMESPACE, + new MockEnvironment(), false, NAMESPACED_BATCH_READ); + + KubernetesClientContextToSourceData data = new NamedSecretContextToSourceDataProvider().get(); + SourceData sourceData = data.apply(context); + + Assertions.assertEquals(sourceData.sourceName(), "secret.red.default"); + Assertions.assertEquals(sourceData.sourceData(), Map.of("color", "really-red")); + } + + /** + * we have two secrets deployed. one matches the query name. the other matches the + * active profile + name, thus is taken also. + */ + @Test + void matchIncludeSingleProfile() { + + V1Secret red = new V1SecretBuilder() + .withMetadata(new V1ObjectMetaBuilder().withNamespace(NAMESPACE).withName("red").build()) + .addToData(COLOR_REALLY_RED) + .build(); + stubCall(red, "/api/v1/namespaces/default/secrets/red"); + + V1Secret mango = new V1SecretBuilder() + .withMetadata(new V1ObjectMetaBuilder().withNamespace(NAMESPACE).withName("red-with-profile").build()) + .addToData("taste", "mango".getBytes()) + .build(); + stubCall(mango, "/api/v1/namespaces/default/secrets/red-with-profile"); + + CoreV1Api api = new CoreV1Api(); + + NormalizedSource source = new NamedSecretNormalizedSource("red", NAMESPACE, false, ConfigUtils.Prefix.DEFAULT, + true, true); + MockEnvironment environment = new MockEnvironment(); + environment.addActiveProfile("with-profile"); + KubernetesClientConfigContext context = new KubernetesClientConfigContext(api, source, NAMESPACE, environment, + false, NAMESPACED_BATCH_READ); + + KubernetesClientContextToSourceData data = new NamedSecretContextToSourceDataProvider().get(); + SourceData sourceData = data.apply(context); + + Assertions.assertEquals(sourceData.sourceName(), "secret.red.red-with-profile.default.with-profile"); + Assertions.assertEquals(sourceData.sourceData().size(), 1); + Assertions.assertEquals(sourceData.sourceData().get("taste"), "mango"); + + } + + /** + * we have two secrets deployed. one matches the query name. the other matches the + * active profile + name, thus is taken also. This takes into consideration the + * prefix, that we explicitly specify. Notice that prefix works for profile based + * secrets as well. + */ + @Test + void matchIncludeSingleProfileWithPrefix() { + + V1Secret red = new V1SecretBuilder() + .withMetadata(new V1ObjectMetaBuilder().withNamespace(NAMESPACE).withName("red").build()) + .addToData(COLOR_REALLY_RED) + .build(); + stubCall(red, "/api/v1/namespaces/default/secrets/red"); + + V1Secret mango = new V1SecretBuilder() + .withMetadata(new V1ObjectMetaBuilder().withNamespace(NAMESPACE).withName("red-with-taste").build()) + .addToData("taste", "mango".getBytes()) + .build(); + stubCall(mango, "/api/v1/namespaces/default/secrets/red-with-taste"); + + CoreV1Api api = new CoreV1Api(); + + NormalizedSource source = new NamedSecretNormalizedSource("red", NAMESPACE, true, PREFIX, true); + MockEnvironment environment = new MockEnvironment(); + environment.addActiveProfile("with-taste"); + KubernetesClientConfigContext context = new KubernetesClientConfigContext(api, source, NAMESPACE, environment, + true, NAMESPACED_BATCH_READ); + + KubernetesClientContextToSourceData data = new NamedSecretContextToSourceDataProvider().get(); + SourceData sourceData = data.apply(context); + + Assertions.assertEquals(sourceData.sourceName(), "secret.red.red-with-taste.default"); + Assertions.assertEquals(sourceData.sourceData().size(), 2); + Assertions.assertEquals(sourceData.sourceData().get("some.color"), "really-red"); + Assertions.assertEquals(sourceData.sourceData().get("some.taste"), "mango"); + + } + + /** + * we have three secrets deployed. one matches the query name. the other two match the + * active profile + name, thus are taken also. This takes into consideration the + * prefix, that we explicitly specify. Notice that prefix works for profile based + * config maps as well. + */ + @Test + void matchIncludeTwoProfilesWithPrefix() { + + V1Secret red = new V1SecretBuilder() + .withMetadata(new V1ObjectMetaBuilder().withNamespace(NAMESPACE).withName("red").build()) + .addToData(COLOR_REALLY_RED) + .build(); + stubCall(red, "/api/v1/namespaces/default/secrets/red"); + + V1Secret mango = new V1SecretBuilder() + .withMetadata(new V1ObjectMetaBuilder().withNamespace(NAMESPACE).withName("red-with-taste").build()) + .addToData("taste", "mango".getBytes()) + .build(); + stubCall(mango, "/api/v1/namespaces/default/secrets/red-with-taste"); + + V1Secret shape = new V1SecretBuilder() + .withMetadata(new V1ObjectMetaBuilder().withNamespace(NAMESPACE).withName("red-with-shape").build()) + .addToData("shape", "round".getBytes()) + .build(); + stubCall(shape, "/api/v1/namespaces/default/secrets/red-with-shape"); + + CoreV1Api api = new CoreV1Api(); + + NormalizedSource source = new NamedSecretNormalizedSource("red", NAMESPACE, true, PREFIX, true); + MockEnvironment environment = new MockEnvironment(); + environment.setActiveProfiles("with-taste", "with-shape"); + KubernetesClientConfigContext context = new KubernetesClientConfigContext(api, source, NAMESPACE, environment, + true, NAMESPACED_BATCH_READ); + + KubernetesClientContextToSourceData data = new NamedSecretContextToSourceDataProvider().get(); + SourceData sourceData = data.apply(context); + + Assertions.assertEquals(sourceData.sourceName(), "secret.red.red-with-shape.red-with-taste.default"); + + Assertions.assertEquals(sourceData.sourceData().size(), 3); + Assertions.assertEquals(sourceData.sourceData().get("some.color"), "really-red"); + Assertions.assertEquals(sourceData.sourceData().get("some.taste"), "mango"); + Assertions.assertEquals(sourceData.sourceData().get("some.shape"), "round"); + + } + + /** + *
+	 *     - proves that single yaml file gets special treatment
+	 * 
+ */ + @Test + void testSingleYaml() { + V1Secret singleYaml = new V1SecretBuilder() + .withMetadata(new V1ObjectMetaBuilder().withName("single-yaml").withNamespace(NAMESPACE).build()) + .addToData("single.yaml", "key: value".getBytes()) + .build(); + stubCall(singleYaml, "/api/v1/namespaces/default/secrets/single-yaml"); + + CoreV1Api api = new CoreV1Api(); + + NormalizedSource source = new NamedSecretNormalizedSource("single-yaml", NAMESPACE, true, false); + KubernetesClientConfigContext context = new KubernetesClientConfigContext(api, source, NAMESPACE, + new MockEnvironment(), false, NAMESPACED_BATCH_READ); + + KubernetesClientContextToSourceData data = new NamedSecretContextToSourceDataProvider().get(); + SourceData sourceData = data.apply(context); + + Assertions.assertEquals(sourceData.sourceName(), "secret.single-yaml.default"); + Assertions.assertEquals(sourceData.sourceData(), Map.of("key", "value")); + } + + /** + *
+	 *     - one secret is deployed with name "red"
+	 *     - one secret is deployed with name "green"
+	 *
+	 *     - we first search for "red" and find it, and it is retrieved from the cluster via the client.
+	 * 	   - we then search for the "green" one, and it is retrieved again from the cluster, non cached.
+	 * 
+ */ + @Test + void nonCache(CapturedOutput output) { + V1Secret red = new V1SecretBuilder() + .withMetadata(new V1ObjectMetaBuilder().withName("red").withNamespace(NAMESPACE).build()) + .addToData("color", "red".getBytes()) + .build(); + stubCall(red, "/api/v1/namespaces/default/secrets/red"); + + V1Secret green = new V1SecretBuilder() + .withMetadata(new V1ObjectMetaBuilder().withName("green").withNamespace(NAMESPACE).build()) + .addToData("color", "green".getBytes()) + .build(); + stubCall(green, "/api/v1/namespaces/default/secrets/green"); + + CoreV1Api api = new CoreV1Api(); + + MockEnvironment environment = new MockEnvironment(); + + NormalizedSource redSource = new NamedSecretNormalizedSource("red", NAMESPACE, true, false); + KubernetesClientConfigContext redContext = new KubernetesClientConfigContext(api, redSource, NAMESPACE, + environment, false, NAMESPACED_BATCH_READ); + KubernetesClientContextToSourceData redData = new NamedSecretContextToSourceDataProvider().get(); + SourceData redSourceData = redData.apply(redContext); + + Assertions.assertEquals(redSourceData.sourceName(), "secret.red.default"); + Assertions.assertEquals(redSourceData.sourceData(), Map.of("color", "red")); + + Assertions.assertFalse(output.getAll().contains("Loaded all secrets in namespace '" + NAMESPACE + "'")); + Assertions.assertTrue(output.getAll().contains("Will read individual secrets in namespace")); + + NormalizedSource greenSource = new NamedSecretNormalizedSource("green", NAMESPACE, true, true); + KubernetesClientConfigContext greenContext = new KubernetesClientConfigContext(api, greenSource, NAMESPACE, + environment, false, NAMESPACED_BATCH_READ); + KubernetesClientContextToSourceData greenData = new NamedSecretContextToSourceDataProvider().get(); + SourceData greenSourceData = greenData.apply(greenContext); + + Assertions.assertEquals(greenSourceData.sourceName(), "secret.green.default"); + Assertions.assertEquals(greenSourceData.sourceData(), Map.of("color", "green")); + + // meaning there is a single entry with such a log statement + String[] out = output.getAll().split("Loaded all secrets in namespace"); + Assertions.assertEquals(out.length, 1); + + // meaning that the second read was done from the cache + out = output.getAll().split("Will read individual secrets in namespace"); + Assertions.assertEquals(out.length, 3); + + } + + private void stubCall(V1Secret secret, String path) { + stubFor(get(path).willReturn(aResponse().withStatus(200).withBody(new JSON().serialize(secret)))); + } + +} diff --git a/spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/applications/sources_order/SourcesOrderTests.java b/spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/applications/sources_order/SourcesOrderTests.java index 3f43f594ee..5798872aa9 100644 --- a/spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/applications/sources_order/SourcesOrderTests.java +++ b/spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/applications/sources_order/SourcesOrderTests.java @@ -16,10 +16,15 @@ package org.springframework.cloud.kubernetes.client.config.applications.sources_order; +import com.github.tomakehurst.wiremock.WireMockServer; import com.github.tomakehurst.wiremock.client.WireMock; +import io.kubernetes.client.openapi.ApiClient; +import io.kubernetes.client.openapi.Configuration; +import io.kubernetes.client.util.ClientBuilder; import org.hamcrest.Matchers; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; @@ -29,6 +34,8 @@ import org.springframework.test.context.junit.jupiter.SpringExtension; import org.springframework.test.web.reactive.server.WebTestClient; +import static com.github.tomakehurst.wiremock.core.WireMockConfiguration.options; + /** * The stub data for this test is in : * {@link org.springframework.cloud.kubernetes.client.config.bootstrap.stubs.SourcesOrderConfigurationStub} @@ -45,6 +52,18 @@ abstract class SourcesOrderTests { @Autowired private WebTestClient webClient; + @BeforeAll + static void setup() { + WireMockServer wireMockServer = new WireMockServer(options().dynamicPort()); + + wireMockServer.start(); + WireMock.configureFor("localhost", wireMockServer.port()); + + ApiClient client = new ClientBuilder().setBasePath("http://localhost:" + wireMockServer.port()).build(); + client.setDebugging(true); + Configuration.setDefaultApiClient(client); + } + @AfterEach void afterEach() { WireMock.reset(); diff --git a/spring-cloud-kubernetes-commons/src/main/java/org/springframework/cloud/kubernetes/commons/config/ConfigUtils.java b/spring-cloud-kubernetes-commons/src/main/java/org/springframework/cloud/kubernetes/commons/config/ConfigUtils.java index 8f8bed7344..f89d881eeb 100644 --- a/spring-cloud-kubernetes-commons/src/main/java/org/springframework/cloud/kubernetes/commons/config/ConfigUtils.java +++ b/spring-cloud-kubernetes-commons/src/main/java/org/springframework/cloud/kubernetes/commons/config/ConfigUtils.java @@ -194,6 +194,10 @@ public static MultipleSourcesContainer processNamedData(List sourceNames, String namespace, boolean decode, boolean includeDefaultProfileData) { + if (strippedSources.isEmpty()) { + return MultipleSourcesContainer.empty(); + } + Map hashByName = strippedSources.stream() .collect(Collectors.toMap(StrippedSourceContainer::name, Function.identity())); @@ -271,11 +275,15 @@ static BooleanSupplier rawDataContainsProfileBasedSource(List activeProf * defined order). */ - public static MultipleSourcesContainer processLabeledData(List containers, + public static MultipleSourcesContainer processLabeledData(List strippedSources, Environment environment, Map labels, String namespace, boolean decode) { + if (strippedSources.isEmpty()) { + return MultipleSourcesContainer.empty(); + } + // find sources by provided labels - List byLabels = containers.stream().filter(one -> { + List byLabels = strippedSources.stream().filter(one -> { Map sourceLabels = one.labels(); Map labelsToSearchAgainst = sourceLabels == null ? Map.of() : sourceLabels; return labelsToSearchAgainst.entrySet().containsAll((labels.entrySet())); From 78eb9a46ae5808887a76465160beda572d215367 Mon Sep 17 00:00:00 2001 From: wind57 Date: Mon, 23 Sep 2024 17:11:41 +0300 Subject: [PATCH 15/21] fix tests --- ...bernetesClientSourcesNamespaceBatched.java | 2 +- .../KubernetesEnvironmentRepositoryTests.java | 32 ++++++++++++------- ...KubernetesPropertySourceSupplierTests.java | 24 +++++++++++++- 3 files changed, 44 insertions(+), 14 deletions(-) diff --git a/spring-cloud-kubernetes-client-config/src/main/java/org/springframework/cloud/kubernetes/client/config/KubernetesClientSourcesNamespaceBatched.java b/spring-cloud-kubernetes-client-config/src/main/java/org/springframework/cloud/kubernetes/client/config/KubernetesClientSourcesNamespaceBatched.java index 9aac395802..6fb8d35dd2 100644 --- a/spring-cloud-kubernetes-client-config/src/main/java/org/springframework/cloud/kubernetes/client/config/KubernetesClientSourcesNamespaceBatched.java +++ b/spring-cloud-kubernetes-client-config/src/main/java/org/springframework/cloud/kubernetes/client/config/KubernetesClientSourcesNamespaceBatched.java @@ -31,7 +31,7 @@ /** * @author wind57 */ -final class KubernetesClientSourcesNamespaceBatched implements SecretsCache, ConfigMapCache { +public final class KubernetesClientSourcesNamespaceBatched implements SecretsCache, ConfigMapCache { private static final LogAccessor LOG = new LogAccessor( LogFactory.getLog(KubernetesClientSourcesNamespaceBatched.class)); diff --git a/spring-cloud-kubernetes-controllers/spring-cloud-kubernetes-configserver/src/test/java/org/springframework/cloud/kubernetes/configserver/KubernetesEnvironmentRepositoryTests.java b/spring-cloud-kubernetes-controllers/spring-cloud-kubernetes-configserver/src/test/java/org/springframework/cloud/kubernetes/configserver/KubernetesEnvironmentRepositoryTests.java index f34aed50ae..740b7ff8c0 100644 --- a/spring-cloud-kubernetes-controllers/spring-cloud-kubernetes-configserver/src/test/java/org/springframework/cloud/kubernetes/configserver/KubernetesEnvironmentRepositoryTests.java +++ b/spring-cloud-kubernetes-controllers/spring-cloud-kubernetes-configserver/src/test/java/org/springframework/cloud/kubernetes/configserver/KubernetesEnvironmentRepositoryTests.java @@ -30,13 +30,14 @@ 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; import org.springframework.cloud.config.environment.Environment; import org.springframework.cloud.kubernetes.client.config.KubernetesClientConfigContext; import org.springframework.cloud.kubernetes.client.config.KubernetesClientConfigMapPropertySource; -import org.springframework.cloud.kubernetes.client.config.KubernetesClientConfigMapsCache; import org.springframework.cloud.kubernetes.client.config.KubernetesClientSecretsPropertySource; +import org.springframework.cloud.kubernetes.client.config.KubernetesClientSourcesNamespaceBatched; import org.springframework.cloud.kubernetes.commons.config.ConfigUtils; import org.springframework.cloud.kubernetes.commons.config.Constants; import org.springframework.cloud.kubernetes.commons.config.NamedConfigMapNormalizedSource; @@ -128,13 +129,13 @@ public static void before() { NormalizedSource defaultSource = new NamedConfigMapNormalizedSource(applicationName, "default", false, true); KubernetesClientConfigContext defaultContext = new KubernetesClientConfigContext(coreApi, defaultSource, - "default", springEnv); + "default", springEnv, true, true); propertySources.add(new KubernetesClientConfigMapPropertySource(defaultContext)); if ("stores".equals(applicationName) && "dev".equals(namespace)) { NormalizedSource devSource = new NamedConfigMapNormalizedSource(applicationName, "dev", false, true); KubernetesClientConfigContext devContext = new KubernetesClientConfigContext(coreApi, devSource, "dev", - springEnv); + springEnv, true, true); propertySources.add(new KubernetesClientConfigMapPropertySource(devContext)); } return propertySources; @@ -144,7 +145,7 @@ public static void before() { NormalizedSource source = new NamedSecretNormalizedSource(applicationName, "default", false, true); KubernetesClientConfigContext context = new KubernetesClientConfigContext(coreApi, source, "default", - springEnv); + springEnv, true, true); propertySources.add(new KubernetesClientSecretsPropertySource(context)); return propertySources; @@ -152,12 +153,19 @@ public static void before() { } @AfterEach - public void after() { - new KubernetesClientConfigMapsCache().discardAll(); + void afterEach() { + new KubernetesClientSourcesNamespaceBatched().discardConfigMaps(); + new KubernetesClientSourcesNamespaceBatched().discardSecrets(); + } + + @BeforeEach + void beforeEach() { + new KubernetesClientSourcesNamespaceBatched().discardConfigMaps(); + new KubernetesClientSourcesNamespaceBatched().discardSecrets(); } @Test - public void testApplicationCase() throws ApiException { + void testApplicationCase() throws ApiException { CoreV1Api coreApi = mock(CoreV1Api.class); when(coreApi.listNamespacedConfigMap(eq("default"), eq(null), eq(null), eq(null), eq(null), eq(null), eq(null), eq(null), eq(null), eq(null), eq(null), eq(null))) @@ -191,7 +199,7 @@ public void testApplicationCase() throws ApiException { } @Test - public void testStoresCase() throws ApiException { + void testStoresCase() throws ApiException { CoreV1Api coreApi = mock(CoreV1Api.class); when(coreApi.listNamespacedConfigMap(eq("default"), eq(null), eq(null), eq(null), eq(null), eq(null), eq(null), eq(null), eq(null), eq(null), eq(null), eq(null))) @@ -238,7 +246,7 @@ public void testStoresCase() throws ApiException { } @Test - public void testStoresProfileCase() throws ApiException { + void testStoresProfileCase() throws ApiException { CoreV1Api coreApi = mock(CoreV1Api.class); when(coreApi.listNamespacedConfigMap(eq("default"), eq(null), eq(null), eq(null), eq(null), eq(null), eq(null), eq(null), eq(null), eq(null), eq(null), eq(null))) @@ -302,7 +310,7 @@ else if (propertySource.getName().equals("secret.stores.default")) { } @Test - public void testApplicationPropertiesAnSecretsOverride() throws ApiException { + void testApplicationPropertiesAnSecretsOverride() throws ApiException { CoreV1Api coreApi = mock(CoreV1Api.class); when(coreApi.listNamespacedConfigMap(eq("default"), eq(null), eq(null), eq(null), eq(null), eq(null), eq(null), eq(null), eq(null), eq(null), eq(null), eq(null))) @@ -343,7 +351,7 @@ public void testApplicationPropertiesAnSecretsOverride() throws ApiException { } @Test - public void testSingleConfigMapMultipleSources() throws ApiException { + void testSingleConfigMapMultipleSources() throws ApiException { CoreV1Api coreApi = mock(CoreV1Api.class); when(coreApi.listNamespacedConfigMap(eq("default"), eq(null), eq(null), eq(null), eq(null), eq(null), eq(null), eq(null), eq(null), eq(null), eq(null), eq(null))) @@ -357,7 +365,7 @@ public void testSingleConfigMapMultipleSources() throws ApiException { NormalizedSource devSource = new NamedConfigMapNormalizedSource(name, namespace, false, ConfigUtils.Prefix.DEFAULT, true, true); KubernetesClientConfigContext devContext = new KubernetesClientConfigContext(coreApi, devSource, "default", - environment); + environment, true, true); propertySources.add(new KubernetesClientConfigMapPropertySource(devContext)); return propertySources; }); diff --git a/spring-cloud-kubernetes-controllers/spring-cloud-kubernetes-configserver/src/test/java/org/springframework/cloud/kubernetes/configserver/KubernetesPropertySourceSupplierTests.java b/spring-cloud-kubernetes-controllers/spring-cloud-kubernetes-configserver/src/test/java/org/springframework/cloud/kubernetes/configserver/KubernetesPropertySourceSupplierTests.java index 3b2e7a5e03..16a8c0ac43 100644 --- a/spring-cloud-kubernetes-controllers/spring-cloud-kubernetes-configserver/src/test/java/org/springframework/cloud/kubernetes/configserver/KubernetesPropertySourceSupplierTests.java +++ b/spring-cloud-kubernetes-controllers/spring-cloud-kubernetes-configserver/src/test/java/org/springframework/cloud/kubernetes/configserver/KubernetesPropertySourceSupplierTests.java @@ -28,10 +28,15 @@ import io.kubernetes.client.openapi.models.V1SecretBuilder; import io.kubernetes.client.openapi.models.V1SecretList; import io.kubernetes.client.openapi.models.V1SecretListBuilder; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import org.mockito.Mockito; import org.springframework.cloud.config.environment.Environment; +import org.springframework.cloud.kubernetes.client.config.KubernetesClientSourcesNamespaceBatched; import org.springframework.cloud.kubernetes.commons.config.Constants; import static org.assertj.core.api.Assertions.assertThat; @@ -71,7 +76,7 @@ class KubernetesPropertySourceSupplierTests { .build(); @BeforeAll - public static void before() throws ApiException { + static void beforeAll() throws ApiException { when(coreApi.listNamespacedConfigMap(eq("default"), eq(null), eq(null), eq(null), eq(null), eq(null), eq(null), eq(null), eq(null), eq(null), eq(null), eq(null))) .thenReturn(CONFIGMAP_DEFAULT_LIST); @@ -93,6 +98,23 @@ public static void before() throws ApiException { .thenReturn(SECRET_TEAM_B_LIST); } + @AfterAll + static void afterAll() { + Mockito.reset(coreApi); + } + + @AfterEach + void afterEach() { + new KubernetesClientSourcesNamespaceBatched().discardConfigMaps(); + new KubernetesClientSourcesNamespaceBatched().discardSecrets(); + } + + @BeforeEach + void beforeEach() { + new KubernetesClientSourcesNamespaceBatched().discardConfigMaps(); + new KubernetesClientSourcesNamespaceBatched().discardSecrets(); + } + @Test void whenCurrentAndExtraNamespacesAddedThenAllConfigMapsAreIncluded() { KubernetesConfigServerProperties kubernetesConfigServerProperties = new KubernetesConfigServerProperties(); From 06d8c91d12059678a4ec0369177949be18f7f80b Mon Sep 17 00:00:00 2001 From: wind57 Date: Mon, 23 Sep 2024 17:17:00 +0300 Subject: [PATCH 16/21] checkstyle --- .../configserver/KubernetesPropertySourceSupplierTests.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spring-cloud-kubernetes-controllers/spring-cloud-kubernetes-configserver/src/test/java/org/springframework/cloud/kubernetes/configserver/KubernetesPropertySourceSupplierTests.java b/spring-cloud-kubernetes-controllers/spring-cloud-kubernetes-configserver/src/test/java/org/springframework/cloud/kubernetes/configserver/KubernetesPropertySourceSupplierTests.java index 16a8c0ac43..ba1991bf9c 100644 --- a/spring-cloud-kubernetes-controllers/spring-cloud-kubernetes-configserver/src/test/java/org/springframework/cloud/kubernetes/configserver/KubernetesPropertySourceSupplierTests.java +++ b/spring-cloud-kubernetes-controllers/spring-cloud-kubernetes-configserver/src/test/java/org/springframework/cloud/kubernetes/configserver/KubernetesPropertySourceSupplierTests.java @@ -33,8 +33,8 @@ import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; - import org.mockito.Mockito; + import org.springframework.cloud.config.environment.Environment; import org.springframework.cloud.kubernetes.client.config.KubernetesClientSourcesNamespaceBatched; import org.springframework.cloud.kubernetes.commons.config.Constants; From dd2db126451a12a025314ee67384f35c8315c600 Mon Sep 17 00:00:00 2001 From: wind57 Date: Mon, 25 Nov 2024 17:06:10 +0200 Subject: [PATCH 17/21] fix --- .../client/config/reload_it/PollingReloadConfigMapTest.java | 5 +++-- .../client/config/reload_it/PollingReloadSecretTest.java | 4 ++-- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/reload_it/PollingReloadConfigMapTest.java b/spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/reload_it/PollingReloadConfigMapTest.java index 0e55494d6e..0edb2a129b 100644 --- a/spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/reload_it/PollingReloadConfigMapTest.java +++ b/spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/reload_it/PollingReloadConfigMapTest.java @@ -181,7 +181,8 @@ AbstractEnvironment environment() { // KubernetesClientConfigMapPropertySource, // otherwise we can't properly test reload functionality ConfigMapConfigProperties configMapConfigProperties = new ConfigMapConfigProperties(true, List.of(), - List.of(), Map.of(), true, CONFIG_MAP_NAME, NAMESPACE, false, true, true, RetryProperties.DEFAULT); + List.of(), Map.of(), true, CONFIG_MAP_NAME, NAMESPACE, false, true, true, + RetryProperties.DEFAULT, false); KubernetesNamespaceProvider namespaceProvider = new KubernetesNamespaceProvider(mockEnvironment); PropertySource propertySource = new KubernetesClientConfigMapPropertySourceLocator(coreV1Api, @@ -204,7 +205,7 @@ ConfigReloadProperties configReloadProperties() { @Primary ConfigMapConfigProperties configMapConfigProperties() { return new ConfigMapConfigProperties(true, List.of(), List.of(), Map.of(), true, CONFIG_MAP_NAME, NAMESPACE, - false, true, FAIL_FAST, RetryProperties.DEFAULT); + false, true, FAIL_FAST, RetryProperties.DEFAULT, false); } @Bean diff --git a/spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/reload_it/PollingReloadSecretTest.java b/spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/reload_it/PollingReloadSecretTest.java index 4173ac8da2..16bc9d6dc9 100644 --- a/spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/reload_it/PollingReloadSecretTest.java +++ b/spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/reload_it/PollingReloadSecretTest.java @@ -187,7 +187,7 @@ AbstractEnvironment environment() { // KubernetesClientSecretPropertySource, // otherwise we can't properly test reload functionality SecretsConfigProperties secretsConfigProperties = new SecretsConfigProperties(true, Map.of(), List.of(), - List.of(), true, SECRET_NAME, NAMESPACE, false, true, false, RetryProperties.DEFAULT); + List.of(), true, SECRET_NAME, NAMESPACE, false, true, false, RetryProperties.DEFAULT, false); KubernetesNamespaceProvider namespaceProvider = new KubernetesNamespaceProvider(mockEnvironment); PropertySource propertySource = new KubernetesClientSecretsPropertySourceLocator(coreV1Api, @@ -210,7 +210,7 @@ ConfigReloadProperties configReloadProperties() { @Primary SecretsConfigProperties secretsConfigProperties() { return new SecretsConfigProperties(true, Map.of(), List.of(), List.of(), true, SECRET_NAME, NAMESPACE, - false, true, FAIL_FAST, RetryProperties.DEFAULT); + false, true, FAIL_FAST, RetryProperties.DEFAULT, false); } @Bean From 51d0b9461917c3a673c1e4e1a40129cc27a72b83 Mon Sep 17 00:00:00 2001 From: wind57 Date: Mon, 25 Nov 2024 17:07:44 +0200 Subject: [PATCH 18/21] fix --- .../client/config/reload_it/EventReloadConfigMapTest.java | 4 ++-- .../client/config/reload_it/EventReloadSecretTest.java | 4 ++-- .../fabric8/config/reload_it/EventReloadConfigMapTest.java | 5 +++-- .../fabric8/config/reload_it/EventReloadSecretTest.java | 4 ++-- .../fabric8/config/reload_it/PollingReloadConfigMapTest.java | 5 +++-- .../fabric8/config/reload_it/PollingReloadSecretTest.java | 4 ++-- 6 files changed, 14 insertions(+), 12 deletions(-) diff --git a/spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/reload_it/EventReloadConfigMapTest.java b/spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/reload_it/EventReloadConfigMapTest.java index a0d458f710..4caea4879e 100644 --- a/spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/reload_it/EventReloadConfigMapTest.java +++ b/spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/reload_it/EventReloadConfigMapTest.java @@ -202,7 +202,7 @@ AbstractEnvironment environment() { // otherwise we can't properly test reload functionality ConfigMapConfigProperties configMapConfigProperties = new ConfigMapConfigProperties(true, List.of(), List.of(), Map.of(), true, CONFIG_MAP_NAME, NAMESPACE, false, true, FAIL_FAST, - RetryProperties.DEFAULT); + RetryProperties.DEFAULT, false); KubernetesNamespaceProvider namespaceProvider = new KubernetesNamespaceProvider(mockEnvironment); PropertySource propertySource = new KubernetesClientConfigMapPropertySourceLocator(coreV1Api, @@ -225,7 +225,7 @@ ConfigReloadProperties configReloadProperties() { @Primary ConfigMapConfigProperties configMapConfigProperties() { return new ConfigMapConfigProperties(true, List.of(), List.of(), Map.of(), true, CONFIG_MAP_NAME, NAMESPACE, - false, true, FAIL_FAST, RetryProperties.DEFAULT); + false, true, FAIL_FAST, RetryProperties.DEFAULT, false); } @Bean diff --git a/spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/reload_it/EventReloadSecretTest.java b/spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/reload_it/EventReloadSecretTest.java index cc4af32b82..7faa00bc47 100644 --- a/spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/reload_it/EventReloadSecretTest.java +++ b/spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/reload_it/EventReloadSecretTest.java @@ -207,7 +207,7 @@ AbstractEnvironment environment() { // KubernetesClientConfigMapPropertySource, // otherwise we can't properly test reload functionality SecretsConfigProperties secretsConfigProperties = new SecretsConfigProperties(true, Map.of(), List.of(), - List.of(), true, SECRET_NAME, NAMESPACE, false, true, FAIL_FAST, RetryProperties.DEFAULT); + List.of(), true, SECRET_NAME, NAMESPACE, false, true, FAIL_FAST, RetryProperties.DEFAULT, false); KubernetesNamespaceProvider namespaceProvider = new KubernetesNamespaceProvider(mockEnvironment); PropertySource propertySource = new KubernetesClientSecretsPropertySourceLocator(coreV1Api, @@ -230,7 +230,7 @@ ConfigReloadProperties configReloadProperties() { @Primary SecretsConfigProperties secretsConfigProperties() { return new SecretsConfigProperties(true, Map.of(), List.of(), List.of(), true, SECRET_NAME, NAMESPACE, - false, true, FAIL_FAST, RetryProperties.DEFAULT); + false, true, FAIL_FAST, RetryProperties.DEFAULT, false); } @Bean diff --git a/spring-cloud-kubernetes-fabric8-config/src/test/java/org/springframework/cloud/kubernetes/fabric8/config/reload_it/EventReloadConfigMapTest.java b/spring-cloud-kubernetes-fabric8-config/src/test/java/org/springframework/cloud/kubernetes/fabric8/config/reload_it/EventReloadConfigMapTest.java index d14efbabab..22f8557b84 100644 --- a/spring-cloud-kubernetes-fabric8-config/src/test/java/org/springframework/cloud/kubernetes/fabric8/config/reload_it/EventReloadConfigMapTest.java +++ b/spring-cloud-kubernetes-fabric8-config/src/test/java/org/springframework/cloud/kubernetes/fabric8/config/reload_it/EventReloadConfigMapTest.java @@ -162,7 +162,8 @@ AbstractEnvironment environment() { // simulate that environment already has a Fabric8ConfigMapPropertySource, // otherwise we can't properly test reload functionality ConfigMapConfigProperties configMapConfigProperties = new ConfigMapConfigProperties(true, List.of(), - List.of(), Map.of(), true, CONFIG_MAP_NAME, NAMESPACE, false, true, true, RetryProperties.DEFAULT); + List.of(), Map.of(), true, CONFIG_MAP_NAME, NAMESPACE, false, true, true, RetryProperties.DEFAULT, + false); KubernetesNamespaceProvider namespaceProvider = new KubernetesNamespaceProvider(mockEnvironment); PropertySource propertySource = new VisibleFabric8ConfigMapPropertySourceLocator(kubernetesClient, @@ -185,7 +186,7 @@ ConfigReloadProperties configReloadProperties() { @Primary ConfigMapConfigProperties configMapConfigProperties() { return new ConfigMapConfigProperties(true, List.of(), List.of(), Map.of(), true, CONFIG_MAP_NAME, NAMESPACE, - false, true, FAIL_FAST, RetryProperties.DEFAULT); + false, true, FAIL_FAST, RetryProperties.DEFAULT, false); } @Bean diff --git a/spring-cloud-kubernetes-fabric8-config/src/test/java/org/springframework/cloud/kubernetes/fabric8/config/reload_it/EventReloadSecretTest.java b/spring-cloud-kubernetes-fabric8-config/src/test/java/org/springframework/cloud/kubernetes/fabric8/config/reload_it/EventReloadSecretTest.java index 546ae964b4..ac795a0dfb 100644 --- a/spring-cloud-kubernetes-fabric8-config/src/test/java/org/springframework/cloud/kubernetes/fabric8/config/reload_it/EventReloadSecretTest.java +++ b/spring-cloud-kubernetes-fabric8-config/src/test/java/org/springframework/cloud/kubernetes/fabric8/config/reload_it/EventReloadSecretTest.java @@ -169,7 +169,7 @@ AbstractEnvironment environment() { // Fabric8SecretsPropertySourceLocator, // otherwise we can't properly test reload functionality SecretsConfigProperties secretsConfigProperties = new SecretsConfigProperties(true, Map.of(), List.of(), - List.of(), true, SECRET_NAME, NAMESPACE, false, true, true, RetryProperties.DEFAULT); + List.of(), true, SECRET_NAME, NAMESPACE, false, true, true, RetryProperties.DEFAULT, false); KubernetesNamespaceProvider namespaceProvider = new KubernetesNamespaceProvider(mockEnvironment); PropertySource propertySource = new VisibleFabric8SecretsPropertySourceLocator(kubernetesClient, @@ -192,7 +192,7 @@ ConfigReloadProperties configReloadProperties() { @Primary SecretsConfigProperties secretsConfigProperties() { return new SecretsConfigProperties(true, Map.of(), List.of(), List.of(), true, SECRET_NAME, NAMESPACE, - false, true, FAIL_FAST, RetryProperties.DEFAULT); + false, true, FAIL_FAST, RetryProperties.DEFAULT, false); } @Bean diff --git a/spring-cloud-kubernetes-fabric8-config/src/test/java/org/springframework/cloud/kubernetes/fabric8/config/reload_it/PollingReloadConfigMapTest.java b/spring-cloud-kubernetes-fabric8-config/src/test/java/org/springframework/cloud/kubernetes/fabric8/config/reload_it/PollingReloadConfigMapTest.java index 372ca8be31..b8c8131486 100644 --- a/spring-cloud-kubernetes-fabric8-config/src/test/java/org/springframework/cloud/kubernetes/fabric8/config/reload_it/PollingReloadConfigMapTest.java +++ b/spring-cloud-kubernetes-fabric8-config/src/test/java/org/springframework/cloud/kubernetes/fabric8/config/reload_it/PollingReloadConfigMapTest.java @@ -152,7 +152,8 @@ AbstractEnvironment environment() { // simulate that environment already has a Fabric8ConfigMapPropertySource, // otherwise we can't properly test reload functionality ConfigMapConfigProperties configMapConfigProperties = new ConfigMapConfigProperties(true, List.of(), - List.of(), Map.of(), true, CONFIG_MAP_NAME, NAMESPACE, false, true, true, RetryProperties.DEFAULT); + List.of(), Map.of(), true, CONFIG_MAP_NAME, NAMESPACE, false, true, true, RetryProperties.DEFAULT, + false); KubernetesNamespaceProvider namespaceProvider = new KubernetesNamespaceProvider(mockEnvironment); PropertySource propertySource = new VisibleFabric8ConfigMapPropertySourceLocator(kubernetesClient, @@ -175,7 +176,7 @@ ConfigReloadProperties configReloadProperties() { @Primary ConfigMapConfigProperties configMapConfigProperties() { return new ConfigMapConfigProperties(true, List.of(), List.of(), Map.of(), true, CONFIG_MAP_NAME, NAMESPACE, - false, true, FAIL_FAST, RetryProperties.DEFAULT); + false, true, FAIL_FAST, RetryProperties.DEFAULT, false); } @Bean diff --git a/spring-cloud-kubernetes-fabric8-config/src/test/java/org/springframework/cloud/kubernetes/fabric8/config/reload_it/PollingReloadSecretTest.java b/spring-cloud-kubernetes-fabric8-config/src/test/java/org/springframework/cloud/kubernetes/fabric8/config/reload_it/PollingReloadSecretTest.java index 68667637ce..95c836ddd7 100644 --- a/spring-cloud-kubernetes-fabric8-config/src/test/java/org/springframework/cloud/kubernetes/fabric8/config/reload_it/PollingReloadSecretTest.java +++ b/spring-cloud-kubernetes-fabric8-config/src/test/java/org/springframework/cloud/kubernetes/fabric8/config/reload_it/PollingReloadSecretTest.java @@ -159,7 +159,7 @@ AbstractEnvironment environment() { // simulate that environment already has a Fabric8SecretsPropertySource, // otherwise we can't properly test reload functionality SecretsConfigProperties secretsConfigProperties = new SecretsConfigProperties(true, Map.of(), List.of(), - List.of(), true, SECRET_NAME, NAMESPACE, false, true, true, RetryProperties.DEFAULT); + List.of(), true, SECRET_NAME, NAMESPACE, false, true, true, RetryProperties.DEFAULT, false); KubernetesNamespaceProvider namespaceProvider = new KubernetesNamespaceProvider(mockEnvironment); PropertySource propertySource = new VisibleFabric8SecretsPropertySourceLocator(kubernetesClient, @@ -182,7 +182,7 @@ ConfigReloadProperties configReloadProperties() { @Primary SecretsConfigProperties secretsConfigProperties() { return new SecretsConfigProperties(true, Map.of(), List.of(), List.of(), true, SECRET_NAME, NAMESPACE, - false, true, FAIL_FAST, RetryProperties.DEFAULT); + false, true, FAIL_FAST, RetryProperties.DEFAULT, false); } @Bean From 30c37531bc40bc219f24549d3f601c868746291f Mon Sep 17 00:00:00 2001 From: wind57 Date: Mon, 25 Nov 2024 17:14:24 +0200 Subject: [PATCH 19/21] fix --- ...sClientConfigMapErrorOnReadingSourceTests.java | 13 +++++++------ ...Fabric8ConfigMapErrorOnReadingSourceTests.java | 13 +++++++------ .../Fabric8SecretErrorOnReadingSourceTests.java | 15 +++++++++------ 3 files changed, 23 insertions(+), 18 deletions(-) diff --git a/spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/KubernetesClientConfigMapErrorOnReadingSourceTests.java b/spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/KubernetesClientConfigMapErrorOnReadingSourceTests.java index 2c79b63c80..918f9e54a1 100644 --- a/spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/KubernetesClientConfigMapErrorOnReadingSourceTests.java +++ b/spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/KubernetesClientConfigMapErrorOnReadingSourceTests.java @@ -113,7 +113,7 @@ void namedSingleConfigMapFails() { stubFor(get(path).willReturn(aResponse().withStatus(500).withBody("Internal Server Error"))); ConfigMapConfigProperties configMapConfigProperties = new ConfigMapConfigProperties(true, List.of(), List.of(), - Map.of(), true, name, namespace, false, true, false, RetryProperties.DEFAULT); + Map.of(), true, name, namespace, false, true, false, RetryProperties.DEFAULT, false); CoreV1Api api = new CoreV1Api(); KubernetesClientConfigMapPropertySourceLocator locator = new KubernetesClientConfigMapPropertySourceLocator(api, @@ -159,7 +159,7 @@ void namedTwoConfigMapsOneFails(CapturedOutput output) { ConfigMapConfigProperties configMapConfigProperties = new ConfigMapConfigProperties(true, List.of(), List.of(sourceOne, sourceTwo), Map.of(), true, null, namespace, false, true, false, - RetryProperties.DEFAULT); + RetryProperties.DEFAULT, false); CoreV1Api api = new CoreV1Api(); KubernetesClientConfigMapPropertySourceLocator locator = new KubernetesClientConfigMapPropertySourceLocator(api, @@ -205,7 +205,7 @@ void namedTwoConfigMapsBothFail(CapturedOutput output) { ConfigMapConfigProperties configMapConfigProperties = new ConfigMapConfigProperties(true, List.of(), List.of(sourceOne, sourceTwo), Map.of(), true, null, namespace, false, true, false, - RetryProperties.DEFAULT); + RetryProperties.DEFAULT, false); CoreV1Api api = new CoreV1Api(); KubernetesClientConfigMapPropertySourceLocator locator = new KubernetesClientConfigMapPropertySourceLocator(api, @@ -249,7 +249,8 @@ void labeledSingleConfigMapFails(CapturedOutput output) { null, null, null); ConfigMapConfigProperties configMapConfigProperties = new ConfigMapConfigProperties(true, List.of(), - List.of(configMapSource), labels, true, null, namespace, false, true, false, RetryProperties.DEFAULT); + List.of(configMapSource), labels, true, null, namespace, false, true, false, RetryProperties.DEFAULT, + false); CoreV1Api api = new CoreV1Api(); KubernetesClientConfigMapPropertySourceLocator locator = new KubernetesClientConfigMapPropertySourceLocator(api, @@ -302,7 +303,7 @@ void labeledTwoConfigMapsOneFails(CapturedOutput output) { ConfigMapConfigProperties configMapConfigProperties = new ConfigMapConfigProperties(true, List.of(), List.of(sourceOne, sourceTwo), Map.of("one", "1", "two", "2"), true, null, namespace, false, true, - false, RetryProperties.DEFAULT); + false, RetryProperties.DEFAULT, false); CoreV1Api api = new CoreV1Api(); KubernetesClientConfigMapPropertySourceLocator locator = new KubernetesClientConfigMapPropertySourceLocator(api, @@ -357,7 +358,7 @@ void labeledTwoConfigMapsBothFail(CapturedOutput output) { ConfigMapConfigProperties configMapConfigProperties = new ConfigMapConfigProperties(true, List.of(), List.of(sourceOne, sourceTwo), Map.of("one", "1", "two", "2"), true, null, namespace, false, true, - false, RetryProperties.DEFAULT); + false, RetryProperties.DEFAULT, false); CoreV1Api api = new CoreV1Api(); KubernetesClientConfigMapPropertySourceLocator locator = new KubernetesClientConfigMapPropertySourceLocator(api, diff --git a/spring-cloud-kubernetes-fabric8-config/src/test/java/org/springframework/cloud/kubernetes/fabric8/config/Fabric8ConfigMapErrorOnReadingSourceTests.java b/spring-cloud-kubernetes-fabric8-config/src/test/java/org/springframework/cloud/kubernetes/fabric8/config/Fabric8ConfigMapErrorOnReadingSourceTests.java index 549cb4dd5f..bd95a7e81b 100644 --- a/spring-cloud-kubernetes-fabric8-config/src/test/java/org/springframework/cloud/kubernetes/fabric8/config/Fabric8ConfigMapErrorOnReadingSourceTests.java +++ b/spring-cloud-kubernetes-fabric8-config/src/test/java/org/springframework/cloud/kubernetes/fabric8/config/Fabric8ConfigMapErrorOnReadingSourceTests.java @@ -74,7 +74,7 @@ void namedSingleConfigMapFails() { mockServer.expect().withPath(path).andReturn(500, "Internal Server Error").once(); ConfigMapConfigProperties configMapConfigProperties = new ConfigMapConfigProperties(true, List.of(), List.of(), - Map.of(), true, name, namespace, false, true, false, RetryProperties.DEFAULT); + Map.of(), true, name, namespace, false, true, false, RetryProperties.DEFAULT, false); Fabric8ConfigMapPropertySourceLocator locator = new Fabric8ConfigMapPropertySourceLocator(mockClient, configMapConfigProperties, new KubernetesNamespaceProvider(new MockEnvironment())); @@ -115,7 +115,7 @@ void namedTwoConfigMapsOneFails() { ConfigMapConfigProperties configMapConfigProperties = new ConfigMapConfigProperties(true, List.of(), List.of(sourceOne, sourceTwo), Map.of(), true, null, namespace, false, true, false, - RetryProperties.DEFAULT); + RetryProperties.DEFAULT, false); Fabric8ConfigMapPropertySourceLocator locator = new Fabric8ConfigMapPropertySourceLocator(mockClient, configMapConfigProperties, new KubernetesNamespaceProvider(new MockEnvironment())); @@ -150,7 +150,7 @@ void namedTwoConfigMapsBothFail() { ConfigMapConfigProperties configMapConfigProperties = new ConfigMapConfigProperties(true, List.of(), List.of(sourceOne, sourceTwo), Map.of(), true, null, namespace, false, true, false, - RetryProperties.DEFAULT); + RetryProperties.DEFAULT, false); Fabric8ConfigMapPropertySourceLocator locator = new Fabric8ConfigMapPropertySourceLocator(mockClient, configMapConfigProperties, new KubernetesNamespaceProvider(new MockEnvironment())); @@ -182,7 +182,8 @@ void labeledSingleConfigMapFails(CapturedOutput output) { Source configMapSource = new Source(null, namespace, labels, null, null, null); ConfigMapConfigProperties configMapConfigProperties = new ConfigMapConfigProperties(true, List.of(), - List.of(configMapSource), labels, true, null, namespace, false, true, false, RetryProperties.DEFAULT); + List.of(configMapSource), labels, true, null, namespace, false, true, false, RetryProperties.DEFAULT, + false); Fabric8ConfigMapPropertySourceLocator locator = new Fabric8ConfigMapPropertySourceLocator(mockClient, configMapConfigProperties, new KubernetesNamespaceProvider(new MockEnvironment())); @@ -228,7 +229,7 @@ void labeledTwoConfigMapsOneFails(CapturedOutput output) { ConfigMapConfigProperties configMapConfigProperties = new ConfigMapConfigProperties(true, List.of(), List.of(sourceOne, sourceTwo), Map.of("one", "1", "two", "2"), true, null, namespace, false, true, - false, RetryProperties.DEFAULT); + false, RetryProperties.DEFAULT, false); Fabric8ConfigMapPropertySourceLocator locator = new Fabric8ConfigMapPropertySourceLocator(mockClient, configMapConfigProperties, new KubernetesNamespaceProvider(new MockEnvironment())); @@ -268,7 +269,7 @@ void labeledTwoConfigMapsBothFail(CapturedOutput output) { ConfigMapConfigProperties configMapConfigProperties = new ConfigMapConfigProperties(true, List.of(), List.of(sourceOne, sourceTwo), Map.of("one", "1", "two", "2"), true, null, namespace, false, true, - false, RetryProperties.DEFAULT); + false, RetryProperties.DEFAULT, false); Fabric8ConfigMapPropertySourceLocator locator = new Fabric8ConfigMapPropertySourceLocator(mockClient, configMapConfigProperties, new KubernetesNamespaceProvider(new MockEnvironment())); diff --git a/spring-cloud-kubernetes-fabric8-config/src/test/java/org/springframework/cloud/kubernetes/fabric8/config/Fabric8SecretErrorOnReadingSourceTests.java b/spring-cloud-kubernetes-fabric8-config/src/test/java/org/springframework/cloud/kubernetes/fabric8/config/Fabric8SecretErrorOnReadingSourceTests.java index 4c203861f3..74b370040b 100644 --- a/spring-cloud-kubernetes-fabric8-config/src/test/java/org/springframework/cloud/kubernetes/fabric8/config/Fabric8SecretErrorOnReadingSourceTests.java +++ b/spring-cloud-kubernetes-fabric8-config/src/test/java/org/springframework/cloud/kubernetes/fabric8/config/Fabric8SecretErrorOnReadingSourceTests.java @@ -74,7 +74,7 @@ void namedSingleSecretFails(CapturedOutput output) { mockServer.expect().withPath(path).andReturn(500, "Internal Server Error").once(); SecretsConfigProperties secretsConfigProperties = new SecretsConfigProperties(true, Map.of(), List.of(), - List.of(), true, name, namespace, false, true, false, RetryProperties.DEFAULT); + List.of(), true, name, namespace, false, true, false, RetryProperties.DEFAULT, false); Fabric8SecretsPropertySourceLocator locator = new Fabric8SecretsPropertySourceLocator(mockClient, secretsConfigProperties, new KubernetesNamespaceProvider(new MockEnvironment())); @@ -113,7 +113,8 @@ void namedTwoSecretsOneFails() { Source sourceTwo = new Source(secretNameTwo, namespace, Map.of(), null, null, null); SecretsConfigProperties secretsConfigProperties = new SecretsConfigProperties(true, Map.of(), List.of(), - List.of(sourceOne, sourceTwo), true, null, namespace, false, true, false, RetryProperties.DEFAULT); + List.of(sourceOne, sourceTwo), true, null, namespace, false, true, false, RetryProperties.DEFAULT, + false); Fabric8SecretsPropertySourceLocator locator = new Fabric8SecretsPropertySourceLocator(mockClient, secretsConfigProperties, new KubernetesNamespaceProvider(new MockEnvironment())); @@ -147,7 +148,8 @@ void namedTwoSecretsBothFail() { Source sourceTwo = new Source(secretNameTwo, namespace, Map.of(), null, null, null); SecretsConfigProperties secretsConfigProperties = new SecretsConfigProperties(true, Map.of(), List.of(), - List.of(sourceOne, sourceTwo), true, null, namespace, false, true, false, RetryProperties.DEFAULT); + List.of(sourceOne, sourceTwo), true, null, namespace, false, true, false, RetryProperties.DEFAULT, + false); Fabric8SecretsPropertySourceLocator locator = new Fabric8SecretsPropertySourceLocator(mockClient, secretsConfigProperties, new KubernetesNamespaceProvider(new MockEnvironment())); @@ -179,7 +181,8 @@ void labeledSingleSecretFails(CapturedOutput output) { Source secretSource = new Source(null, namespace, labels, null, null, null); SecretsConfigProperties secretsConfigProperties = new SecretsConfigProperties(true, labels, List.of(), - List.of(secretSource), true, null, namespace, false, true, false, RetryProperties.DEFAULT); + List.of(secretSource), true, null, namespace, false, true, false, RetryProperties.DEFAULT, + false); Fabric8SecretsPropertySourceLocator locator = new Fabric8SecretsPropertySourceLocator(mockClient, secretsConfigProperties, new KubernetesNamespaceProvider(new MockEnvironment())); @@ -225,7 +228,7 @@ void labeledTwoSecretsOneFails(CapturedOutput output) { SecretsConfigProperties secretsConfigProperties = new SecretsConfigProperties(true, Map.of("one", "1", "two", "2"), List.of(), List.of(sourceOne, sourceTwo), true, null, namespace, false, - true, false, RetryProperties.DEFAULT); + true, false, RetryProperties.DEFAULT, false); Fabric8SecretsPropertySourceLocator locator = new Fabric8SecretsPropertySourceLocator(mockClient, secretsConfigProperties, new KubernetesNamespaceProvider(new MockEnvironment())); @@ -265,7 +268,7 @@ void labeledTwoConfigMapsBothFail(CapturedOutput output) { SecretsConfigProperties secretsConfigProperties = new SecretsConfigProperties(true, Map.of("one", "1", "two", "2"), List.of(), List.of(sourceOne, sourceTwo), true, null, namespace, false, - true, false, RetryProperties.DEFAULT); + true, false, RetryProperties.DEFAULT, false); Fabric8SecretsPropertySourceLocator locator = new Fabric8SecretsPropertySourceLocator(mockClient, secretsConfigProperties, new KubernetesNamespaceProvider(new MockEnvironment())); From 2c6895daae81ef4d99b7f4161fd9a0446dcaedde Mon Sep 17 00:00:00 2001 From: wind57 Date: Mon, 25 Nov 2024 19:00:45 +0200 Subject: [PATCH 20/21] fix --- .../client/config/reload_it/EventReloadConfigMapTest.java | 4 ++-- .../client/config/reload_it/EventReloadSecretTest.java | 4 ++-- .../client/config/reload_it/PollingReloadConfigMapTest.java | 4 ++-- .../client/config/reload_it/PollingReloadSecretTest.java | 4 ++-- .../fabric8/config/reload_it/PollingReloadConfigMapTest.java | 4 ++-- .../fabric8/config/reload_it/PollingReloadSecretTest.java | 4 ++-- 6 files changed, 12 insertions(+), 12 deletions(-) diff --git a/spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/reload_it/EventReloadConfigMapTest.java b/spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/reload_it/EventReloadConfigMapTest.java index 4caea4879e..9d62e943f4 100644 --- a/spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/reload_it/EventReloadConfigMapTest.java +++ b/spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/reload_it/EventReloadConfigMapTest.java @@ -202,7 +202,7 @@ AbstractEnvironment environment() { // otherwise we can't properly test reload functionality ConfigMapConfigProperties configMapConfigProperties = new ConfigMapConfigProperties(true, List.of(), List.of(), Map.of(), true, CONFIG_MAP_NAME, NAMESPACE, false, true, FAIL_FAST, - RetryProperties.DEFAULT, false); + RetryProperties.DEFAULT, true); KubernetesNamespaceProvider namespaceProvider = new KubernetesNamespaceProvider(mockEnvironment); PropertySource propertySource = new KubernetesClientConfigMapPropertySourceLocator(coreV1Api, @@ -225,7 +225,7 @@ ConfigReloadProperties configReloadProperties() { @Primary ConfigMapConfigProperties configMapConfigProperties() { return new ConfigMapConfigProperties(true, List.of(), List.of(), Map.of(), true, CONFIG_MAP_NAME, NAMESPACE, - false, true, FAIL_FAST, RetryProperties.DEFAULT, false); + false, true, FAIL_FAST, RetryProperties.DEFAULT, true); } @Bean diff --git a/spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/reload_it/EventReloadSecretTest.java b/spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/reload_it/EventReloadSecretTest.java index 7faa00bc47..de7db52db7 100644 --- a/spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/reload_it/EventReloadSecretTest.java +++ b/spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/reload_it/EventReloadSecretTest.java @@ -207,7 +207,7 @@ AbstractEnvironment environment() { // KubernetesClientConfigMapPropertySource, // otherwise we can't properly test reload functionality SecretsConfigProperties secretsConfigProperties = new SecretsConfigProperties(true, Map.of(), List.of(), - List.of(), true, SECRET_NAME, NAMESPACE, false, true, FAIL_FAST, RetryProperties.DEFAULT, false); + List.of(), true, SECRET_NAME, NAMESPACE, false, true, FAIL_FAST, RetryProperties.DEFAULT, true); KubernetesNamespaceProvider namespaceProvider = new KubernetesNamespaceProvider(mockEnvironment); PropertySource propertySource = new KubernetesClientSecretsPropertySourceLocator(coreV1Api, @@ -230,7 +230,7 @@ ConfigReloadProperties configReloadProperties() { @Primary SecretsConfigProperties secretsConfigProperties() { return new SecretsConfigProperties(true, Map.of(), List.of(), List.of(), true, SECRET_NAME, NAMESPACE, - false, true, FAIL_FAST, RetryProperties.DEFAULT, false); + false, true, FAIL_FAST, RetryProperties.DEFAULT, true); } @Bean diff --git a/spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/reload_it/PollingReloadConfigMapTest.java b/spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/reload_it/PollingReloadConfigMapTest.java index 0edb2a129b..6e815683f4 100644 --- a/spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/reload_it/PollingReloadConfigMapTest.java +++ b/spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/reload_it/PollingReloadConfigMapTest.java @@ -182,7 +182,7 @@ AbstractEnvironment environment() { // otherwise we can't properly test reload functionality ConfigMapConfigProperties configMapConfigProperties = new ConfigMapConfigProperties(true, List.of(), List.of(), Map.of(), true, CONFIG_MAP_NAME, NAMESPACE, false, true, true, - RetryProperties.DEFAULT, false); + RetryProperties.DEFAULT, true); KubernetesNamespaceProvider namespaceProvider = new KubernetesNamespaceProvider(mockEnvironment); PropertySource propertySource = new KubernetesClientConfigMapPropertySourceLocator(coreV1Api, @@ -205,7 +205,7 @@ ConfigReloadProperties configReloadProperties() { @Primary ConfigMapConfigProperties configMapConfigProperties() { return new ConfigMapConfigProperties(true, List.of(), List.of(), Map.of(), true, CONFIG_MAP_NAME, NAMESPACE, - false, true, FAIL_FAST, RetryProperties.DEFAULT, false); + false, true, FAIL_FAST, RetryProperties.DEFAULT, true); } @Bean diff --git a/spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/reload_it/PollingReloadSecretTest.java b/spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/reload_it/PollingReloadSecretTest.java index 16bc9d6dc9..63504df808 100644 --- a/spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/reload_it/PollingReloadSecretTest.java +++ b/spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/reload_it/PollingReloadSecretTest.java @@ -187,7 +187,7 @@ AbstractEnvironment environment() { // KubernetesClientSecretPropertySource, // otherwise we can't properly test reload functionality SecretsConfigProperties secretsConfigProperties = new SecretsConfigProperties(true, Map.of(), List.of(), - List.of(), true, SECRET_NAME, NAMESPACE, false, true, false, RetryProperties.DEFAULT, false); + List.of(), true, SECRET_NAME, NAMESPACE, false, true, false, RetryProperties.DEFAULT, true); KubernetesNamespaceProvider namespaceProvider = new KubernetesNamespaceProvider(mockEnvironment); PropertySource propertySource = new KubernetesClientSecretsPropertySourceLocator(coreV1Api, @@ -210,7 +210,7 @@ ConfigReloadProperties configReloadProperties() { @Primary SecretsConfigProperties secretsConfigProperties() { return new SecretsConfigProperties(true, Map.of(), List.of(), List.of(), true, SECRET_NAME, NAMESPACE, - false, true, FAIL_FAST, RetryProperties.DEFAULT, false); + false, true, FAIL_FAST, RetryProperties.DEFAULT, true); } @Bean diff --git a/spring-cloud-kubernetes-fabric8-config/src/test/java/org/springframework/cloud/kubernetes/fabric8/config/reload_it/PollingReloadConfigMapTest.java b/spring-cloud-kubernetes-fabric8-config/src/test/java/org/springframework/cloud/kubernetes/fabric8/config/reload_it/PollingReloadConfigMapTest.java index b8c8131486..358f509045 100644 --- a/spring-cloud-kubernetes-fabric8-config/src/test/java/org/springframework/cloud/kubernetes/fabric8/config/reload_it/PollingReloadConfigMapTest.java +++ b/spring-cloud-kubernetes-fabric8-config/src/test/java/org/springframework/cloud/kubernetes/fabric8/config/reload_it/PollingReloadConfigMapTest.java @@ -153,7 +153,7 @@ AbstractEnvironment environment() { // otherwise we can't properly test reload functionality ConfigMapConfigProperties configMapConfigProperties = new ConfigMapConfigProperties(true, List.of(), List.of(), Map.of(), true, CONFIG_MAP_NAME, NAMESPACE, false, true, true, RetryProperties.DEFAULT, - false); + true); KubernetesNamespaceProvider namespaceProvider = new KubernetesNamespaceProvider(mockEnvironment); PropertySource propertySource = new VisibleFabric8ConfigMapPropertySourceLocator(kubernetesClient, @@ -176,7 +176,7 @@ ConfigReloadProperties configReloadProperties() { @Primary ConfigMapConfigProperties configMapConfigProperties() { return new ConfigMapConfigProperties(true, List.of(), List.of(), Map.of(), true, CONFIG_MAP_NAME, NAMESPACE, - false, true, FAIL_FAST, RetryProperties.DEFAULT, false); + false, true, FAIL_FAST, RetryProperties.DEFAULT, true); } @Bean diff --git a/spring-cloud-kubernetes-fabric8-config/src/test/java/org/springframework/cloud/kubernetes/fabric8/config/reload_it/PollingReloadSecretTest.java b/spring-cloud-kubernetes-fabric8-config/src/test/java/org/springframework/cloud/kubernetes/fabric8/config/reload_it/PollingReloadSecretTest.java index 95c836ddd7..ff279d14aa 100644 --- a/spring-cloud-kubernetes-fabric8-config/src/test/java/org/springframework/cloud/kubernetes/fabric8/config/reload_it/PollingReloadSecretTest.java +++ b/spring-cloud-kubernetes-fabric8-config/src/test/java/org/springframework/cloud/kubernetes/fabric8/config/reload_it/PollingReloadSecretTest.java @@ -159,7 +159,7 @@ AbstractEnvironment environment() { // simulate that environment already has a Fabric8SecretsPropertySource, // otherwise we can't properly test reload functionality SecretsConfigProperties secretsConfigProperties = new SecretsConfigProperties(true, Map.of(), List.of(), - List.of(), true, SECRET_NAME, NAMESPACE, false, true, true, RetryProperties.DEFAULT, false); + List.of(), true, SECRET_NAME, NAMESPACE, false, true, true, RetryProperties.DEFAULT, true); KubernetesNamespaceProvider namespaceProvider = new KubernetesNamespaceProvider(mockEnvironment); PropertySource propertySource = new VisibleFabric8SecretsPropertySourceLocator(kubernetesClient, @@ -182,7 +182,7 @@ ConfigReloadProperties configReloadProperties() { @Primary SecretsConfigProperties secretsConfigProperties() { return new SecretsConfigProperties(true, Map.of(), List.of(), List.of(), true, SECRET_NAME, NAMESPACE, - false, true, FAIL_FAST, RetryProperties.DEFAULT, false); + false, true, FAIL_FAST, RetryProperties.DEFAULT, true); } @Bean From 8048d49a38607a0c3e70e1c7419129d764a96adf Mon Sep 17 00:00:00 2001 From: wind57 Date: Mon, 25 Nov 2024 20:56:51 +0200 Subject: [PATCH 21/21] fix --- ...esClientConfigMapErrorOnReadingSourceTests.java | 14 ++++++++------ .../Fabric8ConfigMapErrorOnReadingSourceTests.java | 14 ++++++++------ .../Fabric8SecretErrorOnReadingSourceTests.java | 14 ++++++++------ 3 files changed, 24 insertions(+), 18 deletions(-) diff --git a/spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/KubernetesClientConfigMapErrorOnReadingSourceTests.java b/spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/KubernetesClientConfigMapErrorOnReadingSourceTests.java index 918f9e54a1..28db932638 100644 --- a/spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/KubernetesClientConfigMapErrorOnReadingSourceTests.java +++ b/spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/KubernetesClientConfigMapErrorOnReadingSourceTests.java @@ -58,6 +58,8 @@ @ExtendWith(OutputCaptureExtension.class) class KubernetesClientConfigMapErrorOnReadingSourceTests { + private static final boolean NAMESPACE_BATCHED = true; + private static final V1ConfigMapList SINGLE_CONFIGMAP_LIST = new V1ConfigMapList() .addItemsItem(new V1ConfigMapBuilder() .withMetadata( @@ -113,7 +115,7 @@ void namedSingleConfigMapFails() { stubFor(get(path).willReturn(aResponse().withStatus(500).withBody("Internal Server Error"))); ConfigMapConfigProperties configMapConfigProperties = new ConfigMapConfigProperties(true, List.of(), List.of(), - Map.of(), true, name, namespace, false, true, false, RetryProperties.DEFAULT, false); + Map.of(), true, name, namespace, false, true, false, RetryProperties.DEFAULT, NAMESPACE_BATCHED); CoreV1Api api = new CoreV1Api(); KubernetesClientConfigMapPropertySourceLocator locator = new KubernetesClientConfigMapPropertySourceLocator(api, @@ -159,7 +161,7 @@ void namedTwoConfigMapsOneFails(CapturedOutput output) { ConfigMapConfigProperties configMapConfigProperties = new ConfigMapConfigProperties(true, List.of(), List.of(sourceOne, sourceTwo), Map.of(), true, null, namespace, false, true, false, - RetryProperties.DEFAULT, false); + RetryProperties.DEFAULT, NAMESPACE_BATCHED); CoreV1Api api = new CoreV1Api(); KubernetesClientConfigMapPropertySourceLocator locator = new KubernetesClientConfigMapPropertySourceLocator(api, @@ -205,7 +207,7 @@ void namedTwoConfigMapsBothFail(CapturedOutput output) { ConfigMapConfigProperties configMapConfigProperties = new ConfigMapConfigProperties(true, List.of(), List.of(sourceOne, sourceTwo), Map.of(), true, null, namespace, false, true, false, - RetryProperties.DEFAULT, false); + RetryProperties.DEFAULT, NAMESPACE_BATCHED); CoreV1Api api = new CoreV1Api(); KubernetesClientConfigMapPropertySourceLocator locator = new KubernetesClientConfigMapPropertySourceLocator(api, @@ -250,7 +252,7 @@ void labeledSingleConfigMapFails(CapturedOutput output) { ConfigMapConfigProperties configMapConfigProperties = new ConfigMapConfigProperties(true, List.of(), List.of(configMapSource), labels, true, null, namespace, false, true, false, RetryProperties.DEFAULT, - false); + NAMESPACE_BATCHED); CoreV1Api api = new CoreV1Api(); KubernetesClientConfigMapPropertySourceLocator locator = new KubernetesClientConfigMapPropertySourceLocator(api, @@ -303,7 +305,7 @@ void labeledTwoConfigMapsOneFails(CapturedOutput output) { ConfigMapConfigProperties configMapConfigProperties = new ConfigMapConfigProperties(true, List.of(), List.of(sourceOne, sourceTwo), Map.of("one", "1", "two", "2"), true, null, namespace, false, true, - false, RetryProperties.DEFAULT, false); + false, RetryProperties.DEFAULT, NAMESPACE_BATCHED); CoreV1Api api = new CoreV1Api(); KubernetesClientConfigMapPropertySourceLocator locator = new KubernetesClientConfigMapPropertySourceLocator(api, @@ -358,7 +360,7 @@ void labeledTwoConfigMapsBothFail(CapturedOutput output) { ConfigMapConfigProperties configMapConfigProperties = new ConfigMapConfigProperties(true, List.of(), List.of(sourceOne, sourceTwo), Map.of("one", "1", "two", "2"), true, null, namespace, false, true, - false, RetryProperties.DEFAULT, false); + false, RetryProperties.DEFAULT, NAMESPACE_BATCHED); CoreV1Api api = new CoreV1Api(); KubernetesClientConfigMapPropertySourceLocator locator = new KubernetesClientConfigMapPropertySourceLocator(api, diff --git a/spring-cloud-kubernetes-fabric8-config/src/test/java/org/springframework/cloud/kubernetes/fabric8/config/Fabric8ConfigMapErrorOnReadingSourceTests.java b/spring-cloud-kubernetes-fabric8-config/src/test/java/org/springframework/cloud/kubernetes/fabric8/config/Fabric8ConfigMapErrorOnReadingSourceTests.java index bd95a7e81b..e500a77cd7 100644 --- a/spring-cloud-kubernetes-fabric8-config/src/test/java/org/springframework/cloud/kubernetes/fabric8/config/Fabric8ConfigMapErrorOnReadingSourceTests.java +++ b/spring-cloud-kubernetes-fabric8-config/src/test/java/org/springframework/cloud/kubernetes/fabric8/config/Fabric8ConfigMapErrorOnReadingSourceTests.java @@ -50,6 +50,8 @@ @ExtendWith(OutputCaptureExtension.class) class Fabric8ConfigMapErrorOnReadingSourceTests { + private static final boolean NAMESPACED_BATCHED = true; + private static KubernetesMockServer mockServer; private static KubernetesClient mockClient; @@ -74,7 +76,7 @@ void namedSingleConfigMapFails() { mockServer.expect().withPath(path).andReturn(500, "Internal Server Error").once(); ConfigMapConfigProperties configMapConfigProperties = new ConfigMapConfigProperties(true, List.of(), List.of(), - Map.of(), true, name, namespace, false, true, false, RetryProperties.DEFAULT, false); + Map.of(), true, name, namespace, false, true, false, RetryProperties.DEFAULT, NAMESPACED_BATCHED); Fabric8ConfigMapPropertySourceLocator locator = new Fabric8ConfigMapPropertySourceLocator(mockClient, configMapConfigProperties, new KubernetesNamespaceProvider(new MockEnvironment())); @@ -115,7 +117,7 @@ void namedTwoConfigMapsOneFails() { ConfigMapConfigProperties configMapConfigProperties = new ConfigMapConfigProperties(true, List.of(), List.of(sourceOne, sourceTwo), Map.of(), true, null, namespace, false, true, false, - RetryProperties.DEFAULT, false); + RetryProperties.DEFAULT, NAMESPACED_BATCHED); Fabric8ConfigMapPropertySourceLocator locator = new Fabric8ConfigMapPropertySourceLocator(mockClient, configMapConfigProperties, new KubernetesNamespaceProvider(new MockEnvironment())); @@ -150,7 +152,7 @@ void namedTwoConfigMapsBothFail() { ConfigMapConfigProperties configMapConfigProperties = new ConfigMapConfigProperties(true, List.of(), List.of(sourceOne, sourceTwo), Map.of(), true, null, namespace, false, true, false, - RetryProperties.DEFAULT, false); + RetryProperties.DEFAULT, NAMESPACED_BATCHED); Fabric8ConfigMapPropertySourceLocator locator = new Fabric8ConfigMapPropertySourceLocator(mockClient, configMapConfigProperties, new KubernetesNamespaceProvider(new MockEnvironment())); @@ -183,7 +185,7 @@ void labeledSingleConfigMapFails(CapturedOutput output) { ConfigMapConfigProperties configMapConfigProperties = new ConfigMapConfigProperties(true, List.of(), List.of(configMapSource), labels, true, null, namespace, false, true, false, RetryProperties.DEFAULT, - false); + NAMESPACED_BATCHED); Fabric8ConfigMapPropertySourceLocator locator = new Fabric8ConfigMapPropertySourceLocator(mockClient, configMapConfigProperties, new KubernetesNamespaceProvider(new MockEnvironment())); @@ -229,7 +231,7 @@ void labeledTwoConfigMapsOneFails(CapturedOutput output) { ConfigMapConfigProperties configMapConfigProperties = new ConfigMapConfigProperties(true, List.of(), List.of(sourceOne, sourceTwo), Map.of("one", "1", "two", "2"), true, null, namespace, false, true, - false, RetryProperties.DEFAULT, false); + false, RetryProperties.DEFAULT, NAMESPACED_BATCHED); Fabric8ConfigMapPropertySourceLocator locator = new Fabric8ConfigMapPropertySourceLocator(mockClient, configMapConfigProperties, new KubernetesNamespaceProvider(new MockEnvironment())); @@ -269,7 +271,7 @@ void labeledTwoConfigMapsBothFail(CapturedOutput output) { ConfigMapConfigProperties configMapConfigProperties = new ConfigMapConfigProperties(true, List.of(), List.of(sourceOne, sourceTwo), Map.of("one", "1", "two", "2"), true, null, namespace, false, true, - false, RetryProperties.DEFAULT, false); + false, RetryProperties.DEFAULT, NAMESPACED_BATCHED); Fabric8ConfigMapPropertySourceLocator locator = new Fabric8ConfigMapPropertySourceLocator(mockClient, configMapConfigProperties, new KubernetesNamespaceProvider(new MockEnvironment())); diff --git a/spring-cloud-kubernetes-fabric8-config/src/test/java/org/springframework/cloud/kubernetes/fabric8/config/Fabric8SecretErrorOnReadingSourceTests.java b/spring-cloud-kubernetes-fabric8-config/src/test/java/org/springframework/cloud/kubernetes/fabric8/config/Fabric8SecretErrorOnReadingSourceTests.java index 74b370040b..c8d33e3cca 100644 --- a/spring-cloud-kubernetes-fabric8-config/src/test/java/org/springframework/cloud/kubernetes/fabric8/config/Fabric8SecretErrorOnReadingSourceTests.java +++ b/spring-cloud-kubernetes-fabric8-config/src/test/java/org/springframework/cloud/kubernetes/fabric8/config/Fabric8SecretErrorOnReadingSourceTests.java @@ -50,6 +50,8 @@ @ExtendWith(OutputCaptureExtension.class) class Fabric8SecretErrorOnReadingSourceTests { + private static final boolean NAMESPACED_BATCHED = true; + private static KubernetesMockServer mockServer; private static KubernetesClient mockClient; @@ -74,7 +76,7 @@ void namedSingleSecretFails(CapturedOutput output) { mockServer.expect().withPath(path).andReturn(500, "Internal Server Error").once(); SecretsConfigProperties secretsConfigProperties = new SecretsConfigProperties(true, Map.of(), List.of(), - List.of(), true, name, namespace, false, true, false, RetryProperties.DEFAULT, false); + List.of(), true, name, namespace, false, true, false, RetryProperties.DEFAULT, NAMESPACED_BATCHED); Fabric8SecretsPropertySourceLocator locator = new Fabric8SecretsPropertySourceLocator(mockClient, secretsConfigProperties, new KubernetesNamespaceProvider(new MockEnvironment())); @@ -114,7 +116,7 @@ void namedTwoSecretsOneFails() { SecretsConfigProperties secretsConfigProperties = new SecretsConfigProperties(true, Map.of(), List.of(), List.of(sourceOne, sourceTwo), true, null, namespace, false, true, false, RetryProperties.DEFAULT, - false); + NAMESPACED_BATCHED); Fabric8SecretsPropertySourceLocator locator = new Fabric8SecretsPropertySourceLocator(mockClient, secretsConfigProperties, new KubernetesNamespaceProvider(new MockEnvironment())); @@ -149,7 +151,7 @@ void namedTwoSecretsBothFail() { SecretsConfigProperties secretsConfigProperties = new SecretsConfigProperties(true, Map.of(), List.of(), List.of(sourceOne, sourceTwo), true, null, namespace, false, true, false, RetryProperties.DEFAULT, - false); + NAMESPACED_BATCHED); Fabric8SecretsPropertySourceLocator locator = new Fabric8SecretsPropertySourceLocator(mockClient, secretsConfigProperties, new KubernetesNamespaceProvider(new MockEnvironment())); @@ -182,7 +184,7 @@ void labeledSingleSecretFails(CapturedOutput output) { SecretsConfigProperties secretsConfigProperties = new SecretsConfigProperties(true, labels, List.of(), List.of(secretSource), true, null, namespace, false, true, false, RetryProperties.DEFAULT, - false); + NAMESPACED_BATCHED); Fabric8SecretsPropertySourceLocator locator = new Fabric8SecretsPropertySourceLocator(mockClient, secretsConfigProperties, new KubernetesNamespaceProvider(new MockEnvironment())); @@ -228,7 +230,7 @@ void labeledTwoSecretsOneFails(CapturedOutput output) { SecretsConfigProperties secretsConfigProperties = new SecretsConfigProperties(true, Map.of("one", "1", "two", "2"), List.of(), List.of(sourceOne, sourceTwo), true, null, namespace, false, - true, false, RetryProperties.DEFAULT, false); + true, false, RetryProperties.DEFAULT, NAMESPACED_BATCHED); Fabric8SecretsPropertySourceLocator locator = new Fabric8SecretsPropertySourceLocator(mockClient, secretsConfigProperties, new KubernetesNamespaceProvider(new MockEnvironment())); @@ -268,7 +270,7 @@ void labeledTwoConfigMapsBothFail(CapturedOutput output) { SecretsConfigProperties secretsConfigProperties = new SecretsConfigProperties(true, Map.of("one", "1", "two", "2"), List.of(), List.of(sourceOne, sourceTwo), true, null, namespace, false, - true, false, RetryProperties.DEFAULT, false); + true, false, RetryProperties.DEFAULT, NAMESPACED_BATCHED); Fabric8SecretsPropertySourceLocator locator = new Fabric8SecretsPropertySourceLocator(mockClient, secretsConfigProperties, new KubernetesNamespaceProvider(new MockEnvironment()));