From 2600cb664573294d89d4785b56ac3b95bebdb5f4 Mon Sep 17 00:00:00 2001 From: erabii Date: Thu, 11 May 2023 23:20:50 +0300 Subject: [PATCH] fix 1338 (#1340) --- .../main/asciidoc/property-source-config.adoc | 5 + ...sClientConfigMapPropertySourceLocator.java | 2 + ...tesClientSecretsPropertySourceLocator.java | 2 + ...sClientConfigMapPropertySourceLocator.java | 2 + ...tesClientSecretsPropertySourceLocator.java | 2 + .../KubernetesClientSourcesOrderTests.java | 79 +++++++++++++ ...ableKubernetesClientSourcesOrderTests.java | 79 +++++++++++++ .../sources_order/SourcesOrderApp.java | 35 ++++++ .../controller/SourcesOrderController.java | 50 ++++++++ .../sources_order/properties/Properties.java | 57 +++++++++ .../stubs/SourcesOrderConfigurationStub.java | 102 ++++++++++++++++ .../test/resources/META-INF/spring.factories | 1 + .../resources/retryable-sources-order.yaml | 17 +++ .../src/test/resources/sources-order.yaml | 15 +++ ...Fabric8ConfigMapPropertySourceLocator.java | 2 +- ...leFabric8SecretsPropertySourceLocator.java | 2 + .../retryable_sources_order/Properties.java | 57 +++++++++ .../RetryableSourcesOrderApp.java | 34 ++++++ .../RetryableSourcesOrderController.java | 49 ++++++++ .../RetryableSourcesOrderTests.java | 110 ++++++++++++++++++ .../config/sources_order/Properties.java | 57 +++++++++ .../config/sources_order/SourcesOrderApp.java | 34 ++++++ .../sources_order/SourcesOrderController.java | 49 ++++++++ .../sources_order/SourcesOrderTests.java | 110 ++++++++++++++++++ .../resources/retryable-sources-order.yaml | 17 +++ .../src/test/resources/sources-order.yaml | 15 +++ 26 files changed, 983 insertions(+), 1 deletion(-) create mode 100644 spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/KubernetesClientSourcesOrderTests.java create mode 100644 spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/RetryableKubernetesClientSourcesOrderTests.java create mode 100644 spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/applications/sources_order/SourcesOrderApp.java create mode 100644 spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/applications/sources_order/controller/SourcesOrderController.java create mode 100644 spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/applications/sources_order/properties/Properties.java create mode 100644 spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/boostrap/stubs/SourcesOrderConfigurationStub.java create mode 100644 spring-cloud-kubernetes-client-config/src/test/resources/retryable-sources-order.yaml create mode 100644 spring-cloud-kubernetes-client-config/src/test/resources/sources-order.yaml create mode 100644 spring-cloud-kubernetes-fabric8-config/src/test/java/org/springframework/cloud/kubernetes/fabric8/config/retryable_sources_order/Properties.java create mode 100644 spring-cloud-kubernetes-fabric8-config/src/test/java/org/springframework/cloud/kubernetes/fabric8/config/retryable_sources_order/RetryableSourcesOrderApp.java create mode 100644 spring-cloud-kubernetes-fabric8-config/src/test/java/org/springframework/cloud/kubernetes/fabric8/config/retryable_sources_order/RetryableSourcesOrderController.java create mode 100644 spring-cloud-kubernetes-fabric8-config/src/test/java/org/springframework/cloud/kubernetes/fabric8/config/retryable_sources_order/RetryableSourcesOrderTests.java create mode 100644 spring-cloud-kubernetes-fabric8-config/src/test/java/org/springframework/cloud/kubernetes/fabric8/config/sources_order/Properties.java create mode 100644 spring-cloud-kubernetes-fabric8-config/src/test/java/org/springframework/cloud/kubernetes/fabric8/config/sources_order/SourcesOrderApp.java create mode 100644 spring-cloud-kubernetes-fabric8-config/src/test/java/org/springframework/cloud/kubernetes/fabric8/config/sources_order/SourcesOrderController.java create mode 100644 spring-cloud-kubernetes-fabric8-config/src/test/java/org/springframework/cloud/kubernetes/fabric8/config/sources_order/SourcesOrderTests.java create mode 100644 spring-cloud-kubernetes-fabric8-config/src/test/resources/retryable-sources-order.yaml create mode 100644 spring-cloud-kubernetes-fabric8-config/src/test/resources/sources-order.yaml diff --git a/docs/src/main/asciidoc/property-source-config.adoc b/docs/src/main/asciidoc/property-source-config.adoc index 62eeef34..f3e9a6df 100644 --- a/docs/src/main/asciidoc/property-source-config.adoc +++ b/docs/src/main/asciidoc/property-source-config.adoc @@ -725,6 +725,11 @@ such a method. This, in turn, could be configured via environment properties. Fo Failure to find a namespace from the above steps will result in an Exception being raised. +[[order_of_configMaps_and_secrets]] +=== Order of ConfigMaps and Secrets + +If, for whatever reason, you enabled both configmaps and secrets, and there is a common property between them, the value from the ConfigMap will have a higher precedence. That is: it will override whatever values are found in secrets. + === `PropertySource` Reload WARNING: This functionality has been deprecated in the 2020.0 release. Please see 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 a35d1677..fa178332 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 @@ -23,6 +23,7 @@ import org.springframework.cloud.kubernetes.commons.KubernetesNamespaceProvider; import org.springframework.cloud.kubernetes.commons.config.ConfigMapConfigProperties; import org.springframework.cloud.kubernetes.commons.config.ConfigMapPropertySourceLocator; import org.springframework.cloud.kubernetes.commons.config.NamespaceResolutionFailedException; +import org.springframework.core.annotation.Order; import org.springframework.core.env.ConfigurableEnvironment; import org.springframework.core.env.MapPropertySource; import org.springframework.util.StringUtils; @@ -31,6 +32,7 @@ import org.springframework.util.StringUtils; * @author Ryan Baxter * @author Isik Erhan */ +@Order(0) public class KubernetesClientConfigMapPropertySourceLocator extends ConfigMapPropertySourceLocator { private final CoreV1Api coreV1Api; diff --git a/spring-cloud-kubernetes-client-config/src/main/java/org/springframework/cloud/kubernetes/client/config/KubernetesClientSecretsPropertySourceLocator.java b/spring-cloud-kubernetes-client-config/src/main/java/org/springframework/cloud/kubernetes/client/config/KubernetesClientSecretsPropertySourceLocator.java index 5b3c17c8..386afc00 100644 --- a/spring-cloud-kubernetes-client-config/src/main/java/org/springframework/cloud/kubernetes/client/config/KubernetesClientSecretsPropertySourceLocator.java +++ b/spring-cloud-kubernetes-client-config/src/main/java/org/springframework/cloud/kubernetes/client/config/KubernetesClientSecretsPropertySourceLocator.java @@ -23,6 +23,7 @@ import org.springframework.cloud.kubernetes.commons.KubernetesNamespaceProvider; import org.springframework.cloud.kubernetes.commons.config.NamespaceResolutionFailedException; import org.springframework.cloud.kubernetes.commons.config.SecretsConfigProperties; import org.springframework.cloud.kubernetes.commons.config.SecretsPropertySourceLocator; +import org.springframework.core.annotation.Order; import org.springframework.core.env.ConfigurableEnvironment; import org.springframework.core.env.MapPropertySource; import org.springframework.util.StringUtils; @@ -33,6 +34,7 @@ import static org.springframework.cloud.kubernetes.commons.config.ConfigUtils.ge * @author Ryan Baxter * @author Isik Erhan */ +@Order(1) public class KubernetesClientSecretsPropertySourceLocator extends SecretsPropertySourceLocator { private final CoreV1Api coreV1Api; diff --git a/spring-cloud-kubernetes-client-config/src/main/java/org/springframework/cloud/kubernetes/client/config/RetryableKubernetesClientConfigMapPropertySourceLocator.java b/spring-cloud-kubernetes-client-config/src/main/java/org/springframework/cloud/kubernetes/client/config/RetryableKubernetesClientConfigMapPropertySourceLocator.java index 1dc602ed..7e8bfbc1 100644 --- a/spring-cloud-kubernetes-client-config/src/main/java/org/springframework/cloud/kubernetes/client/config/RetryableKubernetesClientConfigMapPropertySourceLocator.java +++ b/spring-cloud-kubernetes-client-config/src/main/java/org/springframework/cloud/kubernetes/client/config/RetryableKubernetesClientConfigMapPropertySourceLocator.java @@ -22,6 +22,7 @@ import io.kubernetes.client.openapi.apis.CoreV1Api; import org.springframework.cloud.kubernetes.commons.KubernetesNamespaceProvider; import org.springframework.cloud.kubernetes.commons.config.ConfigMapConfigProperties; +import org.springframework.core.annotation.Order; import org.springframework.core.env.Environment; import org.springframework.core.env.PropertySource; import org.springframework.retry.annotation.Retryable; @@ -31,6 +32,7 @@ import org.springframework.retry.annotation.Retryable; * * @author Ryan Baxter */ +@Order(0) class RetryableKubernetesClientConfigMapPropertySourceLocator extends KubernetesClientConfigMapPropertySourceLocator { RetryableKubernetesClientConfigMapPropertySourceLocator(CoreV1Api coreV1Api, ConfigMapConfigProperties properties, diff --git a/spring-cloud-kubernetes-client-config/src/main/java/org/springframework/cloud/kubernetes/client/config/RetryableKubernetesClientSecretsPropertySourceLocator.java b/spring-cloud-kubernetes-client-config/src/main/java/org/springframework/cloud/kubernetes/client/config/RetryableKubernetesClientSecretsPropertySourceLocator.java index ab922d66..3f01b8cc 100644 --- a/spring-cloud-kubernetes-client-config/src/main/java/org/springframework/cloud/kubernetes/client/config/RetryableKubernetesClientSecretsPropertySourceLocator.java +++ b/spring-cloud-kubernetes-client-config/src/main/java/org/springframework/cloud/kubernetes/client/config/RetryableKubernetesClientSecretsPropertySourceLocator.java @@ -22,6 +22,7 @@ import io.kubernetes.client.openapi.apis.CoreV1Api; import org.springframework.cloud.kubernetes.commons.KubernetesNamespaceProvider; import org.springframework.cloud.kubernetes.commons.config.SecretsConfigProperties; +import org.springframework.core.annotation.Order; import org.springframework.core.env.Environment; import org.springframework.core.env.PropertySource; import org.springframework.retry.annotation.Retryable; @@ -31,6 +32,7 @@ import org.springframework.retry.annotation.Retryable; * * @author Ryan Baxter */ +@Order(1) class RetryableKubernetesClientSecretsPropertySourceLocator extends KubernetesClientSecretsPropertySourceLocator { RetryableKubernetesClientSecretsPropertySourceLocator(CoreV1Api coreV1Api, diff --git a/spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/KubernetesClientSourcesOrderTests.java b/spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/KubernetesClientSourcesOrderTests.java new file mode 100644 index 00000000..e141a251 --- /dev/null +++ b/spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/KubernetesClientSourcesOrderTests.java @@ -0,0 +1,79 @@ +/* + * Copyright 2013-2023 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.cloud.kubernetes.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.sources_order.SourcesOrderApp; +import org.springframework.test.context.junit.jupiter.SpringExtension; +import org.springframework.test.web.reactive.server.WebTestClient; + +/** + * The stub data for this test is in : + * {@link org.springframework.cloud.kubernetes.client.config.boostrap.stubs.SourcesOrderConfigurationStub} + * + * @author wind57 + */ +@ExtendWith(SpringExtension.class) +@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, classes = SourcesOrderApp.class, + properties = { "spring.cloud.bootstrap.name=sources-order", "sources.order.stub=true" }) +@AutoConfigureWebTestClient +class KubernetesClientSourcesOrderTests { + + @Autowired + private WebTestClient webClient; + + @AfterEach + void afterEach() { + WireMock.reset(); + } + + @AfterAll + static void afterAll() { + WireMock.shutdownServer(); + } + + /** + *
+	 *	 1. There is one secret deployed: my-secret. It has two properties: {my.one=one, my.key=from-secret}
+	 *	 2. There is one configmap deployed: my-configmap. It has two properties: {my.two=two, my.key=from-configmap}
+	 *
+	 *	 We invoke three endpoints: /one, /two, /key.
+	 *	 The first two prove that both the secret and configmap have been read, the last one proves that
+	 *	 config maps have a higher precedence.
+	 * 
+ */ + @Test + void test() { + this.webClient.get().uri("/one").exchange().expectStatus().isOk().expectBody(String.class) + .value(Matchers.equalTo("one")); + this.webClient.get().uri("/two").exchange().expectStatus().isOk().expectBody(String.class) + .value(Matchers.equalTo("two")); + + this.webClient.get().uri("/key").exchange().expectStatus().isOk().expectBody(String.class) + .value(Matchers.equalTo("from-configmap")); + } + +} diff --git a/spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/RetryableKubernetesClientSourcesOrderTests.java b/spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/RetryableKubernetesClientSourcesOrderTests.java new file mode 100644 index 00000000..21826b07 --- /dev/null +++ b/spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/RetryableKubernetesClientSourcesOrderTests.java @@ -0,0 +1,79 @@ +/* + * Copyright 2013-2023 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.cloud.kubernetes.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.sources_order.SourcesOrderApp; +import org.springframework.test.context.junit.jupiter.SpringExtension; +import org.springframework.test.web.reactive.server.WebTestClient; + +/** + * The stub data for this test is in : + * {@link org.springframework.cloud.kubernetes.client.config.boostrap.stubs.SourcesOrderConfigurationStub} + * + * @author wind57 + */ +@ExtendWith(SpringExtension.class) +@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, classes = SourcesOrderApp.class, + properties = { "spring.cloud.bootstrap.name=retryable-sources-order", "sources.order.stub=true" }) +@AutoConfigureWebTestClient +class RetryableKubernetesClientSourcesOrderTests { + + @Autowired + private WebTestClient webClient; + + @AfterEach + void afterEach() { + WireMock.reset(); + } + + @AfterAll + static void afterAll() { + WireMock.shutdownServer(); + } + + /** + *
+	 *	 1. There is one secret deployed: my-secret. It has two properties: {my.one=one, my.key=from-secret}
+	 *	 2. There is one configmap deployed: my-configmap. It has two properties: {my.two=two, my.key=from-configmap}
+	 *
+	 *	 We invoke three endpoints: /one, /two, /key.
+	 *	 The first two prove that both the secret and configmap have been read, the last one proves that
+	 *	 config maps have a higher precedence.
+	 * 
+ */ + @Test + void test() { + this.webClient.get().uri("/one").exchange().expectStatus().isOk().expectBody(String.class) + .value(Matchers.equalTo("one")); + this.webClient.get().uri("/two").exchange().expectStatus().isOk().expectBody(String.class) + .value(Matchers.equalTo("two")); + + this.webClient.get().uri("/key").exchange().expectStatus().isOk().expectBody(String.class) + .value(Matchers.equalTo("from-configmap")); + } + +} diff --git a/spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/applications/sources_order/SourcesOrderApp.java b/spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/applications/sources_order/SourcesOrderApp.java new file mode 100644 index 00000000..757ddb63 --- /dev/null +++ b/spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/applications/sources_order/SourcesOrderApp.java @@ -0,0 +1,35 @@ +/* + * Copyright 2013-2023 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.cloud.kubernetes.client.config.applications.sources_order; + +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.sources_order.properties.Properties; + +/** + * @author wind57 + */ +@SpringBootApplication +@EnableConfigurationProperties(Properties.class) +public class SourcesOrderApp { + + public static void main(String[] args) { + SpringApplication.run(SourcesOrderApp.class, args); + } + +} diff --git a/spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/applications/sources_order/controller/SourcesOrderController.java b/spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/applications/sources_order/controller/SourcesOrderController.java new file mode 100644 index 00000000..e5288511 --- /dev/null +++ b/spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/applications/sources_order/controller/SourcesOrderController.java @@ -0,0 +1,50 @@ +/* + * Copyright 2013-2023 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.cloud.kubernetes.client.config.applications.sources_order.controller; + +import org.springframework.cloud.kubernetes.client.config.applications.sources_order.properties.Properties; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RestController; + +/** + * @author wind57 + */ +@RestController +public class SourcesOrderController { + + private final Properties properties; + + SourcesOrderController(Properties properties) { + this.properties = properties; + } + + @GetMapping("/key") + String key() { + return properties.getKey(); + } + + @GetMapping("/one") + String one() { + return properties.getOne(); + } + + @GetMapping("/two") + String two() { + return properties.getTwo(); + } + +} diff --git a/spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/applications/sources_order/properties/Properties.java b/spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/applications/sources_order/properties/Properties.java new file mode 100644 index 00000000..14b55ddb --- /dev/null +++ b/spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/applications/sources_order/properties/Properties.java @@ -0,0 +1,57 @@ +/* + * Copyright 2013-2023 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.cloud.kubernetes.client.config.applications.sources_order.properties; + +import org.springframework.boot.context.properties.ConfigurationProperties; + +/** + * @author wind57 + */ +@ConfigurationProperties("my") +public class Properties { + + private String key; + + private String one; + + private String two; + + public String getKey() { + return key; + } + + public void setKey(String key) { + this.key = key; + } + + public String getOne() { + return one; + } + + public void setOne(String one) { + this.one = one; + } + + public String getTwo() { + return two; + } + + public void setTwo(String two) { + this.two = two; + } + +} diff --git a/spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/boostrap/stubs/SourcesOrderConfigurationStub.java b/spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/boostrap/stubs/SourcesOrderConfigurationStub.java new file mode 100644 index 00000000..5c6e6b72 --- /dev/null +++ b/spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/boostrap/stubs/SourcesOrderConfigurationStub.java @@ -0,0 +1,102 @@ +/* + * Copyright 2013-2023 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.cloud.kubernetes.client.config.boostrap.stubs; + +import java.nio.charset.StandardCharsets; +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; + +import com.github.tomakehurst.wiremock.WireMockServer; +import com.github.tomakehurst.wiremock.client.WireMock; +import io.kubernetes.client.openapi.ApiClient; +import io.kubernetes.client.openapi.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.openapi.models.V1Secret; +import io.kubernetes.client.openapi.models.V1SecretBuilder; +import io.kubernetes.client.openapi.models.V1SecretList; +import io.kubernetes.client.util.ClientBuilder; + +import org.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; + +@Order(0) +@Configuration +@ConditionalOnProperty("sources.order.stub") +public class SourcesOrderConfigurationStub { + + @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); + stubConfigMapData(); + stubSecretsData(); + return apiClient; + } + + private void stubConfigMapData() { + + Map configMapData = new HashMap<>(); + configMapData.put("my.key", "from-configmap"); + configMapData.put("my.two", "two"); + + V1ConfigMap myConfigMap = new V1ConfigMapBuilder().withMetadata(new V1ObjectMetaBuilder() + .withName("my-configmap").withNamespace("spring-k8s").withResourceVersion("1").build()) + .addToData(configMapData).build(); + + V1ConfigMapList allConfigMaps = new V1ConfigMapList(); + allConfigMaps.setItems(Collections.singletonList(myConfigMap)); + + // 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)))); + } + + private void stubSecretsData() { + + Map secretData = new HashMap<>(); + secretData.put("my.key", "from-secret".getBytes(StandardCharsets.UTF_8)); + secretData.put("my.one", "one".getBytes(StandardCharsets.UTF_8)); + + V1Secret mySecret = new V1SecretBuilder().withMetadata(new V1ObjectMetaBuilder().withName("my-secret") + .withNamespace("spring-k8s").withResourceVersion("1").build()).addToData(secretData).build(); + + V1SecretList allConfigMaps = new V1SecretList(); + allConfigMaps.setItems(Collections.singletonList(mySecret)); + + // the actual stub for CoreV1Api calls + WireMock.stubFor(WireMock.get("/api/v1/namespaces/spring-k8s/secrets") + .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 index 6332427d..ff2a55b7 100644 --- 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 @@ -1,4 +1,5 @@ org.springframework.cloud.bootstrap.BootstrapConfiguration=\ org.springframework.cloud.kubernetes.client.config.boostrap.stubs.IncludeProfileSpecificSourcesConfigurationStub, \ org.springframework.cloud.kubernetes.client.config.boostrap.stubs.ConfigMapNameAsPrefixConfigurationStub, \ +org.springframework.cloud.kubernetes.client.config.boostrap.stubs.SourcesOrderConfigurationStub, \ org.springframework.cloud.kubernetes.client.config.EnableRetryBootstrapConfiguration diff --git a/spring-cloud-kubernetes-client-config/src/test/resources/retryable-sources-order.yaml b/spring-cloud-kubernetes-client-config/src/test/resources/retryable-sources-order.yaml new file mode 100644 index 00000000..06afb7ca --- /dev/null +++ b/spring-cloud-kubernetes-client-config/src/test/resources/retryable-sources-order.yaml @@ -0,0 +1,17 @@ +spring: + application: + name: sources-order + cloud: + kubernetes: + secrets: + fail-fast: true + enabled: true + enableApi: true + namespace: spring-k8s + sources: + - name: my-secret + config: + fail-fast: true + namespace: spring-k8s + sources: + - name: my-configmap diff --git a/spring-cloud-kubernetes-client-config/src/test/resources/sources-order.yaml b/spring-cloud-kubernetes-client-config/src/test/resources/sources-order.yaml new file mode 100644 index 00000000..c76cb733 --- /dev/null +++ b/spring-cloud-kubernetes-client-config/src/test/resources/sources-order.yaml @@ -0,0 +1,15 @@ +spring: + application: + name: sources-order + cloud: + kubernetes: + secrets: + enabled: true + enableApi: true + namespace: spring-k8s + sources: + - name: my-secret + config: + namespace: spring-k8s + sources: + - name: my-configmap diff --git a/spring-cloud-kubernetes-fabric8-config/src/main/java/org/springframework/cloud/kubernetes/fabric8/config/RetryableFabric8ConfigMapPropertySourceLocator.java b/spring-cloud-kubernetes-fabric8-config/src/main/java/org/springframework/cloud/kubernetes/fabric8/config/RetryableFabric8ConfigMapPropertySourceLocator.java index 941ef384..be8f9c84 100644 --- a/spring-cloud-kubernetes-fabric8-config/src/main/java/org/springframework/cloud/kubernetes/fabric8/config/RetryableFabric8ConfigMapPropertySourceLocator.java +++ b/spring-cloud-kubernetes-fabric8-config/src/main/java/org/springframework/cloud/kubernetes/fabric8/config/RetryableFabric8ConfigMapPropertySourceLocator.java @@ -32,7 +32,7 @@ import org.springframework.retry.annotation.Retryable; * * @author Ryan Baxter */ -@Order(1) +@Order(0) class RetryableFabric8ConfigMapPropertySourceLocator extends Fabric8ConfigMapPropertySourceLocator { RetryableFabric8ConfigMapPropertySourceLocator(KubernetesClient client, ConfigMapConfigProperties properties, diff --git a/spring-cloud-kubernetes-fabric8-config/src/main/java/org/springframework/cloud/kubernetes/fabric8/config/RetryableFabric8SecretsPropertySourceLocator.java b/spring-cloud-kubernetes-fabric8-config/src/main/java/org/springframework/cloud/kubernetes/fabric8/config/RetryableFabric8SecretsPropertySourceLocator.java index 880427fc..d48c54c3 100644 --- a/spring-cloud-kubernetes-fabric8-config/src/main/java/org/springframework/cloud/kubernetes/fabric8/config/RetryableFabric8SecretsPropertySourceLocator.java +++ b/spring-cloud-kubernetes-fabric8-config/src/main/java/org/springframework/cloud/kubernetes/fabric8/config/RetryableFabric8SecretsPropertySourceLocator.java @@ -22,6 +22,7 @@ import io.fabric8.kubernetes.client.KubernetesClient; import org.springframework.cloud.kubernetes.commons.KubernetesNamespaceProvider; import org.springframework.cloud.kubernetes.commons.config.SecretsConfigProperties; +import org.springframework.core.annotation.Order; import org.springframework.core.env.Environment; import org.springframework.core.env.PropertySource; import org.springframework.retry.annotation.Retryable; @@ -31,6 +32,7 @@ import org.springframework.retry.annotation.Retryable; * * @author Ryan Baxter */ +@Order(1) class RetryableFabric8SecretsPropertySourceLocator extends Fabric8SecretsPropertySourceLocator { RetryableFabric8SecretsPropertySourceLocator(KubernetesClient client, SecretsConfigProperties properties, diff --git a/spring-cloud-kubernetes-fabric8-config/src/test/java/org/springframework/cloud/kubernetes/fabric8/config/retryable_sources_order/Properties.java b/spring-cloud-kubernetes-fabric8-config/src/test/java/org/springframework/cloud/kubernetes/fabric8/config/retryable_sources_order/Properties.java new file mode 100644 index 00000000..41397f11 --- /dev/null +++ b/spring-cloud-kubernetes-fabric8-config/src/test/java/org/springframework/cloud/kubernetes/fabric8/config/retryable_sources_order/Properties.java @@ -0,0 +1,57 @@ +/* + * Copyright 2013-2023 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.cloud.kubernetes.fabric8.config.retryable_sources_order; + +import org.springframework.boot.context.properties.ConfigurationProperties; + +/** + * @author wind57 + */ +@ConfigurationProperties("my") +class Properties { + + private String key; + + private String one; + + private String two; + + public String getKey() { + return key; + } + + public void setKey(String key) { + this.key = key; + } + + public String getOne() { + return one; + } + + public void setOne(String one) { + this.one = one; + } + + public String getTwo() { + return two; + } + + public void setTwo(String two) { + this.two = two; + } + +} diff --git a/spring-cloud-kubernetes-fabric8-config/src/test/java/org/springframework/cloud/kubernetes/fabric8/config/retryable_sources_order/RetryableSourcesOrderApp.java b/spring-cloud-kubernetes-fabric8-config/src/test/java/org/springframework/cloud/kubernetes/fabric8/config/retryable_sources_order/RetryableSourcesOrderApp.java new file mode 100644 index 00000000..6bf09c95 --- /dev/null +++ b/spring-cloud-kubernetes-fabric8-config/src/test/java/org/springframework/cloud/kubernetes/fabric8/config/retryable_sources_order/RetryableSourcesOrderApp.java @@ -0,0 +1,34 @@ +/* + * Copyright 2013-2023 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.cloud.kubernetes.fabric8.config.retryable_sources_order; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.boot.context.properties.EnableConfigurationProperties; + +/** + * @author wind57 + */ +@SpringBootApplication +@EnableConfigurationProperties(Properties.class) +class RetryableSourcesOrderApp { + + static void main(String[] args) { + SpringApplication.run(RetryableSourcesOrderApp.class, args); + } + +} diff --git a/spring-cloud-kubernetes-fabric8-config/src/test/java/org/springframework/cloud/kubernetes/fabric8/config/retryable_sources_order/RetryableSourcesOrderController.java b/spring-cloud-kubernetes-fabric8-config/src/test/java/org/springframework/cloud/kubernetes/fabric8/config/retryable_sources_order/RetryableSourcesOrderController.java new file mode 100644 index 00000000..f01afd62 --- /dev/null +++ b/spring-cloud-kubernetes-fabric8-config/src/test/java/org/springframework/cloud/kubernetes/fabric8/config/retryable_sources_order/RetryableSourcesOrderController.java @@ -0,0 +1,49 @@ +/* + * Copyright 2013-2023 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.cloud.kubernetes.fabric8.config.retryable_sources_order; + +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RestController; + +/** + * @author wind57 + */ +@RestController +class RetryableSourcesOrderController { + + private final Properties properties; + + RetryableSourcesOrderController(Properties properties) { + this.properties = properties; + } + + @GetMapping("/retryable-key") + String key() { + return properties.getKey(); + } + + @GetMapping("/retryable-one") + String one() { + return properties.getOne(); + } + + @GetMapping("/retryable-two") + String two() { + return properties.getTwo(); + } + +} diff --git a/spring-cloud-kubernetes-fabric8-config/src/test/java/org/springframework/cloud/kubernetes/fabric8/config/retryable_sources_order/RetryableSourcesOrderTests.java b/spring-cloud-kubernetes-fabric8-config/src/test/java/org/springframework/cloud/kubernetes/fabric8/config/retryable_sources_order/RetryableSourcesOrderTests.java new file mode 100644 index 00000000..c4add4bf --- /dev/null +++ b/spring-cloud-kubernetes-fabric8-config/src/test/java/org/springframework/cloud/kubernetes/fabric8/config/retryable_sources_order/RetryableSourcesOrderTests.java @@ -0,0 +1,110 @@ +/* + * Copyright 2013-2023 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.cloud.kubernetes.fabric8.config.retryable_sources_order; + +import java.nio.charset.StandardCharsets; +import java.util.Base64; +import java.util.HashMap; +import java.util.Map; + +import io.fabric8.kubernetes.api.model.ConfigMapBuilder; +import io.fabric8.kubernetes.api.model.SecretBuilder; +import io.fabric8.kubernetes.client.Config; +import io.fabric8.kubernetes.client.KubernetesClient; +import io.fabric8.kubernetes.client.server.mock.EnableKubernetesMockClient; +import org.hamcrest.Matchers; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.test.web.reactive.server.WebTestClient; + +/** + * @author wind57 + */ +@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, classes = RetryableSourcesOrderApp.class, + properties = { "spring.cloud.bootstrap.name=retryable-sources-order" }) +@EnableKubernetesMockClient(crud = true, https = false) +class RetryableSourcesOrderTests { + + private static KubernetesClient mockClient; + + @Autowired + private WebTestClient webClient; + + @BeforeAll + 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 secretData = new HashMap<>(); + secretData.put("my.key", Base64.getEncoder().encodeToString("from-secret".getBytes(StandardCharsets.UTF_8))); + secretData.put("my.one", Base64.getEncoder().encodeToString("one".getBytes(StandardCharsets.UTF_8))); + createSecret("my-secret", secretData); + + Map configMapData = new HashMap<>(); + configMapData.put("my.key", "from-configmap"); + configMapData.put("my.two", "two"); + createConfigmap("my-configmap", configMapData); + + } + + @AfterAll + static void afterAll() { + System.clearProperty(Config.KUBERNETES_MASTER_SYSTEM_PROPERTY); + } + + private static void createSecret(String name, Map data) { + mockClient.secrets().inNamespace("spring-k8s") + .create(new SecretBuilder().withNewMetadata().withName(name).endMetadata().addToData(data).build()); + } + + private static void createConfigmap(String name, Map data) { + mockClient.configMaps().inNamespace("spring-k8s") + .create(new ConfigMapBuilder().withNewMetadata().withName(name).endMetadata().addToData(data).build()); + } + + /** + *
+	 *	 1. There is one secret deployed: my-secret. It has two properties: {my.one=one, my.key=from-secret}
+	 *	 2. There is one configmap deployed: my-configmap. It has two properties: {my.two=two, my.key=from-configmap}
+	 *
+	 *	 We invoke three endpoints: /one, /two, /key.
+	 *	 The first two prove that both the secret and configmap have been read, the last one proves that
+	 *	 config maps have a higher precedence.
+	 * 
+ */ + @Test + void test() { + this.webClient.get().uri("/retryable-one").exchange().expectStatus().isOk().expectBody(String.class) + .value(Matchers.equalTo("one")); + this.webClient.get().uri("/retryable-two").exchange().expectStatus().isOk().expectBody(String.class) + .value(Matchers.equalTo("two")); + + this.webClient.get().uri("/retryable-key").exchange().expectStatus().isOk().expectBody(String.class) + .value(Matchers.equalTo("from-configmap")); + } + +} diff --git a/spring-cloud-kubernetes-fabric8-config/src/test/java/org/springframework/cloud/kubernetes/fabric8/config/sources_order/Properties.java b/spring-cloud-kubernetes-fabric8-config/src/test/java/org/springframework/cloud/kubernetes/fabric8/config/sources_order/Properties.java new file mode 100644 index 00000000..eb55b259 --- /dev/null +++ b/spring-cloud-kubernetes-fabric8-config/src/test/java/org/springframework/cloud/kubernetes/fabric8/config/sources_order/Properties.java @@ -0,0 +1,57 @@ +/* + * Copyright 2013-2023 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.cloud.kubernetes.fabric8.config.sources_order; + +import org.springframework.boot.context.properties.ConfigurationProperties; + +/** + * @author wind57 + */ +@ConfigurationProperties("my") +class Properties { + + private String key; + + private String one; + + private String two; + + public String getKey() { + return key; + } + + public void setKey(String key) { + this.key = key; + } + + public String getOne() { + return one; + } + + public void setOne(String one) { + this.one = one; + } + + public String getTwo() { + return two; + } + + public void setTwo(String two) { + this.two = two; + } + +} diff --git a/spring-cloud-kubernetes-fabric8-config/src/test/java/org/springframework/cloud/kubernetes/fabric8/config/sources_order/SourcesOrderApp.java b/spring-cloud-kubernetes-fabric8-config/src/test/java/org/springframework/cloud/kubernetes/fabric8/config/sources_order/SourcesOrderApp.java new file mode 100644 index 00000000..676acaab --- /dev/null +++ b/spring-cloud-kubernetes-fabric8-config/src/test/java/org/springframework/cloud/kubernetes/fabric8/config/sources_order/SourcesOrderApp.java @@ -0,0 +1,34 @@ +/* + * Copyright 2013-2023 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.cloud.kubernetes.fabric8.config.sources_order; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.boot.context.properties.EnableConfigurationProperties; + +/** + * @author wind57 + */ +@SpringBootApplication +@EnableConfigurationProperties(Properties.class) +class SourcesOrderApp { + + static void main(String[] args) { + SpringApplication.run(SourcesOrderApp.class, args); + } + +} diff --git a/spring-cloud-kubernetes-fabric8-config/src/test/java/org/springframework/cloud/kubernetes/fabric8/config/sources_order/SourcesOrderController.java b/spring-cloud-kubernetes-fabric8-config/src/test/java/org/springframework/cloud/kubernetes/fabric8/config/sources_order/SourcesOrderController.java new file mode 100644 index 00000000..f9f277da --- /dev/null +++ b/spring-cloud-kubernetes-fabric8-config/src/test/java/org/springframework/cloud/kubernetes/fabric8/config/sources_order/SourcesOrderController.java @@ -0,0 +1,49 @@ +/* + * Copyright 2013-2023 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.cloud.kubernetes.fabric8.config.sources_order; + +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RestController; + +/** + * @author wind57 + */ +@RestController +class SourcesOrderController { + + private final Properties properties; + + SourcesOrderController(Properties properties) { + this.properties = properties; + } + + @GetMapping("/key") + String key() { + return properties.getKey(); + } + + @GetMapping("/one") + String one() { + return properties.getOne(); + } + + @GetMapping("/two") + String two() { + return properties.getTwo(); + } + +} diff --git a/spring-cloud-kubernetes-fabric8-config/src/test/java/org/springframework/cloud/kubernetes/fabric8/config/sources_order/SourcesOrderTests.java b/spring-cloud-kubernetes-fabric8-config/src/test/java/org/springframework/cloud/kubernetes/fabric8/config/sources_order/SourcesOrderTests.java new file mode 100644 index 00000000..bd94b49b --- /dev/null +++ b/spring-cloud-kubernetes-fabric8-config/src/test/java/org/springframework/cloud/kubernetes/fabric8/config/sources_order/SourcesOrderTests.java @@ -0,0 +1,110 @@ +/* + * Copyright 2013-2023 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.cloud.kubernetes.fabric8.config.sources_order; + +import java.nio.charset.StandardCharsets; +import java.util.Base64; +import java.util.HashMap; +import java.util.Map; + +import io.fabric8.kubernetes.api.model.ConfigMapBuilder; +import io.fabric8.kubernetes.api.model.SecretBuilder; +import io.fabric8.kubernetes.client.Config; +import io.fabric8.kubernetes.client.KubernetesClient; +import io.fabric8.kubernetes.client.server.mock.EnableKubernetesMockClient; +import org.hamcrest.Matchers; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.test.web.reactive.server.WebTestClient; + +/** + * @author wind57 + */ +@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, classes = SourcesOrderApp.class, + properties = { "spring.cloud.bootstrap.name=sources-order" }) +@EnableKubernetesMockClient(crud = true, https = false) +class SourcesOrderTests { + + private static KubernetesClient mockClient; + + @Autowired + private WebTestClient webClient; + + @BeforeAll + 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 secretData = new HashMap<>(); + secretData.put("my.key", Base64.getEncoder().encodeToString("from-secret".getBytes(StandardCharsets.UTF_8))); + secretData.put("my.one", Base64.getEncoder().encodeToString("one".getBytes(StandardCharsets.UTF_8))); + createSecret("my-secret", secretData); + + Map configMapData = new HashMap<>(); + configMapData.put("my.key", "from-configmap"); + configMapData.put("my.two", "two"); + createConfigmap("my-configmap", configMapData); + + } + + @AfterAll + static void afterAll() { + System.clearProperty(Config.KUBERNETES_MASTER_SYSTEM_PROPERTY); + } + + private static void createSecret(String name, Map data) { + mockClient.secrets().inNamespace("spring-k8s") + .create(new SecretBuilder().withNewMetadata().withName(name).endMetadata().addToData(data).build()); + } + + private static void createConfigmap(String name, Map data) { + mockClient.configMaps().inNamespace("spring-k8s") + .create(new ConfigMapBuilder().withNewMetadata().withName(name).endMetadata().addToData(data).build()); + } + + /** + *
+	 *	 1. There is one secret deployed: my-secret. It has two properties: {my.one=one, my.key=from-secret}
+	 *	 2. There is one configmap deployed: my-configmap. It has two properties: {my.two=two, my.key=from-configmap}
+	 *
+	 *	 We invoke three endpoints: /one, /two, /key.
+	 *	 The first two prove that both the secret and configmap have been read, the last one proves that
+	 *	 config maps have a higher precedence.
+	 * 
+ */ + @Test + void test() { + this.webClient.get().uri("/one").exchange().expectStatus().isOk().expectBody(String.class) + .value(Matchers.equalTo("one")); + this.webClient.get().uri("/two").exchange().expectStatus().isOk().expectBody(String.class) + .value(Matchers.equalTo("two")); + + this.webClient.get().uri("/key").exchange().expectStatus().isOk().expectBody(String.class) + .value(Matchers.equalTo("from-configmap")); + } + +} diff --git a/spring-cloud-kubernetes-fabric8-config/src/test/resources/retryable-sources-order.yaml b/spring-cloud-kubernetes-fabric8-config/src/test/resources/retryable-sources-order.yaml new file mode 100644 index 00000000..06afb7ca --- /dev/null +++ b/spring-cloud-kubernetes-fabric8-config/src/test/resources/retryable-sources-order.yaml @@ -0,0 +1,17 @@ +spring: + application: + name: sources-order + cloud: + kubernetes: + secrets: + fail-fast: true + enabled: true + enableApi: true + namespace: spring-k8s + sources: + - name: my-secret + config: + fail-fast: true + namespace: spring-k8s + sources: + - name: my-configmap diff --git a/spring-cloud-kubernetes-fabric8-config/src/test/resources/sources-order.yaml b/spring-cloud-kubernetes-fabric8-config/src/test/resources/sources-order.yaml new file mode 100644 index 00000000..c76cb733 --- /dev/null +++ b/spring-cloud-kubernetes-fabric8-config/src/test/resources/sources-order.yaml @@ -0,0 +1,15 @@ +spring: + application: + name: sources-order + cloud: + kubernetes: + secrets: + enabled: true + enableApi: true + namespace: spring-k8s + sources: + - name: my-secret + config: + namespace: spring-k8s + sources: + - name: my-configmap