diff --git a/docs/src/main/asciidoc/property-source-config.adoc b/docs/src/main/asciidoc/property-source-config.adoc index 7d730970..5e6db21f 100644 --- a/docs/src/main/asciidoc/property-source-config.adoc +++ b/docs/src/main/asciidoc/property-source-config.adoc @@ -65,8 +65,38 @@ of the application is resolved. Any matching `ConfigMap` that is found is processed as follows: * Apply individual configuration properties. -* Apply as `yaml` the content of any property named `application.yaml`. -* Apply as a properties file the content of any property named `application.properties`. +* Apply as `yaml` (or `properties`) the content of any property that is named by the value of `spring.application.name` + (if it's not present, by `application.yaml/properties`) +* Apply as a properties file the content of the above name + each active profile. + +An example should make a lot more sense. Let's suppose that `spring.application.name=my-app` and that +we have a single active profile called `k8s`. For a configuration as below: + + +==== +[source] +---- +kind: ConfigMap +apiVersion: v1 +metadata: + name: my-app +data: + my-app.yaml: |- + ... + my-app-k8s.yaml: |- + .. + my-app-dev.yaml: |- + .. + someProp: someValue +---- +==== + +These is what we will end-up loading: + + - `my-app.yaml` treated as a file + - `my-app-k8s.yaml` treated as a file + - `my-app-dev.yaml` _ignored_, since `dev` is _not_ an active profile + - `someProp: someValue` plain property The single exception to the aforementioned flow is when the `ConfigMap` contains a *single* key that indicates the file is a YAML or properties file. In that case, the name of the key does NOT have to be `application.yaml` or diff --git a/spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/applications/single_source_multiple_files/SingleSourceMultipleFilesApp.java b/spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/applications/single_source_multiple_files/SingleSourceMultipleFilesApp.java new file mode 100644 index 00000000..17f7d3c7 --- /dev/null +++ b/spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/applications/single_source_multiple_files/SingleSourceMultipleFilesApp.java @@ -0,0 +1,35 @@ +/* + * Copyright 2013-2022 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.cloud.kubernetes.client.config.applications.single_source_multiple_files; + +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.single_source_multiple_files.properties.Color; +import org.springframework.cloud.kubernetes.client.config.applications.single_source_multiple_files.properties.Name; +import org.springframework.cloud.kubernetes.client.config.applications.single_source_multiple_files.properties.Shape; +import org.springframework.cloud.kubernetes.client.config.applications.single_source_multiple_files.properties.Type; + +@SpringBootApplication +@EnableConfigurationProperties({ Name.class, Shape.class, Color.class, Type.class }) +public class SingleSourceMultipleFilesApp { + + public static void main(String[] args) { + SpringApplication.run(SingleSourceMultipleFilesApp.class, args); + } + +} diff --git a/spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/applications/single_source_multiple_files/SingleSourceMultipleFilesBootstrapTests.java b/spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/applications/single_source_multiple_files/SingleSourceMultipleFilesBootstrapTests.java new file mode 100644 index 00000000..c5f3fb1a --- /dev/null +++ b/spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/applications/single_source_multiple_files/SingleSourceMultipleFilesBootstrapTests.java @@ -0,0 +1,33 @@ +/* + * Copyright 2013-2022 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.cloud.kubernetes.client.config.applications.single_source_multiple_files; + +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.test.context.ActiveProfiles; + +/** + * @author wind57 + */ +@ActiveProfiles("color") +@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, + classes = SingleSourceMultipleFilesApp.class, + properties = { "spring.cloud.bootstrap.name=single-source-multiple-files", + "spring.main.cloud-platform=KUBERNETES", "spring.cloud.bootstrap.enabled=true", + "spring.cloud.kubernetes.client.namespace=spring-k8s", "single.source.multiple.files.stub=true" }) +class SingleSourceMultipleFilesBootstrapTests extends SingleSourceMultipleFilesTests { + +} diff --git a/spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/applications/single_source_multiple_files/SingleSourceMultipleFilesConfigDataTests.java b/spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/applications/single_source_multiple_files/SingleSourceMultipleFilesConfigDataTests.java new file mode 100644 index 00000000..dd9caf95 --- /dev/null +++ b/spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/applications/single_source_multiple_files/SingleSourceMultipleFilesConfigDataTests.java @@ -0,0 +1,63 @@ +/* + * Copyright 2013-2022 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.cloud.kubernetes.client.config.applications.single_source_multiple_files; + +import com.github.tomakehurst.wiremock.WireMockServer; +import com.github.tomakehurst.wiremock.client.WireMock; +import io.kubernetes.client.util.ClientBuilder; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.mockito.MockedStatic; + +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.cloud.kubernetes.client.KubernetesClientUtils; +import org.springframework.test.context.ActiveProfiles; + +import static com.github.tomakehurst.wiremock.core.WireMockConfiguration.options; +import static org.mockito.Mockito.mockStatic; +import static org.springframework.cloud.kubernetes.client.config.boostrap.stubs.SingleSourceMultipleFilesConfigurationStub.stubData; + +/** + * @author wind57 + */ +@ActiveProfiles("color") +@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, + classes = SingleSourceMultipleFilesApp.class, + properties = { "spring.main.cloud-platform=KUBERNETES", + "spring.config.import=kubernetes:,classpath:./single-source-multiple-files.yaml", + "spring.cloud.kubernetes.client.namespace=spring-k8s" }) +class SingleSourceMultipleFilesConfigDataTests extends SingleSourceMultipleFilesTests { + + private static MockedStatic clientUtilsMock; + + @BeforeAll + static void wireMock() { + WireMockServer server = new WireMockServer(options().dynamicPort()); + server.start(); + WireMock.configureFor("localhost", server.port()); + clientUtilsMock = mockStatic(KubernetesClientUtils.class); + clientUtilsMock.when(KubernetesClientUtils::kubernetesApiClient) + .thenReturn(new ClientBuilder().setBasePath(server.baseUrl()).build()); + stubData(); + } + + @AfterAll + static void teardown() { + clientUtilsMock.close(); + } + +} diff --git a/spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/applications/single_source_multiple_files/SingleSourceMultipleFilesTests.java b/spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/applications/single_source_multiple_files/SingleSourceMultipleFilesTests.java new file mode 100644 index 00000000..6ba97cae --- /dev/null +++ b/spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/applications/single_source_multiple_files/SingleSourceMultipleFilesTests.java @@ -0,0 +1,98 @@ +/* + * Copyright 2013-2022 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.cloud.kubernetes.client.config.applications.single_source_multiple_files; + +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.springframework.beans.factory.annotation.Autowired; +import org.springframework.test.web.reactive.server.WebTestClient; + +/** + * @author wind57 + * + * Stub for this test is here : + * {@link org.springframework.cloud.kubernetes.client.config.boostrap.stubs.SingleSourceMultipleFilesConfigurationStub} + * + * issue: https://github.com/spring-cloud/spring-cloud-kubernetes/issues/640 + * + */ +abstract class SingleSourceMultipleFilesTests { + + @Autowired + private WebTestClient webClient; + + @AfterEach + void afterEach() { + WireMock.reset(); + } + + @AfterAll + static void afterAll() { + WireMock.shutdownServer(); + } + + /** + *
+	 *   "fruit-color.properties" is taken since "spring.application.name=fruit" and
+	 *   "color" is an active profile
+	 * 
+ */ + @Test + void color() { + this.webClient.get().uri("/single_source-multiple-files/color").exchange().expectStatus().isOk() + .expectBody(String.class).value(Matchers.equalTo("raw:green###ripe:yellow")); + } + + /** + *
+	 *   "fruit.properties" is read, since it matches "spring.application.name"
+	 * 
+ */ + @Test + void name() { + this.webClient.get().uri("/single_source-multiple-files/name").exchange().expectStatus().isOk() + .expectBody(String.class).value(Matchers.equalTo("banana")); + } + + /** + *
+	 *   shape profile is not active, thus property "fruit-shape.properties" is skipped
+	 *   and as such, a null comes here.
+	 * 
+ */ + @Test + void shape() { + this.webClient.get().uri("/single_source-multiple-files/shape").exchange().expectStatus().isOk() + .expectBody(String.class).value(Matchers.nullValue()); + } + + /** + *
+	 *   this is a non-file property in the configmap
+	 * 
+ */ + @Test + void type() { + this.webClient.get().uri("/single_source-multiple-files/type").exchange().expectStatus().isOk() + .expectBody(String.class).value(Matchers.equalTo("yummy")); + } + +} diff --git a/spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/applications/single_source_multiple_files/controller/SingleSourceMultipleFilesController.java b/spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/applications/single_source_multiple_files/controller/SingleSourceMultipleFilesController.java new file mode 100644 index 00000000..8fb65561 --- /dev/null +++ b/spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/applications/single_source_multiple_files/controller/SingleSourceMultipleFilesController.java @@ -0,0 +1,64 @@ +/* + * 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.single_source_multiple_files.controller; + +import org.springframework.cloud.kubernetes.client.config.applications.single_source_multiple_files.properties.Color; +import org.springframework.cloud.kubernetes.client.config.applications.single_source_multiple_files.properties.Name; +import org.springframework.cloud.kubernetes.client.config.applications.single_source_multiple_files.properties.Shape; +import org.springframework.cloud.kubernetes.client.config.applications.single_source_multiple_files.properties.Type; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RestController; + +@RestController +public class SingleSourceMultipleFilesController { + + private final Name name; + + private final Shape shape; + + private final Color color; + + private final Type type; + + public SingleSourceMultipleFilesController(Name name, Shape shape, Color color, Type type) { + this.name = name; + this.shape = shape; + this.color = color; + this.type = type; + } + + @GetMapping("/single_source-multiple-files/type") + public String type() { + return type.getType(); + } + + @GetMapping("/single_source-multiple-files/shape") + public String shape() { + return shape.getRaw(); + } + + @GetMapping("/single_source-multiple-files/color") + public String color() { + return "raw:" + color.getRaw() + "###" + "ripe:" + color.getRipe(); + } + + @GetMapping("/single_source-multiple-files/name") + public String name() { + return name.getName(); + } + +} diff --git a/spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/applications/single_source_multiple_files/properties/Color.java b/spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/applications/single_source_multiple_files/properties/Color.java new file mode 100644 index 00000000..8d5cb2d4 --- /dev/null +++ b/spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/applications/single_source_multiple_files/properties/Color.java @@ -0,0 +1,44 @@ +/* + * 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.single_source_multiple_files.properties; + +import org.springframework.boot.context.properties.ConfigurationProperties; + +@ConfigurationProperties(prefix = "color.when") +public class Color { + + private String raw; + + private String ripe; + + public String getRaw() { + return raw; + } + + public void setRaw(String raw) { + this.raw = raw; + } + + public String getRipe() { + return ripe; + } + + public void setRipe(String ripe) { + this.ripe = ripe; + } + +} diff --git a/spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/applications/single_source_multiple_files/properties/Name.java b/spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/applications/single_source_multiple_files/properties/Name.java new file mode 100644 index 00000000..21df5011 --- /dev/null +++ b/spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/applications/single_source_multiple_files/properties/Name.java @@ -0,0 +1,34 @@ +/* + * Copyright 2013-2022 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.cloud.kubernetes.client.config.applications.single_source_multiple_files.properties; + +import org.springframework.boot.context.properties.ConfigurationProperties; + +@ConfigurationProperties("cool") +public class Name { + + private String name; + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + +} diff --git a/spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/applications/single_source_multiple_files/properties/Shape.java b/spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/applications/single_source_multiple_files/properties/Shape.java new file mode 100644 index 00000000..3a041536 --- /dev/null +++ b/spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/applications/single_source_multiple_files/properties/Shape.java @@ -0,0 +1,44 @@ +/* + * Copyright 2013-2022 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.cloud.kubernetes.client.config.applications.single_source_multiple_files.properties; + +import org.springframework.boot.context.properties.ConfigurationProperties; + +@ConfigurationProperties("shape.when") +public class Shape { + + private String raw; + + private String ripe; + + public String getRaw() { + return raw; + } + + public void setRaw(String raw) { + this.raw = raw; + } + + public String getRipe() { + return ripe; + } + + public void setRipe(String ripe) { + this.ripe = ripe; + } + +} diff --git a/spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/applications/single_source_multiple_files/properties/Type.java b/spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/applications/single_source_multiple_files/properties/Type.java new file mode 100644 index 00000000..a8e4338b --- /dev/null +++ b/spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/applications/single_source_multiple_files/properties/Type.java @@ -0,0 +1,34 @@ +/* + * Copyright 2013-2022 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.cloud.kubernetes.client.config.applications.single_source_multiple_files.properties; + +import org.springframework.boot.context.properties.ConfigurationProperties; + +@ConfigurationProperties("fruit") +public class Type { + + private String type; + + public String getType() { + return type; + } + + public void setType(String type) { + this.type = type; + } + +} diff --git a/spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/boostrap/stubs/SingleSourceMultipleFilesConfigurationStub.java b/spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/boostrap/stubs/SingleSourceMultipleFilesConfigurationStub.java new file mode 100644 index 00000000..e92948d7 --- /dev/null +++ b/spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/boostrap/stubs/SingleSourceMultipleFilesConfigurationStub.java @@ -0,0 +1,88 @@ +/* + * Copyright 2013-2022 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.cloud.kubernetes.client.config.boostrap.stubs; + +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.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("single.source.multiple.files.stub") +public class SingleSourceMultipleFilesConfigurationStub { + + @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); + stubData(); + return apiClient; + } + + public static void stubData() { + + Map one = new HashMap<>(); + one.put("fruit.type", "yummy"); + one.put("fruit.properties", "cool.name=banana"); + one.put("fruit-color.properties", "color.when.raw=green\ncolor.when.ripe=yellow"); + + // this is not taken, since "shape" is not an active profile + one.put("fruit-shape.properties", "shape.when.raw=small-sphere\nshape.when.ripe=bigger-sphere"); + + V1ConfigMap configMap = new V1ConfigMapBuilder() + .withMetadata(new V1ObjectMetaBuilder().withName("my-configmap").withNamespace("spring-k8s").build()) + .addToData(one).build(); + + V1ConfigMapList allConfigMaps = new V1ConfigMapList(); + allConfigMaps.addItemsItem(configMap); + + // 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 index 33ce8813..8460c48b 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,10 +1,11 @@ org.springframework.cloud.bootstrap.BootstrapConfiguration=\ org.springframework.cloud.kubernetes.client.config.boostrap.stubs.NamedConfigMapWithProfileConfigurationStub, \ org.springframework.cloud.kubernetes.client.config.boostrap.stubs.NamedConfigMapWithPrefixConfigurationStub, \ - org.springframework.cloud.kubernetes.client.config.boostrap.stubs.NamedSecretWithPrefixConfigurationStub, \ +org.springframework.cloud.kubernetes.client.config.boostrap.stubs.NamedSecretWithPrefixConfigurationStub, \ org.springframework.cloud.kubernetes.client.config.boostrap.stubs.NamedSecretWithProfileConfigurationStub, \ org.springframework.cloud.kubernetes.client.config.boostrap.stubs.LabeledSecretWithPrefixConfigurationStub, \ org.springframework.cloud.kubernetes.client.config.boostrap.stubs.LabeledSecretWithProfileConfigurationStub, \ org.springframework.cloud.kubernetes.client.config.boostrap.stubs.LabeledConfigMapWithPrefixConfigurationStub, \ org.springframework.cloud.kubernetes.client.config.boostrap.stubs.LabeledConfigMapWithProfileConfigurationStub, \ +org.springframework.cloud.kubernetes.client.config.boostrap.stubs.SingleSourceMultipleFilesConfigurationStub, \ org.springframework.cloud.kubernetes.client.config.EnableRetryBootstrapConfiguration diff --git a/spring-cloud-kubernetes-client-config/src/test/resources/single-source-multiple-files.yaml b/spring-cloud-kubernetes-client-config/src/test/resources/single-source-multiple-files.yaml new file mode 100644 index 00000000..571e90a0 --- /dev/null +++ b/spring-cloud-kubernetes-client-config/src/test/resources/single-source-multiple-files.yaml @@ -0,0 +1,9 @@ +spring: + application: + name: fruit + cloud: + kubernetes: + config: + namespace: spring-k8s + sources: + - name: my-configmap diff --git a/spring-cloud-kubernetes-commons/src/main/java/org/springframework/cloud/kubernetes/commons/config/SourceDataEntriesProcessor.java b/spring-cloud-kubernetes-commons/src/main/java/org/springframework/cloud/kubernetes/commons/config/SourceDataEntriesProcessor.java index 4052997e..b3faabd7 100644 --- a/spring-cloud-kubernetes-commons/src/main/java/org/springframework/cloud/kubernetes/commons/config/SourceDataEntriesProcessor.java +++ b/spring-cloud-kubernetes-commons/src/main/java/org/springframework/cloud/kubernetes/commons/config/SourceDataEntriesProcessor.java @@ -16,11 +16,13 @@ package org.springframework.cloud.kubernetes.commons.config; +import java.util.Arrays; import java.util.Collections; import java.util.HashMap; import java.util.Map; import java.util.Set; import java.util.stream.Collectors; +import java.util.stream.Stream; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; @@ -28,9 +30,6 @@ import org.apache.commons.logging.LogFactory; import org.springframework.core.env.Environment; import org.springframework.core.env.MapPropertySource; -import static org.springframework.cloud.kubernetes.commons.config.Constants.APPLICATION_PROPERTIES; -import static org.springframework.cloud.kubernetes.commons.config.Constants.APPLICATION_YAML; -import static org.springframework.cloud.kubernetes.commons.config.Constants.APPLICATION_YML; import static org.springframework.cloud.kubernetes.commons.config.PropertySourceUtils.KEY_VALUE_TO_PROPERTIES; import static org.springframework.cloud.kubernetes.commons.config.PropertySourceUtils.PROPERTIES_TO_MAP; import static org.springframework.cloud.kubernetes.commons.config.PropertySourceUtils.throwingMerger; @@ -76,21 +75,45 @@ public class SourceDataEntriesProcessor extends MapPropertySource { private static Map defaultProcessAllEntries(Map input, Environment environment) { - return input.entrySet().stream().map(e -> extractProperties(e.getKey(), e.getValue(), environment)) + // we pass empty Strings on purpose, the logic here is either the value of + // "spring.application.name" + // or literal "application". + String applicationName = ConfigUtils.getApplicationName(environment, "", ""); + String[] activeProfiles = environment.getActiveProfiles(); + + Set fileNames = Stream + .concat(Stream.of(applicationName), + Arrays.stream(activeProfiles).map(profile -> applicationName + "-" + profile)) + .collect(Collectors.toSet()); + + return input.entrySet().stream().map(e -> extractProperties(e.getKey(), e.getValue(), fileNames, environment)) .flatMap(m -> m.entrySet().stream()) .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue, throwingMerger(), HashMap::new)); } - private static Map extractProperties(String resourceName, String content, Environment environment) { + private static Map extractProperties(String resourceName, String content, Set fileNames, + Environment environment) { - if (resourceName.equals(APPLICATION_YAML) || resourceName.equals(APPLICATION_YML)) { - return yamlParserGenerator(environment).andThen(PROPERTIES_TO_MAP).apply(content); - } - else if (resourceName.equals(APPLICATION_PROPERTIES)) { - return KEY_VALUE_TO_PROPERTIES.andThen(PROPERTIES_TO_MAP).apply(content); + if (resourceName.endsWith(".yml") || resourceName.endsWith(".yaml") || resourceName.endsWith(".properties")) { + + if (fileNames.contains(resourceName.split("\\.", 2)[0])) { + if (resourceName.endsWith(".properties")) { + LOG.debug("entry : " + resourceName + " will be treated as a single properties file"); + return KEY_VALUE_TO_PROPERTIES.andThen(PROPERTIES_TO_MAP).apply(content); + } + else { + LOG.debug("entry : " + resourceName + " will be treated as a single yml/yaml file"); + return yamlParserGenerator(environment).andThen(PROPERTIES_TO_MAP).apply(content); + } + } + else { + LOG.warn("entry : " + resourceName + " will be skipped"); + return Collections.emptyMap(); + } } return Collections.singletonMap(resourceName, content); + } } diff --git a/spring-cloud-kubernetes-commons/src/test/java/org/springframework/cloud/kubernetes/commons/config/SourceDataEntriesProcessorTests.java b/spring-cloud-kubernetes-commons/src/test/java/org/springframework/cloud/kubernetes/commons/config/SourceDataEntriesProcessorTests.java new file mode 100644 index 00000000..98647e09 --- /dev/null +++ b/spring-cloud-kubernetes-commons/src/test/java/org/springframework/cloud/kubernetes/commons/config/SourceDataEntriesProcessorTests.java @@ -0,0 +1,174 @@ +/* + * Copyright 2013-2022 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.cloud.kubernetes.commons.config; + +import java.util.Map; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +import org.springframework.mock.env.MockEnvironment; + +/** + * @author wind57 + */ +class SourceDataEntriesProcessorTests { + + @Test + void testSingleYml() { + + Map result = SourceDataEntriesProcessor.processAllEntries(Map.of("one.yml", "key: \n value"), + new MockEnvironment()); + + Assertions.assertEquals(1, result.size()); + Assertions.assertEquals("value", result.get("key")); + } + + @Test + void testSingleYaml() { + + Map result = SourceDataEntriesProcessor.processAllEntries(Map.of("one.yaml", "key: \n value"), + new MockEnvironment()); + + Assertions.assertEquals(1, result.size()); + Assertions.assertEquals("value", result.get("key")); + } + + @Test + void testSingleProperties() { + + Map result = SourceDataEntriesProcessor.processAllEntries(Map.of("one.properties", "key=value"), + new MockEnvironment()); + + Assertions.assertEquals(1, result.size()); + Assertions.assertEquals("value", result.get("key")); + } + + /** + *
+	 *	two properties present, none are file treated
+	 * 
+ */ + @Test + void twoEntriesNoneFileTreated() { + + Map.Entry one = Map.entry("one", "1"); + Map.Entry two = Map.entry("two", "2"); + Map map = Map.ofEntries(one, two); + + Map result = SourceDataEntriesProcessor.processAllEntries(map, new MockEnvironment()); + + Assertions.assertEquals(2, result.size()); + Assertions.assertEquals("1", result.get("one")); + Assertions.assertEquals("2", result.get("two")); + } + + /** + *
+	 *	- two properties present, none are file treated.
+	 *  - even if there is a application.yaml, it is not taken since it is != spring.application.name
+	 * 
+ */ + @Test + void twoEntriesOneIsYamlButNotTaken() { + + Map.Entry one = Map.entry("one", "1"); + Map.Entry myName = Map.entry("my-name.yaml", "color: \n blue"); + Map map = Map.ofEntries(one, myName); + + Map result = SourceDataEntriesProcessor.processAllEntries(map, new MockEnvironment()); + + Assertions.assertEquals(1, result.size()); + Assertions.assertEquals("1", result.get("one")); + } + + /** + *
+	 *	- two properties present, both taken.
+	 *  - second one is treated as a file, since it's name matches "spring.application.name"
+	 * 
+ */ + @Test + void twoEntriesBothTaken() { + + Map.Entry one = Map.entry("one", "1"); + Map.Entry application = Map.entry("application.yaml", "color: \n blue"); + Map map = Map.ofEntries(one, application); + + Map result = SourceDataEntriesProcessor.processAllEntries(map, new MockEnvironment()); + + Assertions.assertEquals(2, result.size()); + Assertions.assertEquals("1", result.get("one")); + Assertions.assertEquals("blue", result.get("color")); + } + + /** + *
+	 *	- three properties present, all taken.
+	 *  - second one is treated as a file, since it's name matches "spring.application.name"
+	 *  - third one is taken since it matches one active profile
+	 * 
+ */ + @Test + void threeEntriesAllTaken() { + + Map.Entry one = Map.entry("one", "1"); + Map.Entry application = Map.entry("application.properties", "color=blue"); + Map.Entry applicationDev = Map.entry("application-dev.properties", "fit=sport"); + Map map = Map.ofEntries(one, application, applicationDev); + + MockEnvironment env = new MockEnvironment(); + env.setActiveProfiles("dev"); + Map result = SourceDataEntriesProcessor.processAllEntries(map, env); + + Assertions.assertEquals(3, result.size()); + Assertions.assertEquals("1", result.get("one")); + Assertions.assertEquals("blue", result.get("color")); + Assertions.assertEquals("sport", result.get("fit")); + } + + /** + *
+	 *	- five properties present, four are taken
+	 *  - second one is treated as a file, since it's name matches "spring.application.name"
+	 *  - third one is taken since it matches one active profile
+	 *  - fourth one is taken since it matches one active profile
+	 * 
+ */ + @Test + void fiveEntriesFourTaken() { + + Map.Entry one = Map.entry("one", "1"); + Map.Entry jacket = Map.entry("jacket.properties", "name=jacket"); + Map.Entry jacketFit = Map.entry("jacket-fit.properties", "fit=sport"); + Map.Entry jacketColor = Map.entry("jacket-color.properties", "color=black"); + Map.Entry jacketSeason = Map.entry("jacket-season.properties", "season=summer"); + Map map = Map.ofEntries(one, jacket, jacketFit, jacketColor, jacketSeason); + + MockEnvironment env = new MockEnvironment(); + env.setProperty("spring.application.name", "jacket"); + env.setActiveProfiles("fit", "color"); + Map result = SourceDataEntriesProcessor.processAllEntries(map, env); + + Assertions.assertEquals(4, result.size()); + Assertions.assertEquals("1", result.get("one")); + Assertions.assertEquals("jacket", result.get("name")); + Assertions.assertEquals("sport", result.get("fit")); + Assertions.assertEquals("black", result.get("color")); + } + +} diff --git a/spring-cloud-kubernetes-fabric8-config/src/test/java/org/springframework/cloud/kubernetes/fabric8/config/single_source_multiple_files/SingleSourceMultipleFilesApp.java b/spring-cloud-kubernetes-fabric8-config/src/test/java/org/springframework/cloud/kubernetes/fabric8/config/single_source_multiple_files/SingleSourceMultipleFilesApp.java new file mode 100644 index 00000000..dae74a94 --- /dev/null +++ b/spring-cloud-kubernetes-fabric8-config/src/test/java/org/springframework/cloud/kubernetes/fabric8/config/single_source_multiple_files/SingleSourceMultipleFilesApp.java @@ -0,0 +1,35 @@ +/* + * Copyright 2013-2022 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.cloud.kubernetes.fabric8.config.single_source_multiple_files; + +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.single_source_multiple_files.properties.Color; +import org.springframework.cloud.kubernetes.fabric8.config.single_source_multiple_files.properties.Name; +import org.springframework.cloud.kubernetes.fabric8.config.single_source_multiple_files.properties.Shape; +import org.springframework.cloud.kubernetes.fabric8.config.single_source_multiple_files.properties.Type; + +@SpringBootApplication +@EnableConfigurationProperties({ Name.class, Shape.class, Color.class, Type.class }) +public class SingleSourceMultipleFilesApp { + + public static void main(String[] args) { + SpringApplication.run(SingleSourceMultipleFilesApp.class, args); + } + +} diff --git a/spring-cloud-kubernetes-fabric8-config/src/test/java/org/springframework/cloud/kubernetes/fabric8/config/single_source_multiple_files/SingleSourceMultipleFilesBootstrapTests.java b/spring-cloud-kubernetes-fabric8-config/src/test/java/org/springframework/cloud/kubernetes/fabric8/config/single_source_multiple_files/SingleSourceMultipleFilesBootstrapTests.java new file mode 100644 index 00000000..38f3d393 --- /dev/null +++ b/spring-cloud-kubernetes-fabric8-config/src/test/java/org/springframework/cloud/kubernetes/fabric8/config/single_source_multiple_files/SingleSourceMultipleFilesBootstrapTests.java @@ -0,0 +1,44 @@ +/* + * Copyright 2013-2022 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.cloud.kubernetes.fabric8.config.single_source_multiple_files; + +import io.fabric8.kubernetes.client.KubernetesClient; +import io.fabric8.kubernetes.client.server.mock.EnableKubernetesMockClient; +import org.junit.jupiter.api.BeforeAll; + +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.test.context.ActiveProfiles; + +/** + * @author wind57 + */ +@ActiveProfiles("color") +@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, + classes = SingleSourceMultipleFilesApp.class, + properties = { "spring.cloud.bootstrap.name=single-source-multiple-files", + "spring.main.cloud-platform=KUBERNETES", "spring.cloud.bootstrap.enabled=true" }) +@EnableKubernetesMockClient(crud = true, https = false) +class SingleSourceMultipleFilesBootstrapTests extends SingleSourceMultipleFilesTests { + + private static KubernetesClient mockClient; + + @BeforeAll + static void setUpBeforeClass() { + setUpBeforeClass(mockClient); + } + +} diff --git a/spring-cloud-kubernetes-fabric8-config/src/test/java/org/springframework/cloud/kubernetes/fabric8/config/single_source_multiple_files/SingleSourceMultipleFilesConfigDataTests.java b/spring-cloud-kubernetes-fabric8-config/src/test/java/org/springframework/cloud/kubernetes/fabric8/config/single_source_multiple_files/SingleSourceMultipleFilesConfigDataTests.java new file mode 100644 index 00000000..6462948c --- /dev/null +++ b/spring-cloud-kubernetes-fabric8-config/src/test/java/org/springframework/cloud/kubernetes/fabric8/config/single_source_multiple_files/SingleSourceMultipleFilesConfigDataTests.java @@ -0,0 +1,44 @@ +/* + * Copyright 2013-2022 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.cloud.kubernetes.fabric8.config.single_source_multiple_files; + +import io.fabric8.kubernetes.client.KubernetesClient; +import io.fabric8.kubernetes.client.server.mock.EnableKubernetesMockClient; +import org.junit.jupiter.api.BeforeAll; + +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.test.context.ActiveProfiles; + +/** + * @author wind57 + */ +@ActiveProfiles("color") +@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, + classes = SingleSourceMultipleFilesApp.class, + properties = { "spring.main.cloud-platform=KUBERNETES", + "spring.config.import=kubernetes:,classpath:./single-source-multiple-files.yaml" }) +@EnableKubernetesMockClient(crud = true, https = false) +class SingleSourceMultipleFilesConfigDataTests extends SingleSourceMultipleFilesTests { + + private static KubernetesClient mockClient; + + @BeforeAll + static void setUpBeforeClass() { + setUpBeforeClass(mockClient); + } + +} diff --git a/spring-cloud-kubernetes-fabric8-config/src/test/java/org/springframework/cloud/kubernetes/fabric8/config/single_source_multiple_files/SingleSourceMultipleFilesTests.java b/spring-cloud-kubernetes-fabric8-config/src/test/java/org/springframework/cloud/kubernetes/fabric8/config/single_source_multiple_files/SingleSourceMultipleFilesTests.java new file mode 100644 index 00000000..552a9d5b --- /dev/null +++ b/spring-cloud-kubernetes-fabric8-config/src/test/java/org/springframework/cloud/kubernetes/fabric8/config/single_source_multiple_files/SingleSourceMultipleFilesTests.java @@ -0,0 +1,115 @@ +/* + * Copyright 2013-2022 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.cloud.kubernetes.fabric8.config.single_source_multiple_files; + +import java.util.HashMap; +import java.util.Map; + +import io.fabric8.kubernetes.api.model.ConfigMapBuilder; +import io.fabric8.kubernetes.client.Config; +import io.fabric8.kubernetes.client.KubernetesClient; +import org.hamcrest.Matchers; +import org.junit.jupiter.api.Test; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.test.web.reactive.server.WebTestClient; + +/** + * @author wind57 + * + * issue: https://github.com/spring-cloud/spring-cloud-kubernetes/issues/640 + */ +abstract class SingleSourceMultipleFilesTests { + + private static KubernetesClient mockClient; + + @Autowired + private WebTestClient webClient; + + static void setUpBeforeClass(KubernetesClient mockClient) { + SingleSourceMultipleFilesTests.mockClient = mockClient; + // 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("fruit.type", "yummy"); + one.put("fruit.properties", "cool.name=banana"); + one.put("fruit-color.properties", "color.when.raw=green\ncolor.when.ripe=yellow"); + + // this is not taken, since "shape" is not an active profile + one.put("fruit-shape.properties", "shape.when.raw=small-sphere\nshape.when.ripe=bigger-sphere"); + createConfigmap(one); + + } + + static void createConfigmap(Map data) { + mockClient.configMaps().inNamespace("spring-k8s").create(new ConfigMapBuilder().withNewMetadata() + .withName("my-configmap").endMetadata().addToData(data).build()); + } + + /** + *
+	 *   "fruit-color.properties" is taken since "spring.application.name=fruit" and
+	 *   "color" is an active profile
+	 * 
+ */ + @Test + void color() { + this.webClient.get().uri("/single_source-multiple-files/color").exchange().expectStatus().isOk() + .expectBody(String.class).value(Matchers.equalTo("raw:green###ripe:yellow")); + } + + /** + *
+	 *   "fruit.properties" is read, since it matches "spring.application.name"
+	 * 
+ */ + @Test + void name() { + this.webClient.get().uri("/single_source-multiple-files/name").exchange().expectStatus().isOk() + .expectBody(String.class).value(Matchers.equalTo("banana")); + } + + /** + *
+	 *   shape profile is not active, thus property "fruit-shape.properties" is skipped
+	 *   and as such, a null comes here.
+	 * 
+ */ + @Test + void shape() { + this.webClient.get().uri("/single_source-multiple-files/shape").exchange().expectStatus().isOk() + .expectBody(String.class).value(Matchers.nullValue()); + } + + /** + *
+	 *   this is a non-file property in the configmap
+	 * 
+ */ + @Test + void type() { + this.webClient.get().uri("/single_source-multiple-files/type").exchange().expectStatus().isOk() + .expectBody(String.class).value(Matchers.equalTo("yummy")); + } + +} diff --git a/spring-cloud-kubernetes-fabric8-config/src/test/java/org/springframework/cloud/kubernetes/fabric8/config/single_source_multiple_files/controller/SingleSourceMultipleFilesController.java b/spring-cloud-kubernetes-fabric8-config/src/test/java/org/springframework/cloud/kubernetes/fabric8/config/single_source_multiple_files/controller/SingleSourceMultipleFilesController.java new file mode 100644 index 00000000..1ff8f6c2 --- /dev/null +++ b/spring-cloud-kubernetes-fabric8-config/src/test/java/org/springframework/cloud/kubernetes/fabric8/config/single_source_multiple_files/controller/SingleSourceMultipleFilesController.java @@ -0,0 +1,64 @@ +/* + * 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.single_source_multiple_files.controller; + +import org.springframework.cloud.kubernetes.fabric8.config.single_source_multiple_files.properties.Color; +import org.springframework.cloud.kubernetes.fabric8.config.single_source_multiple_files.properties.Name; +import org.springframework.cloud.kubernetes.fabric8.config.single_source_multiple_files.properties.Shape; +import org.springframework.cloud.kubernetes.fabric8.config.single_source_multiple_files.properties.Type; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RestController; + +@RestController +public class SingleSourceMultipleFilesController { + + private final Name name; + + private final Shape shape; + + private final Color color; + + private final Type type; + + public SingleSourceMultipleFilesController(Name name, Shape shape, Color color, Type type) { + this.name = name; + this.shape = shape; + this.color = color; + this.type = type; + } + + @GetMapping("/single_source-multiple-files/type") + public String type() { + return type.getType(); + } + + @GetMapping("/single_source-multiple-files/shape") + public String shape() { + return shape.getRaw(); + } + + @GetMapping("/single_source-multiple-files/color") + public String color() { + return "raw:" + color.getRaw() + "###" + "ripe:" + color.getRipe(); + } + + @GetMapping("/single_source-multiple-files/name") + public String name() { + return name.getName(); + } + +} diff --git a/spring-cloud-kubernetes-fabric8-config/src/test/java/org/springframework/cloud/kubernetes/fabric8/config/single_source_multiple_files/properties/Color.java b/spring-cloud-kubernetes-fabric8-config/src/test/java/org/springframework/cloud/kubernetes/fabric8/config/single_source_multiple_files/properties/Color.java new file mode 100644 index 00000000..34cf51dd --- /dev/null +++ b/spring-cloud-kubernetes-fabric8-config/src/test/java/org/springframework/cloud/kubernetes/fabric8/config/single_source_multiple_files/properties/Color.java @@ -0,0 +1,44 @@ +/* + * 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.single_source_multiple_files.properties; + +import org.springframework.boot.context.properties.ConfigurationProperties; + +@ConfigurationProperties(prefix = "color.when") +public class Color { + + private String raw; + + private String ripe; + + public String getRaw() { + return raw; + } + + public void setRaw(String raw) { + this.raw = raw; + } + + public String getRipe() { + return ripe; + } + + public void setRipe(String ripe) { + this.ripe = ripe; + } + +} diff --git a/spring-cloud-kubernetes-fabric8-config/src/test/java/org/springframework/cloud/kubernetes/fabric8/config/single_source_multiple_files/properties/Name.java b/spring-cloud-kubernetes-fabric8-config/src/test/java/org/springframework/cloud/kubernetes/fabric8/config/single_source_multiple_files/properties/Name.java new file mode 100644 index 00000000..e19b512f --- /dev/null +++ b/spring-cloud-kubernetes-fabric8-config/src/test/java/org/springframework/cloud/kubernetes/fabric8/config/single_source_multiple_files/properties/Name.java @@ -0,0 +1,34 @@ +/* + * Copyright 2013-2022 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.cloud.kubernetes.fabric8.config.single_source_multiple_files.properties; + +import org.springframework.boot.context.properties.ConfigurationProperties; + +@ConfigurationProperties("cool") +public class Name { + + private String name; + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + +} diff --git a/spring-cloud-kubernetes-fabric8-config/src/test/java/org/springframework/cloud/kubernetes/fabric8/config/single_source_multiple_files/properties/Shape.java b/spring-cloud-kubernetes-fabric8-config/src/test/java/org/springframework/cloud/kubernetes/fabric8/config/single_source_multiple_files/properties/Shape.java new file mode 100644 index 00000000..cda2835a --- /dev/null +++ b/spring-cloud-kubernetes-fabric8-config/src/test/java/org/springframework/cloud/kubernetes/fabric8/config/single_source_multiple_files/properties/Shape.java @@ -0,0 +1,44 @@ +/* + * Copyright 2013-2022 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.cloud.kubernetes.fabric8.config.single_source_multiple_files.properties; + +import org.springframework.boot.context.properties.ConfigurationProperties; + +@ConfigurationProperties("shape.when") +public class Shape { + + private String raw; + + private String ripe; + + public String getRaw() { + return raw; + } + + public void setRaw(String raw) { + this.raw = raw; + } + + public String getRipe() { + return ripe; + } + + public void setRipe(String ripe) { + this.ripe = ripe; + } + +} diff --git a/spring-cloud-kubernetes-fabric8-config/src/test/java/org/springframework/cloud/kubernetes/fabric8/config/single_source_multiple_files/properties/Type.java b/spring-cloud-kubernetes-fabric8-config/src/test/java/org/springframework/cloud/kubernetes/fabric8/config/single_source_multiple_files/properties/Type.java new file mode 100644 index 00000000..7704e5a7 --- /dev/null +++ b/spring-cloud-kubernetes-fabric8-config/src/test/java/org/springframework/cloud/kubernetes/fabric8/config/single_source_multiple_files/properties/Type.java @@ -0,0 +1,34 @@ +/* + * Copyright 2013-2022 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.cloud.kubernetes.fabric8.config.single_source_multiple_files.properties; + +import org.springframework.boot.context.properties.ConfigurationProperties; + +@ConfigurationProperties("fruit") +public class Type { + + private String type; + + public String getType() { + return type; + } + + public void setType(String type) { + this.type = type; + } + +} diff --git a/spring-cloud-kubernetes-fabric8-config/src/test/resources/single-source-multiple-files.yaml b/spring-cloud-kubernetes-fabric8-config/src/test/resources/single-source-multiple-files.yaml new file mode 100644 index 00000000..571e90a0 --- /dev/null +++ b/spring-cloud-kubernetes-fabric8-config/src/test/resources/single-source-multiple-files.yaml @@ -0,0 +1,9 @@ +spring: + application: + name: fruit + cloud: + kubernetes: + config: + namespace: spring-k8s + sources: + - name: my-configmap diff --git a/spring-cloud-kubernetes-fabric8-leader/src/test/java/org/springframework/cloud/kubernetes/fabric8/leader/LeaderInitiatorTest.java b/spring-cloud-kubernetes-fabric8-leader/src/test/java/org/springframework/cloud/kubernetes/fabric8/leader/LeaderInitiatorTest.java index e54e7e1c..8388de25 100644 --- a/spring-cloud-kubernetes-fabric8-leader/src/test/java/org/springframework/cloud/kubernetes/fabric8/leader/LeaderInitiatorTest.java +++ b/spring-cloud-kubernetes-fabric8-leader/src/test/java/org/springframework/cloud/kubernetes/fabric8/leader/LeaderInitiatorTest.java @@ -83,7 +83,9 @@ public class LeaderInitiatorTest { assertThat(this.leaderInitiator.isRunning()).isTrue(); verify(this.mockFabric8LeaderRecordWatcher).start(); verify(this.mockFabric8PodReadinessWatcher).start(); - Thread.sleep(10); + + // TODO this tests needs to be reviewed not to use sleep + Thread.sleep(1000); verify(this.mockFabric8LeadershipController, atLeastOnce()).update(); }