Adds ConfigData support for ConfigServerInstanceProvider.Function
DiscoveryClient implementation can now register a ConfigServerInstanceProvider.Function using a Bootstrapper registered in spring.factories. If spring.cloud.config.discovery.enabled is true, config client will use the above function to locate the initial instance of config server. A ConfigServerInstanceMonitor will be promoted to the application context so subsequent updates to the instanecs of config server will be reported.
This commit is contained in:
@@ -21,6 +21,7 @@ import org.springframework.boot.actuate.autoconfigure.health.ConditionalOnEnable
|
||||
import org.springframework.boot.actuate.health.HealthIndicator;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||
import org.springframework.cloud.context.refresh.ContextRefresher;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
@@ -43,6 +44,7 @@ import org.springframework.core.env.Environment;
|
||||
public class ConfigClientAutoConfiguration {
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
public ConfigClientProperties configClientProperties(Environment environment, ApplicationContext context) {
|
||||
if (context.getParent() != null && BeanFactoryUtils.beanNamesForTypeIncludingAncestors(context.getParent(),
|
||||
ConfigClientProperties.class).length > 0) {
|
||||
|
||||
@@ -44,6 +44,11 @@ public class ConfigClientProperties {
|
||||
*/
|
||||
public static final String PREFIX = "spring.cloud.config";
|
||||
|
||||
/**
|
||||
* Name of config discovery enabled property.
|
||||
*/
|
||||
public static final String CONFIG_DISCOVERY_ENABLED = PREFIX + ".discovery.enabled";
|
||||
|
||||
/**
|
||||
* Token header name.
|
||||
*/
|
||||
|
||||
@@ -73,6 +73,10 @@ public class ConfigServerConfigDataLoader implements ConfigDataLoader<ConfigServ
|
||||
@Override
|
||||
// TODO: implement retry LoaderInterceptor
|
||||
public ConfigData load(ConfigDataLoaderContext context, ConfigServerConfigDataLocation location) {
|
||||
if (context.getBootstrapContext().isRegistered(ConfigServerInstanceMonitor.class)) {
|
||||
// force initialization if needed
|
||||
context.getBootstrapContext().get(ConfigServerInstanceMonitor.class);
|
||||
}
|
||||
if (context.getBootstrapContext().isRegistered(LoaderInterceptor.class)) {
|
||||
LoaderInterceptor interceptor = context.getBootstrapContext().get(LoaderInterceptor.class);
|
||||
Binder binder = context.getBootstrapContext().get(Binder.class);
|
||||
|
||||
@@ -37,6 +37,7 @@ import org.springframework.util.StringUtils;
|
||||
import org.springframework.web.client.RestTemplate;
|
||||
|
||||
import static org.springframework.cloud.config.client.ConfigClientProperties.AUTHORIZATION;
|
||||
import static org.springframework.cloud.config.client.ConfigClientProperties.CONFIG_DISCOVERY_ENABLED;
|
||||
|
||||
public class ConfigServerConfigDataLocationResolver
|
||||
implements ConfigDataLocationResolver<ConfigServerConfigDataLocation>, Ordered {
|
||||
@@ -109,10 +110,10 @@ public class ConfigServerConfigDataLocationResolver
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
public List<ConfigServerConfigDataLocation> resolveProfileSpecific(ConfigDataLocationResolverContext context,
|
||||
String location, boolean optional, Profiles profiles) {
|
||||
public List<ConfigServerConfigDataLocation> resolveProfileSpecific(
|
||||
ConfigDataLocationResolverContext resolverContext, String location, boolean optional, Profiles profiles) {
|
||||
|
||||
ConfigClientProperties properties = loadProperties(context.getBinder());
|
||||
ConfigClientProperties properties = loadProperties(resolverContext.getBinder());
|
||||
|
||||
String uris = (location.startsWith(getPrefix())) ? location.substring(getPrefix().length()) : location;
|
||||
|
||||
@@ -121,15 +122,41 @@ public class ConfigServerConfigDataLocationResolver
|
||||
properties.setUri(uri);
|
||||
}
|
||||
|
||||
ConfigurableBootstrapContext bootstrapContext = context.getBootstrapContext();
|
||||
ConfigurableBootstrapContext bootstrapContext = resolverContext.getBootstrapContext();
|
||||
bootstrapContext.registerIfAbsent(ConfigClientProperties.class, InstanceSupplier.of(properties));
|
||||
bootstrapContext.addCloseListener(event -> event.getApplicationContext().getBeanFactory().registerSingleton(
|
||||
"configDataConfigClientProperties", event.getBootstrapContext().get(ConfigClientProperties.class)));
|
||||
|
||||
bootstrapContext.registerIfAbsent(RestTemplate.class, InstanceSupplier.from(() -> {
|
||||
ConfigClientProperties props = bootstrapContext.get(ConfigClientProperties.class);
|
||||
bootstrapContext.registerIfAbsent(RestTemplate.class, context -> {
|
||||
ConfigClientProperties props = context.get(ConfigClientProperties.class);
|
||||
return createRestTemplate(props);
|
||||
}));
|
||||
});
|
||||
|
||||
boolean discoveryEnabled = resolverContext.getBinder().bind(CONFIG_DISCOVERY_ENABLED, Boolean.class)
|
||||
.orElse(false);
|
||||
|
||||
if (discoveryEnabled) {
|
||||
// register ConfigServerInstanceMonitor
|
||||
bootstrapContext.registerIfAbsent(ConfigServerInstanceMonitor.class, context -> {
|
||||
ConfigServerInstanceProvider.Function function = context
|
||||
.get(ConfigServerInstanceProvider.Function.class);
|
||||
ConfigServerInstanceProvider instanceProvider = new ConfigServerInstanceProvider(function);
|
||||
instanceProvider.setLog(log);
|
||||
ConfigClientProperties clientProperties = context.get(ConfigClientProperties.class);
|
||||
ConfigServerInstanceMonitor instanceMonitor = new ConfigServerInstanceMonitor(log, clientProperties,
|
||||
instanceProvider);
|
||||
instanceMonitor.setRefreshOnStartup(false);
|
||||
instanceMonitor.refresh();
|
||||
return instanceMonitor;
|
||||
});
|
||||
// promote ConfigServerInstanceMonitor to bean so updates can be made to config client uri
|
||||
bootstrapContext.addCloseListener(event -> {
|
||||
ConfigServerInstanceMonitor configServerInstanceMonitor = event.getBootstrapContext()
|
||||
.get(ConfigServerInstanceMonitor.class);
|
||||
event.getApplicationContext().getBeanFactory().registerSingleton("configServerInstanceMonitor",
|
||||
configServerInstanceMonitor);
|
||||
});
|
||||
}
|
||||
|
||||
List<ConfigServerConfigDataLocation> locations = new ArrayList<>();
|
||||
locations.add(new ConfigServerConfigDataLocation(properties, optional, profiles));
|
||||
|
||||
@@ -0,0 +1,139 @@
|
||||
/*
|
||||
* Copyright 2013-2020 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.config.client;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
|
||||
import org.springframework.cloud.client.ServiceInstance;
|
||||
import org.springframework.cloud.client.discovery.event.HeartbeatEvent;
|
||||
import org.springframework.cloud.client.discovery.event.HeartbeatMonitor;
|
||||
import org.springframework.context.ApplicationEvent;
|
||||
import org.springframework.context.event.ContextRefreshedEvent;
|
||||
import org.springframework.context.event.SmartApplicationListener;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
final class ConfigServerInstanceMonitor implements SmartApplicationListener {
|
||||
|
||||
private final Log log;
|
||||
|
||||
private final ConfigClientProperties config;
|
||||
|
||||
private final ConfigServerInstanceProvider instanceProvider;
|
||||
|
||||
private final HeartbeatMonitor monitor = new HeartbeatMonitor();
|
||||
|
||||
/**
|
||||
* If bootstrap, this should be true, for config data false.
|
||||
*/
|
||||
private boolean refreshOnStartup = true;
|
||||
|
||||
ConfigServerInstanceMonitor(Log log, ConfigClientProperties config, ConfigServerInstanceProvider instanceProvider) {
|
||||
this.log = log;
|
||||
this.config = config;
|
||||
this.instanceProvider = instanceProvider;
|
||||
}
|
||||
|
||||
void setRefreshOnStartup(boolean refreshOnStartup) {
|
||||
this.refreshOnStartup = refreshOnStartup;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supportsEventType(Class<? extends ApplicationEvent> eventType) {
|
||||
return ContextRefreshedEvent.class.isAssignableFrom(eventType)
|
||||
|| HeartbeatEvent.class.isAssignableFrom(eventType);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onApplicationEvent(ApplicationEvent event) {
|
||||
if (event instanceof ContextRefreshedEvent) {
|
||||
startup((ContextRefreshedEvent) event);
|
||||
}
|
||||
else if (event instanceof HeartbeatEvent) {
|
||||
heartbeat((HeartbeatEvent) event);
|
||||
}
|
||||
}
|
||||
|
||||
public void startup(ContextRefreshedEvent event) {
|
||||
if (refreshOnStartup) {
|
||||
refresh();
|
||||
}
|
||||
}
|
||||
|
||||
public void heartbeat(HeartbeatEvent event) {
|
||||
if (this.monitor.update(event.getValue())) {
|
||||
refresh();
|
||||
}
|
||||
}
|
||||
|
||||
void refresh() {
|
||||
try {
|
||||
String serviceId = this.config.getDiscovery().getServiceId();
|
||||
Assert.hasText(serviceId, () -> ConfigClientProperties.PREFIX + ".service-id may not be null or empty");
|
||||
List<String> listOfUrls = new ArrayList<>();
|
||||
List<ServiceInstance> serviceInstances = this.instanceProvider.getConfigServerInstances(serviceId);
|
||||
|
||||
for (int i = 0; i < serviceInstances.size(); i++) {
|
||||
|
||||
ServiceInstance server = serviceInstances.get(i);
|
||||
String url = getHomePage(server);
|
||||
|
||||
if (server.getMetadata().containsKey("password")) {
|
||||
String user = server.getMetadata().get("user");
|
||||
user = user == null ? "user" : user;
|
||||
this.config.setUsername(user);
|
||||
String password = server.getMetadata().get("password");
|
||||
this.config.setPassword(password);
|
||||
}
|
||||
|
||||
if (server.getMetadata().containsKey("configPath")) {
|
||||
String path = server.getMetadata().get("configPath");
|
||||
if (url.endsWith("/") && path.startsWith("/")) {
|
||||
url = url.substring(0, url.length() - 1);
|
||||
}
|
||||
url = url + path;
|
||||
}
|
||||
|
||||
listOfUrls.add(url);
|
||||
}
|
||||
|
||||
if (log.isDebugEnabled()) {
|
||||
log.debug("Updating config uris to " + listOfUrls);
|
||||
}
|
||||
|
||||
String[] uri = new String[listOfUrls.size()];
|
||||
uri = listOfUrls.toArray(uri);
|
||||
this.config.setUri(uri);
|
||||
|
||||
}
|
||||
catch (Exception ex) {
|
||||
if (this.config.isFailFast()) {
|
||||
throw ex;
|
||||
}
|
||||
else if (log.isWarnEnabled()) {
|
||||
log.warn("Could not locate configserver via discovery", ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private String getHomePage(ServiceInstance server) {
|
||||
return server.getUri().toString() + "/";
|
||||
}
|
||||
|
||||
}
|
||||
@@ -32,10 +32,11 @@ import org.springframework.retry.annotation.Retryable;
|
||||
*/
|
||||
public class ConfigServerInstanceProvider {
|
||||
|
||||
private static Log logger = LogFactory.getLog(ConfigServerInstanceProvider.class);
|
||||
private Log log = LogFactory.getLog(getClass());
|
||||
|
||||
private final Function function;
|
||||
|
||||
@Deprecated
|
||||
public ConfigServerInstanceProvider(DiscoveryClient client) {
|
||||
this.function = client::getInstances;
|
||||
}
|
||||
@@ -44,15 +45,23 @@ public class ConfigServerInstanceProvider {
|
||||
this.function = function;
|
||||
}
|
||||
|
||||
void setLog(Log log) {
|
||||
this.log = log;
|
||||
}
|
||||
|
||||
@Retryable(interceptor = "configServerRetryInterceptor")
|
||||
public List<ServiceInstance> getConfigServerInstances(String serviceId) {
|
||||
logger.debug("Locating configserver (" + serviceId + ") via discovery");
|
||||
if (log.isDebugEnabled()) {
|
||||
log.debug("Locating configserver (" + serviceId + ") via discovery");
|
||||
}
|
||||
List<ServiceInstance> instances = this.function.apply(serviceId);
|
||||
if (instances.isEmpty()) {
|
||||
throw new IllegalStateException("No instances found of configserver (" + serviceId + ")");
|
||||
}
|
||||
logger.debug(
|
||||
"Located configserver (" + serviceId + ") via discovery. No of instances found: " + instances.size());
|
||||
if (log.isDebugEnabled()) {
|
||||
log.debug("Located configserver (" + serviceId + ") via discovery. No of instances found: " + instances
|
||||
.size());
|
||||
}
|
||||
return instances;
|
||||
}
|
||||
|
||||
|
||||
@@ -16,26 +16,16 @@
|
||||
|
||||
package org.springframework.cloud.config.client;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
|
||||
import org.springframework.beans.factory.ObjectProvider;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||
import org.springframework.cloud.client.ServiceInstance;
|
||||
import org.springframework.cloud.client.discovery.DiscoveryClient;
|
||||
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
|
||||
import org.springframework.cloud.client.discovery.event.HeartbeatEvent;
|
||||
import org.springframework.cloud.client.discovery.event.HeartbeatMonitor;
|
||||
import org.springframework.cloud.commons.util.UtilAutoConfiguration;
|
||||
import org.springframework.context.ApplicationEvent;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.Import;
|
||||
import org.springframework.context.event.ContextRefreshedEvent;
|
||||
import org.springframework.context.event.SmartApplicationListener;
|
||||
|
||||
/**
|
||||
* Bootstrap configuration for a config client that wants to lookup the config server via
|
||||
@@ -43,14 +33,12 @@ import org.springframework.context.event.SmartApplicationListener;
|
||||
*
|
||||
* @author Dave Syer
|
||||
*/
|
||||
@ConditionalOnProperty(value = "spring.cloud.config.discovery.enabled", matchIfMissing = false)
|
||||
@ConditionalOnProperty(ConfigClientProperties.CONFIG_DISCOVERY_ENABLED)
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
@Import({ UtilAutoConfiguration.class })
|
||||
@EnableDiscoveryClient
|
||||
public class DiscoveryClientConfigServiceBootstrapConfiguration {
|
||||
|
||||
private static Log logger = LogFactory.getLog(DiscoveryClientConfigServiceBootstrapConfiguration.class);
|
||||
|
||||
@Bean
|
||||
public ConfigServerInstanceProvider configServerInstanceProvider(
|
||||
ObjectProvider<ConfigServerInstanceProvider.Function> function,
|
||||
@@ -61,105 +49,16 @@ public class DiscoveryClientConfigServiceBootstrapConfiguration {
|
||||
}
|
||||
DiscoveryClient client = discoveryClient.getIfAvailable();
|
||||
if (client == null) {
|
||||
throw new IllegalStateException("ConfigServerInstanceProvider reqiures a DiscoveryClient or Function");
|
||||
throw new IllegalStateException("ConfigServerInstanceProvider requires a DiscoveryClient or Function");
|
||||
}
|
||||
return new ConfigServerInstanceProvider(client);
|
||||
return new ConfigServerInstanceProvider(client::getInstances);
|
||||
}
|
||||
|
||||
@Bean
|
||||
public SmartApplicationListener heartbeatListener(ConfigClientProperties properties,
|
||||
public ConfigServerInstanceMonitor configServerInstanceMonitor(ConfigClientProperties properties,
|
||||
ConfigServerInstanceProvider provider) {
|
||||
return new HeartbeatListener(properties, provider);
|
||||
}
|
||||
|
||||
private final static class HeartbeatListener implements SmartApplicationListener {
|
||||
|
||||
private final ConfigClientProperties config;
|
||||
|
||||
private final ConfigServerInstanceProvider instanceProvider;
|
||||
|
||||
private final HeartbeatMonitor monitor = new HeartbeatMonitor();
|
||||
|
||||
private HeartbeatListener(ConfigClientProperties config, ConfigServerInstanceProvider instanceProvider) {
|
||||
this.config = config;
|
||||
this.instanceProvider = instanceProvider;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supportsEventType(Class<? extends ApplicationEvent> eventType) {
|
||||
return ContextRefreshedEvent.class.isAssignableFrom(eventType)
|
||||
|| HeartbeatEvent.class.isAssignableFrom(eventType);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onApplicationEvent(ApplicationEvent event) {
|
||||
if (event instanceof ContextRefreshedEvent) {
|
||||
startup((ContextRefreshedEvent) event);
|
||||
}
|
||||
else if (event instanceof HeartbeatEvent) {
|
||||
heartbeat((HeartbeatEvent) event);
|
||||
}
|
||||
}
|
||||
|
||||
public void startup(ContextRefreshedEvent event) {
|
||||
refresh();
|
||||
}
|
||||
|
||||
public void heartbeat(HeartbeatEvent event) {
|
||||
if (this.monitor.update(event.getValue())) {
|
||||
refresh();
|
||||
}
|
||||
}
|
||||
|
||||
private void refresh() {
|
||||
try {
|
||||
String serviceId = this.config.getDiscovery().getServiceId();
|
||||
List<String> listOfUrls = new ArrayList<>();
|
||||
List<ServiceInstance> serviceInstances = this.instanceProvider.getConfigServerInstances(serviceId);
|
||||
|
||||
for (int i = 0; i < serviceInstances.size(); i++) {
|
||||
|
||||
ServiceInstance server = serviceInstances.get(i);
|
||||
String url = getHomePage(server);
|
||||
|
||||
if (server.getMetadata().containsKey("password")) {
|
||||
String user = server.getMetadata().get("user");
|
||||
user = user == null ? "user" : user;
|
||||
this.config.setUsername(user);
|
||||
String password = server.getMetadata().get("password");
|
||||
this.config.setPassword(password);
|
||||
}
|
||||
|
||||
if (server.getMetadata().containsKey("configPath")) {
|
||||
String path = server.getMetadata().get("configPath");
|
||||
if (url.endsWith("/") && path.startsWith("/")) {
|
||||
url = url.substring(0, url.length() - 1);
|
||||
}
|
||||
url = url + path;
|
||||
}
|
||||
|
||||
listOfUrls.add(url);
|
||||
}
|
||||
|
||||
String[] uri = new String[listOfUrls.size()];
|
||||
uri = listOfUrls.toArray(uri);
|
||||
this.config.setUri(uri);
|
||||
|
||||
}
|
||||
catch (Exception ex) {
|
||||
if (this.config.isFailFast()) {
|
||||
throw ex;
|
||||
}
|
||||
else {
|
||||
logger.warn("Could not locate configserver via discovery", ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private String getHomePage(ServiceInstance server) {
|
||||
return server.getUri().toString() + "/";
|
||||
}
|
||||
|
||||
return new ConfigServerInstanceMonitor(LogFactory.getLog(ConfigServerInstanceMonitor.class), properties,
|
||||
provider);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,295 @@
|
||||
/*
|
||||
* 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.config.client;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.Set;
|
||||
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.Disabled;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.mockito.Mockito;
|
||||
|
||||
import org.springframework.boot.BootstrapRegistry;
|
||||
import org.springframework.boot.Bootstrapper;
|
||||
import org.springframework.boot.SpringBootConfiguration;
|
||||
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
|
||||
import org.springframework.boot.builder.SpringApplicationBuilder;
|
||||
import org.springframework.cloud.client.DefaultServiceInstance;
|
||||
import org.springframework.cloud.client.ServiceInstance;
|
||||
import org.springframework.cloud.client.discovery.DiscoveryClient;
|
||||
import org.springframework.cloud.client.discovery.event.HeartbeatEvent;
|
||||
import org.springframework.cloud.config.client.ConfigClientProperties.Credentials;
|
||||
import org.springframework.context.ConfigurableApplicationContext;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.api.Assertions.fail;
|
||||
import static org.mockito.BDDMockito.given;
|
||||
import static org.mockito.Mockito.times;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.springframework.cloud.config.client.ConfigClientProperties.Discovery.DEFAULT_CONFIG_SERVER;
|
||||
|
||||
/**
|
||||
* @author Dave Syer
|
||||
*/
|
||||
public class DiscoveryClientConfigDataConfigurationTests {
|
||||
|
||||
protected ConfigurableApplicationContext context;
|
||||
|
||||
protected DiscoveryClient client = Mockito.mock(DiscoveryClient.class);
|
||||
|
||||
protected ServiceInstance info = new DefaultServiceInstance("app:8877", "app", "foo", 8877, false);
|
||||
|
||||
@AfterEach
|
||||
public void close() {
|
||||
if (this.context != null) {
|
||||
this.context.close();
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void offByDefault() {
|
||||
context = new SpringApplicationBuilder(TestConfig.class)
|
||||
.addBootstrapper(registry -> registry.addCloseListener(event -> {
|
||||
try {
|
||||
event.getBootstrapContext().get(ConfigServerInstanceMonitor.class);
|
||||
fail("ConfigServerInstanceMonitor was created when it shouldn't");
|
||||
}
|
||||
catch (IllegalStateException e) {
|
||||
// expected
|
||||
}
|
||||
})).run();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void onWhenRequested() {
|
||||
givenDiscoveryClientReturnsInfo();
|
||||
|
||||
setupAndRun();
|
||||
|
||||
verifyDiscoveryClientCalledOnce();
|
||||
expectConfigClientPropertiesHasConfigurationFromDiscovery();
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void onWhenHeartbeat() {
|
||||
setupAndRun();
|
||||
|
||||
givenDiscoveryClientReturnsInfo();
|
||||
verifyDiscoveryClientCalledOnce();
|
||||
|
||||
this.context.publishEvent(new HeartbeatEvent(this.context, "new"));
|
||||
|
||||
expectConfigClientPropertiesHasConfigurationFromDiscovery();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void secureWhenRequested() {
|
||||
this.info = new DefaultServiceInstance("app:443", "app", "foo", 443, true);
|
||||
givenDiscoveryClientReturnsInfo();
|
||||
|
||||
setupAndRun();
|
||||
|
||||
verifyDiscoveryClientCalledOnce();
|
||||
expectConfigClientPropertiesHasConfiguration("https://foo:443/");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void multipleInstancesReturnedFromDiscovery() {
|
||||
ServiceInstance info1 = new DefaultServiceInstance("app1:8888", "app", "localhost", 8888, true);
|
||||
ServiceInstance info2 = new DefaultServiceInstance("app2:8888", "app", "localhost1", 8888, false);
|
||||
givenDiscoveryClientReturnsInfo(info1, info2);
|
||||
|
||||
setupAndRun();
|
||||
|
||||
verifyDiscoveryClientCalledOnce();
|
||||
|
||||
ConfigClientProperties properties = this.context.getBean(ConfigClientProperties.class);
|
||||
assertThat(properties.getUri().length).isEqualTo(2);
|
||||
Credentials credentials1 = properties.getCredentials(0);
|
||||
Credentials credentials2 = properties.getCredentials(1);
|
||||
assertThat(credentials1.getUri()).isEqualTo("https://localhost:8888/");
|
||||
assertThat(credentials2.getUri()).isEqualTo("http://localhost1:8888/");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void setsPasssword() {
|
||||
this.info.getMetadata().put("password", "bar");
|
||||
givenDiscoveryClientReturnsInfo();
|
||||
|
||||
setupAndRun();
|
||||
|
||||
ConfigClientProperties locator = this.context.getBean(ConfigClientProperties.class);
|
||||
Credentials credentials = locator.getCredentials(0);
|
||||
assertThat(credentials.getUri()).isEqualTo("http://foo:8877/");
|
||||
assertThat(credentials.getPassword()).isEqualTo("bar");
|
||||
assertThat(credentials.getUsername()).isEqualTo("user");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void setsPath() {
|
||||
this.info.getMetadata().put("configPath", "/bar");
|
||||
givenDiscoveryClientReturnsInfo();
|
||||
|
||||
setupAndRun();
|
||||
|
||||
expectConfigClientPropertiesHasConfiguration("http://foo:8877/bar");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldFailGetConfigServerInstanceFromDiscoveryClient() {
|
||||
givenDiscoveryClientReturnsNoInfo();
|
||||
|
||||
setupAndRun();
|
||||
|
||||
verifyDiscoveryClientCalledOnce();
|
||||
expectConfigClientPropertiesHasDefaultConfiguration();
|
||||
}
|
||||
|
||||
@Test
|
||||
@Disabled
|
||||
public void shouldRetryAndSucceedGetConfigServerInstanceFromDiscoveryClient() {
|
||||
givenDiscoveryClientReturnsInfoOnThirdTry();
|
||||
|
||||
context = setup("spring.cloud.config.retry.maxAttempts=3", "spring.cloud.config.retry.initialInterval=10",
|
||||
"spring.cloud.config.fail-fast=true").run();
|
||||
|
||||
verifyDiscoveryClientCalledThreeTimes();
|
||||
|
||||
this.context.publishEvent(new HeartbeatEvent(this.context, "new"));
|
||||
|
||||
expectConfigClientPropertiesHasConfigurationFromDiscovery();
|
||||
}
|
||||
|
||||
@Test
|
||||
@Disabled
|
||||
public void shouldNotRetryIfNotFailFastPropertySet() {
|
||||
givenDiscoveryClientReturnsInfoOnThirdTry();
|
||||
|
||||
context = setup("spring.cloud.config.retry.maxAttempts=3", "spring.cloud.config.retry.initialInterval=10")
|
||||
.run();
|
||||
|
||||
verifyDiscoveryClientCalledOnce();
|
||||
expectConfigClientPropertiesHasDefaultConfiguration();
|
||||
}
|
||||
|
||||
@Test
|
||||
@Disabled
|
||||
public void shouldRetryAndFailWithExceptionGetConfigServerInstanceFromDiscoveryClient() {
|
||||
givenDiscoveryClientReturnsNoInfo();
|
||||
|
||||
// expectNoInstancesOfConfigServerException();
|
||||
|
||||
context = setup("spring.cloud.config.retry.maxAttempts=3", "spring.cloud.config.retry.initialInterval=10",
|
||||
"spring.cloud.config.fail-fast=true").run();
|
||||
}
|
||||
|
||||
@Test
|
||||
@Disabled
|
||||
public void shouldRetryAndFailWithMessageGetConfigServerInstanceFromDiscoveryClient() {
|
||||
givenDiscoveryClientReturnsNoInfo();
|
||||
|
||||
context = setup("spring.cloud.config.retry.maxAttempts=3", "spring.cloud.config.retry.initialInterval=10",
|
||||
"spring.cloud.config.fail-fast=false").run();
|
||||
|
||||
expectConfigClientPropertiesHasDefaultConfiguration();
|
||||
}
|
||||
|
||||
SpringApplicationBuilder setup(String... env) {
|
||||
return setup(true, env);
|
||||
}
|
||||
|
||||
SpringApplicationBuilder setup(boolean addInstanceProvider, String... env) {
|
||||
SpringApplicationBuilder builder = new SpringApplicationBuilder(TestConfig.class)
|
||||
.properties(addDefaultEnv(env));
|
||||
if (addInstanceProvider) {
|
||||
builder.addBootstrapper(instanceProviderBootstrapper());
|
||||
}
|
||||
return builder.addBootstrapper(registry -> registry.addCloseListener(event -> {
|
||||
ConfigServerInstanceMonitor monitor = event.getBootstrapContext().get(ConfigServerInstanceMonitor.class);
|
||||
assertThat(monitor).as("ConfigServerInstanceMonitor was not created when it should").isNotNull();
|
||||
}));
|
||||
}
|
||||
|
||||
protected Bootstrapper instanceProviderBootstrapper() {
|
||||
return registry -> registry.register(ConfigServerInstanceProvider.Function.class,
|
||||
BootstrapRegistry.InstanceSupplier.from(() -> this.client::getInstances));
|
||||
}
|
||||
|
||||
protected void setupAndRun(String... env) {
|
||||
context = setup(addDefaultEnv(env)).run();
|
||||
}
|
||||
|
||||
private String[] addDefaultEnv(String[] env) {
|
||||
Set<String> set = new LinkedHashSet<>();
|
||||
if (env != null && env.length > 0) {
|
||||
set.addAll(Arrays.asList(env));
|
||||
}
|
||||
set.add("spring.cloud.config.discovery.enabled=true");
|
||||
set.add("spring.config.import=optional:configserver:");
|
||||
return set.toArray(new String[0]);
|
||||
}
|
||||
|
||||
void givenDiscoveryClientReturnsInfo() {
|
||||
givenDiscoveryClientReturnsInfo(this.info);
|
||||
}
|
||||
|
||||
void givenDiscoveryClientReturnsInfo(ServiceInstance... instances) {
|
||||
given(this.client.getInstances(DEFAULT_CONFIG_SERVER)).willReturn(Arrays.asList(instances));
|
||||
}
|
||||
|
||||
void givenDiscoveryClientReturnsNoInfo() {
|
||||
given(this.client.getInstances(DEFAULT_CONFIG_SERVER)).willReturn(Collections.emptyList());
|
||||
}
|
||||
|
||||
void givenDiscoveryClientReturnsInfoOnThirdTry() {
|
||||
given(this.client.getInstances(DEFAULT_CONFIG_SERVER)).willReturn(Collections.emptyList())
|
||||
.willReturn(Collections.emptyList()).willReturn(Collections.singletonList(this.info));
|
||||
}
|
||||
|
||||
void verifyDiscoveryClientCalledOnce() {
|
||||
verify(this.client).getInstances(DEFAULT_CONFIG_SERVER);
|
||||
}
|
||||
|
||||
void verifyDiscoveryClientCalledThreeTimes() {
|
||||
verify(this.client, times(3)).getInstances(DEFAULT_CONFIG_SERVER);
|
||||
}
|
||||
|
||||
void expectConfigClientPropertiesHasConfigurationFromDiscovery() {
|
||||
expectConfigClientPropertiesHasConfiguration("http://foo:8877/");
|
||||
}
|
||||
|
||||
void expectConfigClientPropertiesHasDefaultConfiguration() {
|
||||
expectConfigClientPropertiesHasConfiguration("http://localhost:8888");
|
||||
}
|
||||
|
||||
void expectConfigClientPropertiesHasConfiguration(final String expectedUri) {
|
||||
ConfigClientProperties properties = this.context.getBean(ConfigClientProperties.class);
|
||||
Credentials credentials = properties.getCredentials(0);
|
||||
assertThat(credentials.getUri()).isEqualTo(expectedUri);
|
||||
}
|
||||
|
||||
@SpringBootConfiguration
|
||||
@EnableAutoConfiguration
|
||||
static class TestConfig {
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user