diff --git a/docs/src/main/asciidoc/property-source-config.adoc b/docs/src/main/asciidoc/property-source-config.adoc index 034af722..e27573bb 100644 --- a/docs/src/main/asciidoc/property-source-config.adoc +++ b/docs/src/main/asciidoc/property-source-config.adoc @@ -353,7 +353,7 @@ Notice that `spring.cloud.kubernetes.config.useNameAsPrefix` has a _lower_ prior This allows you to set a "default" strategy for all sources, at the same time allowing to override only a few. If using the config map name is not an option, you can specify a different strategy, called : `explicitPrefix`. Since this is an _explicit_ prefix that -you select, it can only be supplied to the `sources` level. At the same time it has a higher priority than `useNameASPrefix`. Let's suppose we have a third config map with these entries: +you select, it can only be supplied to the `sources` level. At the same time it has a higher priority than `useNameAsPrefix`. Let's suppose we have a third config map with these entries: ==== @@ -400,6 +400,50 @@ will result in three properties being generated: - `config-map-three.greetings.message` equal to `Say Hello from three`. +By default, besides reading the config map that is specified in the `sources` configuration, Spring will also try to read +all properties from "profile aware" sources. The easiest way to explain this is via an example. Let's suppose your application +enables a profile called "dev" and you have a configuration like the one below: + +==== +[source,yaml] +---- +spring: + application: + name: spring-k8s + cloud: + kubernetes: + config: + namespace: default-namespace + sources: + - name: config-map-one +---- +==== + +Besides reading the `config-map-one`, Spring will also try to read `config-map-one-dev`; in this particular order. Each active profile +generates such a profile aware config map. + +Though your application should not be impacted by such a config map, it can be disabled if needed: + +==== +[source,yaml] +---- +spring: + application: + name: spring-k8s + cloud: + kubernetes: + config: + includeProfileSpecificSources: false + namespace: default-namespace + sources: + - name: config-map-one + includeProfileSpecificSources: false +---- +==== + +Notice that just like before, there are two levels where you can specify this property: for all config maps or +for individual ones; the latter having a higher priority. + NOTE: You should check the security configuration section. To access config maps from inside a pod you need to have the correct Kubernetes service accounts, roles and role bindings. diff --git a/spring-cloud-kubernetes-client-config/pom.xml b/spring-cloud-kubernetes-client-config/pom.xml index 6bcb8d94..295033fc 100644 --- a/spring-cloud-kubernetes-client-config/pom.xml +++ b/spring-cloud-kubernetes-client-config/pom.xml @@ -68,6 +68,7 @@ spring-boot-starter-test test + org.springframework.cloud spring-cloud-kubernetes-test-support @@ -86,6 +87,18 @@ test + + org.springframework.boot + spring-boot-starter-web + test + + + + org.springframework.boot + spring-boot-starter-webflux + test + + diff --git a/spring-cloud-kubernetes-client-config/src/main/java/org/springframework/cloud/kubernetes/client/config/KubernetesClientConfigMapPropertySource.java b/spring-cloud-kubernetes-client-config/src/main/java/org/springframework/cloud/kubernetes/client/config/KubernetesClientConfigMapPropertySource.java index 0ec59fd4..ab8049bc 100644 --- a/spring-cloud-kubernetes-client-config/src/main/java/org/springframework/cloud/kubernetes/client/config/KubernetesClientConfigMapPropertySource.java +++ b/spring-cloud-kubernetes-client-config/src/main/java/org/springframework/cloud/kubernetes/client/config/KubernetesClientConfigMapPropertySource.java @@ -39,18 +39,25 @@ public class KubernetesClientConfigMapPropertySource extends ConfigMapPropertySo private static final Log LOG = LogFactory.getLog(KubernetesClientConfigMapPropertySource.class); + @Deprecated public KubernetesClientConfigMapPropertySource(CoreV1Api coreV1Api, String name, String namespace, - Environment environment, String prefix) { - super(getName(name, namespace), getData(coreV1Api, name, namespace, environment, prefix)); + Environment environment) { + super(getName(name, namespace), getData(coreV1Api, name, namespace, environment, "", true)); + } + + public KubernetesClientConfigMapPropertySource(CoreV1Api coreV1Api, String name, String namespace, + Environment environment, String prefix, boolean includeProfileSpecificSources) { + super(getName(name, namespace), + getData(coreV1Api, name, namespace, environment, prefix, includeProfileSpecificSources)); } private static Map getData(CoreV1Api coreV1Api, String name, String namespace, - Environment environment, String prefix) { + Environment environment, String prefix, boolean includeProfileSpecificSources) { try { Set names = new HashSet<>(); names.add(name); - if (environment != null) { + if (environment != null && includeProfileSpecificSources) { for (String activeProfile : environment.getActiveProfiles()) { names.add(name + "-" + activeProfile); } 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 245c63b4..5f1000e9 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 @@ -88,7 +88,7 @@ public class KubernetesClientConfigMapPropertySourceLocator extends ConfigMapPro } return new KubernetesClientConfigMapPropertySource(coreV1Api, name, namespace, environment, - normalizedSource.getPrefix()); + normalizedSource.getPrefix(), normalizedSource.isIncludeProfileSpecificSources()); } } diff --git a/spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/KubernetesClientConfigMapIncludeProfileSpecificSourcesTests.java b/spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/KubernetesClientConfigMapIncludeProfileSpecificSourcesTests.java new file mode 100644 index 00000000..88127664 --- /dev/null +++ b/spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/KubernetesClientConfigMapIncludeProfileSpecificSourcesTests.java @@ -0,0 +1,118 @@ +/* + * Copyright 2013-2020 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 com.github.tomakehurst.wiremock.client.WireMock; +import org.hamcrest.Matchers; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.web.reactive.AutoConfigureWebTestClient; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.cloud.kubernetes.client.config.applications.include_profile_specific_sources.IncludeProfileSpecificSourcesApp; +import org.springframework.test.context.ActiveProfiles; +import org.springframework.test.context.junit.jupiter.SpringExtension; +import org.springframework.test.web.reactive.server.WebTestClient; + +/** + * The stub data for this test is in : IncludeProfileSpecificSourcesConfigurationStub + * + * @author wind57 + */ +@ExtendWith(SpringExtension.class) +@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, classes = IncludeProfileSpecificSourcesApp.class, + properties = { "spring.cloud.bootstrap.name=include-profile-specific-sources", "include.profile.specific.sources=true" }) +@AutoConfigureWebTestClient +@ActiveProfiles("dev") +class KubernetesClientConfigMapIncludeProfileSpecificSourcesTests { + + @Autowired + private WebTestClient webClient; + + @AfterEach + public void afterEach() { + WireMock.reset(); + } + + @AfterAll + public static void afterAll() { + WireMock.shutdownServer(); + } + + /** + *
+	 *   'spring.cloud.kubernetes.config.includeProfileSpecificSources=false'
+	 *   'spring.cloud.kubernetes.config.sources[0].includeProfileSpecificSources=true'
+	 *   'spring.cloud.kubernetes.config.sources[0].name=config-map-one'
+	 *
+	 *   We do not define config-map 'config-map-one', but we do define 'config-map-one-dev'.
+	 *
+	 * 	 As such: @ConfigurationProperties("one") must be resolved from 'config-map-one-dev'
+	 * 
+ */ + @Test + public void testOne() { + this.webClient.get().uri("/profile-specific/one").exchange().expectStatus().isOk().expectBody(String.class) + .value(Matchers.equalTo("one")); + } + + /** + *
+	 *   'spring.cloud.kubernetes.config.includeProfileSpecificSources=false'
+	 *   'spring.cloud.kubernetes.config.sources[1].includeProfileSpecificSources=false'
+	 *   'spring.cloud.kubernetes.config.sources[1].name=config-map-two'
+	 *
+	 *   We define config-map 'config-map-two', but we also define 'config-map-two-dev'.
+	 *   This tests proves that data will be read from 'config-map-two' _only_, even if 'config-map-two-dev'
+	 *   also exists. This happens because of the 'includeProfileSpecificSources=false' property defined at the source level.
+	 *   If this would be incorrect, the value we read from '/profile-specific/two' would have been 'twoDev' and _not_ 'two',
+	 *   simply because 'config-map-two-dev' would override the property value.
+	 *
+	 * 	 As such: @ConfigurationProperties("two") must be resolved from 'config-map-two'
+	 * 
+ */ + @Test + public void testTwo() { + this.webClient.get().uri("/profile-specific/two").exchange().expectStatus().isOk().expectBody(String.class) + .value(Matchers.equalTo("two")); + } + + /** + *
+	 *   'spring.cloud.kubernetes.config.includeProfileSpecificSources=false'
+	 *   'spring.cloud.kubernetes.config.sources[2].name=config-map-three'
+	 *
+	 *   We define config-map 'config-map-three', but we also define 'config-map-three-dev'.
+	 *   This tests proves that data will be read from 'config-map-three' _only_, even if 'config-map-three-dev'
+	 *   also exists. This happens because the 'includeProfileSpecificSources'  property is not defined at the source level,
+	 *   but it is defaulted from the root level, where we set it to false.
+	 *   If this would be incorrect, the value we read from '/profile-specific/three' would have been 'threeDev' and _not_ 'three',
+	 *   simply because 'config-map-three-dev' would override the property value.
+	 *
+	 * 	 As such: @ConfigurationProperties("three") must be resolved from 'config-map-three'
+	 * 
+ */ + @Test + public void testThree() { + this.webClient.get().uri("/profile-specific/three").exchange().expectStatus().isOk().expectBody(String.class) + .value(Matchers.equalTo("three")); + } + +} diff --git a/spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/KubernetesClientConfigMapNameAsPrefixTests.java b/spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/KubernetesClientConfigMapNameAsPrefixTests.java new file mode 100644 index 00000000..8ad5656e --- /dev/null +++ b/spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/KubernetesClientConfigMapNameAsPrefixTests.java @@ -0,0 +1,102 @@ +/* + * Copyright 2013-2020 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 com.github.tomakehurst.wiremock.client.WireMock; +import org.hamcrest.Matchers; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.web.reactive.AutoConfigureWebTestClient; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.cloud.kubernetes.client.config.applications.config_map_name_as_prefix.WithPrefixApp; +import org.springframework.test.context.junit.jupiter.SpringExtension; +import org.springframework.test.web.reactive.server.WebTestClient; + +/** + * The stub data for this test is in : ConfigMapNameAsPrefixConfigurationStub + * + * @author wind57 + */ +@ExtendWith(SpringExtension.class) +@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, classes = WithPrefixApp.class, + properties = { "spring.cloud.bootstrap.name=config-map-name-as-prefix", "config.map.name.as.prefix.stub=true" }) +@AutoConfigureWebTestClient +public class KubernetesClientConfigMapNameAsPrefixTests { + + @Autowired + private WebTestClient webClient; + + @AfterEach + public void afterEach() { + WireMock.reset(); + } + + @AfterAll + public static void afterAll() { + WireMock.shutdownServer(); + } + + /** + *
+	 *   'spring.cloud.kubernetes.config.useNameAsPrefix=true'
+	 *   'spring.cloud.kubernetes.config.sources[0].useNameAsPrefix=false'
+	 * 	 ("one.property", "one")
+	 *
+	 * 	 As such: @ConfigurationProperties("one")
+	 * 
+ */ + @Test + public void testOne() { + this.webClient.get().uri("/prefix/one").exchange().expectStatus().isOk().expectBody(String.class) + .value(Matchers.equalTo("one")); + } + + /** + *
+	 *   'spring.cloud.kubernetes.config.useNameAsPrefix=true'
+	 *   'spring.cloud.kubernetes.config.sources[1].explicitPrefix=two'
+	 * 	 ("property", "two")
+	 *
+	 * 	 As such: @ConfigurationProperties("two")
+	 * 
+ */ + @Test + public void testTwo() { + this.webClient.get().uri("/prefix/two").exchange().expectStatus().isOk().expectBody(String.class) + .value(Matchers.equalTo("two")); + } + + /** + *
+	 *   'spring.cloud.kubernetes.config.useNameAsPrefix=true'
+	 *   'spring.cloud.kubernetes.config.sources[2].name=config-map-three'
+	 * 	 ("property", "three")
+	 *
+	 * 	 As such: @ConfigurationProperties(prefix = "config-map-three")
+	 * 
+ */ + @Test + public void testThree() { + this.webClient.get().uri("/prefix/three").exchange().expectStatus().isOk().expectBody(String.class) + .value(Matchers.equalTo("three")); + } + +} 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 4cc15ebe..47bd1810 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 @@ -63,8 +63,6 @@ class KubernetesClientConfigMapPropertySourceLocatorTests { + "logging.level.org.springframework.cloud.kubernetes=TRACE") .build()); - private static final String API = "/api/v1/namespaces/default/configmaps"; - private static WireMockServer wireMockServer; @BeforeAll @@ -92,7 +90,7 @@ class KubernetesClientConfigMapPropertySourceLocatorTests { @Test void locateWithoutSources() { CoreV1Api api = new CoreV1Api(); - stubFor(get(API) + stubFor(get("/api/v1/namespaces/default/configmaps") .willReturn(aResponse().withStatus(200).withBody(new JSON().serialize(PROPERTIES_CONFIGMAP_LIST)))); ConfigMapConfigProperties configMapConfigProperties = new ConfigMapConfigProperties(); configMapConfigProperties.setName("bootstrap-640"); @@ -107,7 +105,7 @@ class KubernetesClientConfigMapPropertySourceLocatorTests { @Test void locateWithSources() { CoreV1Api api = new CoreV1Api(); - stubFor(get(API) + stubFor(get("/api/v1/namespaces/default/configmaps") .willReturn(aResponse().withStatus(200).withBody(new JSON().serialize(PROPERTIES_CONFIGMAP_LIST)))); ConfigMapConfigProperties configMapConfigProperties = new ConfigMapConfigProperties(); configMapConfigProperties.setName("fake-name"); @@ -135,7 +133,7 @@ class KubernetesClientConfigMapPropertySourceLocatorTests { @Test void testLocateWithoutNamespaceDeprecatedConstructor() { CoreV1Api api = new CoreV1Api(); - stubFor(get(API) + stubFor(get("/api/v1/namespaces/default/configmaps") .willReturn(aResponse().withStatus(200).withBody(new JSON().serialize(PROPERTIES_CONFIGMAP_LIST)))); ConfigMapConfigProperties configMapConfigProperties = new ConfigMapConfigProperties(); configMapConfigProperties.setName("bootstrap-640"); @@ -157,7 +155,7 @@ class KubernetesClientConfigMapPropertySourceLocatorTests { @Test void testLocateWithoutNamespace() { CoreV1Api api = new CoreV1Api(); - stubFor(get(API) + stubFor(get("/api/v1/namespaces/default/configmaps") .willReturn(aResponse().withStatus(200).withBody(new JSON().serialize(PROPERTIES_CONFIGMAP_LIST)))); ConfigMapConfigProperties configMapConfigProperties = new ConfigMapConfigProperties(); configMapConfigProperties.setName("bootstrap-640"); 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 48e04e93..142f5ef0 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 @@ -65,8 +65,6 @@ class KubernetesClientConfigMapPropertySourceTests { "dummy:\n property:\n string2: \"a\"\n int2: 1\n bool2: true\n") .build()); - private static final String API = "/api/v1/namespaces/default/configmaps"; - private static WireMockServer wireMockServer; @BeforeAll @@ -94,11 +92,11 @@ class KubernetesClientConfigMapPropertySourceTests { @Test public void propertiesFile() { CoreV1Api api = new CoreV1Api(); - stubFor(get(API) + stubFor(get("/api/v1/namespaces/default/configmaps") .willReturn(aResponse().withStatus(200).withBody(new JSON().serialize(PROPERTIES_CONFIGMAP_LIST)))); KubernetesClientConfigMapPropertySource propertySource = new KubernetesClientConfigMapPropertySource(api, - "bootstrap-640", "default", new MockEnvironment(), ""); - verify(getRequestedFor(urlEqualTo(API))); + "bootstrap-640", "default", new MockEnvironment(), "", true); + verify(getRequestedFor(urlEqualTo("/api/v1/namespaces/default/configmaps"))); assertThat(propertySource.containsProperty("spring.cloud.kubernetes.configuration.watcher.refreshDelay")) .isTrue(); assertThat(propertySource.getProperty("spring.cloud.kubernetes.configuration.watcher.refreshDelay")) @@ -111,10 +109,11 @@ class KubernetesClientConfigMapPropertySourceTests { @Test public void yamlFile() { CoreV1Api api = new CoreV1Api(); - stubFor(get(API).willReturn(aResponse().withStatus(200).withBody(new JSON().serialize(YAML_CONFIGMAP_LIST)))); + stubFor(get("/api/v1/namespaces/default/configmaps") + .willReturn(aResponse().withStatus(200).withBody(new JSON().serialize(YAML_CONFIGMAP_LIST)))); KubernetesClientConfigMapPropertySource propertySource = new KubernetesClientConfigMapPropertySource(api, - "bootstrap-641", "default", new MockEnvironment(), ""); - verify(getRequestedFor(urlEqualTo(API))); + "bootstrap-641", "default", new MockEnvironment(), "", true); + verify(getRequestedFor(urlEqualTo("/api/v1/namespaces/default/configmaps"))); assertThat(propertySource.containsProperty("dummy.property.string2")).isTrue(); assertThat(propertySource.getProperty("dummy.property.string2")).isEqualTo("a"); assertThat(propertySource.containsProperty("dummy.property.int2")).isTrue(); @@ -127,11 +126,11 @@ class KubernetesClientConfigMapPropertySourceTests { @Test public void propertiesFileWithPrefix() { CoreV1Api api = new CoreV1Api(); - stubFor(get(API) + stubFor(get("/api/v1/namespaces/default/configmaps") .willReturn(aResponse().withStatus(200).withBody(new JSON().serialize(PROPERTIES_CONFIGMAP_LIST)))); KubernetesClientConfigMapPropertySource propertySource = new KubernetesClientConfigMapPropertySource(api, - "bootstrap-640", "default", new MockEnvironment(), "prefix"); - verify(getRequestedFor(urlEqualTo(API))); + "bootstrap-640", "default", new MockEnvironment(), "prefix", true); + verify(getRequestedFor(urlEqualTo("/api/v1/namespaces/default/configmaps"))); assertThat(propertySource.containsProperty("prefix.spring.cloud.kubernetes.configuration.watcher.refreshDelay")) .isTrue(); assertThat(propertySource.getProperty("prefix.spring.cloud.kubernetes.configuration.watcher.refreshDelay")) diff --git a/spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/applications/config_map_name_as_prefix/WithPrefixApp.java b/spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/applications/config_map_name_as_prefix/WithPrefixApp.java new file mode 100644 index 00000000..0bdbb15a --- /dev/null +++ b/spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/applications/config_map_name_as_prefix/WithPrefixApp.java @@ -0,0 +1,34 @@ +/* + * Copyright 2013-2021 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.applications.config_map_name_as_prefix; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.boot.context.properties.EnableConfigurationProperties; +import org.springframework.cloud.kubernetes.client.config.applications.config_map_name_as_prefix.properties.One; +import org.springframework.cloud.kubernetes.client.config.applications.config_map_name_as_prefix.properties.Three; +import org.springframework.cloud.kubernetes.client.config.applications.config_map_name_as_prefix.properties.Two; + +@SpringBootApplication +@EnableConfigurationProperties({ One.class, Two.class, Three.class }) +public class WithPrefixApp { + + public static void main(String[] args) { + SpringApplication.run(WithPrefixApp.class, args); + } + +} diff --git a/spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/applications/config_map_name_as_prefix/controller/Controller.java b/spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/applications/config_map_name_as_prefix/controller/Controller.java new file mode 100644 index 00000000..e4d65578 --- /dev/null +++ b/spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/applications/config_map_name_as_prefix/controller/Controller.java @@ -0,0 +1,55 @@ +/* + * Copyright 2013-2020 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.applications.config_map_name_as_prefix.controller; + +import org.springframework.cloud.kubernetes.client.config.applications.config_map_name_as_prefix.properties.One; +import org.springframework.cloud.kubernetes.client.config.applications.config_map_name_as_prefix.properties.Three; +import org.springframework.cloud.kubernetes.client.config.applications.config_map_name_as_prefix.properties.Two; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RestController; + +@RestController +public class Controller { + + private final One one; + + private final Two two; + + private final Three three; + + public Controller(One one, Two two, Three three) { + this.one = one; + this.two = two; + this.three = three; + } + + @GetMapping("/prefix/one") + public String one() { + return one.getProperty(); + } + + @GetMapping("/prefix/two") + public String two() { + return two.getProperty(); + } + + @GetMapping("/prefix/three") + public String three() { + return three.getProperty(); + } + +} diff --git a/spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/applications/config_map_name_as_prefix/properties/One.java b/spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/applications/config_map_name_as_prefix/properties/One.java new file mode 100644 index 00000000..4a9eb5e4 --- /dev/null +++ b/spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/applications/config_map_name_as_prefix/properties/One.java @@ -0,0 +1,34 @@ +/* + * Copyright 2013-2021 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.applications.config_map_name_as_prefix.properties; + +import org.springframework.boot.context.properties.ConfigurationProperties; + +@ConfigurationProperties("one") +public class One { + + private String property; + + public String getProperty() { + return property; + } + + public void setProperty(String property) { + this.property = property; + } + +} diff --git a/spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/applications/config_map_name_as_prefix/properties/Three.java b/spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/applications/config_map_name_as_prefix/properties/Three.java new file mode 100644 index 00000000..ae83c9a9 --- /dev/null +++ b/spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/applications/config_map_name_as_prefix/properties/Three.java @@ -0,0 +1,34 @@ +/* + * Copyright 2013-2021 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.applications.config_map_name_as_prefix.properties; + +import org.springframework.boot.context.properties.ConfigurationProperties; + +@ConfigurationProperties(prefix = "config-map-three") +public class Three { + + private String property; + + public String getProperty() { + return property; + } + + public void setProperty(String property) { + this.property = property; + } + +} diff --git a/spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/applications/config_map_name_as_prefix/properties/Two.java b/spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/applications/config_map_name_as_prefix/properties/Two.java new file mode 100644 index 00000000..7a3d9d8d --- /dev/null +++ b/spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/applications/config_map_name_as_prefix/properties/Two.java @@ -0,0 +1,34 @@ +/* + * Copyright 2013-2021 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.applications.config_map_name_as_prefix.properties; + +import org.springframework.boot.context.properties.ConfigurationProperties; + +@ConfigurationProperties("two") +public class Two { + + private String property; + + public String getProperty() { + return property; + } + + public void setProperty(String property) { + this.property = property; + } + +} diff --git a/spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/applications/include_profile_specific_sources/IncludeProfileSpecificSourcesApp.java b/spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/applications/include_profile_specific_sources/IncludeProfileSpecificSourcesApp.java new file mode 100644 index 00000000..d719c422 --- /dev/null +++ b/spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/applications/include_profile_specific_sources/IncludeProfileSpecificSourcesApp.java @@ -0,0 +1,34 @@ +/* + * Copyright 2013-2021 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.applications.include_profile_specific_sources; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.boot.context.properties.EnableConfigurationProperties; +import org.springframework.cloud.kubernetes.client.config.applications.include_profile_specific_sources.properties.One; +import org.springframework.cloud.kubernetes.client.config.applications.include_profile_specific_sources.properties.Three; +import org.springframework.cloud.kubernetes.client.config.applications.include_profile_specific_sources.properties.Two; + +@SpringBootApplication +@EnableConfigurationProperties({ One.class, Two.class, Three.class }) +public class IncludeProfileSpecificSourcesApp { + + public static void main(String[] args) { + SpringApplication.run(IncludeProfileSpecificSourcesApp.class, args); + } + +} diff --git a/spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/applications/include_profile_specific_sources/controller/IncludeProfileSpecificSourcesController.java b/spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/applications/include_profile_specific_sources/controller/IncludeProfileSpecificSourcesController.java new file mode 100644 index 00000000..fd8e4db0 --- /dev/null +++ b/spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/applications/include_profile_specific_sources/controller/IncludeProfileSpecificSourcesController.java @@ -0,0 +1,55 @@ +/* + * Copyright 2013-2021 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.applications.include_profile_specific_sources.controller; + +import org.springframework.cloud.kubernetes.client.config.applications.include_profile_specific_sources.properties.One; +import org.springframework.cloud.kubernetes.client.config.applications.include_profile_specific_sources.properties.Three; +import org.springframework.cloud.kubernetes.client.config.applications.include_profile_specific_sources.properties.Two; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RestController; + +@RestController +public class IncludeProfileSpecificSourcesController { + + private final One one; + + private final Two two; + + private final Three three; + + public IncludeProfileSpecificSourcesController(One one, Two two, Three three) { + this.one = one; + this.two = two; + this.three = three; + } + + @GetMapping("/profile-specific/one") + public String one() { + return one.getProperty(); + } + + @GetMapping("/profile-specific/two") + public String two() { + return two.getProperty(); + } + + @GetMapping("/profile-specific/three") + public String three() { + return three.getProperty(); + } + +} diff --git a/spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/applications/include_profile_specific_sources/properties/One.java b/spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/applications/include_profile_specific_sources/properties/One.java new file mode 100644 index 00000000..1e59b034 --- /dev/null +++ b/spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/applications/include_profile_specific_sources/properties/One.java @@ -0,0 +1,34 @@ +/* + * Copyright 2013-2021 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.applications.include_profile_specific_sources.properties; + +import org.springframework.boot.context.properties.ConfigurationProperties; + +@ConfigurationProperties("one") +public class One { + + private String property; + + public String getProperty() { + return property; + } + + public void setProperty(String property) { + this.property = property; + } + +} diff --git a/spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/applications/include_profile_specific_sources/properties/Three.java b/spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/applications/include_profile_specific_sources/properties/Three.java new file mode 100644 index 00000000..e96da1eb --- /dev/null +++ b/spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/applications/include_profile_specific_sources/properties/Three.java @@ -0,0 +1,34 @@ +/* + * Copyright 2013-2021 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.applications.include_profile_specific_sources.properties; + +import org.springframework.boot.context.properties.ConfigurationProperties; + +@ConfigurationProperties("three") +public class Three { + + private String property; + + public String getProperty() { + return property; + } + + public void setProperty(String property) { + this.property = property; + } + +} diff --git a/spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/applications/include_profile_specific_sources/properties/Two.java b/spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/applications/include_profile_specific_sources/properties/Two.java new file mode 100644 index 00000000..4dde04d3 --- /dev/null +++ b/spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/applications/include_profile_specific_sources/properties/Two.java @@ -0,0 +1,34 @@ +/* + * Copyright 2013-2021 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.applications.include_profile_specific_sources.properties; + +import org.springframework.boot.context.properties.ConfigurationProperties; + +@ConfigurationProperties("two") +public class Two { + + private String property; + + public String getProperty() { + return property; + } + + public void setProperty(String property) { + this.property = property; + } + +} diff --git a/spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/boostrap/stubs/ConfigMapNameAsPrefixConfigurationStub.java b/spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/boostrap/stubs/ConfigMapNameAsPrefixConfigurationStub.java new file mode 100644 index 00000000..a639d713 --- /dev/null +++ b/spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/boostrap/stubs/ConfigMapNameAsPrefixConfigurationStub.java @@ -0,0 +1,91 @@ +/* + * Copyright 2013-2020 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.boostrap.stubs; + +import java.util.Arrays; +import java.util.Collections; + +import com.github.tomakehurst.wiremock.WireMockServer; +import com.github.tomakehurst.wiremock.client.WireMock; +import io.kubernetes.client.openapi.ApiClient; +import io.kubernetes.client.openapi.JSON; +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.V1ObjectMetaBuilder; +import io.kubernetes.client.util.ClientBuilder; + +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.core.annotation.Order; + +import static com.github.tomakehurst.wiremock.core.WireMockConfiguration.options; + +/** + * A test bootstrap that takes care to initialize ApiClient _before_ our main bootstrap + * context; with some stub data already present. + * + * @author wind57 + */ +@Order(0) +@Configuration +@ConditionalOnProperty("config.map.name.as.prefix.stub") +public class ConfigMapNameAsPrefixConfigurationStub { + + @Bean + public WireMockServer wireMock() { + WireMockServer server = new WireMockServer(options().dynamicPort()); + server.start(); + WireMock.configureFor("localhost", server.port()); + return server; + } + + @Bean + public ApiClient apiClient(WireMockServer wireMockServer) { + ApiClient apiClient = new ClientBuilder().setBasePath("http://localhost:" + wireMockServer.port()).build(); + io.kubernetes.client.openapi.Configuration.setDefaultApiClient(apiClient); + apiClient.setDebugging(true); + stubData(); + return apiClient; + } + + private void stubData() { + V1ConfigMap one = new V1ConfigMapBuilder() + .withMetadata(new V1ObjectMetaBuilder().withName("config-map-one").withNamespace("spring-k8s") + .withResourceVersion("1").build()) + .addToData(Collections.singletonMap("one.property", "one")).build(); + + V1ConfigMap two = new V1ConfigMapBuilder() + .withMetadata(new V1ObjectMetaBuilder().withName("config-map-two").withNamespace("spring-k8s") + .withResourceVersion("1").build()) + .addToData(Collections.singletonMap("property", "two")).build(); + + V1ConfigMap three = new V1ConfigMapBuilder() + .withMetadata(new V1ObjectMetaBuilder().withName("config-map-three").withNamespace("spring-k8s") + .withResourceVersion("1").build()) + .addToData(Collections.singletonMap("property", "three")).build(); + + V1ConfigMapList allConfigMaps = new V1ConfigMapList(); + allConfigMaps.setItems(Arrays.asList(one, two, three)); + + // the actual stub for CoreV1Api calls + WireMock.stubFor(WireMock.get("/api/v1/namespaces/spring-k8s/configmaps") + .willReturn(WireMock.aResponse().withStatus(200).withBody(new JSON().serialize(allConfigMaps)))); + } + +} diff --git a/spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/boostrap/stubs/IncludeProfileSpecificSourcesConfigurationStub.java b/spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/boostrap/stubs/IncludeProfileSpecificSourcesConfigurationStub.java new file mode 100644 index 00000000..ee37d971 --- /dev/null +++ b/spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/boostrap/stubs/IncludeProfileSpecificSourcesConfigurationStub.java @@ -0,0 +1,101 @@ +/* + * Copyright 2013-2020 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.boostrap.stubs; + +import java.util.Arrays; +import java.util.Collections; + +import com.github.tomakehurst.wiremock.WireMockServer; +import com.github.tomakehurst.wiremock.client.WireMock; +import io.kubernetes.client.openapi.ApiClient; +import io.kubernetes.client.openapi.JSON; +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.V1ObjectMetaBuilder; +import io.kubernetes.client.util.ClientBuilder; + +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.core.annotation.Order; + +import static com.github.tomakehurst.wiremock.core.WireMockConfiguration.options; + +/** + * A test bootstrap that takes care to initialize ApiClient _before_ our main bootstrap + * context; with some stub data already. + * + * @author wind57 + */ +@Order(0) +@Configuration +@ConditionalOnProperty("include.profile.specific.sources") +class IncludeProfileSpecificSourcesConfigurationStub { + + @Bean + public WireMockServer wireMock() { + WireMockServer server = new WireMockServer(options().dynamicPort()); + server.start(); + WireMock.configureFor("localhost", server.port()); + return server; + } + + @Bean + public ApiClient apiClient(WireMockServer wireMockServer) { + ApiClient apiClient = new ClientBuilder().setBasePath("http://localhost:" + wireMockServer.port()).build(); + io.kubernetes.client.openapi.Configuration.setDefaultApiClient(apiClient); + apiClient.setDebugging(true); + stubData(); + return apiClient; + } + + private void stubData() { + V1ConfigMap one = new V1ConfigMapBuilder() + .withMetadata(new V1ObjectMetaBuilder().withName("config-map-one-dev").withNamespace("spring-k8s") + .withResourceVersion("1").build()) + .addToData(Collections.singletonMap("one.property", "one")).build(); + + V1ConfigMap two = new V1ConfigMapBuilder() + .withMetadata(new V1ObjectMetaBuilder().withName("config-map-two").withNamespace("spring-k8s") + .withResourceVersion("1").build()) + .addToData(Collections.singletonMap("two.property", "two")).build(); + + V1ConfigMap twoDev = new V1ConfigMapBuilder() + .withMetadata(new V1ObjectMetaBuilder().withName("config-map-two-dev").withNamespace("spring-k8s") + .withResourceVersion("1").build()) + .addToData(Collections.singletonMap("two.property", "twoDev")).build(); + + V1ConfigMap three = new V1ConfigMapBuilder() + .withMetadata(new V1ObjectMetaBuilder().withName("config-map-three").withNamespace("spring-k8s") + .withResourceVersion("1").build()) + .addToData(Collections.singletonMap("three.property", "three")).build(); + + V1ConfigMap threeDev = new V1ConfigMapBuilder() + .withMetadata(new V1ObjectMetaBuilder().withName("config-map-three-dev").withNamespace("spring-k8s") + .withResourceVersion("1").build()) + .addToData(Collections.singletonMap("three.property", "threeDev")).build(); + + V1ConfigMapList allConfigMaps = new V1ConfigMapList(); + allConfigMaps.setItems(Arrays.asList(one, two, twoDev, three, threeDev)); + + // the actual stub for CoreV1Api calls + WireMock.stubFor(WireMock.get("/api/v1/namespaces/spring-k8s/configmaps") + .willReturn(WireMock.aResponse().withStatus(200).withBody(new JSON().serialize(allConfigMaps)))); + } + +} diff --git a/spring-cloud-kubernetes-client-config/src/test/resources/META-INF/spring.factories b/spring-cloud-kubernetes-client-config/src/test/resources/META-INF/spring.factories new file mode 100644 index 00000000..5d0b2c08 --- /dev/null +++ b/spring-cloud-kubernetes-client-config/src/test/resources/META-INF/spring.factories @@ -0,0 +1,3 @@ +org.springframework.cloud.bootstrap.BootstrapConfiguration=\ +org.springframework.cloud.kubernetes.client.config.boostrap.stubs.IncludeProfileSpecificSourcesConfigurationStub, \ +org.springframework.cloud.kubernetes.client.config.boostrap.stubs.ConfigMapNameAsPrefixConfigurationStub diff --git a/spring-cloud-kubernetes-fabric8-config/src/test/resources/same-key-with-prefix.yaml b/spring-cloud-kubernetes-client-config/src/test/resources/config-map-name-as-prefix.yaml similarity index 100% rename from spring-cloud-kubernetes-fabric8-config/src/test/resources/same-key-with-prefix.yaml rename to spring-cloud-kubernetes-client-config/src/test/resources/config-map-name-as-prefix.yaml diff --git a/spring-cloud-kubernetes-client-config/src/test/resources/include-profile-specific-sources.yaml b/spring-cloud-kubernetes-client-config/src/test/resources/include-profile-specific-sources.yaml new file mode 100644 index 00000000..54999aaa --- /dev/null +++ b/spring-cloud-kubernetes-client-config/src/test/resources/include-profile-specific-sources.yaml @@ -0,0 +1,14 @@ +spring: + application: + name: include-profile-specific-sources + cloud: + kubernetes: + config: + includeProfileSpecificSources: false + namespace: spring-k8s + sources: + - name: config-map-one + includeProfileSpecificSources: true + - name: config-map-two + includeProfileSpecificSources: false + - name: config-map-three diff --git a/spring-cloud-kubernetes-commons/src/main/java/org/springframework/cloud/kubernetes/commons/config/AbstractConfigProperties.java b/spring-cloud-kubernetes-commons/src/main/java/org/springframework/cloud/kubernetes/commons/config/AbstractConfigProperties.java index 4e46486d..4797b789 100644 --- a/spring-cloud-kubernetes-commons/src/main/java/org/springframework/cloud/kubernetes/commons/config/AbstractConfigProperties.java +++ b/spring-cloud-kubernetes-commons/src/main/java/org/springframework/cloud/kubernetes/commons/config/AbstractConfigProperties.java @@ -29,8 +29,12 @@ public abstract class AbstractConfigProperties { protected String namespace; + // use config map name to prefix properties protected boolean useNameAsPrefix; + // use profile name to append config map name + protected boolean includeProfileSpecificSources = true; + public abstract String getConfigurationTarget(); public boolean isEnabled() { @@ -65,4 +69,11 @@ public abstract class AbstractConfigProperties { this.useNameAsPrefix = useNameAsPrefix; } + public boolean isIncludeProfileSpecificSources() { + return includeProfileSpecificSources; + } + + public void setIncludeProfileSpecificSources(boolean includeProfileSpecificSources) { + this.includeProfileSpecificSources = includeProfileSpecificSources; + } } 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 52ad6eb8..4c2673a8 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 @@ -82,10 +82,11 @@ public class ConfigMapConfigProperties extends AbstractConfigProperties { "'spring.cloud.kubernetes.config.useNameAsPrefix' is set to 'true', but 'spring.cloud.kubernetes.config.sources'" + " is empty; as such will default 'useNameAsPrefix' to 'false'"); } - return Collections.singletonList(new NormalizedSource(name, namespace, "")); + return Collections.singletonList(new NormalizedSource(name, namespace, "", includeProfileSpecificSources)); } - return sources.stream().map(s -> s.normalize(name, namespace, useNameAsPrefix)).collect(Collectors.toList()); + return sources.stream().map(s -> s.normalize(name, namespace, useNameAsPrefix, includeProfileSpecificSources)) + .collect(Collectors.toList()); } @Override @@ -114,6 +115,12 @@ public class ConfigMapConfigProperties extends AbstractConfigProperties { */ private Boolean useNameAsPrefix; + /** + * Use profile name to append to a config map name. Can't be a primitive, we need to + * know if it was explicitly set or not + */ + protected Boolean includeProfileSpecificSources; + /** * An explicit prefix to be used for properties. */ @@ -160,6 +167,14 @@ public class ConfigMapConfigProperties extends AbstractConfigProperties { this.explicitPrefix = explicitPrefix; } + public Boolean getIncludeProfileSpecificSources() { + return includeProfileSpecificSources; + } + + public void setIncludeProfileSpecificSources(Boolean includeProfileSpecificSources) { + this.includeProfileSpecificSources = includeProfileSpecificSources; + } + public boolean isEmpty() { return !StringUtils.hasLength(this.name) && !StringUtils.hasLength(this.namespace); } @@ -169,15 +184,18 @@ public class ConfigMapConfigProperties extends AbstractConfigProperties { public NormalizedSource normalize(String defaultName, String defaultNamespace) { String normalizedName = StringUtils.hasLength(this.name) ? this.name : defaultName; String normalizedNamespace = StringUtils.hasLength(this.namespace) ? this.namespace : defaultNamespace; - return new NormalizedSource(normalizedName, normalizedNamespace, ""); + return new NormalizedSource(normalizedName, normalizedNamespace, "", true); } - public NormalizedSource normalize(String defaultName, String defaultNamespace, boolean defaultUseNameAsPrefix) { + public NormalizedSource normalize(String defaultName, String defaultNamespace, boolean defaultUseNameAsPrefix, + boolean defaultIncludeProfileSpecificSources) { String normalizedName = StringUtils.hasLength(this.name) ? this.name : defaultName; String normalizedNamespace = StringUtils.hasLength(this.namespace) ? this.namespace : defaultNamespace; String prefix = ConfigUtils.findPrefix(this.explicitPrefix, useNameAsPrefix, defaultUseNameAsPrefix, normalizedName); - return new NormalizedSource(normalizedName, normalizedNamespace, prefix); + boolean includeProfileSpecificSources = ConfigUtils.includeProfileSpecificSources(defaultIncludeProfileSpecificSources, + this.includeProfileSpecificSources); + return new NormalizedSource(normalizedName, normalizedNamespace, prefix, includeProfileSpecificSources); } @Override @@ -207,18 +225,22 @@ public class ConfigMapConfigProperties extends AbstractConfigProperties { private final String prefix; + private final boolean includeProfileSpecificSources; + // not used, but not removed because of potential compatibility reasons @Deprecated NormalizedSource(String name, String namespace) { this.name = name; this.namespace = namespace; this.prefix = ""; + this.includeProfileSpecificSources = true; } - NormalizedSource(String name, String namespace, String prefix) { + NormalizedSource(String name, String namespace, String prefix, boolean includeProfileSpecificSources) { this.name = name; this.namespace = namespace; this.prefix = Objects.requireNonNull(prefix); + this.includeProfileSpecificSources = includeProfileSpecificSources; } public String getName() { @@ -233,6 +255,10 @@ public class ConfigMapConfigProperties extends AbstractConfigProperties { return prefix; } + public boolean isIncludeProfileSpecificSources() { + return includeProfileSpecificSources; + } + @Override public String toString() { return "{ config-map name : '" + name + "', namespace : '" + namespace + "', prefix : '" + prefix + "' }"; 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 ac92e045..a0e4126d 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 @@ -84,4 +84,19 @@ public final class ConfigUtils { return ""; } + /** + * @param defaultIncludeProfileSpecificSources value of + * 'spring.cloud.kubernetes.config.includeProfileSpecificSources' + * @param includeProfileSpecificSources value of + * 'spring.cloud.kubernetes.config.sources.includeProfileSpecificSources' + * @return useProfileNameAsPrefix to be used in normalized sources + */ + public static boolean includeProfileSpecificSources(boolean defaultIncludeProfileSpecificSources, + Boolean includeProfileSpecificSources) { + if (includeProfileSpecificSources != null) { + return includeProfileSpecificSources; + } + return defaultIncludeProfileSpecificSources; + } + } 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 05406165..4fe06132 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 @@ -218,4 +218,114 @@ public class ConfigMapConfigPropertiesTests { Assertions.assertEquals(sources.get(3).getPrefix(), ""); } + /** + *
+	 * 	spring:
+	 *	  cloud:
+	 *      kubernetes:
+	 *        config:
+	 *          name: config-map-a
+	 *        	namespace: spring-k8s
+	 * 
+ * + * a config as above will result in a NormalizedSource where includeProfileSpecificSources + * will be true (this test proves that the change we added is not a breaking change + * for the already existing functionality) + */ + @Test + public void testUseIncludeProfileSpecificSourcesNoChanges() { + ConfigMapConfigProperties properties = new ConfigMapConfigProperties(); + properties.setSources(Collections.emptyList()); + properties.setName("config-map-a"); + properties.setNamespace("spring-k8s"); + + List sources = properties.determineSources(); + Assertions.assertEquals(sources.size(), 1, "empty sources must generate a List with a single NormalizedSource"); + + Assertions.assertTrue(sources.get(0).isIncludeProfileSpecificSources()); + } + + /** + *
+	 * 	spring:
+	 *	  cloud:
+	 *      kubernetes:
+	 *        config:
+	 *          includeProfileSpecificSources: false
+	 *          name: config-map-a
+	 *        	namespace: spring-k8s
+	 * 
+ * + * a config as above will result in a NormalizedSource where includeProfileSpecificSources + * will be false. Even if we did not define any sources explicitly, one will still be + * created, by default. That one might "flatMap" into multiple other, because of + * multiple profiles. As such this setting still matters and must be propagated to the + * normalized source. + */ + @Test + public void testUseIncludeProfileSpecificSourcesDefaultChanged() { + ConfigMapConfigProperties properties = new ConfigMapConfigProperties(); + properties.setSources(Collections.emptyList()); + properties.setName("config-map-a"); + properties.setNamespace("spring-k8s"); + properties.setIncludeProfileSpecificSources(false); + + List sources = properties.determineSources(); + Assertions.assertEquals(sources.size(), 1, "empty sources must generate a List with a single NormalizedSource"); + + Assertions.assertFalse(sources.get(0).isIncludeProfileSpecificSources()); + } + + /** + *
+	 * 	spring:
+	 *	  cloud:
+	 *      kubernetes:
+	 *        config:
+	 *          includeProfileSpecificSources: false
+	 *          name: config-map-a
+	 *        	namespace: spring-k8s
+	 *        sources:
+	 *          - name: one
+	 *            includeProfileSpecificSources: true
+	 *          - name: two
+	 *          - name: three
+	 *            includeProfileSpecificSources: false
+	 * 
+ * + *
+	 * 	source "one" will have "includeProfileSpecificSources = true".
+	 * 	source "two" will have "includeProfileSpecificSources = false".
+	 * 	source "three" will have "includeProfileSpecificSources = false".
+	 * 
+ */ + @Test + public void testUseIncludeProfileSpecificSourcesDefaultChangedSourceOverride() { + ConfigMapConfigProperties properties = new ConfigMapConfigProperties(); + properties.setSources(Collections.emptyList()); + properties.setName("config-map-a"); + properties.setNamespace("spring-k8s"); + properties.setIncludeProfileSpecificSources(false); + + ConfigMapConfigProperties.Source one = new ConfigMapConfigProperties.Source(); + one.setName("config-map-one"); + one.setIncludeProfileSpecificSources(true); + + ConfigMapConfigProperties.Source two = new ConfigMapConfigProperties.Source(); + two.setName("config-map-two"); + + ConfigMapConfigProperties.Source three = new ConfigMapConfigProperties.Source(); + three.setName("config-map-three"); + three.setIncludeProfileSpecificSources(false); + + properties.setSources(Arrays.asList(one, two, three)); + + List sources = properties.determineSources(); + Assertions.assertEquals(sources.size(), 3); + + Assertions.assertTrue(sources.get(0).isIncludeProfileSpecificSources()); + Assertions.assertFalse(sources.get(1).isIncludeProfileSpecificSources()); + Assertions.assertFalse(sources.get(2).isIncludeProfileSpecificSources()); + } + } diff --git a/spring-cloud-kubernetes-commons/src/test/java/org/springframework/cloud/kubernetes/commons/config/ConfigUtilsTests.java b/spring-cloud-kubernetes-commons/src/test/java/org/springframework/cloud/kubernetes/commons/config/ConfigUtilsTests.java index d513737d..5bed6bfe 100644 --- a/spring-cloud-kubernetes-commons/src/test/java/org/springframework/cloud/kubernetes/commons/config/ConfigUtilsTests.java +++ b/spring-cloud-kubernetes-commons/src/test/java/org/springframework/cloud/kubernetes/commons/config/ConfigUtilsTests.java @@ -54,4 +54,55 @@ public class ConfigUtilsTests { Assertions.assertEquals(result, ""); } + /** + *
+	 *   spring:
+	 *     cloud:
+	 *        kubernetes:
+	 *           config:
+	 *              includeProfileSpecificSources: true
+	 * 
+ * + * above will generate "true" for a normalized source + */ + @Test + public void testUseIncludeProfileSpecificSourcesOnlyDefaultSet() { + Assertions.assertTrue(ConfigUtils.includeProfileSpecificSources(true, null)); + } + + /** + *
+	 *   spring:
+	 *     cloud:
+	 *        kubernetes:
+	 *           config:
+	 *              includeProfileSpecificSources: true
+	 * 
+ * + * above will generate "false" for a normalized source + */ + @Test + public void testUseIncludeProfileSpecificSourcesOnlyDefaultNotSet() { + Assertions.assertFalse(ConfigUtils.includeProfileSpecificSources(false, null)); + } + + /** + *
+	 *   spring:
+	 *     cloud:
+	 *        kubernetes:
+	 *           config:
+	 *              includeProfileSpecificSources: true
+	 *           sources:
+	 *           - name: one
+	 *             includeProfileSpecificSources: false
+	 * 
+ * + * above will generate "false" for a normalized source + */ + @Test + public void testUseIncludeProfileSpecificSourcesSourcesOverridesDefault() { + Assertions.assertFalse(ConfigUtils.includeProfileSpecificSources(true, false)); + } + } diff --git a/spring-cloud-kubernetes-fabric8-config/src/main/java/org/springframework/cloud/kubernetes/fabric8/config/Fabric8ConfigMapPropertySource.java b/spring-cloud-kubernetes-fabric8-config/src/main/java/org/springframework/cloud/kubernetes/fabric8/config/Fabric8ConfigMapPropertySource.java index e53962b9..95629245 100644 --- a/spring-cloud-kubernetes-fabric8-config/src/main/java/org/springframework/cloud/kubernetes/fabric8/config/Fabric8ConfigMapPropertySource.java +++ b/spring-cloud-kubernetes-fabric8-config/src/main/java/org/springframework/cloud/kubernetes/fabric8/config/Fabric8ConfigMapPropertySource.java @@ -44,7 +44,7 @@ public class Fabric8ConfigMapPropertySource extends ConfigMapPropertySource { private static final Log LOG = LogFactory.getLog(Fabric8ConfigMapPropertySource.class); public Fabric8ConfigMapPropertySource(KubernetesClient client, String name) { - this(client, name, null, null, ""); + this(client, name, null, null, "", true); } /** @@ -52,27 +52,27 @@ public class Fabric8ConfigMapPropertySource extends ConfigMapPropertySource { * discouraged. */ @Deprecated - public Fabric8ConfigMapPropertySource(KubernetesClient client, String applicationName, String namespace, + public Fabric8ConfigMapPropertySource(KubernetesClient client, String name, String namespace, Environment environment) { - super(getName(applicationName, getApplicationNamespace(client, namespace)), - getData(client, applicationName, getApplicationNamespace(client, namespace), environment, "")); + super(getName(name, getApplicationNamespace(client, namespace)), + getData(client, name, getApplicationNamespace(client, namespace), environment, "", true)); } - public Fabric8ConfigMapPropertySource(KubernetesClient client, String applicationName, String namespace, - Environment environment, String prefix) { - super(getName(applicationName, getApplicationNamespace(client, namespace)), - getData(client, applicationName, getApplicationNamespace(client, namespace), environment, prefix)); + public Fabric8ConfigMapPropertySource(KubernetesClient client, String name, String namespace, + Environment environment, String prefix, boolean includeProfileSpecificSources) { + super(getName(name, getApplicationNamespace(client, namespace)), getData(client, name, + getApplicationNamespace(client, namespace), environment, prefix, includeProfileSpecificSources)); } - private static Map getData(KubernetesClient client, String applicationName, String namespace, - Environment environment, String prefix) { + private static Map getData(KubernetesClient client, String name, String namespace, + Environment environment, String prefix, boolean includeProfileSpecificSources) { try { - Map data = getConfigMapData(client, namespace, applicationName); + Map data = getConfigMapData(client, namespace, name); Map result = new HashMap<>(processAllEntries(data, environment)); - if (environment != null) { + if (environment != null && includeProfileSpecificSources) { for (String activeProfile : environment.getActiveProfiles()) { - String mapNameWithProfile = applicationName + "-" + activeProfile; + String mapNameWithProfile = name + "-" + activeProfile; Map dataWithProfile = getConfigMapData(client, namespace, mapNameWithProfile); result.putAll(processAllEntries(dataWithProfile, environment)); } @@ -88,8 +88,7 @@ public class Fabric8ConfigMapPropertySource extends ConfigMapPropertySource { } catch (Exception e) { - LOG.warn("Can't read configMap with name: [" + applicationName + "] in namespace: [" + namespace - + "]. Ignoring.", e); + LOG.warn("Can't read configMap with name: [" + name + "] in namespace: [" + namespace + "]. Ignoring.", e); } return Collections.emptyMap(); 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 344f272a..f9614e84 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 @@ -69,7 +69,7 @@ public class Fabric8ConfigMapPropertySourceLocator extends ConfigMapPropertySour String namespace = getApplicationNamespace(this.client, normalizedSource.getNamespace(), configurationTarget, provider); return new Fabric8ConfigMapPropertySource(this.client, applicationName, namespace, environment, - normalizedSource.getPrefix()); + normalizedSource.getPrefix(), normalizedSource.isIncludeProfileSpecificSources()); } } diff --git a/spring-cloud-kubernetes-fabric8-config/src/test/java/org/springframework/cloud/kubernetes/fabric8/config/ConfigMapWithIncludeProfileSpecificSourcesTests.java b/spring-cloud-kubernetes-fabric8-config/src/test/java/org/springframework/cloud/kubernetes/fabric8/config/ConfigMapWithIncludeProfileSpecificSourcesTests.java new file mode 100644 index 00000000..7d1a7021 --- /dev/null +++ b/spring-cloud-kubernetes-fabric8-config/src/test/java/org/springframework/cloud/kubernetes/fabric8/config/ConfigMapWithIncludeProfileSpecificSourcesTests.java @@ -0,0 +1,151 @@ +/* + * Copyright 2013-2021 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.HashMap; +import java.util.Map; + +import io.fabric8.kubernetes.client.Config; +import io.fabric8.kubernetes.client.KubernetesClient; +import io.fabric8.kubernetes.client.server.mock.EnableKubernetesMockClient; +import org.hamcrest.Matchers; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.web.reactive.AutoConfigureWebTestClient; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.cloud.kubernetes.fabric8.config.include_profile_specific_sources.IncludeProfileSpecificSourcesApp; +import org.springframework.test.context.ActiveProfiles; +import org.springframework.test.context.junit.jupiter.SpringExtension; +import org.springframework.test.web.reactive.server.WebTestClient; + +/** + * @author wind57 + */ +@ExtendWith(SpringExtension.class) +@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, classes = IncludeProfileSpecificSourcesApp.class, + properties = { "spring.cloud.bootstrap.name=include-profile-specific-sources" }) +@AutoConfigureWebTestClient +@EnableKubernetesMockClient(crud = true, https = false) +@ActiveProfiles("dev") +class ConfigMapWithIncludeProfileSpecificSourcesTests { + + private static KubernetesClient mockClient; + + @Autowired + private WebTestClient webClient; + + @BeforeAll + public static void setUpBeforeClass() { + + // 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, "test"); + System.setProperty(Config.KUBERNETES_HTTP2_DISABLE, "true"); + + Map one = new HashMap<>(); + one.put("one.property", "one"); + createConfigmap("config-map-one-dev", one); + + Map two = new HashMap<>(); + two.put("two.property", "two"); + createConfigmap("config-map-two", two); + + Map twoDev = new HashMap<>(); + twoDev.put("two.property", "twoDev"); + createConfigmap("config-map-two-dev", twoDev); + + Map three = new HashMap<>(); + three.put("three.property", "three"); + createConfigmap("config-map-three", three); + + Map threeDev = new HashMap<>(); + threeDev.put("three.property", "threeDev"); + createConfigmap("config-map-three-dev", threeDev); + + } + + private static void createConfigmap(String name, Map data) { + mockClient.configMaps().inNamespace("spring-k8s").createNew().withNewMetadata().withName(name).endMetadata() + .addToData(data).done(); + } + + /** + *
+	 *   'spring.cloud.kubernetes.config.includeProfileSpecificSources=false'
+	 *   'spring.cloud.kubernetes.config.sources[0].includeProfileSpecificSources=true'
+	 *   'spring.cloud.kubernetes.config.sources[0].name=config-map-one'
+	 *
+	 *   We do not define config-map 'config-map-one', but we do define 'config-map-one-dev'.
+	 *
+	 * 	 As such: @ConfigurationProperties("one") must be resolved from 'config-map-one-dev'
+	 * 
+ */ + @Test + public void testOne() { + this.webClient.get().uri("/profile-specific/one").exchange().expectStatus().isOk().expectBody(String.class) + .value(Matchers.equalTo("one")); + } + + /** + *
+	 *   'spring.cloud.kubernetes.config.includeProfileSpecificSources=false'
+	 *   'spring.cloud.kubernetes.config.sources[1].includeProfileSpecificSources=false'
+	 *   'spring.cloud.kubernetes.config.sources[1].name=config-map-two'
+	 *
+	 *   We define config-map 'config-map-two', but we also define 'config-map-two-dev'.
+	 *   This tests proves that data will be read from 'config-map-two' _only_, even if 'config-map-two-dev'
+	 *   also exists. This happens because of the 'includeProfileSpecificSources=false' property defined at the source level.
+	 *   If this would be incorrect, the value we read from '/profile-specific/two' would have been 'twoDev' and _not_ 'two',
+	 *   simply because 'config-map-two-dev' would override the property value.
+	 *
+	 * 	 As such: @ConfigurationProperties("two") must be resolved from 'config-map-two'
+	 * 
+ */ + @Test + public void testTwo() { + this.webClient.get().uri("/profile-specific/two").exchange().expectStatus().isOk().expectBody(String.class) + .value(Matchers.equalTo("two")); + } + + /** + *
+	 *   'spring.cloud.kubernetes.config.includeProfileSpecificSources=false'
+	 *   'spring.cloud.kubernetes.config.sources[2].name=config-map-three'
+	 *
+	 *   We define config-map 'config-map-three', but we also define 'config-map-three-dev'.
+	 *   This tests proves that data will be read from 'config-map-three' _only_, even if 'config-map-three-dev'
+	 *   also exists. This happens because the 'includeProfileSpecificSources'  property is not defined at the source level,
+	 *   but it is defaulted from the root level, where we set it to false.
+	 *   If this would be incorrect, the value we read from '/profile-specific/three' would have been 'threeDev' and _not_ 'three',
+	 *   simply because 'config-map-three-dev' would override the property value.
+	 *
+	 * 	 As such: @ConfigurationProperties("three") must be resolved from 'config-map-three'
+	 * 
+ */ + @Test + public void testThree() { + this.webClient.get().uri("/profile-specific/three").exchange().expectStatus().isOk().expectBody(String.class) + .value(Matchers.equalTo("three")); + } + +} diff --git a/spring-cloud-kubernetes-fabric8-config/src/test/java/org/springframework/cloud/kubernetes/fabric8/config/ConfigMapWithPrefixTests.java b/spring-cloud-kubernetes-fabric8-config/src/test/java/org/springframework/cloud/kubernetes/fabric8/config/ConfigMapWithPrefixTests.java index 934034f3..e232034e 100644 --- a/spring-cloud-kubernetes-fabric8-config/src/test/java/org/springframework/cloud/kubernetes/fabric8/config/ConfigMapWithPrefixTests.java +++ b/spring-cloud-kubernetes-fabric8-config/src/test/java/org/springframework/cloud/kubernetes/fabric8/config/ConfigMapWithPrefixTests.java @@ -39,10 +39,10 @@ import org.springframework.test.web.reactive.server.WebTestClient; */ @ExtendWith(SpringExtension.class) @SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, classes = WithPrefixApp.class, - properties = { "spring.cloud.bootstrap.name=same-key-with-prefix" }) + properties = { "spring.cloud.bootstrap.name=config-map-name-as-prefix" }) @AutoConfigureWebTestClient @EnableKubernetesMockClient(crud = true, https = false) -public class ConfigMapWithPrefixTests { +class ConfigMapWithPrefixTests { private static KubernetesClient mockClient; @@ -62,21 +62,20 @@ public class ConfigMapWithPrefixTests { Map one = new HashMap<>(); one.put("one.property", "one"); - createConfigmap(mockClient, "config-map-one", one); + createConfigmap("config-map-one", one); Map two = new HashMap<>(); two.put("property", "two"); - createConfigmap(mockClient, "config-map-two", two); + createConfigmap("config-map-two", two); Map three = new HashMap<>(); three.put("property", "three"); - createConfigmap(mockClient, "config-map-three", three); + createConfigmap("config-map-three", three); } - private static void createConfigmap(KubernetesClient client, String name, Map data) { - - client.configMaps().inNamespace("spring-k8s").createNew().withNewMetadata().withName(name).endMetadata() + private static void createConfigmap(String name, Map data) { + mockClient.configMaps().inNamespace("spring-k8s").createNew().withNewMetadata().withName(name).endMetadata() .addToData(data).done(); } @@ -91,7 +90,7 @@ public class ConfigMapWithPrefixTests { */ @Test public void testOne() { - this.webClient.get().uri("/one").exchange().expectStatus().isOk().expectBody(String.class) + this.webClient.get().uri("/prefix/one").exchange().expectStatus().isOk().expectBody(String.class) .value(Matchers.equalTo("one")); } @@ -106,7 +105,7 @@ public class ConfigMapWithPrefixTests { */ @Test public void testTwo() { - this.webClient.get().uri("/two").exchange().expectStatus().isOk().expectBody(String.class) + this.webClient.get().uri("/prefix/two").exchange().expectStatus().isOk().expectBody(String.class) .value(Matchers.equalTo("two")); } @@ -121,7 +120,7 @@ public class ConfigMapWithPrefixTests { */ @Test public void testThree() { - this.webClient.get().uri("/three").exchange().expectStatus().isOk().expectBody(String.class) + this.webClient.get().uri("/prefix/three").exchange().expectStatus().isOk().expectBody(String.class) .value(Matchers.equalTo("three")); } diff --git a/spring-cloud-kubernetes-fabric8-config/src/test/java/org/springframework/cloud/kubernetes/fabric8/config/include_profile_specific_sources/IncludeProfileSpecificSourcesApp.java b/spring-cloud-kubernetes-fabric8-config/src/test/java/org/springframework/cloud/kubernetes/fabric8/config/include_profile_specific_sources/IncludeProfileSpecificSourcesApp.java new file mode 100644 index 00000000..992de0d8 --- /dev/null +++ b/spring-cloud-kubernetes-fabric8-config/src/test/java/org/springframework/cloud/kubernetes/fabric8/config/include_profile_specific_sources/IncludeProfileSpecificSourcesApp.java @@ -0,0 +1,34 @@ +/* + * Copyright 2013-2021 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.include_profile_specific_sources; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.boot.context.properties.EnableConfigurationProperties; +import org.springframework.cloud.kubernetes.fabric8.config.include_profile_specific_sources.properties.One; +import org.springframework.cloud.kubernetes.fabric8.config.include_profile_specific_sources.properties.Three; +import org.springframework.cloud.kubernetes.fabric8.config.include_profile_specific_sources.properties.Two; + +@SpringBootApplication +@EnableConfigurationProperties({ One.class, Two.class, Three.class }) +public class IncludeProfileSpecificSourcesApp { + + public static void main(String[] args) { + SpringApplication.run(IncludeProfileSpecificSourcesApp.class, args); + } + +} diff --git a/spring-cloud-kubernetes-fabric8-config/src/test/java/org/springframework/cloud/kubernetes/fabric8/config/include_profile_specific_sources/controller/IncludeProfileSpecificSourcesController.java b/spring-cloud-kubernetes-fabric8-config/src/test/java/org/springframework/cloud/kubernetes/fabric8/config/include_profile_specific_sources/controller/IncludeProfileSpecificSourcesController.java new file mode 100644 index 00000000..e6aa453c --- /dev/null +++ b/spring-cloud-kubernetes-fabric8-config/src/test/java/org/springframework/cloud/kubernetes/fabric8/config/include_profile_specific_sources/controller/IncludeProfileSpecificSourcesController.java @@ -0,0 +1,55 @@ +/* + * Copyright 2013-2021 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.include_profile_specific_sources.controller; + +import org.springframework.cloud.kubernetes.fabric8.config.include_profile_specific_sources.properties.One; +import org.springframework.cloud.kubernetes.fabric8.config.include_profile_specific_sources.properties.Three; +import org.springframework.cloud.kubernetes.fabric8.config.include_profile_specific_sources.properties.Two; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RestController; + +@RestController +public class IncludeProfileSpecificSourcesController { + + private final One one; + + private final Two two; + + private final Three three; + + public IncludeProfileSpecificSourcesController(One one, Two two, Three three) { + this.one = one; + this.two = two; + this.three = three; + } + + @GetMapping("/profile-specific/one") + public String one() { + return one.getProperty(); + } + + @GetMapping("/profile-specific/two") + public String two() { + return two.getProperty(); + } + + @GetMapping("/profile-specific/three") + public String three() { + return three.getProperty(); + } + +} diff --git a/spring-cloud-kubernetes-fabric8-config/src/test/java/org/springframework/cloud/kubernetes/fabric8/config/include_profile_specific_sources/properties/One.java b/spring-cloud-kubernetes-fabric8-config/src/test/java/org/springframework/cloud/kubernetes/fabric8/config/include_profile_specific_sources/properties/One.java new file mode 100644 index 00000000..af88a5c0 --- /dev/null +++ b/spring-cloud-kubernetes-fabric8-config/src/test/java/org/springframework/cloud/kubernetes/fabric8/config/include_profile_specific_sources/properties/One.java @@ -0,0 +1,34 @@ +/* + * Copyright 2013-2021 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.include_profile_specific_sources.properties; + +import org.springframework.boot.context.properties.ConfigurationProperties; + +@ConfigurationProperties("one") +public class One { + + private String property; + + public String getProperty() { + return property; + } + + public void setProperty(String property) { + this.property = property; + } + +} diff --git a/spring-cloud-kubernetes-fabric8-config/src/test/java/org/springframework/cloud/kubernetes/fabric8/config/include_profile_specific_sources/properties/Three.java b/spring-cloud-kubernetes-fabric8-config/src/test/java/org/springframework/cloud/kubernetes/fabric8/config/include_profile_specific_sources/properties/Three.java new file mode 100644 index 00000000..239f4374 --- /dev/null +++ b/spring-cloud-kubernetes-fabric8-config/src/test/java/org/springframework/cloud/kubernetes/fabric8/config/include_profile_specific_sources/properties/Three.java @@ -0,0 +1,34 @@ +/* + * Copyright 2013-2021 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.include_profile_specific_sources.properties; + +import org.springframework.boot.context.properties.ConfigurationProperties; + +@ConfigurationProperties("three") +public class Three { + + private String property; + + public String getProperty() { + return property; + } + + public void setProperty(String property) { + this.property = property; + } + +} diff --git a/spring-cloud-kubernetes-fabric8-config/src/test/java/org/springframework/cloud/kubernetes/fabric8/config/include_profile_specific_sources/properties/Two.java b/spring-cloud-kubernetes-fabric8-config/src/test/java/org/springframework/cloud/kubernetes/fabric8/config/include_profile_specific_sources/properties/Two.java new file mode 100644 index 00000000..c14b1b0f --- /dev/null +++ b/spring-cloud-kubernetes-fabric8-config/src/test/java/org/springframework/cloud/kubernetes/fabric8/config/include_profile_specific_sources/properties/Two.java @@ -0,0 +1,34 @@ +/* + * Copyright 2013-2021 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.include_profile_specific_sources.properties; + +import org.springframework.boot.context.properties.ConfigurationProperties; + +@ConfigurationProperties("two") +public class Two { + + private String property; + + public String getProperty() { + return property; + } + + public void setProperty(String property) { + this.property = property; + } + +} diff --git a/spring-cloud-kubernetes-fabric8-config/src/test/java/org/springframework/cloud/kubernetes/fabric8/config/with_prefix/controller/Controller.java b/spring-cloud-kubernetes-fabric8-config/src/test/java/org/springframework/cloud/kubernetes/fabric8/config/with_prefix/controller/Controller.java index 86886d10..adbed9f9 100644 --- a/spring-cloud-kubernetes-fabric8-config/src/test/java/org/springframework/cloud/kubernetes/fabric8/config/with_prefix/controller/Controller.java +++ b/spring-cloud-kubernetes-fabric8-config/src/test/java/org/springframework/cloud/kubernetes/fabric8/config/with_prefix/controller/Controller.java @@ -37,17 +37,17 @@ public class Controller { this.three = three; } - @GetMapping("/one") + @GetMapping("/prefix/one") public String one() { return one.getProperty(); } - @GetMapping("/two") + @GetMapping("/prefix/two") public String two() { return two.getProperty(); } - @GetMapping("/three") + @GetMapping("/prefix/three") public String three() { return three.getProperty(); } diff --git a/spring-cloud-kubernetes-fabric8-config/src/test/resources/config-map-name-as-prefix.yaml b/spring-cloud-kubernetes-fabric8-config/src/test/resources/config-map-name-as-prefix.yaml new file mode 100644 index 00000000..b52095e4 --- /dev/null +++ b/spring-cloud-kubernetes-fabric8-config/src/test/resources/config-map-name-as-prefix.yaml @@ -0,0 +1,14 @@ +spring: + application: + name: with-prefix + cloud: + kubernetes: + config: + useNameAsPrefix: true + namespace: spring-k8s + sources: + - name: config-map-one + useNameAsPrefix: false + - name: config-map-two + explicitPrefix: two + - name: config-map-three diff --git a/spring-cloud-kubernetes-fabric8-config/src/test/resources/include-profile-specific-sources.yaml b/spring-cloud-kubernetes-fabric8-config/src/test/resources/include-profile-specific-sources.yaml new file mode 100644 index 00000000..54999aaa --- /dev/null +++ b/spring-cloud-kubernetes-fabric8-config/src/test/resources/include-profile-specific-sources.yaml @@ -0,0 +1,14 @@ +spring: + application: + name: include-profile-specific-sources + cloud: + kubernetes: + config: + includeProfileSpecificSources: false + namespace: spring-k8s + sources: + - name: config-map-one + includeProfileSpecificSources: true + - name: config-map-two + includeProfileSpecificSources: false + - name: config-map-three