Adds support for config discovery in ConfigData.

ConsulConfigServerBootstrapper.java is registered in spring.factories

The bootstrapper registers an ConfigServerInstanceProvider.Function that uses ConsulDiscoveryClient
This commit is contained in:
spencergibb
2020-09-21 16:06:32 -04:00
parent c702c15be7
commit 5e7c2ba6ee
6 changed files with 155 additions and 24 deletions

View File

@@ -21,12 +21,12 @@ import java.util.Arrays;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.function.Supplier;
import java.util.stream.Collectors;
import com.ecwid.consul.v1.ConsulClient;
import org.apache.commons.logging.Log;
import org.springframework.boot.BootstrapContext;
import org.springframework.boot.BootstrapRegistry.InstanceSupplier;
import org.springframework.boot.ConfigurableBootstrapContext;
import org.springframework.boot.context.config.ConfigDataLocationNotFoundException;
@@ -82,12 +82,12 @@ public class ConsulConfigDataLocationResolver implements ConfigDataLocationResol
}
@Override
public List<ConsulConfigDataLocation> resolveProfileSpecific(ConfigDataLocationResolverContext context,
public List<ConsulConfigDataLocation> resolveProfileSpecific(ConfigDataLocationResolverContext resolverContext,
String location, boolean optional, Profiles profiles) throws ConfigDataLocationNotFoundException {
UriComponents locationUri = parseLocation(context, location);
UriComponents locationUri = parseLocation(resolverContext, location);
ConsulConfigProperties properties = loadConfigProperties(context.getBinder());
ConsulConfigProperties properties = loadConfigProperties(resolverContext.getBinder());
ConsulPropertySources consulPropertySources = new ConsulPropertySources(properties, log);
@@ -95,13 +95,14 @@ public class ConsulConfigDataLocationResolver implements ConfigDataLocationResol
? consulPropertySources.getAutomaticContexts(profiles.getAccepted())
: getCustomContexts(locationUri, properties);
registerBean(context, ConsulProperties.class, loadProperties(context.getBinder(), locationUri));
registerBean(resolverContext, ConsulProperties.class, loadProperties(resolverContext.getBinder(), locationUri));
registerAndPromoteBean(context, ConsulConfigProperties.class, () -> properties);
registerAndPromoteBean(resolverContext, ConsulConfigProperties.class, InstanceSupplier.of(properties));
registerAndPromoteBean(context, ConsulClient.class, () -> createConsulClient(context));
registerAndPromoteBean(resolverContext, ConsulClient.class, this::createConsulClient);
registerAndPromoteBean(context, ConsulConfigIndexes.class, ConsulConfigDataIndexes::new);
registerAndPromoteBean(resolverContext, ConsulConfigIndexes.class,
InstanceSupplier.from(ConsulConfigDataIndexes::new));
return contexts.stream().map(propertySourceContext -> new ConsulConfigDataLocation(propertySourceContext,
optional, properties, consulPropertySources)).collect(Collectors.toList());
@@ -144,12 +145,8 @@ public class ConsulConfigDataLocationResolver implements ConfigDataLocationResol
return UriComponentsBuilder.fromUriString(uri).build();
}
public <T> void registerBean(ConfigDataLocationResolverContext context, Class<T> type, T instance) {
context.getBootstrapContext().registerIfAbsent(type, InstanceSupplier.of(instance));
}
protected <T> void registerAndPromoteBean(ConfigDataLocationResolverContext context, Class<T> type,
Supplier<T> supplier) {
InstanceSupplier<T> supplier) {
registerBean(context, type, supplier);
context.getBootstrapContext().addCloseListener(event -> {
T instance = event.getBootstrapContext().get(type);
@@ -158,13 +155,18 @@ public class ConsulConfigDataLocationResolver implements ConfigDataLocationResol
});
}
protected <T> void registerBean(ConfigDataLocationResolverContext context, Class<T> type, Supplier<T> supplier) {
ConfigurableBootstrapContext bootstrapContext = context.getBootstrapContext();
bootstrapContext.registerIfAbsent(type, InstanceSupplier.from(supplier));
public <T> void registerBean(ConfigDataLocationResolverContext context, Class<T> type, T instance) {
context.getBootstrapContext().registerIfAbsent(type, InstanceSupplier.of(instance));
}
protected ConsulClient createConsulClient(ConfigDataLocationResolverContext context) {
ConsulProperties properties = context.getBootstrapContext().get(ConsulProperties.class);
protected <T> void registerBean(ConfigDataLocationResolverContext context, Class<T> type,
InstanceSupplier<T> supplier) {
ConfigurableBootstrapContext bootstrapContext = context.getBootstrapContext();
bootstrapContext.registerIfAbsent(type, supplier);
}
protected ConsulClient createConsulClient(BootstrapContext context) {
ConsulProperties properties = context.get(ConsulProperties.class);
return ConsulAutoConfiguration.createConsulClient(properties);
}

View File

@@ -18,12 +18,12 @@ package org.springframework.cloud.consul.config;
import java.util.Collections;
import java.util.List;
import java.util.function.Supplier;
import java.util.stream.Collectors;
import org.apache.commons.logging.LogFactory;
import org.junit.jupiter.api.Test;
import org.springframework.boot.BootstrapRegistry.InstanceSupplier;
import org.springframework.boot.context.config.ConfigDataLocationResolverContext;
import org.springframework.boot.context.config.Profiles;
import org.springframework.boot.context.properties.bind.Binder;
@@ -93,18 +93,18 @@ public class ConsulConfigDataLocationResolverTests {
return new ConsulConfigDataLocationResolver(LogFactory.getLog(getClass())) {
@Override
public <T> void registerBean(ConfigDataLocationResolverContext context, Class<T> type, T instance) {
// do nothing
}
@Override
protected <T> void registerBean(ConfigDataLocationResolverContext context, Class<T> type,
Supplier<T> supplier) {
InstanceSupplier<T> supplier) {
// do nothing
}
@Override
protected <T> void registerAndPromoteBean(ConfigDataLocationResolverContext context, Class<T> type,
Supplier<T> supplier) {
InstanceSupplier<T> supplier) {
// do nothing
}
};

View File

@@ -38,9 +38,14 @@ import org.springframework.core.style.ToStringCreator;
* @author Venil Noronha
* @author Richard Kettelerij
*/
@ConfigurationProperties("spring.cloud.consul.discovery")
@ConfigurationProperties(ConsulDiscoveryProperties.PREFIX)
public class ConsulDiscoveryProperties {
/**
* Consul discovery properties prefix.
*/
public static final String PREFIX = "spring.cloud.consul.discovery";
protected static final String MANAGEMENT = "management";
private HostInfo hostInfo;

View File

@@ -0,0 +1,60 @@
/*
* Copyright 2015-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.consul.discovery.configclient;
import com.ecwid.consul.v1.ConsulClient;
import org.springframework.boot.BootstrapRegistry;
import org.springframework.boot.Bootstrapper;
import org.springframework.boot.context.properties.bind.Binder;
import org.springframework.cloud.commons.util.InetUtils;
import org.springframework.cloud.commons.util.InetUtilsProperties;
import org.springframework.cloud.config.client.ConfigClientProperties;
import org.springframework.cloud.config.client.ConfigServerInstanceProvider;
import org.springframework.cloud.consul.ConsulAutoConfiguration;
import org.springframework.cloud.consul.ConsulProperties;
import org.springframework.cloud.consul.discovery.ConsulDiscoveryClient;
import org.springframework.cloud.consul.discovery.ConsulDiscoveryProperties;
public class ConsulConfigServerBootstrapper implements Bootstrapper {
@Override
public void intitialize(BootstrapRegistry registry) {
registry.registerIfAbsent(ConsulProperties.class, context -> {
Binder binder = context.get(Binder.class);
return binder.bind(ConsulProperties.PREFIX, ConsulProperties.class).orElseGet(ConsulProperties::new);
});
registry.registerIfAbsent(ConsulClient.class, context -> {
ConsulProperties consulProperties = context.get(ConsulProperties.class);
return ConsulAutoConfiguration.createConsulClient(consulProperties);
});
registry.registerIfAbsent(ConfigServerInstanceProvider.Function.class, context -> {
Binder binder = context.get(Binder.class);
boolean enabled = binder.bind(ConfigClientProperties.CONFIG_DISCOVERY_ENABLED, Boolean.class).orElse(false);
if (!enabled) {
return null;
}
ConsulClient consulClient = context.get(ConsulClient.class);
ConsulDiscoveryProperties properties = binder
.bind(ConsulDiscoveryProperties.PREFIX, ConsulDiscoveryProperties.class)
.orElseGet(() -> new ConsulDiscoveryProperties(new InetUtils(new InetUtilsProperties())));
return new ConsulDiscoveryClient(consulClient, properties)::getInstances;
});
}
}

View File

@@ -6,6 +6,9 @@ org.springframework.cloud.consul.discovery.ConsulDiscoveryClientConfiguration,\
org.springframework.cloud.consul.discovery.reactive.ConsulReactiveDiscoveryClientConfiguration,\
org.springframework.cloud.consul.discovery.ConsulCatalogWatchAutoConfiguration, \
org.springframework.cloud.consul.support.ConsulHeartbeatAutoConfiguration
org.springframework.cloud.bootstrap.BootstrapConfiguration=\
org.springframework.cloud.consul.discovery.configclient.ConsulDiscoveryClientConfigServiceBootstrapConfiguration
org.springframework.boot.Bootstrapper=\
org.springframework.cloud.consul.discovery.configclient.ConsulConfigServerBootstrapper

View File

@@ -0,0 +1,61 @@
/*
* Copyright 2015-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.consul.discovery.configclient;
import org.junit.jupiter.api.Test;
import org.springframework.boot.SpringBootConfiguration;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.cloud.config.client.ConfigServerInstanceProvider;
import static org.assertj.core.api.Assertions.assertThat;
public class ConsulConfigServerBootstrapperTests {
@Test
public void notEnabledDoesNotAddInstanceProviderFn() {
new SpringApplicationBuilder(TestConfig.class)
.properties("spring.cloud.service-registry.auto-registration.enabled=false")
.addBootstrapper(registry -> registry.addCloseListener(event -> {
ConfigServerInstanceProvider.Function providerFn = event.getBootstrapContext()
.get(ConfigServerInstanceProvider.Function.class);
assertThat(providerFn).as("ConfigServerInstanceProvider.Function was created when it shouldn't")
.isNull();
})).run().close();
}
@Test
public void enabledDoesAddsInstanceProviderFn() {
new SpringApplicationBuilder(TestConfig.class)
.properties("spring.cloud.config.discovery.enabled=true",
"spring.cloud.service-registry.auto-registration.enabled=false")
.addBootstrapper(registry -> registry.addCloseListener(event -> {
ConfigServerInstanceProvider.Function providerFn = event.getBootstrapContext()
.get(ConfigServerInstanceProvider.Function.class);
assertThat(providerFn).as("ConfigServerInstanceProvider.Function was not created when it should.")
.isNotNull();
})).run().close();
}
@SpringBootConfiguration
@EnableAutoConfiguration
static class TestConfig {
}
}