Skip to content

Commit

Permalink
Merge branch '3.1.x'
Browse files Browse the repository at this point in the history
  • Loading branch information
ryanjbaxter committed Jun 13, 2023
2 parents 6b7baa7 + 7347629 commit 49be07a
Show file tree
Hide file tree
Showing 6 changed files with 281 additions and 15 deletions.
22 changes: 22 additions & 0 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,26 @@
<artifactId>spring-cloud-test-support</artifactId>
<version>${spring-cloud-commons.version}</version>
</dependency>
<dependency>
<groupId>org.testcontainers</groupId>
<artifactId>testcontainers</artifactId>
<version>${testcontainers.version}</version>
</dependency>
<dependency>
<groupId>org.testcontainers</groupId>
<artifactId>mockserver</artifactId>
<version>${testcontainers.version}</version>
</dependency>
<dependency>
<groupId>org.testcontainers</groupId>
<artifactId>junit-jupiter</artifactId>
<version>${testcontainers.version}</version>
</dependency>
<dependency>
<groupId>org.mock-server</groupId>
<artifactId>mockserver-client-java</artifactId>
<version>${mockserverclient.version}</version>
</dependency>
</dependencies>
</dependencyManagement>

Expand All @@ -134,6 +154,8 @@
<spring-cloud-commons.version>4.0.4-SNAPSHOT</spring-cloud-commons.version>
<spring-cloud-config.version>4.0.4-SNAPSHOT</spring-cloud-config.version>
<spring-cloud-openfeign.version>4.0.4-SNAPSHOT</spring-cloud-openfeign.version>
<testcontainers.version>1.17.6</testcontainers.version>
<mockserverclient.version>5.15.0</mockserverclient.version>
</properties>

<profiles>
Expand Down
20 changes: 20 additions & 0 deletions spring-cloud-zookeeper-discovery/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -156,6 +156,26 @@
<artifactId>reactor-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.testcontainers</groupId>
<artifactId>testcontainers</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.testcontainers</groupId>
<artifactId>junit-jupiter</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.testcontainers</groupId>
<artifactId>mockserver</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.mock-server</groupId>
<artifactId>mockserver-client-java</artifactId>
<scope>test</scope>
</dependency>
</dependencies>

</project>
Original file line number Diff line number Diff line change
Expand Up @@ -17,23 +17,31 @@
package org.springframework.cloud.zookeeper.discovery.configclient;

import java.util.Collections;
import java.util.List;
import java.util.stream.Stream;

import org.apache.commons.logging.Log;
import org.apache.curator.RetryPolicy;
import org.apache.curator.framework.CuratorFramework;
import org.apache.curator.retry.ExponentialBackoffRetry;
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.BootstrapRegistryInitializer;
import org.springframework.boot.context.properties.bind.BindHandler;
import org.springframework.boot.context.properties.bind.Bindable;
import org.springframework.boot.context.properties.bind.Binder;
import org.springframework.cloud.client.ServiceInstance;
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.ZookeeperProperties;
import org.springframework.cloud.zookeeper.discovery.ConditionalOnZookeeperDiscoveryEnabled;
import org.springframework.cloud.zookeeper.discovery.ZookeeperDiscoveryClient;
import org.springframework.cloud.zookeeper.discovery.ZookeeperDiscoveryProperties;
Expand All @@ -45,6 +53,12 @@

public class ZookeeperConfigServerBootstrapper implements BootstrapRegistryInitializer {

private static boolean isEnabled(Binder binder) {
return binder.bind(ConfigClientProperties.CONFIG_DISCOVERY_ENABLED, Boolean.class).orElse(false) &&
binder.bind(ConditionalOnZookeeperDiscoveryEnabled.PROPERTY, Boolean.class).orElse(true) &&
binder.bind("spring.cloud.discovery.enabled", Boolean.class).orElse(true);
}

@Override
@SuppressWarnings("unchecked")
public void initialize(BootstrapRegistry registry) {
Expand Down Expand Up @@ -95,20 +109,17 @@ public void initialize(BootstrapRegistry registry) {
}
ServiceDiscovery<ZookeeperInstance> serviceDiscovery = context.get(ServiceDiscovery.class);
ZookeeperDependencies dependencies = binder.bind(ZookeeperDependencies.PREFIX, Bindable
.of(ZookeeperDependencies.class), getBindHandler(context))
.of(ZookeeperDependencies.class), getBindHandler(context))
.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 (id) -> Collections.emptyList();
}
return context.get(ZookeeperDiscoveryClient.class)::getInstances;
});
// We need to pass the lambda here so we do not create a new instance of ConfigServerInstanceProvider.Function
// which would result in a ClassNotFoundException when Spring Cloud Config is not on the classpath
registry.registerIfAbsent(ConfigServerInstanceProvider.Function.class, ZookeeperFunction::create);

// promote beans to context
registry.addCloseListener(event -> {
Expand All @@ -124,10 +135,63 @@ private BindHandler getBindHandler(org.springframework.boot.BootstrapContext con
return context.getOrElse(BindHandler.class, null);
}

private boolean isEnabled(Binder binder) {
return binder.bind(ConfigClientProperties.CONFIG_DISCOVERY_ENABLED, Boolean.class).orElse(false) &&
binder.bind(ConditionalOnZookeeperDiscoveryEnabled.PROPERTY, Boolean.class).orElse(true) &&
binder.bind("spring.cloud.discovery.enabled", Boolean.class).orElse(true);
/*
* This Function is executed when loading config data. Because of this we cannot rely on the
* BootstrapContext because Boot has not finished loading all the configuration data so if we
* ask the BootstrapContext for configuration data it will not have it. The apply method in this function
* is passed the Binder and BindHandler from the config data context which has the configuration properties that
* have been loaded so far in the config data process.
*
* We will create many of the same beans in this function as we do above in the initializer above. We do both
* to maintain compatibility since we are promoting those beans to the main application context.
*/
static final class ZookeeperFunction implements ConfigServerInstanceProvider.Function {

private final BootstrapContext context;

private ZookeeperFunction(BootstrapContext context) {
this.context = context;
}

static ZookeeperFunction create(BootstrapContext context) {
return new ZookeeperFunction(context);
}

@Override
public List<ServiceInstance> apply(String serviceId) {
return apply(serviceId, null, null, null);
}

@Override
public List<ServiceInstance> apply(String serviceId, Binder binder, BindHandler bindHandler, Log log) {
if (binder == null || !isEnabled(binder)) {
return Collections.emptyList();
}

ZookeeperProperties properties = binder.bind(ZookeeperProperties.PREFIX, Bindable.of(ZookeeperProperties.class))
.orElse(new ZookeeperProperties());
RetryPolicy retryPolicy = new ExponentialBackoffRetry(properties.getBaseSleepTimeMs(), properties.getMaxRetries(),
properties.getMaxSleepMs());
try {
CuratorFramework curatorFramework = CuratorFactory.curatorFramework(properties, retryPolicy, Stream::of,
() -> null, () -> null);
InstanceSerializer<ZookeeperInstance> serializer = new JsonInstanceSerializer<>(ZookeeperInstance.class);
ZookeeperDiscoveryProperties discoveryProperties = binder.bind(ZookeeperDiscoveryProperties.PREFIX, Bindable
.of(ZookeeperDiscoveryProperties.class), bindHandler)
.orElseGet(() -> new ZookeeperDiscoveryProperties(new InetUtils(new InetUtilsProperties())));
DefaultServiceDiscoveryCustomizer customizer = new DefaultServiceDiscoveryCustomizer(curatorFramework, discoveryProperties, serializer);
ServiceDiscovery<ZookeeperInstance> serviceDiscovery = customizer.customize(ServiceDiscoveryBuilder.builder(ZookeeperInstance.class));
ZookeeperDependencies dependencies = binder.bind(ZookeeperDependencies.PREFIX, Bindable
.of(ZookeeperDependencies.class), bindHandler)
.orElseGet(ZookeeperDependencies::new);
return new ZookeeperDiscoveryClient(serviceDiscovery, dependencies, discoveryProperties).getInstances(serviceId);
}
catch (Exception e) {
log.warn("Error fetching config server instance from Zookeeper", e);
return Collections.emptyList();
}

}
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@

import java.util.concurrent.atomic.AtomicReference;

import org.apache.commons.logging.Log;
import org.apache.curator.framework.CuratorFramework;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.Test;
Expand All @@ -31,6 +32,7 @@
import org.springframework.boot.context.properties.bind.BindContext;
import org.springframework.boot.context.properties.bind.BindHandler;
import org.springframework.boot.context.properties.bind.Bindable;
import org.springframework.boot.context.properties.bind.Binder;
import org.springframework.boot.context.properties.source.ConfigurationPropertyName;
import org.springframework.cloud.config.client.ConfigServerInstanceProvider;
import org.springframework.cloud.zookeeper.discovery.ZookeeperDiscoveryClient;
Expand All @@ -39,6 +41,7 @@

import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatThrownBy;
import static org.mockito.Mockito.mock;

public class ZookeeperConfigServerBootstrapperTests {

Expand All @@ -59,7 +62,8 @@ public void notEnabledReturnsEmptyList() {
.addBootstrapRegistryInitializer(registry -> registry.addCloseListener(event -> {
ConfigServerInstanceProvider.Function providerFn = event.getBootstrapContext()
.get(ConfigServerInstanceProvider.Function.class);
assertThat(providerFn.apply("id")).as("ConfigServerInstanceProvider.Function should return empty list")
Log log = mock(Log.class);
assertThat(providerFn.apply("id", event.getBootstrapContext().get(Binder.class), event.getBootstrapContext().get(BindHandler.class), log)).as("ConfigServerInstanceProvider.Function should return empty list")
.isEmpty();
})).run();
CuratorFramework curatorFramework = context.getBean("curatorFramework", CuratorFramework.class);
Expand All @@ -80,7 +84,8 @@ public void zookeeperDiscoveryClientDisabledReturnsEmptyList() {
.addBootstrapRegistryInitializer(registry -> registry.addCloseListener(event -> {
ConfigServerInstanceProvider.Function providerFn = event.getBootstrapContext()
.get(ConfigServerInstanceProvider.Function.class);
assertThat(providerFn.apply("id")).as("ConfigServerInstanceProvider.Function should return empty list")
Log log = mock(Log.class);
assertThat(providerFn.apply("id", event.getBootstrapContext().get(Binder.class), event.getBootstrapContext().get(BindHandler.class), log)).as("ConfigServerInstanceProvider.Function should return empty list")
.isEmpty();
})).run();
CuratorFramework curatorFramework = context.getBean("curatorFramework", CuratorFramework.class);
Expand All @@ -101,7 +106,7 @@ public void discoveryClientDisabledReturnsEmptyList() {
.addBootstrapRegistryInitializer(registry -> registry.addCloseListener(event -> {
ConfigServerInstanceProvider.Function providerFn = event.getBootstrapContext()
.get(ConfigServerInstanceProvider.Function.class);
assertThat(providerFn.apply("id")).as("ConfigServerInstanceProvider.Function should return empty list")
assertThat(providerFn.apply("id", event.getBootstrapContext().get(Binder.class), event.getBootstrapContext().get(BindHandler.class), mock(Log.class))).as("ConfigServerInstanceProvider.Function should return empty list")
.isEmpty();
})).run();
CuratorFramework curatorFramework = context.getBean("curatorFramework", CuratorFramework.class);
Expand All @@ -124,7 +129,7 @@ public void enabledAddsInstanceProviderFn() {
.addBootstrapRegistryInitializer(registry -> registry.addCloseListener(event -> {
ConfigServerInstanceProvider.Function providerFn = event.getBootstrapContext()
.get(ConfigServerInstanceProvider.Function.class);
assertThat(providerFn.apply("id")).as("Should return empty list.")
assertThat(providerFn.apply("id", event.getBootstrapContext().get(Binder.class), event.getBootstrapContext().get(BindHandler.class), mock(Log.class))).as("Should return empty list.")
.isNotNull();
bootstrapDiscoveryClient.set(event.getBootstrapContext().get(ZookeeperDiscoveryClient.class));
})).run();
Expand Down
Loading

0 comments on commit 49be07a

Please sign in to comment.