Merge branch '2.0.x' into main

This commit is contained in:
Ryan Baxter
2021-08-18 07:30:28 -04:00
16 changed files with 830 additions and 15 deletions

View File

@@ -268,6 +268,135 @@ spec:
----
====
You could run into a situation where there are multiple configs maps that have the same property names. For example:
====
[source,yaml]
----
kind: ConfigMap
apiVersion: v1
metadata:
name: config-map-one
data:
application.yml: |-
greeting:
message: Say Hello from one
----
====
and
====
[source,yaml]
----
kind: ConfigMap
apiVersion: v1
metadata:
name: config-map-two
data:
application.yml: |-
greeting:
message: Say Hello from two
----
====
Depending on the order in which you place these in `bootstrap.yaml|properties`, you might end up with an un-expected result (the last config map wins). For example:
====
[source,yaml]
----
spring:
application:
name: cloud-k8s-app
cloud:
kubernetes:
config:
namespace: default-namespace
sources:
- name: config-map-two
- name: config-map-one
----
====
will result in property `greetings.message` being `Say Hello from one`.
There is a way to change this default configuration by specifying `useNameAsPrefix`. For example:
====
[source,yaml]
----
spring:
application:
name: with-prefix
cloud:
kubernetes:
config:
useNameAsPrefix: true
namespace: default-namespace
sources:
- name: config-map-one
useNameAsPrefix: false
- name: config-map-two
----
====
Such a configuration will result in two properties being generated:
- `greetings.message` equal to `Say Hello from one`.
- `config-map-two.greetings.message` equal to `Say Hello from two`
Notice that `spring.cloud.kubernetes.config.useNameAsPrefix` has a _lower_ priority than `spring.cloud.kubernetes.config.sources.useNameAsPrefix`.
This allows you to set a "default" strategy for all sources, at the same time allowing to override only a few.
If using the config map name is not an option, you can specify a different strategy, called : `explicitPrefix`. Since this is an _explicit_ prefix that
you select, it can only be supplied to the `sources` level. At the same time it has a higher priority than `useNameASPrefix`. Let's suppose we have a third config map with these entries:
====
[source,yaml]
----
kind: ConfigMap
apiVersion: v1
metadata:
name: config-map-three
data:
application.yml: |-
greeting:
message: Say Hello from three
----
====
A configuration like the one below:
====
[source,yaml]
----
spring:
application:
name: with-prefix
cloud:
kubernetes:
config:
useNameAsPrefix: true
namespace: default-namespace
sources:
- name: config-map-one
useNameAsPrefix: false
- name: config-map-two
explicitPrefix: two
- name: config-map-three
----
====
will result in three properties being generated:
- `greetings.message` equal to `Say Hello from one`.
- `two.greetings.message` equal to `Say Hello from two`.
- `config-map-three.greetings.message` equal to `Say Hello from three`.
NOTE: You should check the security configuration section. To access config maps from inside a pod you need to have the correct
Kubernetes service accounts, roles and role bindings.

View File

@@ -30,6 +30,7 @@ import org.apache.commons.logging.LogFactory;
import org.springframework.cloud.kubernetes.commons.config.ConfigMapPropertySource;
import org.springframework.core.env.Environment;
import org.springframework.util.CollectionUtils;
/**
* @author Ryan Baxter
@@ -39,12 +40,12 @@ public class KubernetesClientConfigMapPropertySource extends ConfigMapPropertySo
private static final Log LOG = LogFactory.getLog(KubernetesClientConfigMapPropertySource.class);
public KubernetesClientConfigMapPropertySource(CoreV1Api coreV1Api, String name, String namespace,
Environment environment) {
super(getName(name, namespace), getData(coreV1Api, name, namespace, environment));
Environment environment, String prefix) {
super(getName(name, namespace), getData(coreV1Api, name, namespace, environment, prefix));
}
private static Map<String, Object> getData(CoreV1Api coreV1Api, String name, String namespace,
Environment environment) {
Environment environment, String prefix) {
try {
Set<String> names = new HashSet<>();
@@ -60,6 +61,12 @@ public class KubernetesClientConfigMapPropertySource extends ConfigMapPropertySo
.map(map -> processAllEntries(map.getData(), environment)).collect(Collectors.toList())
.forEach(result::putAll);
if (!"".equals(prefix)) {
Map<String, Object> withPrefix = CollectionUtils.newHashMap(result.size());
result.forEach((key, value) -> withPrefix.put(prefix + "." + key, value));
return withPrefix;
}
return result;
}
catch (ApiException e) {

View File

@@ -59,7 +59,7 @@ public class KubernetesClientConfigMapPropertySourceLocator extends ConfigMapPro
String fallbackNamespace = kubernetesNamespaceProvider != null ? kubernetesNamespaceProvider.getNamespace()
: kubernetesClientProperties.getNamespace();
return new KubernetesClientConfigMapPropertySource(coreV1Api, name,
getNamespace(normalizedSource, fallbackNamespace), environment);
getNamespace(normalizedSource, fallbackNamespace), environment, normalizedSource.getPrefix());
}
}

View File

@@ -97,7 +97,7 @@ class KubernetesClientConfigMapPropertySourceTests {
stubFor(get(API)
.willReturn(aResponse().withStatus(200).withBody(new JSON().serialize(PROPERTIES_CONFIGMAP_LIST))));
KubernetesClientConfigMapPropertySource propertySource = new KubernetesClientConfigMapPropertySource(api,
"bootstrap-640", "default", new MockEnvironment());
"bootstrap-640", "default", new MockEnvironment(), "");
verify(getRequestedFor(urlEqualTo(API)));
assertThat(propertySource.containsProperty("spring.cloud.kubernetes.configuration.watcher.refreshDelay"))
.isTrue();
@@ -113,7 +113,7 @@ class KubernetesClientConfigMapPropertySourceTests {
CoreV1Api api = new CoreV1Api();
stubFor(get(API).willReturn(aResponse().withStatus(200).withBody(new JSON().serialize(YAML_CONFIGMAP_LIST))));
KubernetesClientConfigMapPropertySource propertySource = new KubernetesClientConfigMapPropertySource(api,
"bootstrap-641", "default", new MockEnvironment());
"bootstrap-641", "default", new MockEnvironment(), "");
verify(getRequestedFor(urlEqualTo(API)));
assertThat(propertySource.containsProperty("dummy.property.string2")).isTrue();
assertThat(propertySource.getProperty("dummy.property.string2")).isEqualTo("a");
@@ -124,4 +124,22 @@ class KubernetesClientConfigMapPropertySourceTests {
}
@Test
public void propertiesFileWithPrefix() {
CoreV1Api api = new CoreV1Api();
stubFor(get(API)
.willReturn(aResponse().withStatus(200).withBody(new JSON().serialize(PROPERTIES_CONFIGMAP_LIST))));
KubernetesClientConfigMapPropertySource propertySource = new KubernetesClientConfigMapPropertySource(api,
"bootstrap-640", "default", new MockEnvironment(), "prefix");
verify(getRequestedFor(urlEqualTo(API)));
assertThat(propertySource.containsProperty("prefix.spring.cloud.kubernetes.configuration.watcher.refreshDelay"))
.isTrue();
assertThat(propertySource.getProperty("prefix.spring.cloud.kubernetes.configuration.watcher.refreshDelay"))
.isEqualTo("0");
assertThat(propertySource.containsProperty("prefix.logging.level.org.springframework.cloud.kubernetes"))
.isTrue();
assertThat(propertySource.getProperty("prefix.logging.level.org.springframework.cloud.kubernetes"))
.isEqualTo("TRACE");
}
}

View File

@@ -29,6 +29,8 @@ public abstract class AbstractConfigProperties {
protected String namespace;
protected boolean useNameAsPrefix;
public abstract String getConfigurationTarget();
public boolean isEnabled() {
@@ -55,4 +57,12 @@ public abstract class AbstractConfigProperties {
this.namespace = namespace;
}
public boolean isUseNameAsPrefix() {
return useNameAsPrefix;
}
public void setUseNameAsPrefix(boolean useNameAsPrefix) {
this.useNameAsPrefix = useNameAsPrefix;
}
}

View File

@@ -21,6 +21,9 @@ import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.util.StringUtils;
@@ -32,6 +35,8 @@ import org.springframework.util.StringUtils;
@ConfigurationProperties("spring.cloud.kubernetes.config")
public class ConfigMapConfigProperties extends AbstractConfigProperties {
private static final Log LOG = LogFactory.getLog(ConfigMapConfigProperties.class);
private boolean enableApi = true;
private List<String> paths = Collections.emptyList();
@@ -72,10 +77,16 @@ public class ConfigMapConfigProperties extends AbstractConfigProperties {
*/
public List<NormalizedSource> determineSources() {
if (this.sources.isEmpty()) {
return Collections.singletonList(new NormalizedSource(name, namespace));
if (useNameAsPrefix) {
LOG.warn(
"'spring.cloud.kubernetes.config.useNameAsPrefix' is set to 'true', but 'spring.cloud.kubernetes.config.sources'"
+ " is empty; as such will default 'useNameAsPrefix' to 'false'");
}
return Collections.singletonList(new NormalizedSource(name, namespace, ""));
}
return sources.stream().map(s -> s.normalize(name, namespace)).collect(Collectors.toList());
return sources.stream().map(s -> s.normalize(name, namespace, useNameAsPrefix, s.getExplicitPrefix()))
.collect(Collectors.toList());
}
@Override
@@ -98,6 +109,17 @@ public class ConfigMapConfigProperties extends AbstractConfigProperties {
*/
private String namespace;
/**
* Use config map name as prefix for properties. Can't be a primitive, we need to
* know if it was explicitly set or not
*/
private Boolean useNameAsPrefix;
/**
* An explicit prefix to be used for properties.
*/
private String explicitPrefix;
public Source() {
}
@@ -123,14 +145,60 @@ public class ConfigMapConfigProperties extends AbstractConfigProperties {
this.namespace = namespace;
}
public Boolean isUseNameAsPrefix() {
return useNameAsPrefix;
}
public void setUseNameAsPrefix(Boolean useNameAsPrefix) {
this.useNameAsPrefix = useNameAsPrefix;
}
public String getExplicitPrefix() {
return explicitPrefix;
}
public void setExplicitPrefix(String explicitPrefix) {
this.explicitPrefix = explicitPrefix;
}
public boolean isEmpty() {
return !StringUtils.hasLength(this.name) && !StringUtils.hasLength(this.namespace);
}
// not used, but not removed because of potential compatibility reasons
@Deprecated
public NormalizedSource normalize(String defaultName, String defaultNamespace) {
String normalizedName = StringUtils.hasLength(this.name) ? this.name : defaultName;
String normalizedNamespace = StringUtils.hasLength(this.namespace) ? this.namespace : defaultNamespace;
return new NormalizedSource(normalizedName, normalizedNamespace);
return new NormalizedSource(normalizedName, normalizedNamespace, "");
}
public NormalizedSource normalize(String defaultName, String defaultNamespace, boolean defaultUseNameAsPrefix,
String explicitPrefix) {
String normalizedName = StringUtils.hasLength(this.name) ? this.name : defaultName;
String normalizedNamespace = StringUtils.hasLength(this.namespace) ? this.namespace : defaultNamespace;
// if explicitPrefix is set, it takes priority over useNameAsPrefix
// (either the one from 'spring.cloud.kubernetes.config' or
// 'spring.cloud.kubernetes.config.sources')
if (StringUtils.hasText(explicitPrefix)) {
return new NormalizedSource(normalizedName, normalizedNamespace, explicitPrefix);
}
// useNameAsPrefix is a java.lang.Boolean and if it's != null, users have
// specified it explicitly
if (this.useNameAsPrefix != null) {
if (useNameAsPrefix) {
return new NormalizedSource(normalizedName, normalizedNamespace, normalizedName);
}
return new NormalizedSource(normalizedName, normalizedNamespace, "");
}
if (defaultUseNameAsPrefix) {
return new NormalizedSource(normalizedName, normalizedNamespace, normalizedName);
}
return new NormalizedSource(normalizedName, normalizedNamespace, "");
}
@Override
@@ -158,9 +226,20 @@ public class ConfigMapConfigProperties extends AbstractConfigProperties {
private final String namespace;
private final String prefix;
// not used, but not removed because of potential compatibility reasons
@Deprecated
NormalizedSource(String name, String namespace) {
this.name = name;
this.namespace = namespace;
this.prefix = "";
}
NormalizedSource(String name, String namespace, String prefix) {
this.name = name;
this.namespace = namespace;
this.prefix = Objects.requireNonNull(prefix);
}
public String getName() {
@@ -171,9 +250,13 @@ public class ConfigMapConfigProperties extends AbstractConfigProperties {
return this.namespace;
}
public String getPrefix() {
return prefix;
}
@Override
public String toString() {
return "{ config-map name : '" + name + "', namespace : '" + namespace + "' }";
return "{ config-map name : '" + name + "', namespace : '" + namespace + "', prefix : '" + prefix + "' }";
}
@Override

View File

@@ -0,0 +1,221 @@
/*
* 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.commons.config;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
/**
* @author wind57
*/
public class ConfigMapConfigPropertiesTests {
/**
* <pre>
* spring:
* cloud:
* kubernetes:
* config:
* name: config-map-a
* namespace: spring-k8s
* </pre>
*
* a config as above will result in a NormalizedSource where prefix is empty
*/
@Test
public void testUseNameAsPrefixUnsetEmptySources() {
ConfigMapConfigProperties properties = new ConfigMapConfigProperties();
properties.setSources(Collections.emptyList());
properties.setName("config-map-a");
properties.setNamespace("spring-k8s");
List<ConfigMapConfigProperties.NormalizedSource> sources = properties.determineSources();
Assertions.assertEquals(sources.size(), 1, "empty sources must generate a List with a single NormalizedSource");
Assertions.assertEquals(sources.get(0).getPrefix(), "",
"empty sources must generate a List with a single NormalizedSource, where prefix is empty");
}
/**
* <pre>
* spring:
* cloud:
* kubernetes:
* config:
* useNameAsPrefix: true
* name: config-map-a
* namespace: spring-k8s
* </pre>
*
* a config as above will result in a NormalizedSource where prefix is empty, even if
* "useNameAsPrefix: true", because sources are empty
*/
@Test
public void testUseNameAsPrefixSetEmptySources() {
ConfigMapConfigProperties properties = new ConfigMapConfigProperties();
properties.setSources(Collections.emptyList());
properties.setUseNameAsPrefix(true);
properties.setName("config-map-a");
properties.setNamespace("spring-k8s");
List<ConfigMapConfigProperties.NormalizedSource> sources = properties.determineSources();
Assertions.assertEquals(sources.size(), 1, "empty sources must generate a List with a single NormalizedSource");
Assertions.assertEquals(sources.get(0).getPrefix(), "",
"empty sources must generate a List with a single NormalizedSource, where prefix is empty,"
+ "no matter of 'spring.cloud.kubernetes.config.useNameAsPrefix' value");
}
/**
* <pre>
* spring:
* cloud:
* kubernetes:
* config:
* useNameAsPrefix: true
* namespace: spring-k8s
* sources:
* - name: config-map-one
* </pre>
*
* a config as above will result in a NormalizedSource where prefix will be equal to
* the config map name
*/
@Test
public void testUseNameAsPrefixUnsetNonEmptySources() {
ConfigMapConfigProperties properties = new ConfigMapConfigProperties();
properties.setUseNameAsPrefix(true);
properties.setNamespace("spring-k8s");
ConfigMapConfigProperties.Source one = new ConfigMapConfigProperties.Source();
one.setName("config-map-one");
properties.setSources(Collections.singletonList(one));
List<ConfigMapConfigProperties.NormalizedSource> sources = properties.determineSources();
Assertions.assertEquals(sources.size(), 1, "a single NormalizedSource is expected");
Assertions.assertEquals(sources.get(0).getPrefix(), "config-map-one");
}
/**
* <pre>
* spring:
* cloud:
* kubernetes:
* config:
* useNameAsPrefix: true
* namespace: spring-k8s
* sources:
* - name: config-map-one
* useNameAsPrefix: false
* - name: config-map-two
* useNameAsPrefix: true
* - name: config-map-three
* </pre>
*
* this test proves that 'spring.cloud.kubernetes.config.sources[].useNameAsPrefix'
* will override 'spring.cloud.kubernetes.config.useNameAsPrefix'. For the last entry
* in sources, since there is no explicit 'useNameAsPrefix', the one from
* 'spring.cloud.kubernetes.config.useNameAsPrefix' will be taken.
*/
@Test
public void testUseNameAsPrefixSetNonEmptySources() {
ConfigMapConfigProperties properties = new ConfigMapConfigProperties();
properties.setUseNameAsPrefix(true);
properties.setNamespace("spring-k8s");
ConfigMapConfigProperties.Source one = new ConfigMapConfigProperties.Source();
one.setName("config-map-one");
one.setUseNameAsPrefix(false);
ConfigMapConfigProperties.Source two = new ConfigMapConfigProperties.Source();
two.setName("config-map-two");
two.setUseNameAsPrefix(true);
ConfigMapConfigProperties.Source three = new ConfigMapConfigProperties.Source();
three.setName("config-map-three");
properties.setSources(Arrays.asList(one, two, three));
List<ConfigMapConfigProperties.NormalizedSource> sources = properties.determineSources();
Assertions.assertEquals(sources.size(), 3, "3 NormalizedSources are expected");
Assertions.assertEquals(sources.get(0).getPrefix(), "");
Assertions.assertEquals(sources.get(1).getPrefix(), "config-map-two");
Assertions.assertEquals(sources.get(2).getPrefix(), "config-map-three");
}
/**
* <pre>
* spring:
* cloud:
* kubernetes:
* config:
* useNameAsPrefix: false
* namespace: spring-k8s
* sources:
* - name: config-map-one
* useNameAsPrefix: false
* explicitPrefix: one
* - name: config-map-two
* useNameAsPrefix: true
* explicitPrefix: two
* - name: config-map-three
* explicitPrefix: three
* - name: config-map-four
* </pre>
*
*/
@Test
public void testMultipleCases() {
ConfigMapConfigProperties properties = new ConfigMapConfigProperties();
properties.setUseNameAsPrefix(false);
properties.setNamespace("spring-k8s");
ConfigMapConfigProperties.Source one = new ConfigMapConfigProperties.Source();
one.setNamespace("config-map-one");
one.setUseNameAsPrefix(false);
one.setExplicitPrefix("one");
ConfigMapConfigProperties.Source two = new ConfigMapConfigProperties.Source();
two.setNamespace("config-map-two");
two.setUseNameAsPrefix(true);
two.setExplicitPrefix("two");
ConfigMapConfigProperties.Source three = new ConfigMapConfigProperties.Source();
three.setNamespace("config-map-three");
three.setExplicitPrefix("three");
ConfigMapConfigProperties.Source four = new ConfigMapConfigProperties.Source();
four.setNamespace("config-map-four");
properties.setSources(Arrays.asList(one, two, three, four));
List<ConfigMapConfigProperties.NormalizedSource> sources = properties.determineSources();
Assertions.assertEquals(sources.size(), 4, "4 NormalizedSources are expected");
Assertions.assertEquals(sources.get(0).getPrefix(), "one");
Assertions.assertEquals(sources.get(1).getPrefix(), "two");
Assertions.assertEquals(sources.get(2).getPrefix(), "three");
Assertions.assertEquals(sources.get(3).getPrefix(), "");
}
}

View File

@@ -27,6 +27,7 @@ import org.apache.commons.logging.LogFactory;
import org.springframework.cloud.kubernetes.commons.config.ConfigMapPropertySource;
import org.springframework.core.env.Environment;
import org.springframework.core.env.MapPropertySource;
import org.springframework.util.CollectionUtils;
import static org.springframework.cloud.kubernetes.fabric8.config.Fabric8ConfigUtils.getApplicationNamespace;
import static org.springframework.cloud.kubernetes.fabric8.config.Fabric8ConfigUtils.getConfigMapData;
@@ -43,17 +44,23 @@ public class Fabric8ConfigMapPropertySource extends ConfigMapPropertySource {
private static final Log LOG = LogFactory.getLog(Fabric8ConfigMapPropertySource.class);
public Fabric8ConfigMapPropertySource(KubernetesClient client, String name) {
this(client, name, null, null);
this(client, name, null, null, "");
}
public Fabric8ConfigMapPropertySource(KubernetesClient client, String applicationName, String namespace,
Environment environment) {
super(getName(applicationName, getApplicationNamespace(client, namespace)),
getData(client, applicationName, getApplicationNamespace(client, namespace), environment));
getData(client, applicationName, getApplicationNamespace(client, namespace), environment, ""));
}
public Fabric8ConfigMapPropertySource(KubernetesClient client, String applicationName, String namespace,
Environment environment, String prefix) {
super(getName(applicationName, getApplicationNamespace(client, namespace)),
getData(client, applicationName, getApplicationNamespace(client, namespace), environment, prefix));
}
private static Map<String, Object> getData(KubernetesClient client, String applicationName, String namespace,
Environment environment) {
Environment environment, String prefix) {
try {
Map<String, String> data = getConfigMapData(client, namespace, applicationName);
Map<String, Object> result = new HashMap<>(processAllEntries(data, environment));
@@ -66,11 +73,17 @@ public class Fabric8ConfigMapPropertySource extends ConfigMapPropertySource {
}
}
if (!"".equals(prefix)) {
Map<String, Object> withPrefix = CollectionUtils.newHashMap(result.size());
result.forEach((key, value) -> withPrefix.put(prefix + "." + key, value));
return withPrefix;
}
return result;
}
catch (Exception e) {
LOG.warn("Can't read configMap with name: [" + applicationName + "] in namespace:[" + namespace
LOG.warn("Can't read configMap with name: [" + applicationName + "] in namespace: [" + namespace
+ "]. Ignoring.", e);
}

View File

@@ -49,7 +49,8 @@ public class Fabric8ConfigMapPropertySourceLocator extends ConfigMapPropertySour
String configurationTarget, ConfigurableEnvironment environment) {
String namespaceName = getApplicationNamespace(this.client, normalizedSource.getNamespace(),
configurationTarget);
return new Fabric8ConfigMapPropertySource(this.client, applicationName, namespaceName, environment);
return new Fabric8ConfigMapPropertySource(this.client, applicationName, namespaceName, environment,
normalizedSource.getPrefix());
}
}

View File

@@ -0,0 +1,128 @@
/*
* Copyright 2013-2021 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.cloud.kubernetes.fabric8.config;
import java.util.HashMap;
import java.util.Map;
import io.fabric8.kubernetes.client.Config;
import io.fabric8.kubernetes.client.KubernetesClient;
import io.fabric8.kubernetes.client.server.mock.EnableKubernetesMockClient;
import org.hamcrest.Matchers;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.reactive.AutoConfigureWebTestClient;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.cloud.kubernetes.fabric8.config.with_prefix.WithPrefixApp;
import org.springframework.test.context.junit.jupiter.SpringExtension;
import org.springframework.test.web.reactive.server.WebTestClient;
/**
* @author wind57
*/
@ExtendWith(SpringExtension.class)
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, classes = WithPrefixApp.class,
properties = { "spring.cloud.bootstrap.name=same-key-with-prefix" })
@AutoConfigureWebTestClient
@EnableKubernetesMockClient(crud = true, https = false)
public class ConfigMapWithPrefixTests {
private static KubernetesClient mockClient;
@Autowired
private WebTestClient webClient;
@BeforeAll
public static void setUpBeforeClass() {
// Configure the kubernetes master url to point to the mock server
System.setProperty(Config.KUBERNETES_MASTER_SYSTEM_PROPERTY, mockClient.getConfiguration().getMasterUrl());
System.setProperty(Config.KUBERNETES_TRUST_CERT_SYSTEM_PROPERTY, "true");
System.setProperty(Config.KUBERNETES_AUTH_TRYKUBECONFIG_SYSTEM_PROPERTY, "false");
System.setProperty(Config.KUBERNETES_AUTH_TRYSERVICEACCOUNT_SYSTEM_PROPERTY, "false");
System.setProperty(Config.KUBERNETES_NAMESPACE_SYSTEM_PROPERTY, "test");
System.setProperty(Config.KUBERNETES_HTTP2_DISABLE, "true");
Map<String, String> one = new HashMap<>();
one.put("one.property", "one");
createConfigmap(mockClient, "config-map-one", one);
Map<String, String> two = new HashMap<>();
two.put("property", "two");
createConfigmap(mockClient, "config-map-two", two);
Map<String, String> three = new HashMap<>();
three.put("property", "three");
createConfigmap(mockClient, "config-map-three", three);
}
private static void createConfigmap(KubernetesClient client, String name, Map<String, String> data) {
client.configMaps().inNamespace("spring-k8s").createNew().withNewMetadata().withName(name).endMetadata()
.addToData(data).done();
}
/**
* <pre>
* 'spring.cloud.kubernetes.config.useNameAsPrefix=true'
* 'spring.cloud.kubernetes.config.sources[0].useNameAsPrefix=false'
* ("one.property", "one")
*
* As such: @ConfigurationProperties("one")
* </pre>
*/
@Test
public void testOne() {
this.webClient.get().uri("/one").exchange().expectStatus().isOk().expectBody(String.class)
.value(Matchers.equalTo("one"));
}
/**
* <pre>
* 'spring.cloud.kubernetes.config.useNameAsPrefix=true'
* 'spring.cloud.kubernetes.config.sources[1].explicitPrefix=two'
* ("property", "two")
*
* As such: @ConfigurationProperties("two")
* </pre>
*/
@Test
public void testTwo() {
this.webClient.get().uri("/two").exchange().expectStatus().isOk().expectBody(String.class)
.value(Matchers.equalTo("two"));
}
/**
* <pre>
* 'spring.cloud.kubernetes.config.useNameAsPrefix=true'
* 'spring.cloud.kubernetes.config.sources[2].name=config-map-three'
* ("property", "three")
*
* As such: @ConfigurationProperties(prefix = "config-map-three")
* </pre>
*/
@Test
public void testThree() {
this.webClient.get().uri("/three").exchange().expectStatus().isOk().expectBody(String.class)
.value(Matchers.equalTo("three"));
}
}

View File

@@ -0,0 +1,34 @@
/*
* Copyright 2013-2021 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.cloud.kubernetes.fabric8.config.with_prefix;
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.with_prefix.properties.One;
import org.springframework.cloud.kubernetes.fabric8.config.with_prefix.properties.Three;
import org.springframework.cloud.kubernetes.fabric8.config.with_prefix.properties.Two;
@SpringBootApplication
@EnableConfigurationProperties({ One.class, Two.class, Three.class })
public class WithPrefixApp {
public static void main(String[] args) {
SpringApplication.run(WithPrefixApp.class, args);
}
}

View File

@@ -0,0 +1,55 @@
/*
* Copyright 2013-2021 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.cloud.kubernetes.fabric8.config.with_prefix.controller;
import org.springframework.cloud.kubernetes.fabric8.config.with_prefix.properties.One;
import org.springframework.cloud.kubernetes.fabric8.config.with_prefix.properties.Three;
import org.springframework.cloud.kubernetes.fabric8.config.with_prefix.properties.Two;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class Controller {
private final One one;
private final Two two;
private final Three three;
public Controller(One one, Two two, Three three) {
this.one = one;
this.two = two;
this.three = three;
}
@GetMapping("/one")
public String one() {
return one.getProperty();
}
@GetMapping("/two")
public String two() {
return two.getProperty();
}
@GetMapping("/three")
public String three() {
return three.getProperty();
}
}

View File

@@ -0,0 +1,34 @@
/*
* Copyright 2013-2021 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.cloud.kubernetes.fabric8.config.with_prefix.properties;
import org.springframework.boot.context.properties.ConfigurationProperties;
@ConfigurationProperties("one")
public class One {
private String property;
public String getProperty() {
return property;
}
public void setProperty(String property) {
this.property = property;
}
}

View File

@@ -0,0 +1,34 @@
/*
* Copyright 2013-2021 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.cloud.kubernetes.fabric8.config.with_prefix.properties;
import org.springframework.boot.context.properties.ConfigurationProperties;
@ConfigurationProperties(prefix = "config-map-three")
public class Three {
private String property;
public String getProperty() {
return property;
}
public void setProperty(String property) {
this.property = property;
}
}

View File

@@ -0,0 +1,34 @@
/*
* Copyright 2013-2021 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.cloud.kubernetes.fabric8.config.with_prefix.properties;
import org.springframework.boot.context.properties.ConfigurationProperties;
@ConfigurationProperties("two")
public class Two {
private String property;
public String getProperty() {
return property;
}
public void setProperty(String property) {
this.property = property;
}
}

View File

@@ -0,0 +1,14 @@
spring:
application:
name: with-prefix
cloud:
kubernetes:
config:
useNameAsPrefix: true
namespace: spring-k8s
sources:
- name: config-map-one
useNameAsPrefix: false
- name: config-map-two
explicitPrefix: two
- name: config-map-three