Skip to content

Commit cb3d7ca

Browse files
author
Ryan Baxter
authored
Config Server Controller Should Return All PropertySources (#1600)
1 parent 52f4106 commit cb3d7ca

File tree

23 files changed

+436
-96
lines changed

23 files changed

+436
-96
lines changed

spring-cloud-kubernetes-client-config/src/main/java/org/springframework/cloud/kubernetes/client/config/KubernetesClientConfigContext.java

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,5 +27,10 @@
2727
* @author wind57
2828
*/
2929
public record KubernetesClientConfigContext(CoreV1Api client, NormalizedSource normalizedSource, String namespace,
30-
Environment environment) {
30+
Environment environment, boolean includeDefaultProfileData) {
31+
32+
public KubernetesClientConfigContext(CoreV1Api client, NormalizedSource normalizedSource, String namespace,
33+
Environment environment) {
34+
this(client, normalizedSource, namespace, environment, true);
35+
}
3136
}

spring-cloud-kubernetes-client-config/src/main/java/org/springframework/cloud/kubernetes/client/config/KubernetesClientConfigMapsCache.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@
3535
*
3636
* @author wind57
3737
*/
38-
final class KubernetesClientConfigMapsCache implements ConfigMapCache {
38+
public final class KubernetesClientConfigMapsCache implements ConfigMapCache {
3939

4040
private static final LogAccessor LOG = new LogAccessor(LogFactory.getLog(KubernetesClientConfigMapsCache.class));
4141

spring-cloud-kubernetes-client-config/src/main/java/org/springframework/cloud/kubernetes/client/config/KubernetesClientConfigUtils.java

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -108,12 +108,23 @@ static MultipleSourcesContainer configMapsDataByLabels(CoreV1Api coreV1Api, Stri
108108
* </pre>
109109
*/
110110
static MultipleSourcesContainer secretsDataByName(CoreV1Api coreV1Api, String namespace,
111-
LinkedHashSet<String> sourceNames, Environment environment) {
111+
LinkedHashSet<String> sourceNames, Environment environment, boolean includeDefaultProfileData) {
112112
List<StrippedSourceContainer> strippedSecrets = strippedSecrets(coreV1Api, namespace);
113113
if (strippedSecrets.isEmpty()) {
114114
return MultipleSourcesContainer.empty();
115115
}
116-
return ConfigUtils.processNamedData(strippedSecrets, environment, sourceNames, namespace, DECODE);
116+
return ConfigUtils.processNamedData(strippedSecrets, environment, sourceNames, namespace, DECODE,
117+
includeDefaultProfileData);
118+
}
119+
120+
static MultipleSourcesContainer secretsDataByName(CoreV1Api coreV1Api, String namespace,
121+
LinkedHashSet<String> sourceNames, Environment environment) {
122+
return secretsDataByName(coreV1Api, namespace, sourceNames, environment, true);
123+
}
124+
125+
static MultipleSourcesContainer configMapsDataByName(CoreV1Api coreV1Api, String namespace,
126+
LinkedHashSet<String> sourceNames, Environment environment) {
127+
return configMapsDataByName(coreV1Api, namespace, sourceNames, environment, true);
117128
}
118129

119130
/**
@@ -125,12 +136,13 @@ static MultipleSourcesContainer secretsDataByName(CoreV1Api coreV1Api, String na
125136
* </pre>
126137
*/
127138
static MultipleSourcesContainer configMapsDataByName(CoreV1Api coreV1Api, String namespace,
128-
LinkedHashSet<String> sourceNames, Environment environment) {
139+
LinkedHashSet<String> sourceNames, Environment environment, boolean includeDefaultProfileData) {
129140
List<StrippedSourceContainer> strippedConfigMaps = strippedConfigMaps(coreV1Api, namespace);
130141
if (strippedConfigMaps.isEmpty()) {
131142
return MultipleSourcesContainer.empty();
132143
}
133-
return ConfigUtils.processNamedData(strippedConfigMaps, environment, sourceNames, namespace, DECODE);
144+
return ConfigUtils.processNamedData(strippedConfigMaps, environment, sourceNames, namespace, DECODE,
145+
includeDefaultProfileData);
134146
}
135147

136148
private static List<StrippedSourceContainer> strippedConfigMaps(CoreV1Api coreV1Api, String namespace) {

spring-cloud-kubernetes-client-config/src/main/java/org/springframework/cloud/kubernetes/client/config/NamedConfigMapContextToSourceDataProvider.java

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
import java.util.LinkedHashSet;
2020
import java.util.function.Supplier;
2121

22+
import org.springframework.cloud.kubernetes.commons.config.ConfigUtils;
2223
import org.springframework.cloud.kubernetes.commons.config.MultipleSourcesContainer;
2324
import org.springframework.cloud.kubernetes.commons.config.NamedConfigMapNormalizedSource;
2425
import org.springframework.cloud.kubernetes.commons.config.NamedSourceData;
@@ -42,10 +43,19 @@ public KubernetesClientContextToSourceData get() {
4243
NamedConfigMapNormalizedSource source = (NamedConfigMapNormalizedSource) context.normalizedSource();
4344

4445
return new NamedSourceData() {
46+
@Override
47+
protected String generateSourceName(String target, String sourceName, String namespace,
48+
String[] activeProfiles) {
49+
if (source.appendProfileToName()) {
50+
return ConfigUtils.sourceName(target, sourceName, namespace, activeProfiles);
51+
}
52+
return super.generateSourceName(target, sourceName, namespace, activeProfiles);
53+
}
54+
4555
@Override
4656
public MultipleSourcesContainer dataSupplier(LinkedHashSet<String> sourceNames) {
4757
return KubernetesClientConfigUtils.configMapsDataByName(context.client(), context.namespace(),
48-
sourceNames, context.environment());
58+
sourceNames, context.environment(), context.includeDefaultProfileData());
4959
}
5060
}.compute(source.name().orElseThrow(), source.prefix(), source.target(), source.profileSpecificSources(),
5161
source.failFast(), context.namespace(), context.environment().getActiveProfiles());

spring-cloud-kubernetes-client-config/src/main/java/org/springframework/cloud/kubernetes/client/config/NamedSecretContextToSourceDataProvider.java

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
import java.util.LinkedHashSet;
2020
import java.util.function.Supplier;
2121

22+
import org.springframework.cloud.kubernetes.commons.config.ConfigUtils;
2223
import org.springframework.cloud.kubernetes.commons.config.MultipleSourcesContainer;
2324
import org.springframework.cloud.kubernetes.commons.config.NamedSecretNormalizedSource;
2425
import org.springframework.cloud.kubernetes.commons.config.NamedSourceData;
@@ -41,10 +42,19 @@ public KubernetesClientContextToSourceData get() {
4142
NamedSecretNormalizedSource source = (NamedSecretNormalizedSource) context.normalizedSource();
4243

4344
return new NamedSourceData() {
45+
@Override
46+
protected String generateSourceName(String target, String sourceName, String namespace,
47+
String[] activeProfiles) {
48+
if (source.appendProfileToName()) {
49+
return ConfigUtils.sourceName(target, sourceName, namespace, activeProfiles);
50+
}
51+
return super.generateSourceName(target, sourceName, namespace, activeProfiles);
52+
}
53+
4454
@Override
4555
public MultipleSourcesContainer dataSupplier(LinkedHashSet<String> sourceNames) {
4656
return KubernetesClientConfigUtils.secretsDataByName(context.client(), context.namespace(),
47-
sourceNames, context.environment());
57+
sourceNames, context.environment(), context.includeDefaultProfileData());
4858
}
4959
}.compute(source.name().orElseThrow(), source.prefix(), source.target(), source.profileSpecificSources(),
5060
source.failFast(), context.namespace(), context.environment().getActiveProfiles());

spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/NamedConfigMapContextToSourceDataProviderTests.java

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -164,17 +164,17 @@ void matchIncludeSingleProfile() {
164164
stubCall(configMapList);
165165
CoreV1Api api = new CoreV1Api();
166166

167-
NormalizedSource source = new NamedConfigMapNormalizedSource(RED_CONFIG_MAP_NAME, NAMESPACE, true, true);
167+
NormalizedSource source = new NamedConfigMapNormalizedSource(RED_CONFIG_MAP_NAME, NAMESPACE, true,
168+
ConfigUtils.Prefix.DEFAULT, true, true);
168169
MockEnvironment environment = new MockEnvironment();
169170
environment.setActiveProfiles("with-profile");
170-
KubernetesClientConfigContext context = new KubernetesClientConfigContext(api, source, NAMESPACE, environment);
171+
KubernetesClientConfigContext context = new KubernetesClientConfigContext(api, source, NAMESPACE, environment, false);
171172

172173
KubernetesClientContextToSourceData data = new NamedConfigMapContextToSourceDataProvider().get();
173174
SourceData sourceData = data.apply(context);
174175

175-
Assertions.assertEquals(sourceData.sourceName(), "configmap.red.red-with-profile.default");
176-
Assertions.assertEquals(sourceData.sourceData().size(), 2);
177-
Assertions.assertEquals(sourceData.sourceData().get("color"), "really-red");
176+
Assertions.assertEquals(sourceData.sourceName(), "configmap.red.red-with-profile.default.with-profile");
177+
Assertions.assertEquals(sourceData.sourceData().size(), 1);
178178
Assertions.assertEquals(sourceData.sourceData().get("taste"), "mango");
179179

180180
}

spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/NamedSecretContextToSourceDataProviderTests.java

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -217,17 +217,17 @@ void matchIncludeSingleProfile() {
217217
stubCall(secretList);
218218
CoreV1Api api = new CoreV1Api();
219219

220-
NormalizedSource source = new NamedSecretNormalizedSource("red", NAMESPACE, false, true);
220+
NormalizedSource source = new NamedSecretNormalizedSource("red", NAMESPACE, false, ConfigUtils.Prefix.DEFAULT,
221+
true, true);
221222
MockEnvironment environment = new MockEnvironment();
222223
environment.addActiveProfile("with-profile");
223-
KubernetesClientConfigContext context = new KubernetesClientConfigContext(api, source, NAMESPACE, environment);
224+
KubernetesClientConfigContext context = new KubernetesClientConfigContext(api, source, NAMESPACE, environment, false);
224225

225226
KubernetesClientContextToSourceData data = new NamedSecretContextToSourceDataProvider().get();
226227
SourceData sourceData = data.apply(context);
227228

228-
Assertions.assertEquals(sourceData.sourceName(), "secret.red.red-with-profile.default");
229-
Assertions.assertEquals(sourceData.sourceData().size(), 2);
230-
Assertions.assertEquals(sourceData.sourceData().get("color"), "really-red");
229+
Assertions.assertEquals(sourceData.sourceName(), "secret.red.red-with-profile.default.with-profile");
230+
Assertions.assertEquals(sourceData.sourceData().size(), 1);
231231
Assertions.assertEquals(sourceData.sourceData().get("taste"), "mango");
232232

233233
}

spring-cloud-kubernetes-commons/src/main/java/org/springframework/cloud/kubernetes/commons/config/ConfigUtils.java

Lines changed: 39 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
package org.springframework.cloud.kubernetes.commons.config;
1818

1919
import java.util.ArrayList;
20+
import java.util.Arrays;
2021
import java.util.Base64;
2122
import java.util.HashMap;
2223
import java.util.LinkedHashSet;
@@ -161,13 +162,27 @@ public static String sourceName(String target, String applicationName, String na
161162
return target + PROPERTY_SOURCE_NAME_SEPARATOR + applicationName + PROPERTY_SOURCE_NAME_SEPARATOR + namespace;
162163
}
163164

165+
public static String sourceName(String target, String applicationName, String namespace, String[] profiles) {
166+
String name = sourceName(target, applicationName, namespace);
167+
if (profiles != null && profiles.length > 0) {
168+
name = name + PROPERTY_SOURCE_NAME_SEPARATOR + StringUtils.arrayToDelimitedString(profiles, "-");
169+
}
170+
return name;
171+
}
172+
173+
public static MultipleSourcesContainer processNamedData(List<StrippedSourceContainer> strippedSources,
174+
Environment environment, LinkedHashSet<String> sourceNames, String namespace, boolean decode) {
175+
return processNamedData(strippedSources, environment, sourceNames, namespace, decode, true);
176+
}
177+
164178
/**
165179
* transforms raw data from one or multiple sources into an entry of source names and
166180
* flattened data that they all hold (potentially overriding entries without any
167181
* defined order).
168182
*/
169183
public static MultipleSourcesContainer processNamedData(List<StrippedSourceContainer> strippedSources,
170-
Environment environment, LinkedHashSet<String> sourceNames, String namespace, boolean decode) {
184+
Environment environment, LinkedHashSet<String> sourceNames, String namespace, boolean decode,
185+
boolean includeDefaultProfileData) {
171186

172187
Map<String, StrippedSourceContainer> hashByName = strippedSources.stream()
173188
.collect(Collectors.toMap(StrippedSourceContainer::name, Function.identity()));
@@ -189,14 +204,35 @@ public static MultipleSourcesContainer processNamedData(List<StrippedSourceConta
189204
if (decode) {
190205
rawData = decodeData(rawData);
191206
}
192-
data.putAll(SourceDataEntriesProcessor.processAllEntries(rawData == null ? Map.of() : rawData,
193-
environment));
207+
208+
// In some cases we want to include properties from the default profile along with any active profiles
209+
// In these cases includeDefaultProfileData will be true
210+
// If includeDefaultProfileData is false then we want to make sure that we only return properties from any active profiles
211+
212+
//Check the source to see if it contains any active profiles
213+
boolean containsActiveProfile = environment.getActiveProfiles().length == 0
214+
|| Arrays.stream(environment.getActiveProfiles())
215+
.anyMatch(p -> source.contains("-" + p) || "default".equals(p));
216+
if (includeDefaultProfileData || containsActiveProfile
217+
|| containsDataWithProfile(rawData, environment.getActiveProfiles())) {
218+
data.putAll(SourceDataEntriesProcessor.processAllEntries(rawData == null ? Map.of() : rawData,
219+
environment, includeDefaultProfileData));
220+
}
194221
}
195222
});
196223

197224
return new MultipleSourcesContainer(foundSourceNames, data);
198225
}
199226

227+
/*
228+
* In the case there the data contains yaml or properties files we need to check their names to see if they
229+
* contain any active profiles.
230+
*/
231+
private static boolean containsDataWithProfile(Map<String, String> rawData, String[] activeProfiles) {
232+
return rawData.keySet().stream().anyMatch(
233+
key -> Arrays.stream(activeProfiles).anyMatch(p -> key.contains("-" + p) || "default".equals(p)));
234+
}
235+
200236
/**
201237
* transforms raw data from one or multiple sources into an entry of source names and
202238
* flattened data that they all hold (potentially overriding entries without any

spring-cloud-kubernetes-commons/src/main/java/org/springframework/cloud/kubernetes/commons/config/NamedConfigMapNormalizedSource.java

Lines changed: 19 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -29,18 +29,25 @@ public final class NamedConfigMapNormalizedSource extends NormalizedSource {
2929

3030
private final boolean includeProfileSpecificSources;
3131

32+
private final boolean appendProfileToName;
33+
3234
public NamedConfigMapNormalizedSource(String name, String namespace, boolean failFast, ConfigUtils.Prefix prefix,
3335
boolean includeProfileSpecificSources) {
34-
super(name, namespace, failFast);
35-
this.prefix = Objects.requireNonNull(prefix);
36-
this.includeProfileSpecificSources = includeProfileSpecificSources;
36+
this(name, namespace, failFast, prefix, includeProfileSpecificSources, false);
3737
}
3838

3939
public NamedConfigMapNormalizedSource(String name, String namespace, boolean failFast,
4040
boolean includeProfileSpecificSources) {
41+
this(name, namespace, failFast, ConfigUtils.Prefix.DEFAULT, includeProfileSpecificSources);
42+
}
43+
44+
public NamedConfigMapNormalizedSource(String name, String namespace, boolean failFast, ConfigUtils.Prefix prefix,
45+
boolean includeProfileSpecificSources, boolean appendProfileToName) {
4146
super(name, namespace, failFast);
42-
this.prefix = ConfigUtils.Prefix.DEFAULT;
47+
this.prefix = Objects.requireNonNull(prefix);
4348
this.includeProfileSpecificSources = includeProfileSpecificSources;
49+
this.appendProfileToName = appendProfileToName;
50+
4451
}
4552

4653
public ConfigUtils.Prefix prefix() {
@@ -51,6 +58,14 @@ public boolean profileSpecificSources() {
5158
return includeProfileSpecificSources;
5259
}
5360

61+
/**
62+
* append or not the active profiles to the name of the generated source.
63+
* At the moment this is true only for config server generated sources.
64+
*/
65+
public boolean appendProfileToName() {
66+
return appendProfileToName;
67+
}
68+
5469
@Override
5570
public NormalizedSourceType type() {
5671
return NormalizedSourceType.NAMED_CONFIG_MAP;

spring-cloud-kubernetes-commons/src/main/java/org/springframework/cloud/kubernetes/commons/config/NamedSecretNormalizedSource.java

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -29,24 +29,38 @@ public final class NamedSecretNormalizedSource extends NormalizedSource {
2929

3030
private final boolean includeProfileSpecificSources;
3131

32+
private final boolean appendProfileToName;
33+
3234
public NamedSecretNormalizedSource(String name, String namespace, boolean failFast, ConfigUtils.Prefix prefix,
33-
boolean includeProfileSpecificSources) {
35+
boolean includeProfileSpecificSources, boolean appendProfileToName) {
3436
super(name, namespace, failFast);
3537
this.prefix = Objects.requireNonNull(prefix);
3638
this.includeProfileSpecificSources = includeProfileSpecificSources;
39+
this.appendProfileToName = appendProfileToName;
3740
}
3841

3942
public NamedSecretNormalizedSource(String name, String namespace, boolean failFast,
4043
boolean includeProfileSpecificSources) {
41-
super(name, namespace, failFast);
42-
this.prefix = ConfigUtils.Prefix.DEFAULT;
43-
this.includeProfileSpecificSources = includeProfileSpecificSources;
44+
this(name, namespace, failFast, ConfigUtils.Prefix.DEFAULT, includeProfileSpecificSources, false);
45+
}
46+
47+
public NamedSecretNormalizedSource(String name, String namespace, boolean failFast, ConfigUtils.Prefix prefix,
48+
boolean includeProfileSpecificSources) {
49+
this(name, namespace, failFast, prefix, includeProfileSpecificSources, false);
4450
}
4551

4652
public boolean profileSpecificSources() {
4753
return includeProfileSpecificSources;
4854
}
4955

56+
/**
57+
* append or not the active profiles to the name of the generated source.
58+
* At the moment this is true only for config server generated sources.
59+
*/
60+
public boolean appendProfileToName() {
61+
return appendProfileToName;
62+
}
63+
5064
public ConfigUtils.Prefix prefix() {
5165
return prefix;
5266
}

spring-cloud-kubernetes-commons/src/main/java/org/springframework/cloud/kubernetes/commons/config/NamedSourceData.java

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,11 @@ public final SourceData compute(String sourceName, ConfigUtils.Prefix prefix, St
6767
}
6868

6969
String names = data.names().stream().sorted().collect(Collectors.joining(PROPERTY_SOURCE_NAME_SEPARATOR));
70-
return new SourceData(ConfigUtils.sourceName(target, names, namespace), data.data());
70+
return new SourceData(generateSourceName(target, names, namespace, activeProfiles), data.data());
71+
}
72+
73+
protected String generateSourceName(String target, String sourceName, String namespace, String[] activeProfiles) {
74+
return ConfigUtils.sourceName(target, sourceName, namespace);
7175
}
7276

7377
/**

0 commit comments

Comments
 (0)