From 300dc4cdf1cb35804055d4eb4a70c7ce1b21cb3d Mon Sep 17 00:00:00 2001 From: Spencer Gibb Date: Mon, 11 Jun 2018 17:18:45 -0400 Subject: [PATCH] Adds support for using zookeeper discovery to lookup config server. fixes gh-159 --- pom.xml | 13 ++ spring-cloud-zookeeper-discovery/pom.xml | 15 +++ ...ookeeperConfigServerAutoConfiguration.java | 59 +++++++++ ...ntConfigServiceBootstrapConfiguration.java | 53 ++++++++ .../ZookeeperAutoServiceRegistration.java | 10 +- .../main/resources/META-INF/spring.factories | 4 + ...ntConfigServiceAutoConfigurationTests.java | 116 ++++++++++++++++++ ...perConfigServerAutoConfigurationTests.java | 73 +++++++++++ 8 files changed, 342 insertions(+), 1 deletion(-) create mode 100644 spring-cloud-zookeeper-discovery/src/main/java/org/springframework/cloud/zookeeper/discovery/configclient/ZookeeperConfigServerAutoConfiguration.java create mode 100644 spring-cloud-zookeeper-discovery/src/main/java/org/springframework/cloud/zookeeper/discovery/configclient/ZookeeperDiscoveryClientConfigServiceBootstrapConfiguration.java create mode 100644 spring-cloud-zookeeper-discovery/src/test/java/org/springframework/cloud/zookeeper/discovery/configclient/DiscoveryClientConfigServiceAutoConfigurationTests.java create mode 100644 spring-cloud-zookeeper-discovery/src/test/java/org/springframework/cloud/zookeeper/discovery/configclient/ZookeeperConfigServerAutoConfigurationTests.java diff --git a/pom.xml b/pom.xml index dfaf06c7..75ed6d55 100644 --- a/pom.xml +++ b/pom.xml @@ -205,6 +205,13 @@ pom import + + org.springframework.cloud + spring-cloud-config-dependencies + ${spring-cloud-config.version} + pom + import + org.springframework.cloud spring-cloud-openfeign-dependencies @@ -212,6 +219,11 @@ pom import + + org.springframework.cloud + spring-cloud-test-support + ${spring-cloud-commons.version} + @@ -219,6 +231,7 @@ 2.17 2.0.0.BUILD-SNAPSHOT 2.0.0.BUILD-SNAPSHOT + 2.0.0.BUILD-SNAPSHOT 2.0.0.BUILD-SNAPSHOT 2.0.0.BUILD-SNAPSHOT diff --git a/spring-cloud-zookeeper-discovery/pom.xml b/spring-cloud-zookeeper-discovery/pom.xml index 36d8de9b..eb6f2a51 100644 --- a/spring-cloud-zookeeper-discovery/pom.xml +++ b/spring-cloud-zookeeper-discovery/pom.xml @@ -108,6 +108,16 @@ spring-boot-autoconfigure-processor true + + org.springframework.cloud + spring-cloud-config-client + true + + + org.springframework.cloud + spring-cloud-config-server + true + org.springframework.boot spring-boot-starter-web @@ -118,6 +128,11 @@ spring-boot-starter-test test + + org.springframework.cloud + spring-cloud-test-support + test + commons-configuration commons-configuration diff --git a/spring-cloud-zookeeper-discovery/src/main/java/org/springframework/cloud/zookeeper/discovery/configclient/ZookeeperConfigServerAutoConfiguration.java b/spring-cloud-zookeeper-discovery/src/main/java/org/springframework/cloud/zookeeper/discovery/configclient/ZookeeperConfigServerAutoConfiguration.java new file mode 100644 index 00000000..821a21b3 --- /dev/null +++ b/spring-cloud-zookeeper-discovery/src/main/java/org/springframework/cloud/zookeeper/discovery/configclient/ZookeeperConfigServerAutoConfiguration.java @@ -0,0 +1,59 @@ +/* + * Copyright 2013-2016 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 + * + * http://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 javax.annotation.PostConstruct; + +import org.apache.curator.framework.CuratorFramework; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; +import org.springframework.boot.context.properties.EnableConfigurationProperties; +import org.springframework.cloud.config.server.config.ConfigServerProperties; +import org.springframework.cloud.zookeeper.discovery.ZookeeperDiscoveryProperties; +import org.springframework.context.annotation.Configuration; +import org.springframework.util.StringUtils; + +/** + * Extra configuration for config server if it happens to be registered with Zookeeper. + * + * @author Dave Syer + */ +@Configuration +@EnableConfigurationProperties +@ConditionalOnClass({ ZookeeperDiscoveryProperties.class, CuratorFramework.class, + ConfigServerProperties.class }) +public class ZookeeperConfigServerAutoConfiguration { + + @Autowired(required = false) + private ZookeeperDiscoveryProperties properties; + + @Autowired(required = false) + private ConfigServerProperties server; + + @PostConstruct + public void init() { + if (this.properties == null || this.server == null) { + return; + } + String prefix = this.server.getPrefix(); + if (StringUtils.hasText(prefix)) { + this.properties.getMetadata().put("configPath", prefix); + } + } + +} diff --git a/spring-cloud-zookeeper-discovery/src/main/java/org/springframework/cloud/zookeeper/discovery/configclient/ZookeeperDiscoveryClientConfigServiceBootstrapConfiguration.java b/spring-cloud-zookeeper-discovery/src/main/java/org/springframework/cloud/zookeeper/discovery/configclient/ZookeeperDiscoveryClientConfigServiceBootstrapConfiguration.java new file mode 100644 index 00000000..0ff259b0 --- /dev/null +++ b/spring-cloud-zookeeper-discovery/src/main/java/org/springframework/cloud/zookeeper/discovery/configclient/ZookeeperDiscoveryClientConfigServiceBootstrapConfiguration.java @@ -0,0 +1,53 @@ +/* + * Copyright 2013-2016 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 + * + * http://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.springframework.boot.autoconfigure.condition.ConditionalOnClass; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.cloud.commons.util.InetUtils; +import org.springframework.cloud.config.client.ConfigServicePropertySourceLocator; +import org.springframework.cloud.zookeeper.ZookeeperAutoConfiguration; +import org.springframework.cloud.zookeeper.discovery.ZookeeperDiscoveryAutoConfiguration; +import org.springframework.cloud.zookeeper.discovery.ZookeeperDiscoveryClientConfiguration; +import org.springframework.cloud.zookeeper.discovery.ZookeeperDiscoveryProperties; +import org.springframework.cloud.zookeeper.support.CuratorServiceDiscoveryAutoConfiguration; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Import; +import org.springframework.core.annotation.Order; + +/** + * Helper for config client that wants to lookup the config server via discovery. + * + * @author Spencer Gibb + */ +@ConditionalOnClass(ConfigServicePropertySourceLocator.class) +@ConditionalOnProperty(value = "spring.cloud.config.discovery.enabled", matchIfMissing = false) +@Configuration +@Import({ ZookeeperAutoConfiguration.class, ZookeeperDiscoveryClientConfiguration.class, + CuratorServiceDiscoveryAutoConfiguration.class, ZookeeperDiscoveryAutoConfiguration.class}) +@Order(0) +public class ZookeeperDiscoveryClientConfigServiceBootstrapConfiguration { + + @Bean + public ZookeeperDiscoveryProperties zookeeperDiscoveryProperties(InetUtils inetUtils) { + ZookeeperDiscoveryProperties properties = new ZookeeperDiscoveryProperties(inetUtils); + // for bootstrap, registration is not needed, just discovery client + properties.setRegister(false); + return properties; + } +} diff --git a/spring-cloud-zookeeper-discovery/src/main/java/org/springframework/cloud/zookeeper/serviceregistry/ZookeeperAutoServiceRegistration.java b/spring-cloud-zookeeper-discovery/src/main/java/org/springframework/cloud/zookeeper/serviceregistry/ZookeeperAutoServiceRegistration.java index 785012c0..8dd157f5 100644 --- a/spring-cloud-zookeeper-discovery/src/main/java/org/springframework/cloud/zookeeper/serviceregistry/ZookeeperAutoServiceRegistration.java +++ b/spring-cloud-zookeeper-discovery/src/main/java/org/springframework/cloud/zookeeper/serviceregistry/ZookeeperAutoServiceRegistration.java @@ -19,6 +19,7 @@ package org.springframework.cloud.zookeeper.serviceregistry; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.springframework.cloud.client.serviceregistry.AbstractAutoServiceRegistration; +import org.springframework.cloud.client.serviceregistry.AutoServiceRegistrationProperties; import org.springframework.cloud.zookeeper.discovery.ZookeeperDiscoveryProperties; /** @@ -38,7 +39,14 @@ public class ZookeeperAutoServiceRegistration extends AbstractAutoServiceRegistr public ZookeeperAutoServiceRegistration(ZookeeperServiceRegistry registry, ZookeeperRegistration registration, ZookeeperDiscoveryProperties properties) { - super(registry); + this(registry, registration, properties, null); + } + + public ZookeeperAutoServiceRegistration(ZookeeperServiceRegistry registry, + ZookeeperRegistration registration, + ZookeeperDiscoveryProperties properties, + AutoServiceRegistrationProperties arProperties) { + super(registry, arProperties); this.registration = registration; this.properties = properties; if (this.properties.getInstancePort() != null) { diff --git a/spring-cloud-zookeeper-discovery/src/main/resources/META-INF/spring.factories b/spring-cloud-zookeeper-discovery/src/main/resources/META-INF/spring.factories index 7f8ed4a1..b53623ba 100644 --- a/spring-cloud-zookeeper-discovery/src/main/resources/META-INF/spring.factories +++ b/spring-cloud-zookeeper-discovery/src/main/resources/META-INF/spring.factories @@ -15,3 +15,7 @@ org.springframework.cloud.zookeeper.discovery.ZookeeperDiscoveryClientConfigurat # Environment Post Processors org.springframework.boot.env.EnvironmentPostProcessor=\ org.springframework.cloud.zookeeper.discovery.dependency.DependencyEnvironmentPostProcessor + + +org.springframework.cloud.bootstrap.BootstrapConfiguration=\ +org.springframework.cloud.zookeeper.discovery.configclient.ZookeeperDiscoveryClientConfigServiceBootstrapConfiguration diff --git a/spring-cloud-zookeeper-discovery/src/test/java/org/springframework/cloud/zookeeper/discovery/configclient/DiscoveryClientConfigServiceAutoConfigurationTests.java b/spring-cloud-zookeeper-discovery/src/test/java/org/springframework/cloud/zookeeper/discovery/configclient/DiscoveryClientConfigServiceAutoConfigurationTests.java new file mode 100644 index 00000000..fdd20262 --- /dev/null +++ b/spring-cloud-zookeeper-discovery/src/test/java/org/springframework/cloud/zookeeper/discovery/configclient/DiscoveryClientConfigServiceAutoConfigurationTests.java @@ -0,0 +1,116 @@ +/* + * Copyright 2013-2016 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 + * + * http://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.Arrays; + +import org.junit.After; +import org.junit.Test; +import org.junit.runner.RunWith; + +import org.springframework.boot.autoconfigure.context.PropertyPlaceholderAutoConfiguration; +import org.springframework.boot.test.util.TestPropertyValues; +import org.springframework.cloud.client.DefaultServiceInstance; +import org.springframework.cloud.client.ServiceInstance; +import org.springframework.cloud.commons.util.UtilAutoConfiguration; +import org.springframework.cloud.config.client.ConfigClientProperties; +import org.springframework.cloud.config.client.DiscoveryClientConfigServiceBootstrapConfiguration; +import org.springframework.cloud.test.ClassPathExclusions; +import org.springframework.cloud.test.ModifiedClassPathRunner; +import org.springframework.cloud.zookeeper.ZookeeperAutoConfiguration; +import org.springframework.cloud.zookeeper.discovery.ZookeeperDiscoveryClient; +import org.springframework.cloud.zookeeper.discovery.ZookeeperDiscoveryClientConfiguration; +import org.springframework.cloud.zookeeper.discovery.ZookeeperDiscoveryProperties; +import org.springframework.context.annotation.AnnotationConfigApplicationContext; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +import static org.junit.Assert.assertEquals; +import static org.mockito.BDDMockito.given; +import static org.mockito.Mockito.atLeast; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; + +/** + * @author Dave Syer + */ +@RunWith(ModifiedClassPathRunner.class) +@ClassPathExclusions({ "spring-retry-*.jar", "spring-boot-starter-aop-*.jar" }) +public class DiscoveryClientConfigServiceAutoConfigurationTests { + + private AnnotationConfigApplicationContext context; + + @After + public void close() { + if (this.context != null) { + if (this.context.getParent() != null) { + ((AnnotationConfigApplicationContext) this.context.getParent()).close(); + } + this.context.close(); + } + } + + @Test + public void onWhenRequested() { + setup("server.port=7000", "spring.cloud.config.discovery.enabled=true", + "spring.cloud.zookeeper.discovery.instance-port:7001", + "spring.cloud.zookeeper.discovery.instance-host:foo", + "spring.cloud.config.discovery.service-id:configserver"); + assertEquals( 1, this.context + .getBeanNamesForType(ZookeeperConfigServerAutoConfiguration.class).length); + ZookeeperDiscoveryClient client = this.context.getParent().getBean( + ZookeeperDiscoveryClient.class); + verify(client, atLeast(2)).getInstances("configserver"); + ConfigClientProperties locator = this.context + .getBean(ConfigClientProperties.class); + assertEquals("http://foo:7001/", locator.getUri()[0]); + } + + private void setup(String... env) { + AnnotationConfigApplicationContext parent = new AnnotationConfigApplicationContext(); + TestPropertyValues.of(env).applyTo(parent); + parent.register(UtilAutoConfiguration.class, + PropertyPlaceholderAutoConfiguration.class, EnvironmentKnobbler.class, + ZookeeperDiscoveryClientConfigServiceBootstrapConfiguration.class, + DiscoveryClientConfigServiceBootstrapConfiguration.class, + ConfigClientProperties.class); + parent.refresh(); + this.context = new AnnotationConfigApplicationContext(); + this.context.setParent(parent); + this.context.register(PropertyPlaceholderAutoConfiguration.class, + ZookeeperConfigServerAutoConfiguration.class, ZookeeperAutoConfiguration.class, + ZookeeperDiscoveryClientConfiguration.class); + this.context.refresh(); + } + + @Configuration + protected static class EnvironmentKnobbler { + + @Bean + public ZookeeperDiscoveryClient zookeeperDiscoveryClient( + ZookeeperDiscoveryProperties properties) { + ZookeeperDiscoveryClient client = mock(ZookeeperDiscoveryClient.class); + ServiceInstance instance = new DefaultServiceInstance("configserver", + properties.getInstanceHost(), properties.getInstancePort(), false); + given(client.getInstances("configserver")) + .willReturn(Arrays.asList(instance)); + return client; + } + + } + +} diff --git a/spring-cloud-zookeeper-discovery/src/test/java/org/springframework/cloud/zookeeper/discovery/configclient/ZookeeperConfigServerAutoConfigurationTests.java b/spring-cloud-zookeeper-discovery/src/test/java/org/springframework/cloud/zookeeper/discovery/configclient/ZookeeperConfigServerAutoConfigurationTests.java new file mode 100644 index 00000000..f8c37237 --- /dev/null +++ b/spring-cloud-zookeeper-discovery/src/test/java/org/springframework/cloud/zookeeper/discovery/configclient/ZookeeperConfigServerAutoConfigurationTests.java @@ -0,0 +1,73 @@ +/* + * Copyright 2013-2016 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 + * + * http://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.junit.After; +import org.junit.Test; + +import org.springframework.boot.WebApplicationType; +import org.springframework.boot.autoconfigure.context.PropertyPlaceholderAutoConfiguration; +import org.springframework.boot.builder.SpringApplicationBuilder; +import org.springframework.cloud.config.server.config.ConfigServerProperties; +import org.springframework.cloud.zookeeper.discovery.ZookeeperDiscoveryProperties; +import org.springframework.context.ConfigurableApplicationContext; +import org.springframework.context.annotation.AnnotationConfigApplicationContext; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.Assert.assertEquals; + +/** + * @author Dave Syer + */ +public class ZookeeperConfigServerAutoConfigurationTests { + + private ConfigurableApplicationContext context; + + @After + public void close() { + if (this.context != null) { + this.context.close(); + } + } + + @Test + public void offByDefault() { + this.context = new AnnotationConfigApplicationContext( + ZookeeperConfigServerAutoConfiguration.class); + assertEquals(0, + this.context.getBeanNamesForType(ZookeeperDiscoveryProperties.class).length); + } + + @Test + public void onWhenRequested() { + setup("spring.cloud.config.server.prefix=/config"); + assertEquals(1, + this.context.getBeanNamesForType(ZookeeperDiscoveryProperties.class).length); + ZookeeperDiscoveryProperties properties = this.context.getBean(ZookeeperDiscoveryProperties.class); + assertThat(properties.getMetadata()).containsEntry("configPath", "/config"); + } + + private void setup(String... env) { + this.context = new SpringApplicationBuilder( + PropertyPlaceholderAutoConfiguration.class, + ZookeeperConfigServerAutoConfiguration.class, + ConfigServerProperties.class, ZookeeperDiscoveryProperties.class) + .web(WebApplicationType.NONE) + .properties(env).run(); + } + +}