Fixes profile ordering of remote vs local.

Previously a local profiles specific file would have more priority than a remote profile specific file. Using a new ConfigData Option, this is now fixed.

Fixes gh-1795
This commit is contained in:
spencergibb
2021-04-08 17:39:25 -04:00
parent e07a7e8fad
commit 3056d5f842
9 changed files with 142 additions and 18 deletions

View File

@@ -19,6 +19,7 @@ package org.springframework.cloud.config.client;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.EnumSet;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
@@ -28,6 +29,7 @@ import org.apache.commons.logging.Log;
import org.springframework.boot.context.config.ConfigData;
import org.springframework.boot.context.config.ConfigData.Option;
import org.springframework.boot.context.config.ConfigData.Options;
import org.springframework.boot.context.config.ConfigDataLoader;
import org.springframework.boot.context.config.ConfigDataLoaderContext;
import org.springframework.boot.context.properties.bind.Binder;
@@ -62,6 +64,8 @@ public class ConfigServerConfigDataLoader implements ConfigDataLoader<ConfigServ
*/
public static final String CONFIG_CLIENT_PROPERTYSOURCE_NAME = "configClient";
private static final EnumSet<Option> ALL_OPTIONS = EnumSet.allOf(Option.class);
protected final Log logger;
public ConfigServerConfigDataLoader(Log logger) {
@@ -91,7 +95,7 @@ public class ConfigServerConfigDataLoader implements ConfigDataLoader<ConfigServ
public ConfigData doLoad(ConfigDataLoaderContext context, ConfigServerConfigDataResource resource) {
ConfigClientProperties properties = resource.getProperties();
List<PropertySource<?>> composite = new ArrayList<>();
List<PropertySource<?>> propertySources = new ArrayList<>();
Exception error = null;
String errorBody = null;
try {
@@ -113,7 +117,7 @@ public class ConfigServerConfigDataLoader implements ConfigDataLoader<ConfigServ
@SuppressWarnings("unchecked")
Map<String, Object> map = translateOrigins(source.getName(),
(Map<String, Object>) source.getSource());
composite.add(0,
propertySources.add(0,
new OriginTrackedMapPropertySource("configserver:" + source.getName(), map));
}
}
@@ -127,17 +131,31 @@ public class ConfigServerConfigDataLoader implements ConfigDataLoader<ConfigServ
}
// the existence of this property source confirms a successful
// response from config server
composite.add(0, new MapPropertySource(CONFIG_CLIENT_PROPERTYSOURCE_NAME, map));
try {
return new ConfigData(composite, Option.IGNORE_IMPORTS, Option.IGNORE_PROFILES);
propertySources.add(0, new MapPropertySource(CONFIG_CLIENT_PROPERTYSOURCE_NAME, map));
if (ALL_OPTIONS.size() == 1) {
// boot 2.4.2 and prior
return new ConfigData(propertySources);
}
catch (NoSuchFieldError e) {
// IGNORE_PROFILES was added in boot 2.4.3, for backwards
// compatibility
// IGNORE_IMPORTS alone causes NPE prior to 2.4.3
// this will still throw an error if spring.profiles.include in
// remote config
return new ConfigData(composite);
else if (ALL_OPTIONS.size() == 2) {
// boot 2.4.3 and 2.4.4
return new ConfigData(propertySources, Option.IGNORE_IMPORTS, Option.IGNORE_PROFILES);
}
else if (ALL_OPTIONS.size() > 2) {
// boot 2.4.5+
return new ConfigData(propertySources, propertySource -> {
String propertySourceName = propertySource.getName();
List<Option> options = new ArrayList<>();
options.add(Option.IGNORE_IMPORTS);
options.add(Option.IGNORE_PROFILES);
for (String profile : resource.getAcceptedProfiles()) {
// TODO: switch to match
if (propertySourceName.contains("-" + profile + ".")) {
// TODO: switch to Options.with() when implemented
options.add(Option.PROFILE_SPECIFIC);
}
}
return Options.of(options.toArray(new Option[0]));
});
}
}
}

View File

@@ -47,12 +47,15 @@ public class ConfigServerConfigDataResource extends ConfigDataResource {
}
public String getProfiles() {
List<String> accepted = profiles.getAccepted();
if (StringUtils.hasText(properties.getProfile())
&& !properties.getProfile().equals(ConfigClientProperties.DEFAULT_PROFILE)) {
return properties.getProfile();
}
return StringUtils.collectionToCommaDelimitedString(accepted);
return StringUtils.collectionToCommaDelimitedString(getAcceptedProfiles());
}
List<String> getAcceptedProfiles() {
return this.profiles.getAccepted();
}
@Override

View File

@@ -29,6 +29,7 @@ import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.boot.context.config.ConfigData;
import org.springframework.boot.context.config.ConfigData.Option;
import org.springframework.boot.context.config.ConfigData.Options;
import org.springframework.boot.context.properties.bind.BindContext;
import org.springframework.boot.context.properties.bind.BindHandler;
import org.springframework.boot.context.properties.bind.Bindable;
@@ -37,6 +38,7 @@ import org.springframework.boot.context.properties.source.ConfigurationPropertyN
import org.springframework.cloud.config.client.ConfigServerBootstrapper.LoaderInterceptor;
import org.springframework.cloud.config.environment.Environment;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.core.env.PropertySource;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpMethod;
import org.springframework.http.ResponseEntity;
@@ -110,9 +112,14 @@ public class ConfigServerConfigDataCustomizationIntegrationTests {
hasBinder = context.getBinder() != null;
ConfigData configData = context.getInvocation().apply(context.getLoaderContext(), context.getResource());
assertThat(configData).as("ConfigData was null for location %s", context.getResource()).isNotNull();
assertThat(configData.getOptions()).as("ConfigData.options was null for location %s", context.getResource())
.isNotNull();
assertThat(configData.getOptions()).contains(Option.IGNORE_IMPORTS, Option.IGNORE_PROFILES);
assertThat(configData.getPropertySources()).hasSize(1);
PropertySource<?> propertySource = configData.getPropertySources().iterator().next();
Options options = configData.getOptions(propertySource);
assertThat(options).as("ConfigData.options was null for location %s property source %s",
context.getResource(), propertySource.getName()).isNotNull();
assertThat(options.contains(Option.IGNORE_IMPORTS)).isTrue();
assertThat(options.contains(Option.IGNORE_PROFILES)).isTrue();
assertThat(options.contains(Option.PROFILE_SPECIFIC)).isFalse();
return configData;
}