Adds ConfigData support for s.c.config.discover.enabled=true
adds ZookeeperConfigServerBootstrapper to optionally instantiate ZookeeperDiscoveryClient and register a instance provider for config client
This commit is contained in:
@@ -85,9 +85,7 @@ public class ZookeeperConfigDataLocationResolver implements ConfigDataLocationRe
|
||||
String location, boolean optional, Profiles profiles) throws ConfigDataLocationNotFoundException {
|
||||
UriComponents locationUri = parseLocation(location);
|
||||
|
||||
ZookeeperConfigProperties properties = loadConfigProperties(context.getBinder());
|
||||
context.getBootstrapContext().register(ZookeeperConfigProperties.class, InstanceSupplier.of(properties));
|
||||
|
||||
// create curator
|
||||
ZookeeperProperties zookeeperProperties = loadProperties(context.getBinder(), locationUri);
|
||||
context.getBootstrapContext().register(ZookeeperProperties.class, InstanceSupplier.of(zookeeperProperties));
|
||||
|
||||
@@ -97,11 +95,16 @@ public class ZookeeperConfigDataLocationResolver implements ConfigDataLocationRe
|
||||
context.getBootstrapContext().registerIfAbsent(CuratorFramework.class, InstanceSupplier
|
||||
.from(() -> curatorFramework(context.getBootstrapContext(), zookeeperProperties, optional)));
|
||||
|
||||
// create locations
|
||||
ZookeeperConfigProperties properties = loadConfigProperties(context.getBinder());
|
||||
context.getBootstrapContext().register(ZookeeperConfigProperties.class, InstanceSupplier.of(properties));
|
||||
|
||||
ZookeeperPropertySources sources = new ZookeeperPropertySources(properties, log);
|
||||
|
||||
List<String> contexts = (locationUri == null || CollectionUtils.isEmpty(locationUri.getPathSegments()))
|
||||
? sources.getAutomaticContexts(profiles.getAccepted()) : getCustomContexts(locationUri);
|
||||
|
||||
// promote beans to context
|
||||
context.getBootstrapContext().addCloseListener(event -> {
|
||||
CuratorFramework curatorFramework = event.getBootstrapContext().get(CuratorFramework.class);
|
||||
event.getApplicationContext().getBeanFactory().registerSingleton("configDataCuratorFramework",
|
||||
|
||||
@@ -30,9 +30,14 @@ import org.springframework.util.StringUtils;
|
||||
* @author Spencer Gibb
|
||||
* @since 1.0.0
|
||||
*/
|
||||
@ConfigurationProperties("spring.cloud.zookeeper.discovery")
|
||||
@ConfigurationProperties(ZookeeperDiscoveryProperties.PREFIX)
|
||||
public class ZookeeperDiscoveryProperties {
|
||||
|
||||
/**
|
||||
* Zookeeper Discovery Properties prefix.
|
||||
*/
|
||||
public static final String PREFIX = "spring.cloud.zookeeper.discovery";
|
||||
|
||||
/**
|
||||
* Default URI spec.
|
||||
*/
|
||||
|
||||
@@ -0,0 +1,160 @@
|
||||
/*
|
||||
* 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.zookeeper.discovery.configclient;
|
||||
|
||||
import java.util.function.Supplier;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.apache.curator.RetryPolicy;
|
||||
import org.apache.curator.drivers.TracerDriver;
|
||||
import org.apache.curator.ensemble.EnsembleProvider;
|
||||
import org.apache.curator.framework.CuratorFramework;
|
||||
import org.apache.curator.x.discovery.ServiceDiscovery;
|
||||
import org.apache.curator.x.discovery.ServiceDiscoveryBuilder;
|
||||
import org.apache.curator.x.discovery.details.InstanceSerializer;
|
||||
import org.apache.curator.x.discovery.details.JsonInstanceSerializer;
|
||||
|
||||
import org.springframework.boot.BootstrapContext;
|
||||
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.zookeeper.CuratorFactory;
|
||||
import org.springframework.cloud.zookeeper.CuratorFrameworkCustomizer;
|
||||
import org.springframework.cloud.zookeeper.ZookeeperProperties;
|
||||
import org.springframework.cloud.zookeeper.discovery.ZookeeperDiscoveryClient;
|
||||
import org.springframework.cloud.zookeeper.discovery.ZookeeperDiscoveryProperties;
|
||||
import org.springframework.cloud.zookeeper.discovery.ZookeeperInstance;
|
||||
import org.springframework.cloud.zookeeper.discovery.dependency.ZookeeperDependencies;
|
||||
import org.springframework.cloud.zookeeper.support.DefaultServiceDiscoveryCustomizer;
|
||||
import org.springframework.cloud.zookeeper.support.ServiceDiscoveryCustomizer;
|
||||
import org.springframework.util.ClassUtils;
|
||||
|
||||
public class ZookeeperConfigServerBootstrapper implements Bootstrapper {
|
||||
|
||||
private final Log log = LogFactory.getLog(getClass());
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
public void intitialize(BootstrapRegistry registry) {
|
||||
if (!ClassUtils.isPresent("org.springframework.cloud.config.client.ConfigServerInstanceProvider", null)) {
|
||||
return;
|
||||
}
|
||||
// create curator
|
||||
registry.registerIfAbsent(ZookeeperProperties.class, context -> context.get(Binder.class)
|
||||
.bind(ZookeeperProperties.PREFIX, ZookeeperProperties.class).orElseGet(ZookeeperProperties::new));
|
||||
registry.registerIfAbsent(RetryPolicy.class, context -> {
|
||||
ZookeeperProperties properties = context.get(ZookeeperProperties.class);
|
||||
return CuratorFactory.retryPolicy(properties);
|
||||
});
|
||||
registry.registerIfAbsent(CuratorFramework.class, context -> {
|
||||
ZookeeperProperties zookeeperProperties = context.get(ZookeeperProperties.class);
|
||||
return curatorFramework(context, zookeeperProperties);
|
||||
});
|
||||
|
||||
// create discovery
|
||||
registry.registerIfAbsent(ZookeeperDiscoveryProperties.class,
|
||||
context -> context.get(Binder.class)
|
||||
.bind(ZookeeperDiscoveryProperties.PREFIX, ZookeeperDiscoveryProperties.class)
|
||||
.orElseGet(() -> new ZookeeperDiscoveryProperties(new InetUtils(new InetUtilsProperties()))));
|
||||
registry.registerIfAbsent(InstanceSerializer.class,
|
||||
context -> new JsonInstanceSerializer<>(ZookeeperInstance.class));
|
||||
registry.registerIfAbsent(ServiceDiscoveryCustomizer.class, context -> {
|
||||
CuratorFramework curator = context.get(CuratorFramework.class);
|
||||
ZookeeperDiscoveryProperties properties = context.get(ZookeeperDiscoveryProperties.class);
|
||||
InstanceSerializer<ZookeeperInstance> serializer = context.get(InstanceSerializer.class);
|
||||
return new DefaultServiceDiscoveryCustomizer(curator, properties, serializer);
|
||||
});
|
||||
registry.registerIfAbsent(ServiceDiscovery.class, context -> {
|
||||
ServiceDiscoveryCustomizer customizer = context.get(ServiceDiscoveryCustomizer.class);
|
||||
return customizer.customize(ServiceDiscoveryBuilder.builder(ZookeeperInstance.class));
|
||||
});
|
||||
registry.registerIfAbsent(ZookeeperDiscoveryClient.class, context -> {
|
||||
Binder binder = context.get(Binder.class);
|
||||
if (!isEnabled(binder)) {
|
||||
return null;
|
||||
}
|
||||
ServiceDiscovery<ZookeeperInstance> serviceDiscovery = context.get(ServiceDiscovery.class);
|
||||
ZookeeperDependencies dependencies = binder.bind(ZookeeperDependencies.PREFIX, ZookeeperDependencies.class)
|
||||
.orElseGet(ZookeeperDependencies::new);
|
||||
ZookeeperDiscoveryProperties discoveryProperties = context.get(ZookeeperDiscoveryProperties.class);
|
||||
|
||||
return new ZookeeperDiscoveryClient(serviceDiscovery, dependencies, discoveryProperties);
|
||||
});
|
||||
|
||||
// create instance provider
|
||||
registry.registerIfAbsent(ConfigServerInstanceProvider.Function.class, context -> {
|
||||
if (!isEnabled(context.get(Binder.class))) {
|
||||
return null;
|
||||
}
|
||||
return context.get(ZookeeperDiscoveryClient.class)::getInstances;
|
||||
});
|
||||
|
||||
// promote beans to context
|
||||
registry.addCloseListener(event -> {
|
||||
ZookeeperDiscoveryClient discoveryClient = event.getBootstrapContext().get(ZookeeperDiscoveryClient.class);
|
||||
if (discoveryClient != null) {
|
||||
event.getApplicationContext().getBeanFactory().registerSingleton("zookeeperServiceDiscovery",
|
||||
discoveryClient);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private boolean isEnabled(Binder binder) {
|
||||
return binder.bind(ConfigClientProperties.CONFIG_DISCOVERY_ENABLED, Boolean.class).orElse(false);
|
||||
}
|
||||
|
||||
protected CuratorFramework curatorFramework(BootstrapContext context, ZookeeperProperties properties) {
|
||||
|
||||
Supplier<Stream<CuratorFrameworkCustomizer>> customizers;
|
||||
// TODO: use new apis after milestone release
|
||||
try {
|
||||
CuratorFrameworkCustomizer customizer = context.get(CuratorFrameworkCustomizer.class);
|
||||
customizers = () -> Stream.of(customizer);
|
||||
}
|
||||
catch (IllegalStateException e) {
|
||||
customizers = () -> null;
|
||||
}
|
||||
try {
|
||||
return CuratorFactory.curatorFramework(properties, context.get(RetryPolicy.class), customizers,
|
||||
supplier(context, EnsembleProvider.class), supplier(context, TracerDriver.class));
|
||||
}
|
||||
catch (Exception e) {
|
||||
if (log.isDebugEnabled()) {
|
||||
log.debug("Unable to connect to zookeeper", e);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private <T> Supplier<T> supplier(BootstrapContext context, Class<T> type) {
|
||||
try {
|
||||
// TODO: use new apis after milestone release
|
||||
T instance = context.get(type);
|
||||
return () -> instance;
|
||||
}
|
||||
catch (IllegalStateException e) {
|
||||
return () -> null;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -37,9 +37,14 @@ import static org.springframework.cloud.zookeeper.discovery.DependencyPathUtils.
|
||||
* @author Olga Maciaszek-Sharma
|
||||
* @since 1.0.0
|
||||
*/
|
||||
@ConfigurationProperties("spring.cloud.zookeeper")
|
||||
@ConfigurationProperties(ZookeeperDependencies.PREFIX)
|
||||
public class ZookeeperDependencies {
|
||||
|
||||
/**
|
||||
* Zookeeper Dependencies prefix.
|
||||
*/
|
||||
public static final String PREFIX = "spring.cloud.zookeeper";
|
||||
|
||||
/**
|
||||
* Common prefix that will be applied to all Zookeeper dependencies' paths.
|
||||
*/
|
||||
|
||||
@@ -18,3 +18,6 @@ org.springframework.cloud.zookeeper.discovery.dependency.DependencyEnvironmentPo
|
||||
|
||||
org.springframework.cloud.bootstrap.BootstrapConfiguration=\
|
||||
org.springframework.cloud.zookeeper.discovery.configclient.ZookeeperDiscoveryClientConfigServiceBootstrapConfiguration
|
||||
|
||||
org.springframework.boot.Bootstrapper=\
|
||||
org.springframework.cloud.zookeeper.discovery.configclient.ZookeeperConfigServerBootstrapper
|
||||
@@ -0,0 +1,65 @@
|
||||
/*
|
||||
* 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.zookeeper.discovery.configclient;
|
||||
|
||||
import org.apache.catalina.webresources.TomcatURLStreamHandlerFactory;
|
||||
import org.apache.curator.test.TestingServer;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
import org.springframework.boot.SpringBootConfiguration;
|
||||
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
|
||||
import org.springframework.boot.builder.SpringApplicationBuilder;
|
||||
import org.springframework.cloud.test.ClassPathExclusions;
|
||||
import org.springframework.cloud.test.ModifiedClassPathRunner;
|
||||
import org.springframework.context.ConfigurableApplicationContext;
|
||||
import org.springframework.util.SocketUtils;
|
||||
|
||||
@RunWith(ModifiedClassPathRunner.class)
|
||||
@ClassPathExclusions({ "spring-cloud-config-client-*.jar", "spring-cloud-config-server-*.jar" })
|
||||
public class ZookeeperConfigServerBootstrapperNoConfigClientTests {
|
||||
|
||||
@Test
|
||||
public void contextLoads() throws Exception {
|
||||
TestingServer testingServer = null;
|
||||
ConfigurableApplicationContext context = null;
|
||||
try {
|
||||
TomcatURLStreamHandlerFactory.disable();
|
||||
int port = SocketUtils.findAvailableTcpPort();
|
||||
testingServer = new TestingServer(port);
|
||||
context = new SpringApplicationBuilder(TestConfig.class).properties("--server.port=0",
|
||||
"spring.cloud.config.discovery.enabled=true", "spring.cloud.zookeeper.connect-string=localhost:" + port,
|
||||
"spring.cloud.service-registry.auto-registration.enabled=false")
|
||||
.run();
|
||||
}
|
||||
finally {
|
||||
if (context != null) {
|
||||
context.close();
|
||||
}
|
||||
if (testingServer != null) {
|
||||
testingServer.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@SpringBootConfiguration
|
||||
@EnableAutoConfiguration
|
||||
static class TestConfig {
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,98 @@
|
||||
/*
|
||||
* 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.zookeeper.discovery.configclient;
|
||||
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
|
||||
import org.apache.curator.test.TestingServer;
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
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 org.springframework.cloud.zookeeper.discovery.ZookeeperDiscoveryClient;
|
||||
import org.springframework.context.ConfigurableApplicationContext;
|
||||
import org.springframework.util.SocketUtils;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
public class ZookeeperConfigServerBootstrapperTests {
|
||||
|
||||
private TestingServer testingServer;
|
||||
|
||||
private ConfigurableApplicationContext context;
|
||||
|
||||
private int port;
|
||||
|
||||
@BeforeEach
|
||||
public void init() throws Exception {
|
||||
port = SocketUtils.findAvailableTcpPort();
|
||||
testingServer = new TestingServer(port);
|
||||
}
|
||||
|
||||
@AfterEach
|
||||
public void after() throws Exception {
|
||||
if (context != null) {
|
||||
context.close();
|
||||
}
|
||||
if (testingServer != null) {
|
||||
testingServer.close();
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void notEnabledDoesNotAddInstanceProviderFn() {
|
||||
new SpringApplicationBuilder(TestConfig.class)
|
||||
.properties("--server.port=0", "spring.cloud.service-registry.auto-registration.enabled=false",
|
||||
"spring.cloud.zookeeper.connect-string=localhost:" + port)
|
||||
.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 enabledAddsInstanceProviderFn() {
|
||||
AtomicReference<ZookeeperDiscoveryClient> bootstrapDiscoveryClient = new AtomicReference<>();
|
||||
context = new SpringApplicationBuilder(TestConfig.class)
|
||||
.properties("--server.port=0", "spring.cloud.config.discovery.enabled=true",
|
||||
"spring.cloud.zookeeper.connect-string=localhost:" + port,
|
||||
"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();
|
||||
bootstrapDiscoveryClient.set(event.getBootstrapContext().get(ZookeeperDiscoveryClient.class));
|
||||
})).run();
|
||||
|
||||
ZookeeperDiscoveryClient discoveryClient = context.getBean(ZookeeperDiscoveryClient.class);
|
||||
assertThat(discoveryClient == bootstrapDiscoveryClient.get()).isTrue();
|
||||
}
|
||||
|
||||
@SpringBootConfiguration
|
||||
@EnableAutoConfiguration
|
||||
static class TestConfig {
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user