Config Server Controller Should Return All PropertySources (#1600)
This commit is contained in:
@@ -27,5 +27,10 @@ import org.springframework.core.env.Environment;
|
||||
* @author wind57
|
||||
*/
|
||||
public record KubernetesClientConfigContext(CoreV1Api client, NormalizedSource normalizedSource, String namespace,
|
||||
Environment environment) {
|
||||
Environment environment, boolean includeDefaultProfileData) {
|
||||
|
||||
public KubernetesClientConfigContext(CoreV1Api client, NormalizedSource normalizedSource, String namespace,
|
||||
Environment environment) {
|
||||
this(client, normalizedSource, namespace, environment, true);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -35,7 +35,7 @@ import org.springframework.core.log.LogAccessor;
|
||||
*
|
||||
* @author wind57
|
||||
*/
|
||||
final class KubernetesClientConfigMapsCache implements ConfigMapCache {
|
||||
public final class KubernetesClientConfigMapsCache implements ConfigMapCache {
|
||||
|
||||
private static final LogAccessor LOG = new LogAccessor(LogFactory.getLog(KubernetesClientConfigMapsCache.class));
|
||||
|
||||
|
||||
@@ -108,12 +108,23 @@ public final class KubernetesClientConfigUtils {
|
||||
* </pre>
|
||||
*/
|
||||
static MultipleSourcesContainer secretsDataByName(CoreV1Api coreV1Api, String namespace,
|
||||
LinkedHashSet<String> sourceNames, Environment environment) {
|
||||
LinkedHashSet<String> sourceNames, Environment environment, boolean includeDefaultProfileData) {
|
||||
List<StrippedSourceContainer> strippedSecrets = strippedSecrets(coreV1Api, namespace);
|
||||
if (strippedSecrets.isEmpty()) {
|
||||
return MultipleSourcesContainer.empty();
|
||||
}
|
||||
return ConfigUtils.processNamedData(strippedSecrets, environment, sourceNames, namespace, DECODE);
|
||||
return ConfigUtils.processNamedData(strippedSecrets, environment, sourceNames, namespace, DECODE,
|
||||
includeDefaultProfileData);
|
||||
}
|
||||
|
||||
static MultipleSourcesContainer secretsDataByName(CoreV1Api coreV1Api, String namespace,
|
||||
LinkedHashSet<String> sourceNames, Environment environment) {
|
||||
return secretsDataByName(coreV1Api, namespace, sourceNames, environment, true);
|
||||
}
|
||||
|
||||
static MultipleSourcesContainer configMapsDataByName(CoreV1Api coreV1Api, String namespace,
|
||||
LinkedHashSet<String> sourceNames, Environment environment) {
|
||||
return configMapsDataByName(coreV1Api, namespace, sourceNames, environment, true);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -125,12 +136,13 @@ public final class KubernetesClientConfigUtils {
|
||||
* </pre>
|
||||
*/
|
||||
static MultipleSourcesContainer configMapsDataByName(CoreV1Api coreV1Api, String namespace,
|
||||
LinkedHashSet<String> sourceNames, Environment environment) {
|
||||
LinkedHashSet<String> sourceNames, Environment environment, boolean includeDefaultProfileData) {
|
||||
List<StrippedSourceContainer> strippedConfigMaps = strippedConfigMaps(coreV1Api, namespace);
|
||||
if (strippedConfigMaps.isEmpty()) {
|
||||
return MultipleSourcesContainer.empty();
|
||||
}
|
||||
return ConfigUtils.processNamedData(strippedConfigMaps, environment, sourceNames, namespace, DECODE);
|
||||
return ConfigUtils.processNamedData(strippedConfigMaps, environment, sourceNames, namespace, DECODE,
|
||||
includeDefaultProfileData);
|
||||
}
|
||||
|
||||
private static List<StrippedSourceContainer> strippedConfigMaps(CoreV1Api coreV1Api, String namespace) {
|
||||
|
||||
@@ -19,6 +19,7 @@ package org.springframework.cloud.kubernetes.client.config;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
import org.springframework.cloud.kubernetes.commons.config.ConfigUtils;
|
||||
import org.springframework.cloud.kubernetes.commons.config.MultipleSourcesContainer;
|
||||
import org.springframework.cloud.kubernetes.commons.config.NamedConfigMapNormalizedSource;
|
||||
import org.springframework.cloud.kubernetes.commons.config.NamedSourceData;
|
||||
@@ -42,10 +43,19 @@ final class NamedConfigMapContextToSourceDataProvider implements Supplier<Kubern
|
||||
NamedConfigMapNormalizedSource source = (NamedConfigMapNormalizedSource) context.normalizedSource();
|
||||
|
||||
return new NamedSourceData() {
|
||||
@Override
|
||||
protected String generateSourceName(String target, String sourceName, String namespace,
|
||||
String[] activeProfiles) {
|
||||
if (source.appendProfileToName()) {
|
||||
return ConfigUtils.sourceName(target, sourceName, namespace, activeProfiles);
|
||||
}
|
||||
return super.generateSourceName(target, sourceName, namespace, activeProfiles);
|
||||
}
|
||||
|
||||
@Override
|
||||
public MultipleSourcesContainer dataSupplier(LinkedHashSet<String> sourceNames) {
|
||||
return KubernetesClientConfigUtils.configMapsDataByName(context.client(), context.namespace(),
|
||||
sourceNames, context.environment());
|
||||
sourceNames, context.environment(), context.includeDefaultProfileData());
|
||||
}
|
||||
}.compute(source.name().orElseThrow(), source.prefix(), source.target(), source.profileSpecificSources(),
|
||||
source.failFast(), context.namespace(), context.environment().getActiveProfiles());
|
||||
|
||||
@@ -19,6 +19,7 @@ package org.springframework.cloud.kubernetes.client.config;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
import org.springframework.cloud.kubernetes.commons.config.ConfigUtils;
|
||||
import org.springframework.cloud.kubernetes.commons.config.MultipleSourcesContainer;
|
||||
import org.springframework.cloud.kubernetes.commons.config.NamedSecretNormalizedSource;
|
||||
import org.springframework.cloud.kubernetes.commons.config.NamedSourceData;
|
||||
@@ -41,10 +42,19 @@ final class NamedSecretContextToSourceDataProvider implements Supplier<Kubernete
|
||||
NamedSecretNormalizedSource source = (NamedSecretNormalizedSource) context.normalizedSource();
|
||||
|
||||
return new NamedSourceData() {
|
||||
@Override
|
||||
protected String generateSourceName(String target, String sourceName, String namespace,
|
||||
String[] activeProfiles) {
|
||||
if (source.appendProfileToName()) {
|
||||
return ConfigUtils.sourceName(target, sourceName, namespace, activeProfiles);
|
||||
}
|
||||
return super.generateSourceName(target, sourceName, namespace, activeProfiles);
|
||||
}
|
||||
|
||||
@Override
|
||||
public MultipleSourcesContainer dataSupplier(LinkedHashSet<String> sourceNames) {
|
||||
return KubernetesClientConfigUtils.secretsDataByName(context.client(), context.namespace(),
|
||||
sourceNames, context.environment());
|
||||
sourceNames, context.environment(), context.includeDefaultProfileData());
|
||||
}
|
||||
}.compute(source.name().orElseThrow(), source.prefix(), source.target(), source.profileSpecificSources(),
|
||||
source.failFast(), context.namespace(), context.environment().getActiveProfiles());
|
||||
|
||||
@@ -164,17 +164,17 @@ class NamedConfigMapContextToSourceDataProviderTests {
|
||||
stubCall(configMapList);
|
||||
CoreV1Api api = new CoreV1Api();
|
||||
|
||||
NormalizedSource source = new NamedConfigMapNormalizedSource(RED_CONFIG_MAP_NAME, NAMESPACE, true, true);
|
||||
NormalizedSource source = new NamedConfigMapNormalizedSource(RED_CONFIG_MAP_NAME, NAMESPACE, true,
|
||||
ConfigUtils.Prefix.DEFAULT, true, true);
|
||||
MockEnvironment environment = new MockEnvironment();
|
||||
environment.setActiveProfiles("with-profile");
|
||||
KubernetesClientConfigContext context = new KubernetesClientConfigContext(api, source, NAMESPACE, environment);
|
||||
KubernetesClientConfigContext context = new KubernetesClientConfigContext(api, source, NAMESPACE, environment, false);
|
||||
|
||||
KubernetesClientContextToSourceData data = new NamedConfigMapContextToSourceDataProvider().get();
|
||||
SourceData sourceData = data.apply(context);
|
||||
|
||||
Assertions.assertEquals(sourceData.sourceName(), "configmap.red.red-with-profile.default");
|
||||
Assertions.assertEquals(sourceData.sourceData().size(), 2);
|
||||
Assertions.assertEquals(sourceData.sourceData().get("color"), "really-red");
|
||||
Assertions.assertEquals(sourceData.sourceName(), "configmap.red.red-with-profile.default.with-profile");
|
||||
Assertions.assertEquals(sourceData.sourceData().size(), 1);
|
||||
Assertions.assertEquals(sourceData.sourceData().get("taste"), "mango");
|
||||
|
||||
}
|
||||
|
||||
@@ -217,17 +217,17 @@ class NamedSecretContextToSourceDataProviderTests {
|
||||
stubCall(secretList);
|
||||
CoreV1Api api = new CoreV1Api();
|
||||
|
||||
NormalizedSource source = new NamedSecretNormalizedSource("red", NAMESPACE, false, true);
|
||||
NormalizedSource source = new NamedSecretNormalizedSource("red", NAMESPACE, false, ConfigUtils.Prefix.DEFAULT,
|
||||
true, true);
|
||||
MockEnvironment environment = new MockEnvironment();
|
||||
environment.addActiveProfile("with-profile");
|
||||
KubernetesClientConfigContext context = new KubernetesClientConfigContext(api, source, NAMESPACE, environment);
|
||||
KubernetesClientConfigContext context = new KubernetesClientConfigContext(api, source, NAMESPACE, environment, false);
|
||||
|
||||
KubernetesClientContextToSourceData data = new NamedSecretContextToSourceDataProvider().get();
|
||||
SourceData sourceData = data.apply(context);
|
||||
|
||||
Assertions.assertEquals(sourceData.sourceName(), "secret.red.red-with-profile.default");
|
||||
Assertions.assertEquals(sourceData.sourceData().size(), 2);
|
||||
Assertions.assertEquals(sourceData.sourceData().get("color"), "really-red");
|
||||
Assertions.assertEquals(sourceData.sourceName(), "secret.red.red-with-profile.default.with-profile");
|
||||
Assertions.assertEquals(sourceData.sourceData().size(), 1);
|
||||
Assertions.assertEquals(sourceData.sourceData().get("taste"), "mango");
|
||||
|
||||
}
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
package org.springframework.cloud.kubernetes.commons.config;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Base64;
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedHashSet;
|
||||
@@ -161,13 +162,27 @@ public final class ConfigUtils {
|
||||
return target + PROPERTY_SOURCE_NAME_SEPARATOR + applicationName + PROPERTY_SOURCE_NAME_SEPARATOR + namespace;
|
||||
}
|
||||
|
||||
public static String sourceName(String target, String applicationName, String namespace, String[] profiles) {
|
||||
String name = sourceName(target, applicationName, namespace);
|
||||
if (profiles != null && profiles.length > 0) {
|
||||
name = name + PROPERTY_SOURCE_NAME_SEPARATOR + StringUtils.arrayToDelimitedString(profiles, "-");
|
||||
}
|
||||
return name;
|
||||
}
|
||||
|
||||
public static MultipleSourcesContainer processNamedData(List<StrippedSourceContainer> strippedSources,
|
||||
Environment environment, LinkedHashSet<String> sourceNames, String namespace, boolean decode) {
|
||||
return processNamedData(strippedSources, environment, sourceNames, namespace, decode, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* transforms raw data from one or multiple sources into an entry of source names and
|
||||
* flattened data that they all hold (potentially overriding entries without any
|
||||
* defined order).
|
||||
*/
|
||||
public static MultipleSourcesContainer processNamedData(List<StrippedSourceContainer> strippedSources,
|
||||
Environment environment, LinkedHashSet<String> sourceNames, String namespace, boolean decode) {
|
||||
Environment environment, LinkedHashSet<String> sourceNames, String namespace, boolean decode,
|
||||
boolean includeDefaultProfileData) {
|
||||
|
||||
Map<String, StrippedSourceContainer> hashByName = strippedSources.stream()
|
||||
.collect(Collectors.toMap(StrippedSourceContainer::name, Function.identity()));
|
||||
@@ -189,14 +204,35 @@ public final class ConfigUtils {
|
||||
if (decode) {
|
||||
rawData = decodeData(rawData);
|
||||
}
|
||||
data.putAll(SourceDataEntriesProcessor.processAllEntries(rawData == null ? Map.of() : rawData,
|
||||
environment));
|
||||
|
||||
// In some cases we want to include properties from the default profile along with any active profiles
|
||||
// In these cases includeDefaultProfileData will be true
|
||||
// If includeDefaultProfileData is false then we want to make sure that we only return properties from any active profiles
|
||||
|
||||
//Check the source to see if it contains any active profiles
|
||||
boolean containsActiveProfile = environment.getActiveProfiles().length == 0
|
||||
|| Arrays.stream(environment.getActiveProfiles())
|
||||
.anyMatch(p -> source.contains("-" + p) || "default".equals(p));
|
||||
if (includeDefaultProfileData || containsActiveProfile
|
||||
|| containsDataWithProfile(rawData, environment.getActiveProfiles())) {
|
||||
data.putAll(SourceDataEntriesProcessor.processAllEntries(rawData == null ? Map.of() : rawData,
|
||||
environment, includeDefaultProfileData));
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
return new MultipleSourcesContainer(foundSourceNames, data);
|
||||
}
|
||||
|
||||
/*
|
||||
* In the case there the data contains yaml or properties files we need to check their names to see if they
|
||||
* contain any active profiles.
|
||||
*/
|
||||
private static boolean containsDataWithProfile(Map<String, String> rawData, String[] activeProfiles) {
|
||||
return rawData.keySet().stream().anyMatch(
|
||||
key -> Arrays.stream(activeProfiles).anyMatch(p -> key.contains("-" + p) || "default".equals(p)));
|
||||
}
|
||||
|
||||
/**
|
||||
* transforms raw data from one or multiple sources into an entry of source names and
|
||||
* flattened data that they all hold (potentially overriding entries without any
|
||||
|
||||
@@ -29,18 +29,25 @@ public final class NamedConfigMapNormalizedSource extends NormalizedSource {
|
||||
|
||||
private final boolean includeProfileSpecificSources;
|
||||
|
||||
private final boolean appendProfileToName;
|
||||
|
||||
public NamedConfigMapNormalizedSource(String name, String namespace, boolean failFast, ConfigUtils.Prefix prefix,
|
||||
boolean includeProfileSpecificSources) {
|
||||
super(name, namespace, failFast);
|
||||
this.prefix = Objects.requireNonNull(prefix);
|
||||
this.includeProfileSpecificSources = includeProfileSpecificSources;
|
||||
this(name, namespace, failFast, prefix, includeProfileSpecificSources, false);
|
||||
}
|
||||
|
||||
public NamedConfigMapNormalizedSource(String name, String namespace, boolean failFast,
|
||||
boolean includeProfileSpecificSources) {
|
||||
this(name, namespace, failFast, ConfigUtils.Prefix.DEFAULT, includeProfileSpecificSources);
|
||||
}
|
||||
|
||||
public NamedConfigMapNormalizedSource(String name, String namespace, boolean failFast, ConfigUtils.Prefix prefix,
|
||||
boolean includeProfileSpecificSources, boolean appendProfileToName) {
|
||||
super(name, namespace, failFast);
|
||||
this.prefix = ConfigUtils.Prefix.DEFAULT;
|
||||
this.prefix = Objects.requireNonNull(prefix);
|
||||
this.includeProfileSpecificSources = includeProfileSpecificSources;
|
||||
this.appendProfileToName = appendProfileToName;
|
||||
|
||||
}
|
||||
|
||||
public ConfigUtils.Prefix prefix() {
|
||||
@@ -51,6 +58,14 @@ public final class NamedConfigMapNormalizedSource extends NormalizedSource {
|
||||
return includeProfileSpecificSources;
|
||||
}
|
||||
|
||||
/**
|
||||
* append or not the active profiles to the name of the generated source.
|
||||
* At the moment this is true only for config server generated sources.
|
||||
*/
|
||||
public boolean appendProfileToName() {
|
||||
return appendProfileToName;
|
||||
}
|
||||
|
||||
@Override
|
||||
public NormalizedSourceType type() {
|
||||
return NormalizedSourceType.NAMED_CONFIG_MAP;
|
||||
|
||||
@@ -29,24 +29,38 @@ public final class NamedSecretNormalizedSource extends NormalizedSource {
|
||||
|
||||
private final boolean includeProfileSpecificSources;
|
||||
|
||||
private final boolean appendProfileToName;
|
||||
|
||||
public NamedSecretNormalizedSource(String name, String namespace, boolean failFast, ConfigUtils.Prefix prefix,
|
||||
boolean includeProfileSpecificSources) {
|
||||
boolean includeProfileSpecificSources, boolean appendProfileToName) {
|
||||
super(name, namespace, failFast);
|
||||
this.prefix = Objects.requireNonNull(prefix);
|
||||
this.includeProfileSpecificSources = includeProfileSpecificSources;
|
||||
this.appendProfileToName = appendProfileToName;
|
||||
}
|
||||
|
||||
public NamedSecretNormalizedSource(String name, String namespace, boolean failFast,
|
||||
boolean includeProfileSpecificSources) {
|
||||
super(name, namespace, failFast);
|
||||
this.prefix = ConfigUtils.Prefix.DEFAULT;
|
||||
this.includeProfileSpecificSources = includeProfileSpecificSources;
|
||||
this(name, namespace, failFast, ConfigUtils.Prefix.DEFAULT, includeProfileSpecificSources, false);
|
||||
}
|
||||
|
||||
public NamedSecretNormalizedSource(String name, String namespace, boolean failFast, ConfigUtils.Prefix prefix,
|
||||
boolean includeProfileSpecificSources) {
|
||||
this(name, namespace, failFast, prefix, includeProfileSpecificSources, false);
|
||||
}
|
||||
|
||||
public boolean profileSpecificSources() {
|
||||
return includeProfileSpecificSources;
|
||||
}
|
||||
|
||||
/**
|
||||
* append or not the active profiles to the name of the generated source.
|
||||
* At the moment this is true only for config server generated sources.
|
||||
*/
|
||||
public boolean appendProfileToName() {
|
||||
return appendProfileToName;
|
||||
}
|
||||
|
||||
public ConfigUtils.Prefix prefix() {
|
||||
return prefix;
|
||||
}
|
||||
|
||||
@@ -67,7 +67,11 @@ public abstract class NamedSourceData {
|
||||
}
|
||||
|
||||
String names = data.names().stream().sorted().collect(Collectors.joining(PROPERTY_SOURCE_NAME_SEPARATOR));
|
||||
return new SourceData(ConfigUtils.sourceName(target, names, namespace), data.data());
|
||||
return new SourceData(generateSourceName(target, names, namespace, activeProfiles), data.data());
|
||||
}
|
||||
|
||||
protected String generateSourceName(String target, String sourceName, String namespace, String[] activeProfiles) {
|
||||
return ConfigUtils.sourceName(target, sourceName, namespace);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -24,7 +24,6 @@ import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
@@ -53,6 +52,11 @@ public class SourceDataEntriesProcessor extends MapPropertySource {
|
||||
}
|
||||
|
||||
public static Map<String, Object> processAllEntries(Map<String, String> input, Environment environment) {
|
||||
return processAllEntries(input, environment, true);
|
||||
}
|
||||
|
||||
public static Map<String, Object> processAllEntries(Map<String, String> input, Environment environment,
|
||||
boolean includeDefaultProfileData) {
|
||||
|
||||
Set<Map.Entry<String, String>> entrySet = input.entrySet();
|
||||
if (entrySet.size() == 1) {
|
||||
@@ -71,7 +75,11 @@ public class SourceDataEntriesProcessor extends MapPropertySource {
|
||||
}
|
||||
}
|
||||
|
||||
return defaultProcessAllEntries(input, environment);
|
||||
return defaultProcessAllEntries(input, environment, includeDefaultProfileData);
|
||||
}
|
||||
|
||||
static List<Map.Entry<String, String>> sorted(Map<String, String> input, Environment environment) {
|
||||
return sorted(input, environment, true);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -84,7 +92,8 @@ public class SourceDataEntriesProcessor extends MapPropertySource {
|
||||
* 3. then plain properties
|
||||
* </pre>
|
||||
*/
|
||||
static List<Map.Entry<String, String>> sorted(Map<String, String> input, Environment environment) {
|
||||
static List<Map.Entry<String, String>> sorted(Map<String, String> input, Environment environment,
|
||||
boolean includeDefaultProfileData) {
|
||||
|
||||
record WeightedEntry(Map.Entry<String, String> entry, int weight) {
|
||||
|
||||
@@ -95,37 +104,57 @@ public class SourceDataEntriesProcessor extends MapPropertySource {
|
||||
String applicationName = ConfigUtils.getApplicationName(environment, "", "");
|
||||
String[] activeProfiles = environment.getActiveProfiles();
|
||||
|
||||
// In some cases we want to include the properties from the default profile along with any properties from any active profiles
|
||||
// In this case includeDefaultProfileData will be true or the active profile will be default
|
||||
// In the case where includeDefaultProfileData is false we only want to include the properties from the active profiles
|
||||
boolean includeDataEntry = includeDefaultProfileData || Arrays.asList(environment.getActiveProfiles()).contains("default");
|
||||
|
||||
// the order here is important, first has to come "application.yaml" and then
|
||||
// "application-dev.yaml"
|
||||
List<String> orderedFileNames = Stream.concat(Stream.of(applicationName),
|
||||
Arrays.stream(activeProfiles).map(profile -> applicationName + "-" + profile)).toList();
|
||||
List<String> orderedFileNames = new ArrayList<>();
|
||||
if (includeDataEntry) {
|
||||
orderedFileNames.add(applicationName);
|
||||
}
|
||||
orderedFileNames.addAll(Arrays.stream(activeProfiles).map(profile -> applicationName + "-" + profile).toList());
|
||||
|
||||
int current = orderedFileNames.size() - 1;
|
||||
List<WeightedEntry> weightedEntries = new ArrayList<>();
|
||||
for (Map.Entry<String, String> entry : input.entrySet()) {
|
||||
String key = entry.getKey();
|
||||
if (key.endsWith(".yml") || key.endsWith(".yaml") || key.endsWith(".properties")) {
|
||||
String withoutExtension = key.split("\\.", 2)[0];
|
||||
int index = orderedFileNames.indexOf(withoutExtension);
|
||||
if (index >= 0) {
|
||||
weightedEntries.add(new WeightedEntry(entry, index));
|
||||
|
||||
if (input.entrySet().stream().noneMatch(entry -> entry.getKey().endsWith(".yml")
|
||||
|| entry.getKey().endsWith(".yaml") || entry.getKey().endsWith(".properties"))) {
|
||||
for (Map.Entry<String, String> entry : input.entrySet()) {
|
||||
weightedEntries.add(new WeightedEntry(entry, ++current));
|
||||
}
|
||||
}
|
||||
else {
|
||||
for (Map.Entry<String, String> entry : input.entrySet()) {
|
||||
String key = entry.getKey();
|
||||
if (key.endsWith(".yml") || key.endsWith(".yaml") || key.endsWith(".properties")) {
|
||||
String withoutExtension = key.split("\\.", 2)[0];
|
||||
int index = orderedFileNames.indexOf(withoutExtension);
|
||||
if (index >= 0) {
|
||||
weightedEntries.add(new WeightedEntry(entry, index));
|
||||
}
|
||||
else {
|
||||
LOG.warn("entry : " + key + " will be skipped");
|
||||
}
|
||||
}
|
||||
else {
|
||||
LOG.warn("entry : " + key + " will be skipped");
|
||||
if (includeDataEntry) {
|
||||
weightedEntries.add(new WeightedEntry(entry, ++current));
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
weightedEntries.add(new WeightedEntry(entry, ++current));
|
||||
}
|
||||
}
|
||||
|
||||
return weightedEntries.stream().sorted(Comparator.comparing(WeightedEntry::weight)).map(WeightedEntry::entry)
|
||||
.toList();
|
||||
}
|
||||
|
||||
private static Map<String, Object> defaultProcessAllEntries(Map<String, String> input, Environment environment) {
|
||||
private static Map<String, Object> defaultProcessAllEntries(Map<String, String> input, Environment environment,
|
||||
boolean includeDefaultProfile) {
|
||||
|
||||
List<Map.Entry<String, String>> sortedEntries = sorted(input, environment);
|
||||
List<Map.Entry<String, String>> sortedEntries = sorted(input, environment, includeDefaultProfile);
|
||||
Map<String, Object> result = new HashMap<>();
|
||||
for (Map.Entry<String, String> entry : sortedEntries) {
|
||||
result.putAll(extractProperties(entry.getKey(), entry.getValue(), environment));
|
||||
|
||||
@@ -135,6 +135,24 @@ class SourceDataEntriesProcessorSortedTests {
|
||||
Assertions.assertEquals(result.get(2).getValue(), "other_value");
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSimplePropertyAndTwoApplicationsDoNotIncludeDefaultProfile() {
|
||||
|
||||
Map<String, String> k8sSource = new LinkedHashMap<>();
|
||||
k8sSource.put("simple", "other_value");
|
||||
k8sSource.put("application.properties", "key=value");
|
||||
k8sSource.put("application-dev.properties", "key-dev=value-dev");
|
||||
|
||||
MockEnvironment mockEnvironment = new MockEnvironment();
|
||||
mockEnvironment.setActiveProfiles("dev");
|
||||
|
||||
List<Map.Entry<String, String>> result = SourceDataEntriesProcessor.sorted(k8sSource, mockEnvironment, false);
|
||||
Assertions.assertEquals(1, result.size());
|
||||
|
||||
Assertions.assertEquals(result.get(0).getKey(), "application-dev.properties");
|
||||
Assertions.assertEquals(result.get(0).getValue(), "key-dev=value-dev");
|
||||
}
|
||||
|
||||
@Test
|
||||
void testComplex() {
|
||||
|
||||
|
||||
@@ -36,6 +36,7 @@ import org.springframework.cloud.kubernetes.client.config.KubernetesClientSecret
|
||||
import org.springframework.cloud.kubernetes.commons.ConditionalOnKubernetesConfigEnabled;
|
||||
import org.springframework.cloud.kubernetes.commons.ConditionalOnKubernetesSecretsEnabled;
|
||||
import org.springframework.cloud.kubernetes.commons.KubernetesNamespaceProvider;
|
||||
import org.springframework.cloud.kubernetes.commons.config.ConfigUtils;
|
||||
import org.springframework.cloud.kubernetes.commons.config.NamedConfigMapNormalizedSource;
|
||||
import org.springframework.cloud.kubernetes.commons.config.NamedSecretNormalizedSource;
|
||||
import org.springframework.cloud.kubernetes.commons.config.NormalizedSource;
|
||||
@@ -77,9 +78,9 @@ public class KubernetesConfigServerAutoConfiguration {
|
||||
namespaces.forEach(space -> {
|
||||
|
||||
NamedConfigMapNormalizedSource source = new NamedConfigMapNormalizedSource(applicationName, space,
|
||||
false, true);
|
||||
false, ConfigUtils.Prefix.DEFAULT, true, true);
|
||||
KubernetesClientConfigContext context = new KubernetesClientConfigContext(coreApi, source, space,
|
||||
springEnv);
|
||||
springEnv, false);
|
||||
|
||||
propertySources.add(new KubernetesClientConfigMapPropertySource(context));
|
||||
});
|
||||
@@ -96,9 +97,10 @@ public class KubernetesConfigServerAutoConfiguration {
|
||||
List<MapPropertySource> propertySources = new ArrayList<>();
|
||||
|
||||
namespaces.forEach(space -> {
|
||||
NormalizedSource source = new NamedSecretNormalizedSource(applicationName, space, false, false);
|
||||
NormalizedSource source = new NamedSecretNormalizedSource(applicationName, space, false,
|
||||
ConfigUtils.Prefix.DEFAULT, true, true);
|
||||
KubernetesClientConfigContext context = new KubernetesClientConfigContext(coreApi, source, space,
|
||||
springEnv);
|
||||
springEnv, false);
|
||||
propertySources.add(new KubernetesClientSecretsPropertySource(context));
|
||||
});
|
||||
|
||||
|
||||
@@ -0,0 +1,31 @@
|
||||
/*
|
||||
* Copyright 2013-2024 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.configserver;
|
||||
|
||||
import org.springframework.core.env.MutablePropertySources;
|
||||
import org.springframework.core.env.StandardEnvironment;
|
||||
|
||||
/**
|
||||
* @author Ryan Baxter
|
||||
*/
|
||||
public class KubernetesConfigServerEnvironment extends StandardEnvironment {
|
||||
|
||||
KubernetesConfigServerEnvironment(MutablePropertySources mutablePropertySources) {
|
||||
super(mutablePropertySources);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -16,7 +16,11 @@
|
||||
|
||||
package org.springframework.cloud.kubernetes.configserver;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import io.kubernetes.client.openapi.apis.CoreV1Api;
|
||||
import org.apache.commons.logging.Log;
|
||||
@@ -26,6 +30,7 @@ import org.springframework.cloud.config.environment.Environment;
|
||||
import org.springframework.cloud.config.environment.PropertySource;
|
||||
import org.springframework.cloud.config.server.environment.EnvironmentRepository;
|
||||
import org.springframework.core.env.MapPropertySource;
|
||||
import org.springframework.core.env.MutablePropertySources;
|
||||
import org.springframework.core.env.StandardEnvironment;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
@@ -56,26 +61,51 @@ public class KubernetesEnvironmentRepository implements EnvironmentRepository {
|
||||
|
||||
@Override
|
||||
public Environment findOne(String application, String profile, String label, boolean includeOrigin) {
|
||||
String[] profiles = StringUtils.commaDelimitedListToStringArray(profile);
|
||||
if (!StringUtils.hasText(profile)) {
|
||||
profile = "default";
|
||||
}
|
||||
List<String> profiles = new java.util.ArrayList<>(
|
||||
Arrays.stream(StringUtils.commaDelimitedListToStringArray(profile)).toList());
|
||||
|
||||
Collections.reverse(profiles);
|
||||
if (!profiles.contains("default")) {
|
||||
profiles.add("default");
|
||||
}
|
||||
Environment environment = new Environment(application, profiles.toArray(profiles.toArray(new String[0])), label,
|
||||
null, null);
|
||||
LOG.info("Profiles: " + profile);
|
||||
LOG.info("Application: " + application);
|
||||
LOG.info("Label: " + label);
|
||||
Environment environment = new Environment(application, profiles, label, null, null);
|
||||
try {
|
||||
StandardEnvironment springEnv = new StandardEnvironment();
|
||||
springEnv.setActiveProfiles(profiles);
|
||||
if (!"application".equalsIgnoreCase(application)) {
|
||||
addApplicationConfiguration(environment, springEnv, application);
|
||||
for (String activeProfile : profiles) {
|
||||
try {
|
||||
// This is needed so that when we get the application name in
|
||||
// SourceDataProcessor.sorted that it actually
|
||||
// exists in the Environment
|
||||
StandardEnvironment springEnv = new KubernetesConfigServerEnvironment(
|
||||
createPropertySources(application));
|
||||
springEnv.setActiveProfiles(activeProfile);
|
||||
if (!"application".equalsIgnoreCase(application)) {
|
||||
addApplicationConfiguration(environment, springEnv, application);
|
||||
}
|
||||
}
|
||||
catch (Exception e) {
|
||||
LOG.warn(e);
|
||||
}
|
||||
addApplicationConfiguration(environment, springEnv, "application");
|
||||
return environment;
|
||||
}
|
||||
catch (Exception e) {
|
||||
LOG.warn(e);
|
||||
}
|
||||
StandardEnvironment springEnv = new KubernetesConfigServerEnvironment(createPropertySources("application"));
|
||||
addApplicationConfiguration(environment, springEnv, "application");
|
||||
return environment;
|
||||
}
|
||||
|
||||
private MutablePropertySources createPropertySources(String application) {
|
||||
Map<String, Object> applicationProperties = new HashMap<>();
|
||||
applicationProperties.put("spring.application.name", application);
|
||||
MapPropertySource propertySource = new MapPropertySource("kubernetes-config-server", applicationProperties);
|
||||
MutablePropertySources mutablePropertySources = new MutablePropertySources();
|
||||
mutablePropertySources.addFirst(propertySource);
|
||||
return mutablePropertySources;
|
||||
}
|
||||
|
||||
private void addApplicationConfiguration(Environment environment, StandardEnvironment springEnv,
|
||||
String applicationName) {
|
||||
kubernetesPropertySourceSuppliers.stream().forEach(supplier -> {
|
||||
|
||||
@@ -27,6 +27,7 @@ import io.kubernetes.client.openapi.models.V1ObjectMetaBuilder;
|
||||
import io.kubernetes.client.openapi.models.V1SecretBuilder;
|
||||
import io.kubernetes.client.openapi.models.V1SecretList;
|
||||
import io.kubernetes.client.openapi.models.V1SecretListBuilder;
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.Assertions;
|
||||
import org.junit.jupiter.api.BeforeAll;
|
||||
import org.junit.jupiter.api.Test;
|
||||
@@ -34,7 +35,9 @@ import org.junit.jupiter.api.Test;
|
||||
import org.springframework.cloud.config.environment.Environment;
|
||||
import org.springframework.cloud.kubernetes.client.config.KubernetesClientConfigContext;
|
||||
import org.springframework.cloud.kubernetes.client.config.KubernetesClientConfigMapPropertySource;
|
||||
import org.springframework.cloud.kubernetes.client.config.KubernetesClientConfigMapsCache;
|
||||
import org.springframework.cloud.kubernetes.client.config.KubernetesClientSecretsPropertySource;
|
||||
import org.springframework.cloud.kubernetes.commons.config.ConfigUtils;
|
||||
import org.springframework.cloud.kubernetes.commons.config.NamedConfigMapNormalizedSource;
|
||||
import org.springframework.cloud.kubernetes.commons.config.NamedSecretNormalizedSource;
|
||||
import org.springframework.cloud.kubernetes.commons.config.NormalizedSource;
|
||||
@@ -56,6 +59,19 @@ class KubernetesEnvironmentRepositoryTests {
|
||||
|
||||
private static final String DEFAULT_NAMESPACE = "default";
|
||||
|
||||
private static final V1ConfigMapList CONFIGMAP_ONE_LIST = new V1ConfigMapList()
|
||||
.addItemsItem(new V1ConfigMapBuilder()
|
||||
.withMetadata(
|
||||
new V1ObjectMetaBuilder().withName("storessingle").withNamespace(DEFAULT_NAMESPACE).build())
|
||||
.addToData("storessingle.yaml", VALUE)
|
||||
.addToData("storessingle-dev.yaml",
|
||||
"dummy:\n property:\n string2: \"dev\"\n int2: 1\n bool2: false\n")
|
||||
.addToData("storessingle-qa.yaml",
|
||||
"dummy:\n property:\n string2: \"qa\"\n int2: 2\n bool2: true\n")
|
||||
.addToData("storessingle-prod.yaml",
|
||||
"dummy:\n property:\n string2: \"prod\"\n int2: 3\n bool2: true\n")
|
||||
.build());
|
||||
|
||||
private static final V1ConfigMapList CONFIGMAP_DEFAULT_LIST = new V1ConfigMapList()
|
||||
.addItemsItem(new V1ConfigMapBuilder()
|
||||
.withMetadata(
|
||||
@@ -105,13 +121,14 @@ class KubernetesEnvironmentRepositoryTests {
|
||||
true);
|
||||
KubernetesClientConfigContext defaultContext = new KubernetesClientConfigContext(coreApi, defaultSource,
|
||||
"default", springEnv);
|
||||
|
||||
NormalizedSource devSource = new NamedConfigMapNormalizedSource(applicationName, "dev", false, true);
|
||||
KubernetesClientConfigContext devContext = new KubernetesClientConfigContext(coreApi, devSource, "dev",
|
||||
springEnv);
|
||||
|
||||
propertySources.add(new KubernetesClientConfigMapPropertySource(defaultContext));
|
||||
propertySources.add(new KubernetesClientConfigMapPropertySource(devContext));
|
||||
|
||||
if ("stores".equals(applicationName) && "dev".equals(namespace)) {
|
||||
NormalizedSource devSource = new NamedConfigMapNormalizedSource(applicationName, "dev", false, true);
|
||||
KubernetesClientConfigContext devContext = new KubernetesClientConfigContext(coreApi, devSource, "dev",
|
||||
springEnv);
|
||||
propertySources.add(new KubernetesClientConfigMapPropertySource(devContext));
|
||||
}
|
||||
return propertySources;
|
||||
});
|
||||
KUBERNETES_PROPERTY_SOURCE_SUPPLIER.add((coreApi, applicationName, namespace, springEnv) -> {
|
||||
@@ -126,6 +143,11 @@ class KubernetesEnvironmentRepositoryTests {
|
||||
});
|
||||
}
|
||||
|
||||
@AfterEach
|
||||
public void after() {
|
||||
new KubernetesClientConfigMapsCache().discardAll();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testApplicationCase() throws ApiException {
|
||||
CoreV1Api coreApi = mock(CoreV1Api.class);
|
||||
@@ -168,12 +190,11 @@ class KubernetesEnvironmentRepositoryTests {
|
||||
KubernetesEnvironmentRepository environmentRepository = new KubernetesEnvironmentRepository(coreApi,
|
||||
KUBERNETES_PROPERTY_SOURCE_SUPPLIER, "default");
|
||||
Environment environment = environmentRepository.findOne("stores", "", "");
|
||||
assertThat(environment.getPropertySources().size()).isEqualTo(5);
|
||||
assertThat(environment.getPropertySources().size()).isEqualTo(4);
|
||||
environment.getPropertySources().forEach(propertySource -> {
|
||||
assertThat(propertySource.getName().equals("configmap.application.default")
|
||||
|| propertySource.getName().equals("secret.application.default")
|
||||
|| propertySource.getName().equals("configmap.stores.default")
|
||||
|| propertySource.getName().equals("configmap.stores.dev")
|
||||
|| propertySource.getName().equals("secret.stores.default")).isTrue();
|
||||
if (propertySource.getName().equals("configmap.application.default")) {
|
||||
assertThat(propertySource.getSource().size()).isEqualTo(3);
|
||||
@@ -192,12 +213,6 @@ class KubernetesEnvironmentRepositoryTests {
|
||||
assertThat(propertySource.getSource().get("dummy.property.bool2")).isEqualTo(true);
|
||||
assertThat(propertySource.getSource().get("dummy.property.string2")).isEqualTo("a");
|
||||
}
|
||||
if (propertySource.getName().equals("configmap.stores.dev")) {
|
||||
assertThat(propertySource.getSource().size()).isEqualTo(3);
|
||||
assertThat(propertySource.getSource().get("dummy.property.int2")).isEqualTo(1);
|
||||
assertThat(propertySource.getSource().get("dummy.property.bool2")).isEqualTo(true);
|
||||
assertThat(propertySource.getSource().get("dummy.property.string2")).isEqualTo("dev");
|
||||
}
|
||||
if (propertySource.getName().equals("secret.stores.default")) {
|
||||
assertThat(propertySource.getSource().size()).isEqualTo(2);
|
||||
assertThat(propertySource.getSource().get("username")).isEqualTo("stores");
|
||||
@@ -218,13 +233,14 @@ class KubernetesEnvironmentRepositoryTests {
|
||||
KubernetesEnvironmentRepository environmentRepository = new KubernetesEnvironmentRepository(coreApi,
|
||||
KUBERNETES_PROPERTY_SOURCE_SUPPLIER, "default");
|
||||
Environment environment = environmentRepository.findOne("stores", "dev", "");
|
||||
assertThat(environment.getPropertySources().size()).isEqualTo(5);
|
||||
assertThat(environment.getPropertySources().size()).isEqualTo(6);
|
||||
environment.getPropertySources().forEach(propertySource -> {
|
||||
assertThat(propertySource.getName().equals("configmap.application.default")
|
||||
|| propertySource.getName().equals("secret.application.default")
|
||||
|| propertySource.getName().equals("configmap.stores.stores-dev.default")
|
||||
|| propertySource.getName().equals("configmap.stores.dev")
|
||||
|| propertySource.getName().equals("secret.stores.stores-dev.default")).isTrue();
|
||||
|| propertySource.getName().equals("secret.stores.default")
|
||||
|| propertySource.getName().equals("secret.stores.stores-dev.default")
|
||||
|| propertySource.getName().equals("configmap.stores.default")).isTrue();
|
||||
if (propertySource.getName().equals("configmap.application.default")) {
|
||||
assertThat(propertySource.getSource().size()).isEqualTo(3);
|
||||
assertThat(propertySource.getSource().get("dummy.property.int2")).isEqualTo(1);
|
||||
@@ -236,24 +252,29 @@ class KubernetesEnvironmentRepositoryTests {
|
||||
assertThat(propertySource.getSource().get("username")).isEqualTo("user");
|
||||
assertThat(propertySource.getSource().get("password")).isEqualTo("p455w0rd");
|
||||
}
|
||||
else if (propertySource.getName().equals("configmap.stores.stores-dev.default")) {
|
||||
assertThat(propertySource.getSource().size()).isEqualTo(4);
|
||||
assertThat(propertySource.getSource().get("dummy.property.int2")).isEqualTo(2);
|
||||
assertThat(propertySource.getSource().get("dummy.property.bool2")).isEqualTo(false);
|
||||
assertThat(propertySource.getSource().get("dummy.property.string2")).isEqualTo("b");
|
||||
assertThat(propertySource.getSource().get("dummy.property.string1")).isEqualTo("a");
|
||||
}
|
||||
else if (propertySource.getName().equals("configmap.stores.dev")) {
|
||||
assertThat(propertySource.getSource().size()).isEqualTo(3);
|
||||
assertThat(propertySource.getSource().get("dummy.property.int2")).isEqualTo(1);
|
||||
assertThat(propertySource.getSource().get("dummy.property.bool2")).isEqualTo(true);
|
||||
assertThat(propertySource.getSource().get("dummy.property.string2")).isEqualTo("dev");
|
||||
}
|
||||
else if (propertySource.getName().equals("secret.stores.stores-dev.default")) {
|
||||
assertThat(propertySource.getSource().size()).isEqualTo(2);
|
||||
assertThat(propertySource.getSource().get("username")).isEqualTo("stores-dev");
|
||||
assertThat(propertySource.getSource().get("password")).isEqualTo("password-from-stores-dev");
|
||||
}
|
||||
else if (propertySource.getName().equals("configmap.stores.stores-dev.default")) {
|
||||
assertThat(propertySource.getSource().size()).isEqualTo(4);
|
||||
assertThat(propertySource.getSource().get("dummy.property.int2")).isEqualTo(2);
|
||||
assertThat(propertySource.getSource().get("dummy.property.bool2")).isEqualTo(false);
|
||||
assertThat(propertySource.getSource().get("dummy.property.string1")).isEqualTo("a");
|
||||
assertThat(propertySource.getSource().get("dummy.property.string2")).isEqualTo("b");
|
||||
}
|
||||
else if (propertySource.getName().equals("configmap.stores.default")) {
|
||||
assertThat(propertySource.getSource().size()).isEqualTo(3);
|
||||
assertThat(propertySource.getSource().get("dummy.property.int2")).isEqualTo(1);
|
||||
assertThat(propertySource.getSource().get("dummy.property.bool2")).isEqualTo(true);
|
||||
assertThat(propertySource.getSource().get("dummy.property.string2")).isEqualTo("a");
|
||||
}
|
||||
else if (propertySource.getName().equals("secret.stores.default")) {
|
||||
assertThat(propertySource.getSource().size()).isEqualTo(2);
|
||||
assertThat(propertySource.getSource().get("username")).isEqualTo("stores");
|
||||
assertThat(propertySource.getSource().get("password")).isEqualTo("password-from-stores");
|
||||
}
|
||||
else {
|
||||
Assertions.fail("no match in property source names");
|
||||
}
|
||||
@@ -292,4 +313,52 @@ class KubernetesEnvironmentRepositoryTests {
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSingleConfigMapMultipleSources() throws ApiException {
|
||||
CoreV1Api coreApi = mock(CoreV1Api.class);
|
||||
when(coreApi.listNamespacedConfigMap(eq("default"), eq(null), eq(null), eq(null), eq(null), eq(null), eq(null),
|
||||
eq(null), eq(null), eq(null), eq(null), eq(null))).thenReturn(CONFIGMAP_ONE_LIST);
|
||||
when(coreApi.listNamespacedSecret(eq("default"), eq(null), eq(null), eq(null), eq(null), eq(null), eq(null),
|
||||
eq(null), eq(null), eq(null), eq(null), eq(null))).thenReturn(new V1SecretList());
|
||||
List<KubernetesPropertySourceSupplier> suppliers = new ArrayList<>();
|
||||
suppliers.add((coreV1Api, name, namespace, environment) -> {
|
||||
List<MapPropertySource> propertySources = new ArrayList<>();
|
||||
NormalizedSource devSource = new NamedConfigMapNormalizedSource(name, namespace, false,
|
||||
ConfigUtils.Prefix.DEFAULT, true, true);
|
||||
KubernetesClientConfigContext devContext = new KubernetesClientConfigContext(coreApi, devSource, "default",
|
||||
environment);
|
||||
propertySources.add(new KubernetesClientConfigMapPropertySource(devContext));
|
||||
return propertySources;
|
||||
});
|
||||
KubernetesEnvironmentRepository environmentRepository = new KubernetesEnvironmentRepository(coreApi, suppliers,
|
||||
"default");
|
||||
Environment environment = environmentRepository.findOne("storessingle", "", "");
|
||||
assertThat(environment.getPropertySources().size()).isEqualTo(1);
|
||||
assertThat(environment.getPropertySources().get(0).getName())
|
||||
.isEqualTo("configmap.storessingle.default.default");
|
||||
|
||||
environment = environmentRepository.findOne("storessingle", "dev", "");
|
||||
assertThat(environment.getPropertySources().size()).isEqualTo(2);
|
||||
assertThat(environment.getPropertySources().get(0).getName()).isEqualTo("configmap.storessingle.default.dev");
|
||||
assertThat(environment.getPropertySources().get(1).getName())
|
||||
.isEqualTo("configmap.storessingle.default.default");
|
||||
|
||||
environment = environmentRepository.findOne("storessingle", "dev,prod", "");
|
||||
assertThat(environment.getPropertySources().size()).isEqualTo(3);
|
||||
assertThat(environment.getPropertySources().get(0).getName()).isEqualTo("configmap.storessingle.default.prod");
|
||||
assertThat(environment.getPropertySources().get(0).getSource().get("dummy.property.int2")).isEqualTo(3);
|
||||
assertThat(environment.getPropertySources().get(0).getSource().get("dummy.property.bool2")).isEqualTo(true);
|
||||
assertThat(environment.getPropertySources().get(0).getSource().get("dummy.property.string2")).isEqualTo("prod");
|
||||
assertThat(environment.getPropertySources().get(1).getName()).isEqualTo("configmap.storessingle.default.dev");
|
||||
assertThat(environment.getPropertySources().get(1).getSource().get("dummy.property.int2")).isEqualTo(1);
|
||||
assertThat(environment.getPropertySources().get(1).getSource().get("dummy.property.bool2")).isEqualTo(false);
|
||||
assertThat(environment.getPropertySources().get(1).getSource().get("dummy.property.string2")).isEqualTo("dev");
|
||||
assertThat(environment.getPropertySources().get(2).getName())
|
||||
.isEqualTo("configmap.storessingle.default.default");
|
||||
assertThat(environment.getPropertySources().get(2).getSource().get("dummy.property.int2")).isEqualTo(1);
|
||||
assertThat(environment.getPropertySources().get(2).getSource().get("dummy.property.bool2")).isEqualTo(true);
|
||||
assertThat(environment.getPropertySources().get(2).getSource().get("dummy.property.string2")).isEqualTo("a");
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -49,7 +49,7 @@ class KubernetesPropertySourceSupplierTests {
|
||||
private static final CoreV1Api coreApi = mock(CoreV1Api.class);
|
||||
|
||||
private static final V1ConfigMapList CONFIGMAP_DEFAULT_LIST = new V1ConfigMapList()
|
||||
.addItemsItem(buildConfigMap("gateway", "tdefault"));
|
||||
.addItemsItem(buildConfigMap("gateway", "default"));
|
||||
|
||||
private static final V1ConfigMapList CONFIGMAP_TEAM_A_LIST = new V1ConfigMapList()
|
||||
.addItemsItem(buildConfigMap("stores", "team-a"));
|
||||
|
||||
@@ -51,8 +51,17 @@ abstract class ConfigServerIntegrationTest {
|
||||
|
||||
@BeforeEach
|
||||
void beforeEach() {
|
||||
V1ConfigMapList TEST_CONFIGMAP = new V1ConfigMapList().addItemsItem(new V1ConfigMapBuilder().withMetadata(
|
||||
new V1ObjectMetaBuilder().withName("test-cm").withNamespace("default").withResourceVersion("1").build())
|
||||
V1ConfigMapList TEST_CONFIGMAP = new V1ConfigMapList().addItemsItem(new V1ConfigMapBuilder()
|
||||
.withMetadata(new V1ObjectMetaBuilder().withName("test-cm").withNamespace("default")
|
||||
.withResourceVersion("1").build())
|
||||
.addToData("test-cm-dev.yaml",
|
||||
"dummy:\n property:\n string2: \"dev\"\n int2: 1\n bool2: false\n")
|
||||
.addToData("test-cm-qa.yaml",
|
||||
"dummy:\n property:\n string2: \"qa\"\n int2: 2\n bool2: true\n")
|
||||
.addToData("test-cm-prod.yaml",
|
||||
"dummy:\n property:\n string2: \"prod\"\n int2: 3\n bool2: true\n")
|
||||
.addToData("test-cm.yaml",
|
||||
"dummy:\n property:\n string2: \"default\"\n int2: 4\n bool2: true\n")
|
||||
.addToData("app.name", "test").build());
|
||||
|
||||
V1SecretList TEST_SECRET = new V1SecretListBuilder()
|
||||
@@ -74,11 +83,34 @@ abstract class ConfigServerIntegrationTest {
|
||||
void enabled() {
|
||||
Environment env = testRestTemplate.getForObject("/test-cm/default", Environment.class);
|
||||
assertThat(env.getPropertySources().size()).isEqualTo(2);
|
||||
assertThat(env.getPropertySources().get(0).getName().equals("configmap.test-cm.default")).isTrue();
|
||||
assertThat(env.getPropertySources().get(0).getName().equals("configmap.test-cm.default.default")).isTrue();
|
||||
assertThat(env.getPropertySources().get(0).getSource().get("app.name")).isEqualTo("test");
|
||||
assertThat(env.getPropertySources().get(1).getName().equals("secret.test-cm.default")).isTrue();
|
||||
assertThat(env.getPropertySources().get(1).getName().equals("secret.test-cm.default.default")).isTrue();
|
||||
assertThat(env.getPropertySources().get(1).getSource().get("password")).isEqualTo("p455w0rd");
|
||||
assertThat(env.getPropertySources().get(1).getSource().get("username")).isEqualTo("user");
|
||||
|
||||
Environment devprod = testRestTemplate.getForObject("/test-cm/dev,prod", Environment.class);
|
||||
assertThat(devprod.getPropertySources().size()).isEqualTo(4);
|
||||
assertThat(devprod.getPropertySources().get(0).getName().equals("configmap.test-cm.default.prod")).isTrue();
|
||||
assertThat(devprod.getPropertySources().get(0).getSource().size()).isEqualTo(3);
|
||||
assertThat(devprod.getPropertySources().get(0).getSource().get("dummy.property.int2")).isEqualTo(3);
|
||||
assertThat(devprod.getPropertySources().get(0).getSource().get("dummy.property.bool2")).isEqualTo(true);
|
||||
assertThat(devprod.getPropertySources().get(0).getSource().get("dummy.property.string2")).isEqualTo("prod");
|
||||
assertThat(devprod.getPropertySources().get(1).getName().equals("configmap.test-cm.default.dev")).isTrue();
|
||||
assertThat(devprod.getPropertySources().get(1).getSource().size()).isEqualTo(3);
|
||||
assertThat(devprod.getPropertySources().get(1).getSource().get("dummy.property.int2")).isEqualTo(1);
|
||||
assertThat(devprod.getPropertySources().get(1).getSource().get("dummy.property.bool2")).isEqualTo(false);
|
||||
assertThat(devprod.getPropertySources().get(1).getSource().get("dummy.property.string2")).isEqualTo("dev");
|
||||
assertThat(devprod.getPropertySources().get(2).getName().equals("configmap.test-cm.default.default")).isTrue();
|
||||
assertThat(devprod.getPropertySources().get(2).getSource().size()).isEqualTo(4);
|
||||
assertThat(devprod.getPropertySources().get(2).getSource().get("app.name")).isEqualTo("test");
|
||||
assertThat(devprod.getPropertySources().get(2).getSource().get("dummy.property.int2")).isEqualTo(4);
|
||||
assertThat(devprod.getPropertySources().get(2).getSource().get("dummy.property.bool2")).isEqualTo(true);
|
||||
assertThat(devprod.getPropertySources().get(2).getSource().get("dummy.property.string2")).isEqualTo("default");
|
||||
assertThat(devprod.getPropertySources().get(3).getName().equals("secret.test-cm.default.default")).isTrue();
|
||||
assertThat(devprod.getPropertySources().get(3).getSource().size()).isEqualTo(2);
|
||||
assertThat(devprod.getPropertySources().get(3).getSource().get("password")).isEqualTo("p455w0rd");
|
||||
assertThat(devprod.getPropertySources().get(3).getSource().get("username")).isEqualTo("user");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -19,6 +19,7 @@ package org.springframework.cloud.kubernetes.fabric8.config;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
import org.springframework.cloud.kubernetes.commons.config.ConfigUtils;
|
||||
import org.springframework.cloud.kubernetes.commons.config.MultipleSourcesContainer;
|
||||
import org.springframework.cloud.kubernetes.commons.config.NamedConfigMapNormalizedSource;
|
||||
import org.springframework.cloud.kubernetes.commons.config.NamedSourceData;
|
||||
@@ -49,6 +50,16 @@ final class NamedConfigMapContextToSourceDataProvider implements Supplier<Fabric
|
||||
NamedConfigMapNormalizedSource source = (NamedConfigMapNormalizedSource) context.normalizedSource();
|
||||
|
||||
return new NamedSourceData() {
|
||||
|
||||
@Override
|
||||
protected String generateSourceName(String target, String sourceName, String namespace,
|
||||
String[] activeProfiles) {
|
||||
if (source.appendProfileToName()) {
|
||||
return ConfigUtils.sourceName(target, sourceName, namespace, activeProfiles);
|
||||
}
|
||||
return super.generateSourceName(target, sourceName, namespace, activeProfiles);
|
||||
}
|
||||
|
||||
@Override
|
||||
public MultipleSourcesContainer dataSupplier(LinkedHashSet<String> sourceNames) {
|
||||
return Fabric8ConfigUtils.configMapsDataByName(context.client(), context.namespace(), sourceNames,
|
||||
|
||||
@@ -19,6 +19,7 @@ package org.springframework.cloud.kubernetes.fabric8.config;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
import org.springframework.cloud.kubernetes.commons.config.ConfigUtils;
|
||||
import org.springframework.cloud.kubernetes.commons.config.MultipleSourcesContainer;
|
||||
import org.springframework.cloud.kubernetes.commons.config.NamedSecretNormalizedSource;
|
||||
import org.springframework.cloud.kubernetes.commons.config.NamedSourceData;
|
||||
@@ -40,6 +41,15 @@ final class NamedSecretContextToSourceDataProvider implements Supplier<Fabric8Co
|
||||
NamedSecretNormalizedSource source = (NamedSecretNormalizedSource) context.normalizedSource();
|
||||
|
||||
return new NamedSourceData() {
|
||||
@Override
|
||||
protected String generateSourceName(String target, String sourceName, String namespace,
|
||||
String[] activeProfiles) {
|
||||
if (source.appendProfileToName()) {
|
||||
return ConfigUtils.sourceName(target, sourceName, namespace, activeProfiles);
|
||||
}
|
||||
return super.generateSourceName(target, sourceName, namespace, activeProfiles);
|
||||
}
|
||||
|
||||
@Override
|
||||
public MultipleSourcesContainer dataSupplier(LinkedHashSet<String> sourceNames) {
|
||||
return Fabric8ConfigUtils.secretsDataByName(context.client(), context.namespace(), sourceNames,
|
||||
|
||||
@@ -148,14 +148,15 @@ class NamedConfigMapContextToSourceDataProviderTests {
|
||||
// add one more profile and specify that we want profile based config maps
|
||||
MockEnvironment env = new MockEnvironment();
|
||||
env.setActiveProfiles("with-profile");
|
||||
NormalizedSource normalizedSource = new NamedConfigMapNormalizedSource("red", NAMESPACE, true, true);
|
||||
NormalizedSource normalizedSource = new NamedConfigMapNormalizedSource("red", NAMESPACE, true,
|
||||
ConfigUtils.Prefix.DEFAULT, true, true);
|
||||
|
||||
Fabric8ConfigContext context = new Fabric8ConfigContext(mockClient, normalizedSource, NAMESPACE, env);
|
||||
|
||||
Fabric8ContextToSourceData data = new NamedConfigMapContextToSourceDataProvider().get();
|
||||
SourceData sourceData = data.apply(context);
|
||||
|
||||
Assertions.assertEquals(sourceData.sourceName(), "configmap.red.red-with-profile.default");
|
||||
Assertions.assertEquals(sourceData.sourceName(), "configmap.red.red-with-profile.default.with-profile");
|
||||
Assertions.assertEquals(sourceData.sourceData().size(), 2);
|
||||
Assertions.assertEquals(sourceData.sourceData().get("color"), "really-red");
|
||||
Assertions.assertEquals(sourceData.sourceData().get("taste"), "mango");
|
||||
|
||||
@@ -196,13 +196,14 @@ class NamedSecretContextToSourceDataProviderTests {
|
||||
// add one more profile and specify that we want profile based config maps
|
||||
MockEnvironment env = new MockEnvironment();
|
||||
env.setActiveProfiles("with-profile");
|
||||
NormalizedSource normalizedSource = new NamedSecretNormalizedSource("red", NAMESPACE, true, true);
|
||||
NormalizedSource normalizedSource = new NamedSecretNormalizedSource("red", NAMESPACE, true,
|
||||
ConfigUtils.Prefix.DEFAULT, true, true);
|
||||
Fabric8ConfigContext context = new Fabric8ConfigContext(mockClient, normalizedSource, NAMESPACE, env);
|
||||
|
||||
Fabric8ContextToSourceData data = new NamedSecretContextToSourceDataProvider().get();
|
||||
SourceData sourceData = data.apply(context);
|
||||
|
||||
Assertions.assertEquals(sourceData.sourceName(), "secret.red.red-with-profile.default");
|
||||
Assertions.assertEquals(sourceData.sourceName(), "secret.red.red-with-profile.default.with-profile");
|
||||
Assertions.assertEquals(sourceData.sourceData().size(), 2);
|
||||
Assertions.assertEquals(sourceData.sourceData().get("color"), "really-red");
|
||||
Assertions.assertEquals(sourceData.sourceData().get("taste"), "mango");
|
||||
|
||||
Reference in New Issue
Block a user