Config Server Controller Should Return All PropertySources (#1600)

This commit is contained in:
Ryan Baxter
2024-03-28 09:59:29 -04:00
committed by GitHub
parent 52f410648a
commit cb3d7ca781
23 changed files with 443 additions and 103 deletions

View File

@@ -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);
}
}

View File

@@ -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));

View File

@@ -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) {

View File

@@ -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());

View File

@@ -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());

View File

@@ -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");
}

View File

@@ -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");
}

View File

@@ -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

View File

@@ -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;

View File

@@ -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;
}

View File

@@ -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);
}
/**

View File

@@ -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));

View File

@@ -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() {

View File

@@ -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));
});

View File

@@ -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);
}
}

View File

@@ -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 -> {

View File

@@ -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");
}
}

View File

@@ -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"));

View File

@@ -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");
}
}

View File

@@ -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,

View File

@@ -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,

View File

@@ -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");

View File

@@ -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");