Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -273,9 +273,6 @@ void testWithPrefix() {
* assert that name of the SourceData (it must use its name, not its labels) and
* values in the SourceData must be prefixed (since we have provided a delayed
* prefix).
*
* Also notice that the prefix is made up from both configmap names.
*
*/
@Test
void testTwoConfigmapsWithPrefix() {
Expand Down Expand Up @@ -318,10 +315,10 @@ void testTwoConfigmapsWithPrefix() {
String secondKey = keys.next();

if (firstKey.contains("first")) {
Assertions.assertEquals(firstKey, "another-blue-configmap.blue-configmap.first");
Assertions.assertEquals(firstKey, "blue-configmap.first");
}

Assertions.assertEquals(secondKey, "another-blue-configmap.blue-configmap.second");
Assertions.assertEquals(secondKey, "another-blue-configmap.second");
Assertions.assertEquals(properties.get(firstKey), "blue");
Assertions.assertEquals(properties.get(secondKey), "blue");
}
Expand Down Expand Up @@ -446,8 +443,8 @@ void searchWithLabelsOneConfigMapFoundAndOneFromProfileFound() {
SourceData sourceData = data.apply(context);

Assertions.assertEquals(sourceData.sourceData().size(), 2);
Assertions.assertEquals(sourceData.sourceData().get("color-configmap.color-configmap-k8s.one"), "1");
Assertions.assertEquals(sourceData.sourceData().get("color-configmap.color-configmap-k8s.two"), "2");
Assertions.assertEquals(sourceData.sourceData().get("color-configmap.one"), "1");
Assertions.assertEquals(sourceData.sourceData().get("color-configmap-k8s.two"), "2");
Assertions.assertEquals(sourceData.sourceName(), "configmap.color-configmap.color-configmap-k8s.default");

}
Expand Down Expand Up @@ -523,14 +520,10 @@ void searchWithLabelsTwoConfigMapsFoundAndOneFromProfileFound() {
SourceData sourceData = data.apply(context);

Assertions.assertEquals(sourceData.sourceData().size(), 4);
Assertions.assertEquals(sourceData.sourceData()
.get("color-configmap.color-configmap-k8s.shape-configmap.shape-configmap-k8s.one"), "1");
Assertions.assertEquals(sourceData.sourceData()
.get("color-configmap.color-configmap-k8s.shape-configmap.shape-configmap-k8s.two"), "2");
Assertions.assertEquals(sourceData.sourceData()
.get("color-configmap.color-configmap-k8s.shape-configmap.shape-configmap-k8s.four"), "4");
Assertions.assertEquals(sourceData.sourceData()
.get("color-configmap.color-configmap-k8s.shape-configmap.shape-configmap-k8s.five"), "5");
Assertions.assertEquals(sourceData.sourceData().get("color-configmap.one"), "1");
Assertions.assertEquals(sourceData.sourceData().get("shape-configmap.two"), "2");
Assertions.assertEquals(sourceData.sourceData().get("color-configmap-k8s.four"), "4");
Assertions.assertEquals(sourceData.sourceData().get("shape-configmap-k8s.five"), "5");

Assertions.assertEquals(sourceData.sourceName(),
"configmap.color-configmap.color-configmap-k8s.shape-configmap.shape-configmap-k8s.default");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -242,9 +242,6 @@ void testWithPrefix() {
* "color=blue" (on both). we search with the same labels, find them, and assert that
* name of the SourceData (it must use its name, not its labels) and values in the
* SourceData must be prefixed (since we have provided a delayed prefix).
*
* Also notice that the prefix is made up from both secret names.
*
*/
@Test
void testTwoSecretsWithPrefix() {
Expand Down Expand Up @@ -292,10 +289,10 @@ void testTwoSecretsWithPrefix() {
String secondKey = keys.next();

if (firstKey.contains("first")) {
Assertions.assertEquals(firstKey, "another-blue-secret.blue-secret.first");
Assertions.assertEquals(firstKey, "blue-secret.first");
}

Assertions.assertEquals(secondKey, "another-blue-secret.blue-secret.second");
Assertions.assertEquals(secondKey, "another-blue-secret.second");
Assertions.assertEquals(properties.get(firstKey), "blue");
Assertions.assertEquals(properties.get(secondKey), "blue");
}
Expand Down Expand Up @@ -383,8 +380,8 @@ void searchWithLabelsOneSecretFoundAndOneFromProfileFound() {
SourceData sourceData = data.apply(context);

Assertions.assertEquals(sourceData.sourceData().size(), 2);
Assertions.assertEquals(sourceData.sourceData().get("color-secret.color-secret-k8s.one"), "1");
Assertions.assertEquals(sourceData.sourceData().get("color-secret.color-secret-k8s.two"), "2");
Assertions.assertEquals(sourceData.sourceData().get("color-secret.one"), "1");
Assertions.assertEquals(sourceData.sourceData().get("color-secret-k8s.two"), "2");
Assertions.assertEquals(sourceData.sourceName(), "secret.color-secret.color-secret-k8s.default");

}
Expand Down Expand Up @@ -460,14 +457,10 @@ void searchWithLabelsTwoSecretsFoundAndOneFromProfileFound() {
SourceData sourceData = data.apply(context);

Assertions.assertEquals(sourceData.sourceData().size(), 4);
Assertions.assertEquals(
sourceData.sourceData().get("color-secret.color-secret-k8s.shape-secret.shape-secret-k8s.one"), "1");
Assertions.assertEquals(
sourceData.sourceData().get("color-secret.color-secret-k8s.shape-secret.shape-secret-k8s.two"), "2");
Assertions.assertEquals(
sourceData.sourceData().get("color-secret.color-secret-k8s.shape-secret.shape-secret-k8s.four"), "4");
Assertions.assertEquals(
sourceData.sourceData().get("color-secret.color-secret-k8s.shape-secret.shape-secret-k8s.five"), "5");
Assertions.assertEquals(sourceData.sourceData().get("color-secret.one"), "1");
Assertions.assertEquals(sourceData.sourceData().get("shape-secret.two"), "2");
Assertions.assertEquals(sourceData.sourceData().get("color-secret-k8s.four"), "4");
Assertions.assertEquals(sourceData.sourceData().get("shape-secret-k8s.five"), "5");

Assertions.assertEquals(sourceData.sourceName(),
"secret.color-secret.color-secret-k8s.shape-secret.shape-secret-k8s.default");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@

import org.springframework.boot.context.properties.ConfigurationProperties;

@ConfigurationProperties("green-configmap.green-configmap-k8s.green-configmap-prod.green-purple-configmap.green-purple-configmap-k8s")
@ConfigurationProperties("green")
public class Green {

private String two;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@

import org.springframework.boot.context.properties.ConfigurationProperties;

@ConfigurationProperties("green-purple-secret.green-purple-secret-k8s.green-secret.green-secret-k8s.green-secret-prod")
@ConfigurationProperties("green")
public class Green {

private String two;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ spring:
includeProfileSpecificSources: false
- labels:
color: green
explicitPrefix: green
- labels:
color: red
- labels:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ spring:
includeProfileSpecificSources: false
- labels:
color: green
explicitPrefix: green
- labels:
color: red
- labels:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
import java.util.Arrays;
import java.util.Base64;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
Expand Down Expand Up @@ -163,12 +164,17 @@ public static void onException(boolean failFast, Exception e) {
* is appended with prefix. So if incoming is "a=b", the result will be : "prefix.a=b"
*/
public static SourceData withPrefix(String target, PrefixContext context) {
Map<String, Object> withPrefix = CollectionUtils.newHashMap(context.data().size());
context.data().forEach((key, value) -> withPrefix.put(context.prefix() + "." + key, value));
LinkedHashMap<String, Map<String, Object>> allWithPrefix = new LinkedHashMap<>(context.data().data().size());
context.data().data().forEach((source, data) -> {
Map<String, Object> withPrefix = new HashMap<>(data.size());
data.forEach((key, value) -> withPrefix.put(context.prefix().apply(source) + "." + key, value));
allWithPrefix.put(source, withPrefix);
});

String propertySourceTokens = String.join(PROPERTY_SOURCE_NAME_SEPARATOR,
context.propertySourceNames().stream().sorted().collect(Collectors.toCollection(LinkedHashSet::new)));
return new SourceData(sourceName(target, propertySourceTokens, context.namespace()), withPrefix);
context.data().data().keySet().stream().sorted().collect(Collectors.toCollection(LinkedHashSet::new)));
return new SourceData(sourceName(target, propertySourceTokens, context.namespace()),
new MultipleSourcesContainer(allWithPrefix, false).flatten());
}

public static String sourceName(String target, String applicationName, String namespace) {
Expand Down Expand Up @@ -200,8 +206,7 @@ public static MultipleSourcesContainer processNamedData(List<StrippedSourceConta
Map<String, StrippedSourceContainer> hashByName = strippedSources.stream()
.collect(Collectors.toMap(StrippedSourceContainer::name, Function.identity()));

LinkedHashSet<String> foundSourceNames = new LinkedHashSet<>();
Map<String, Object> data = new HashMap<>();
LinkedHashMap<String, Map<String, Object>> data = new LinkedHashMap<>();

// this is an ordered stream, and it means that non-profile based sources will be
// processed before profile based sources. This way, we replicate that
Expand All @@ -211,7 +216,7 @@ public static MultipleSourcesContainer processNamedData(List<StrippedSourceConta
StrippedSourceContainer stripped = hashByName.get(sourceName);
if (stripped != null) {
LOG.debug("Found source with name : '" + sourceName + "' in namespace: '" + namespace + "'");
foundSourceNames.add(sourceName);

// see if data is a single yaml/properties file and if it needs decoding
Map<String, String> rawData = stripped.data();
if (decode) {
Expand All @@ -225,16 +230,19 @@ public static MultipleSourcesContainer processNamedData(List<StrippedSourceConta
* sure that we only return properties from any active profiles
*/
if (processSource(includeDefaultProfileData, environment, sourceName, rawData)) {
data.putAll(SourceDataEntriesProcessor.processAllEntries(rawData == null ? Map.of() : rawData,
environment, includeDefaultProfileData));
data.put(sourceName, SourceDataEntriesProcessor.processAllEntries(
rawData == null ? Map.of() : rawData, environment, includeDefaultProfileData));
}
else {
data.put(sourceName, Map.of());
}
}
else {
LOG.warn("sourceName : " + sourceName + " was requested, but not found in namespace : " + namespace);
}
});

return new MultipleSourcesContainer(foundSourceNames, data);
return new MultipleSourcesContainer(data, false);
}

static boolean processSource(boolean includeDefaultProfileData, Environment environment, String sourceName,
Expand Down Expand Up @@ -311,22 +319,20 @@ public static MultipleSourcesContainer processLabeledData(List<StrippedSourceCon
all.addAll(byLabels);
all.addAll(byProfile);

LinkedHashSet<String> sourceNames = new LinkedHashSet<>();
Map<String, Object> result = new HashMap<>();
LinkedHashMap<String, Map<String, Object>> result = new LinkedHashMap<>();

all.forEach(source -> {
String foundSourceName = source.name();
LOG.debug("Loaded source with name : '" + foundSourceName + " in namespace: '" + namespace + "'");
sourceNames.add(foundSourceName);

Map<String, String> rawData = source.data();
if (decode) {
rawData = decodeData(rawData);
}
result.putAll(SourceDataEntriesProcessor.processAllEntries(rawData, environment));
result.put(foundSourceName, SourceDataEntriesProcessor.processAllEntries(rawData, environment));
});

return new MultipleSourcesContainer(sourceNames, result);
return new MultipleSourcesContainer(result, false);
}

private static Map<String, String> decodeData(Map<String, String> data) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,13 +19,13 @@
import java.util.Arrays;
import java.util.Map;
import java.util.Set;
import java.util.function.Function;
import java.util.stream.Collectors;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import static org.springframework.cloud.kubernetes.commons.config.ConfigUtils.onException;
import static org.springframework.cloud.kubernetes.commons.config.Constants.ERROR_PROPERTY;
import static org.springframework.cloud.kubernetes.commons.config.Constants.PROPERTY_SOURCE_NAME_SEPARATOR;

/**
Expand Down Expand Up @@ -53,7 +53,7 @@ public final SourceData compute(Map<String, String> labels, ConfigUtils.Prefix p
// need this check because when there is no data, the name of the property
// source is using provided labels,
// unlike when the data is present: when we use secret names
if (data.names().isEmpty()) {
if (data.data().isEmpty()) {
String names = labels.keySet()
.stream()
.sorted()
Expand All @@ -63,29 +63,31 @@ public final SourceData compute(Map<String, String> labels, ConfigUtils.Prefix p

if (prefix != ConfigUtils.Prefix.DEFAULT) {

String prefixToUse;
if (prefix == ConfigUtils.Prefix.KNOWN) {
prefixToUse = prefix.prefixProvider().get();
Function<String, String> prefixToUse;
if (prefix == ConfigUtils.Prefix.DELAYED) {
prefixToUse = Function.identity();
}
else {
prefixToUse = data.names()
.stream()
.sorted()
.collect(Collectors.joining(PROPERTY_SOURCE_NAME_SEPARATOR));
String knownPrefix = prefix.prefixProvider().get();
prefixToUse = ignored -> knownPrefix;
}

PrefixContext prefixContext = new PrefixContext(data.data(), prefixToUse, namespace, data.names());
PrefixContext prefixContext = new PrefixContext(data, prefixToUse, namespace);
return ConfigUtils.withPrefix(target, prefixContext);
}
}
catch (Exception e) {
LOG.warn("Failure in reading labeled sources");
onException(failFast, e);
data = new MultipleSourcesContainer(data.names(), Map.of(ERROR_PROPERTY, "true"));
data = new MultipleSourcesContainer(data.data(), true);
}

String names = data.names().stream().sorted().collect(Collectors.joining(PROPERTY_SOURCE_NAME_SEPARATOR));
return new SourceData(ConfigUtils.sourceName(target, names, namespace), data.data());
String names = data.data()
.keySet()
.stream()
.sorted()
.collect(Collectors.joining(PROPERTY_SOURCE_NAME_SEPARATOR));
return new SourceData(ConfigUtils.sourceName(target, names, namespace), data.flatten());

}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,21 +16,37 @@

package org.springframework.cloud.kubernetes.commons.config;

import java.util.LinkedHashSet;
import java.util.LinkedHashMap;
import java.util.Map;

/**
* @author wind57
*
* Container that stores multiple sources, to be exact their names and their flattenned
* data. We force a LinkedHashSet on purpose, to preserve the order of sources.
* Container that stores multiple sources, mapping their names to their data. We force a
* LinkedHashMap on purpose, to preserve the order of sources.
*/
public record MultipleSourcesContainer(LinkedHashSet<String> names, Map<String, Object> data) {
public record MultipleSourcesContainer(LinkedHashMap<String, Map<String, Object>> data, boolean error) {

private static final MultipleSourcesContainer EMPTY = new MultipleSourcesContainer(new LinkedHashSet<>(0),
Map.of());
private static final MultipleSourcesContainer EMPTY = new MultipleSourcesContainer(new LinkedHashMap<>(0), false);

public static MultipleSourcesContainer empty() {
return EMPTY;
}

/**
* Flatten all sources into a single map. For duplicate properties the last source
* will be kept.
* @return the flattened map
*/
public Map<String, Object> flatten() {
if (error) {
return Map.of(Constants.ERROR_PROPERTY, "true");
}

Map<String, Object> result = new LinkedHashMap<>();
for (Map<String, Object> data : data.values()) {
result.putAll(data);
}
return result;
}
}
Loading
Loading