Merge remote-tracking branch 'origin/1.0.x'

This commit is contained in:
Ryan Baxter
2019-06-19 10:58:42 -04:00
4 changed files with 192 additions and 2 deletions

View File

@@ -182,6 +182,59 @@ However, if the `production` profile is active, the configuration becomes:
If both profiles are active, the property that appears last within the `ConfigMap` overwrites any preceding values.
Another option is to create a different config map per profile and spring boot will automatically fetch it based
on active profiles
====
[source,yaml]
----
kind: ConfigMap
apiVersion: v1
metadata:
name: demo
data:
application.yml: |-
greeting:
message: Say Hello to the World
farewell:
message: Say Goodbye
----
====
====
[source,yaml]
----
kind: ConfigMap
apiVersion: v1
metadata:
name: demo-development
data:
application.yml: |-
spring:
profiles: development
greeting:
message: Say Hello to the Developers
farewell:
message: Say Goodbye to the Developers
----
====
====
[source,yaml]
----
kind: ConfigMap
apiVersion: v1
metadata:
name: demo-production
data:
application.yml: |-
spring:
profiles: production
greeting:
message: Say Hello to the Ops
farewell:
message: Say Goodbye
----
====
To tell Spring Boot which `profile` should be enabled at bootstrap, you can pass `SPRING_PROFILES_ACTIVE` environment variable.
To do so, you can launch your Spring Boot application with an environment variable that you can define it in the PodSpec at the container specification.

View File

@@ -40,6 +40,7 @@ import static org.springframework.cloud.kubernetes.config.PropertySourceUtils.ya
* A {@link MapPropertySource} that uses Kubernetes config maps.
*
* @author Ioannis Canellos
* @author Ali Shahbour
*/
public class ConfigMapPropertySource extends MapPropertySource {
@@ -59,7 +60,7 @@ public class ConfigMapPropertySource extends MapPropertySource {
public ConfigMapPropertySource(KubernetesClient client, String name, String namespace,
String[] profiles) {
this(client, name, null, createEnvironmentWithActiveProfiles(profiles));
this(client, name, namespace, createEnvironmentWithActiveProfiles(profiles));
}
private static Environment createEnvironmentWithActiveProfiles(
@@ -88,13 +89,33 @@ public class ConfigMapPropertySource extends MapPropertySource {
private static Map<String, String> getData(KubernetesClient client, String name,
String namespace, Environment environment) {
try {
Map<String, String> result = new HashMap<>();
ConfigMap map = StringUtils.isEmpty(namespace)
? client.configMaps().withName(name).get()
: client.configMaps().inNamespace(namespace).withName(name).get();
if (map != null) {
return processAllEntries(map.getData(), environment);
result.putAll(processAllEntries(map.getData(), environment));
}
if (environment != null) {
for (String activeProfile:environment.getActiveProfiles()) {
String mapNameWithProfile = name + "-" + activeProfile;
ConfigMap mapWithProfile = StringUtils.isEmpty(namespace)
? client.configMaps().withName(mapNameWithProfile).get()
: client.configMaps().inNamespace(namespace).withName(mapNameWithProfile).get();
if (mapWithProfile != null) {
result.putAll(processAllEntries(mapWithProfile.getData(), environment));
}
}
}
return result;
}
catch (Exception e) {
LOG.warn("Can't read configMap with name: [" + name + "] in namespace:["
@@ -104,6 +125,7 @@ public class ConfigMapPropertySource extends MapPropertySource {
return new HashMap<>();
}
private static Map<String, String> processAllEntries(Map<String, String> input,
Environment environment) {

View File

@@ -0,0 +1,111 @@
/*
* Copyright 2013-2019 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
*
* http://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.config;
import java.util.HashMap;
import io.fabric8.kubernetes.api.model.ConfigMapBuilder;
import io.fabric8.kubernetes.client.Config;
import io.fabric8.kubernetes.client.KubernetesClient;
import io.fabric8.kubernetes.client.server.mock.KubernetesServer;
import org.junit.BeforeClass;
import org.junit.ClassRule;
import org.junit.Test;
import org.junit.runner.RunWith;
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.config.example.App;
import org.springframework.test.context.ActiveProfiles;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.test.web.reactive.server.WebTestClient;
import static org.springframework.cloud.kubernetes.config.ConfigMapTestUtil.readResourceFile;
/**
* @author Ali Shahbour
*/
@RunWith(SpringRunner.class)
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, classes = App.class, properties = {
"spring.application.name=configmap-with-active-profile-name-example",
"spring.cloud.kubernetes.reload.enabled=false" })
@ActiveProfiles("development")
@AutoConfigureWebTestClient
public class ConfigMapsWithActiveProfilesNameSpringBootTest {
private static final String APPLICATION_NAME = "configmap-with-active-profile-name-example";
@ClassRule
public static KubernetesServer server = new KubernetesServer();
private static KubernetesClient mockClient;
@Autowired(required = false)
Config config;
@Autowired
private WebTestClient webClient;
@BeforeClass
public static void setUpBeforeClass() {
mockClient = server.getClient();
// 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");
HashMap<String, String> data = new HashMap<>();
data.put("application.yml", readResourceFile("application-with-profiles.yaml"));
server.expect().withPath("/api/v1/namespaces/test/configmaps/" + APPLICATION_NAME)
.andReturn(200, new ConfigMapBuilder().withNewMetadata()
.withName(APPLICATION_NAME).endMetadata().addToData(data).build())
.always();
HashMap<String, String> dataWithName = new HashMap<>();
dataWithName.put("application.yml",
readResourceFile("application-with-active-profiles-name.yaml"));
server.expect()
.withPath("/api/v1/namespaces/test/configmaps/" + APPLICATION_NAME
+ "-development")
.andReturn(200,
new ConfigMapBuilder().withNewMetadata()
.withName(APPLICATION_NAME + "-development").endMetadata()
.addToData(dataWithName).build())
.always();
}
@Test
public void testGreetingEndpoint() {
this.webClient.get().uri("/api/greeting").exchange().expectStatus().isOk()
.expectBody().jsonPath("content")
.isEqualTo("Hello ConfigMap Active Profile Name, World!");
}
@Test
public void testFarewellEndpoint() {
this.webClient.get().uri("/api/farewell").exchange().expectStatus().isOk()
.expectBody().jsonPath("content")
.isEqualTo("Goodbye ConfigMap default, World!");
}
}

View File

@@ -0,0 +1,4 @@
spring:
profiles: development
bean:
greeting: "Hello ConfigMap Active Profile Name, %s!"