Add support for multiple configuration path prefixes.
Fixes gh-450
This commit is contained in:
committed by
spencergibb
parent
30041b76c5
commit
3c4ceb0823
@@ -339,7 +339,7 @@ The watch uses a Spring `TaskScheduler` to schedule the call to consul. By defau
|
||||
[[spring-cloud-consul-config]]
|
||||
== Distributed Configuration with Consul
|
||||
|
||||
Consul provides a https://consul.io/docs/agent/http/kv.html[Key/Value Store] for storing configuration and other metadata. Spring Cloud Consul Config is an alternative to the https://github.com/spring-cloud/spring-cloud-config[Config Server and Client]. Configuration is loaded into the Spring Environment during the special "bootstrap" phase. Configuration is stored in the `/config` folder by default. Multiple `PropertySource` instances are created based on the application's name and the active profiles that mimicks the Spring Cloud Config order of resolving properties. For example, an application with the name "testApp" and with the "dev" profile will have the following property sources created:
|
||||
Consul provides a https://consul.io/docs/agent/http/kv.html[Key/Value Store] for storing configuration and other metadata. Spring Cloud Consul Config is an alternative to the https://github.com/spring-cloud/spring-cloud-config[Config Server and Client]. Configuration is loaded into the Spring Environment during the special "bootstrap" phase. Configuration is stored in the `/config` folder by default. Multiple `PropertySource` instances are created based on the application's name and the active profiles that mimics the Spring Cloud Config order of resolving properties. For example, an application with the name "testApp" and with the "dev" profile will have the following property sources created:
|
||||
|
||||
----
|
||||
config/testApp,dev/
|
||||
|
||||
@@ -16,13 +16,19 @@
|
||||
|
||||
package org.springframework.cloud.consul.config;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import javax.annotation.PostConstruct;
|
||||
import javax.validation.constraints.NotEmpty;
|
||||
import javax.validation.constraints.NotNull;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
import org.springframework.boot.context.properties.DeprecatedConfigurationProperty;
|
||||
import org.springframework.core.style.ToStringCreator;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
|
||||
import static org.springframework.cloud.consul.config.ConsulConfigProperties.PREFIX;
|
||||
@@ -41,7 +47,7 @@ public class ConsulConfigProperties {
|
||||
|
||||
private boolean enabled = true;
|
||||
|
||||
private String prefix = "config";
|
||||
private List<String> prefixes = new ArrayList<>(Collections.singletonList("config"));
|
||||
|
||||
@NotEmpty
|
||||
private String defaultContext = "application";
|
||||
@@ -92,12 +98,31 @@ public class ConsulConfigProperties {
|
||||
this.enabled = enabled;
|
||||
}
|
||||
|
||||
public String getPrefix() {
|
||||
return this.prefix;
|
||||
public List<String> getPrefixes() {
|
||||
return this.prefixes;
|
||||
}
|
||||
|
||||
public void setPrefixes(List<String> prefixes) {
|
||||
this.prefixes = prefixes;
|
||||
}
|
||||
|
||||
@DeprecatedConfigurationProperty(reason = "replaced to support multiple prefixes",
|
||||
replacement = PREFIX + ".prefixes")
|
||||
public String getPrefix() {
|
||||
if (CollectionUtils.isEmpty(this.prefixes)) {
|
||||
return null;
|
||||
}
|
||||
return this.prefixes.get(0);
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
public void setPrefix(String prefix) {
|
||||
this.prefix = prefix;
|
||||
if (prefix != null) {
|
||||
this.prefixes = new ArrayList<>(Collections.singletonList(prefix));
|
||||
}
|
||||
else {
|
||||
this.prefixes = new ArrayList<>();
|
||||
}
|
||||
}
|
||||
|
||||
public @NotEmpty String getDefaultContext() {
|
||||
@@ -166,7 +191,7 @@ public class ConsulConfigProperties {
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return new ToStringCreator(this).append("enabled", this.enabled).append("prefix", this.prefix)
|
||||
return new ToStringCreator(this).append("enabled", this.enabled).append("prefixes", this.prefixes)
|
||||
.append("defaultContext", this.defaultContext).append("profileSeparator", this.profileSeparator)
|
||||
.append("format", this.format).append("dataKey", this.dataKey).append("aclToken", this.aclToken)
|
||||
.append("watch", this.watch).append("failFast", this.failFast).append("name", this.name).toString();
|
||||
|
||||
@@ -85,8 +85,8 @@ public class ConsulPropertySourceLocator implements PropertySourceLocator, Consu
|
||||
CompositePropertySource composite = new CompositePropertySource("consul");
|
||||
|
||||
for (String propertySourceContext : this.contexts) {
|
||||
ConsulPropertySource propertySource = sources.createPropertySource(propertySourceContext, true,
|
||||
this.consul, contextIndex::put);
|
||||
ConsulPropertySource propertySource = sources.createPropertySource(propertySourceContext, this.consul,
|
||||
contextIndex::put);
|
||||
if (propertySource != null) {
|
||||
composite.addPropertySource(propertySource);
|
||||
}
|
||||
|
||||
@@ -53,24 +53,25 @@ public class ConsulPropertySources {
|
||||
|
||||
public List<String> getAutomaticContexts(List<String> profiles, boolean reverse) {
|
||||
List<String> contexts = new ArrayList<>();
|
||||
String prefix = properties.getPrefix();
|
||||
String defaultContext = getContext(prefix, properties.getDefaultContext());
|
||||
List<String> suffixes = getSuffixes();
|
||||
for (String suffix : suffixes) {
|
||||
contexts.add(defaultContext + suffix);
|
||||
}
|
||||
for (String suffix : suffixes) {
|
||||
addProfiles(contexts, defaultContext, profiles, suffix);
|
||||
}
|
||||
for (String prefix : this.properties.getPrefixes()) {
|
||||
String defaultContext = getContext(prefix, properties.getDefaultContext());
|
||||
List<String> suffixes = getSuffixes();
|
||||
for (String suffix : suffixes) {
|
||||
contexts.add(defaultContext + suffix);
|
||||
}
|
||||
for (String suffix : suffixes) {
|
||||
addProfiles(contexts, defaultContext, profiles, suffix);
|
||||
}
|
||||
|
||||
// getName() defaults to ${spring.application.name} or application
|
||||
String baseContext = getContext(prefix, properties.getName());
|
||||
// getName() defaults to ${spring.application.name} or application
|
||||
String baseContext = getContext(prefix, properties.getName());
|
||||
|
||||
for (String suffix : suffixes) {
|
||||
contexts.add(baseContext + suffix);
|
||||
}
|
||||
for (String suffix : suffixes) {
|
||||
addProfiles(contexts, baseContext, profiles, suffix);
|
||||
for (String suffix : suffixes) {
|
||||
contexts.add(baseContext + suffix);
|
||||
}
|
||||
for (String suffix : suffixes) {
|
||||
addProfiles(contexts, baseContext, profiles, suffix);
|
||||
}
|
||||
}
|
||||
if (reverse) {
|
||||
// we build them backwards, first wins, so reverse
|
||||
|
||||
@@ -16,6 +16,8 @@
|
||||
|
||||
package org.springframework.cloud.consul.config;
|
||||
|
||||
import java.util.Collections;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import org.springframework.boot.test.context.runner.ApplicationContextRunner;
|
||||
@@ -39,7 +41,7 @@ public class ConsulConfigBootstrapConfigurationTests {
|
||||
this.contextRunner.withUserConfiguration(TestConfig.class).withInitializer(new ConsulTestcontainers())
|
||||
.withUserConfiguration(ConsulConfigBootstrapConfiguration.class).run(context -> {
|
||||
ConsulConfigProperties config = context.getBean(ConsulConfigProperties.class);
|
||||
assertThat(config.getPrefix()).as("Prefix did not match").isEqualTo("platform-config");
|
||||
assertThat(config.getPrefixes().get(0)).as("Prefix did not match").isEqualTo("platform-config");
|
||||
assertThat(config.getDefaultContext()).as("Default context did not match").isEqualTo("defaults");
|
||||
});
|
||||
}
|
||||
@@ -53,7 +55,7 @@ public class ConsulConfigBootstrapConfigurationTests {
|
||||
this.contextRunner.withUserConfiguration(ConsulConfigBootstrapConfiguration.class)
|
||||
.withInitializer(new ConsulTestcontainers()).run(context -> {
|
||||
ConsulConfigProperties config = context.getBean(ConsulConfigProperties.class);
|
||||
assertThat(config.getPrefix()).as("Prefix did not match").isEqualTo("config");
|
||||
assertThat(config.getPrefixes().get(0)).as("Prefix did not match").isEqualTo("config");
|
||||
assertThat(config.getDefaultContext()).as("Default context did not match").isEqualTo("application");
|
||||
});
|
||||
}
|
||||
@@ -66,7 +68,7 @@ public class ConsulConfigBootstrapConfigurationTests {
|
||||
@Bean
|
||||
public ConsulConfigProperties consulConfigProperties() {
|
||||
ConsulConfigProperties config = new ConsulConfigProperties();
|
||||
config.setPrefix("platform-config");
|
||||
config.setPrefixes(Collections.singletonList("platform-config"));
|
||||
config.setDefaultContext("defaults");
|
||||
return config;
|
||||
}
|
||||
|
||||
@@ -68,7 +68,7 @@ public class ConsulConfigDataCustomizationIntegrationTests {
|
||||
context = application.run("--spring.application.name=" + APP_NAME,
|
||||
"--spring.config.import=consul:" + ConsulTestcontainers.getHost() + ":"
|
||||
+ ConsulTestcontainers.getPort(),
|
||||
"--spring.cloud.consul.config.prefix=" + ROOT, "--spring.cloud.consul.config.watch.delay=10");
|
||||
"--spring.cloud.consul.config.prefixes=" + ROOT, "--spring.cloud.consul.config.watch.delay=10");
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,119 @@
|
||||
/*
|
||||
* Copyright 2013-2019 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.consul.config;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
import com.ecwid.consul.v1.ConsulClient;
|
||||
import org.junit.jupiter.api.AfterAll;
|
||||
import org.junit.jupiter.api.BeforeAll;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.boot.WebApplicationType;
|
||||
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
|
||||
import org.springframework.boot.builder.SpringApplicationBuilder;
|
||||
import org.springframework.cloud.consul.test.ConsulTestcontainers;
|
||||
import org.springframework.context.ConfigurableApplicationContext;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.core.env.ConfigurableEnvironment;
|
||||
import org.springframework.test.annotation.DirtiesContext;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
/**
|
||||
* @author Spencer Gibb
|
||||
*/
|
||||
@DirtiesContext
|
||||
public class ConsulConfigDataMultiplePrefixesIntegrationTests {
|
||||
|
||||
private static final String APP_NAME = "testConsulConfigData";
|
||||
|
||||
private static final String PREFIX = "_configDataMultiplePrefixesIntegrationTests_config__";
|
||||
|
||||
private static final String PREFIX2 = "_configDataMultiplePrefixesIntegrationTests_config2__";
|
||||
|
||||
private static final String ROOT = PREFIX + UUID.randomUUID();
|
||||
|
||||
private static final String ROOT2 = PREFIX2 + UUID.randomUUID();
|
||||
|
||||
private static final String VALUE1 = "testPropVal";
|
||||
|
||||
private static final String TEST_PROP = "testProp";
|
||||
|
||||
private static final String TEST_PROP_CANONICAL = "test-prop";
|
||||
|
||||
private static final String KEY1 = ROOT + "/application/" + TEST_PROP;
|
||||
|
||||
private static final String VALUE2 = "testPropVal2";
|
||||
|
||||
private static final String TEST_PROP2 = "testProp2";
|
||||
|
||||
private static final String TEST_PROP2_CANONICAL = "test-prop2";
|
||||
|
||||
private static final String KEY2 = ROOT2 + "/application/" + TEST_PROP2;
|
||||
|
||||
private static ConfigurableApplicationContext context;
|
||||
|
||||
private static ConfigurableEnvironment environment;
|
||||
|
||||
private static ConsulClient client;
|
||||
|
||||
@BeforeAll
|
||||
public static void setup() {
|
||||
ConsulTestcontainers.start();
|
||||
client = ConsulTestcontainers.client();
|
||||
client.deleteKVValues(PREFIX);
|
||||
client.deleteKVValues(PREFIX2);
|
||||
client.setKVValue(KEY1, VALUE1);
|
||||
client.setKVValue(KEY2, VALUE2);
|
||||
|
||||
context = new SpringApplicationBuilder(Config.class).web(WebApplicationType.NONE).run(
|
||||
"--logging.level.org.springframework.cloud.consul.config.ConfigWatch=TRACE",
|
||||
"--spring.application.name=" + APP_NAME,
|
||||
"--spring.config.import=consul:" + ConsulTestcontainers.getHost() + ":"
|
||||
+ ConsulTestcontainers.getPort(),
|
||||
"--spring.cloud.consul.config.prefixes=" + ROOT + "," + ROOT2,
|
||||
"--spring.cloud.consul.config.watch.delay=10", "--spring.cloud.consul.config.watch.wait-time=1");
|
||||
|
||||
client = context.getBean(ConsulClient.class);
|
||||
environment = context.getEnvironment();
|
||||
}
|
||||
|
||||
@AfterAll
|
||||
public static void teardown() {
|
||||
client.deleteKVValues(PREFIX);
|
||||
client.deleteKVValues(PREFIX2);
|
||||
if (context != null) {
|
||||
context.close();
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void propertyLoaded() {
|
||||
String testProp = environment.getProperty(TEST_PROP_CANONICAL);
|
||||
assertThat(testProp).as(TEST_PROP + " was wrong").isEqualTo(VALUE1);
|
||||
String testProp2 = environment.getProperty(TEST_PROP2_CANONICAL);
|
||||
assertThat(testProp2).as(TEST_PROP2 + " was wrong").isEqualTo(VALUE2);
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@EnableAutoConfiguration
|
||||
static class Config {
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -91,7 +91,7 @@ public class ConsulPropertySourceLocatorTests {
|
||||
"--spring.application.name=" + APP_NAME, "--spring.config.use-legacy-processing=true",
|
||||
"--spring.cloud.consul.host=" + ConsulTestcontainers.getHost(),
|
||||
"--spring.cloud.consul.port=" + ConsulTestcontainers.getPort(),
|
||||
"--spring.cloud.consul.config.prefix=" + ROOT, "--spring.cloud.consul.config.watch.delay=10");
|
||||
"--spring.cloud.consul.config.prefixes=" + ROOT, "--spring.cloud.consul.config.watch.delay=10");
|
||||
|
||||
client = context.getBean(ConsulClient.class);
|
||||
environment = context.getEnvironment();
|
||||
|
||||
Reference in New Issue
Block a user