WIP on loadbalancer stuff
- Added caching for stickyrule to work - Adding polling conditions for flickering test - Added better exception handling - Removed wiremock tests, did some refactoring - Added default value of LoadBalancer for a dependency - Running build in the travis container, added caching, bumped up Groovy to 2.4.4
This commit is contained in:
committed by
Spencer Gibb
parent
d9d3976650
commit
85a139f60f
10
.travis.yml
10
.travis.yml
@@ -1,3 +1,9 @@
|
||||
sudo: false
|
||||
|
||||
cache:
|
||||
directories:
|
||||
- $HOME/.m2
|
||||
|
||||
language: java
|
||||
before_install:
|
||||
- git config user.name "$GIT_NAME"
|
||||
@@ -10,8 +16,8 @@ install:
|
||||
- mvn --settings .settings.xml install -P docs -q -U -DskipTests=true -Dmaven.test.redirectTestOutputToFile=true
|
||||
- ./docs/src/main/asciidoc/ghpages.sh
|
||||
script:
|
||||
- '[ "${TRAVIS_PULL_REQUEST}" != "false" ] || mvn --settings .settings.xml deploy -nsu -Dmaven.test.redirectTestOutputToFile=true'
|
||||
- '[ "${TRAVIS_PULL_REQUEST}" = "false" ] || mvn --settings .settings.xml install -nsu -Dmaven.test.redirectTestOutputToFile=true'
|
||||
- '[ "${TRAVIS_PULL_REQUEST}" != "false" ] || mvn --settings .settings.xml clean deploy -nsu'
|
||||
- '[ "${TRAVIS_PULL_REQUEST}" = "false" ] || mvn --settings .settings.xml clean install -nsu'
|
||||
env:
|
||||
global:
|
||||
- GIT_NAME="Spencer Gibb"
|
||||
|
||||
2
pom.xml
2
pom.xml
@@ -224,7 +224,7 @@
|
||||
<dependency>
|
||||
<groupId>org.codehaus.groovy</groupId>
|
||||
<artifactId>groovy-all</artifactId>
|
||||
<version>2.4.3</version>
|
||||
<version>2.4.4</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
package org.springframework.cloud.zookeeper.config;
|
||||
package org.springframework.cloud.zookeeper.common;
|
||||
|
||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||
import org.springframework.cloud.zookeeper.ZookeeperAutoConfiguration;
|
||||
@@ -1,4 +1,4 @@
|
||||
package org.springframework.cloud.zookeeper.config;
|
||||
package org.springframework.cloud.zookeeper.common;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
package org.springframework.cloud.zookeeper.config;
|
||||
package org.springframework.cloud.zookeeper.common;
|
||||
|
||||
import java.nio.charset.Charset;
|
||||
import java.util.ArrayList;
|
||||
@@ -1,4 +1,4 @@
|
||||
package org.springframework.cloud.zookeeper.config;
|
||||
package org.springframework.cloud.zookeeper.common;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
@@ -0,0 +1,17 @@
|
||||
package org.springframework.cloud.zookeeper.discovery;
|
||||
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
/**
|
||||
* Wrapper annotation to enable Ribbon for Zookeeper
|
||||
*/
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target({ElementType.TYPE, ElementType.METHOD})
|
||||
@ConditionalOnProperty(value = "ribbon.zookeeper.enabled", matchIfMissing = true)
|
||||
public @interface ConditionalOnRibbonZookeeper {
|
||||
}
|
||||
@@ -18,7 +18,6 @@ package org.springframework.cloud.zookeeper.discovery;
|
||||
|
||||
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||
import org.springframework.cloud.netflix.ribbon.RibbonAutoConfiguration;
|
||||
import org.springframework.cloud.netflix.ribbon.RibbonClients;
|
||||
@@ -31,7 +30,7 @@ import org.springframework.context.annotation.Configuration;
|
||||
@Configuration
|
||||
@EnableConfigurationProperties
|
||||
@ConditionalOnBean(SpringClientFactory.class)
|
||||
@ConditionalOnProperty(value = "ribbon.zookeeper.enabled", matchIfMissing = true)
|
||||
@ConditionalOnRibbonZookeeper
|
||||
@AutoConfigureAfter(RibbonAutoConfiguration.class)
|
||||
@RibbonClients(defaultConfiguration = ZookeeperRibbonClientConfiguration.class)
|
||||
public class RibbonZookeeperAutoConfiguration {
|
||||
|
||||
@@ -20,15 +20,15 @@ import com.netflix.client.config.IClientConfig;
|
||||
import com.netflix.config.ConfigurationManager;
|
||||
import com.netflix.config.DynamicPropertyFactory;
|
||||
import com.netflix.config.DynamicStringProperty;
|
||||
import com.netflix.loadbalancer.ILoadBalancer;
|
||||
import com.netflix.loadbalancer.ServerList;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||
import org.springframework.cloud.zookeeper.discovery.dependency.DependenciesPassedCondition;
|
||||
import org.springframework.cloud.zookeeper.discovery.dependency.ConditionalOnDependenciesPassed;
|
||||
import org.springframework.cloud.zookeeper.discovery.dependency.DependenciesBasedLoadBalancer;
|
||||
import org.springframework.cloud.zookeeper.discovery.dependency.ZookeeperDependencies;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Conditional;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
import javax.annotation.PostConstruct;
|
||||
@@ -37,7 +37,7 @@ import static com.netflix.client.config.CommonClientConfigKey.DeploymentContextB
|
||||
import static com.netflix.client.config.CommonClientConfigKey.EnableZoneAffinity;
|
||||
|
||||
/**
|
||||
* Preprocessor that configures defaults for eureka-discovered ribbon clients. Such as:
|
||||
* Preprocessor that configures defaults for zookeeper-discovered ribbon clients. Such as:
|
||||
* <code>@zone</code>, NIWSServerListClassName, DeploymentContextBasedVipAddresses,
|
||||
* NFLoadBalancerRuleClassName, NIWSServerListFilterClassName and more
|
||||
*
|
||||
@@ -61,14 +61,20 @@ public class ZookeeperRibbonClientConfiguration {
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
@Conditional(DependenciesPassedCondition.class)
|
||||
@ConditionalOnProperty(value = "spring.cloud.zookeeper.dependencies.enabled", matchIfMissing = true)
|
||||
@ConditionalOnDependenciesPassed
|
||||
public ServerList<?> ribbonServerListFromDependencies(IClientConfig config, ZookeeperDependencies zookeeperDependencies) {
|
||||
ZookeeperServerList serverList = new ZookeeperServerList(serviceDiscovery.getServiceDiscovery());
|
||||
serverList.initFromDependencies(config, zookeeperDependencies);
|
||||
return serverList;
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
@ConditionalOnDependenciesPassed
|
||||
public ILoadBalancer dependenciesBasedLoadBalancer(ZookeeperDependencies zookeeperDependencies, ServerList serverList) {
|
||||
return new DependenciesBasedLoadBalancer(zookeeperDependencies, serverList);
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
public ServerList<?> ribbonServerList(IClientConfig config) {
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
package org.springframework.cloud.zookeeper.discovery.dependency;
|
||||
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||
import org.springframework.context.annotation.Conditional;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
/**
|
||||
* Annotation to turn on a feature if Zookeeper dependencies have been passed
|
||||
*
|
||||
* @author Marcin Grzejszczak, 4financeIT
|
||||
*/
|
||||
@Target({ElementType.TYPE, ElementType.METHOD})
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Conditional(DependenciesPassedCondition.class)
|
||||
@ConditionalOnProperty(value = "spring.cloud.zookeeper.dependencies.enabled", matchIfMissing = true)
|
||||
public @interface ConditionalOnDependenciesPassed {
|
||||
}
|
||||
@@ -0,0 +1,75 @@
|
||||
package org.springframework.cloud.zookeeper.discovery.dependency;
|
||||
|
||||
import com.netflix.loadbalancer.*;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
/**
|
||||
* LoadBalancer that delegates to other rules depending on the provided load balancing strategy
|
||||
* in the {@link ZookeeperDependencies.ZookeeperDependency#getLoadBalancerType()}
|
||||
*
|
||||
* @author Marcin Grzejszczak, 4financeIT
|
||||
*/
|
||||
@Slf4j
|
||||
public class DependenciesBasedLoadBalancer extends BaseLoadBalancer {
|
||||
|
||||
private final Map<String, IRule> ruleCache = new ConcurrentHashMap<>();
|
||||
|
||||
private final ZookeeperDependencies zookeeperDependencies;
|
||||
|
||||
public DependenciesBasedLoadBalancer(ZookeeperDependencies zookeeperDependencies, ServerList serverList) {
|
||||
this.zookeeperDependencies = zookeeperDependencies;
|
||||
setServersList(serverList.getInitialListOfServers());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Server chooseServer(Object key) {
|
||||
String keyAsString = (String) key;
|
||||
ZookeeperDependencies.ZookeeperDependency dependency = zookeeperDependencies.getDependencyForAlias(keyAsString);
|
||||
if (dependency == null) {
|
||||
log.debug("No dependency found for alias [{}] - will use the default rule which is [{}]", keyAsString, rule);
|
||||
return rule.choose(key);
|
||||
};
|
||||
cacheEntryIfMissing(keyAsString, dependency);
|
||||
log.debug("Will try to retrieve dependency for key [{}]. Current cache contents [{}]", keyAsString, ruleCache);
|
||||
return ruleCache.get(keyAsString).choose(key);
|
||||
}
|
||||
|
||||
private void cacheEntryIfMissing(String keyAsString, ZookeeperDependencies.ZookeeperDependency dependency) {
|
||||
if (!ruleCache.containsKey(keyAsString)) {
|
||||
log.debug("Cache doesn't contain entry for [{}]", keyAsString);
|
||||
ruleCache.put(keyAsString, chooseRuleForLoadBalancerType(dependency.getLoadBalancerType()));
|
||||
}
|
||||
}
|
||||
|
||||
private IRule chooseRuleForLoadBalancerType(LoadBalancerType type) {
|
||||
switch (type) {
|
||||
case ROUND_ROBIN:
|
||||
return getRoundRobinRule();
|
||||
case RANDOM:
|
||||
return getRandomRule();
|
||||
case STICKY:
|
||||
return getStickyRule();
|
||||
default:
|
||||
throw new IllegalArgumentException("Unknown load balancer type " + type);
|
||||
}
|
||||
}
|
||||
|
||||
private RoundRobinRule getRoundRobinRule() {
|
||||
return new RoundRobinRule(this);
|
||||
}
|
||||
|
||||
private IRule getRandomRule() {
|
||||
RandomRule randomRule = new RandomRule();
|
||||
randomRule.setLoadBalancer(this);
|
||||
return randomRule;
|
||||
}
|
||||
|
||||
private IRule getStickyRule() {
|
||||
StickyRule stickyRule = new StickyRule(getRoundRobinRule());
|
||||
stickyRule.setLoadBalancer(this);
|
||||
return stickyRule;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
/*
|
||||
* Copyright 2013-2015 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.dependency;
|
||||
|
||||
import com.netflix.loadbalancer.ILoadBalancer;
|
||||
import com.netflix.loadbalancer.Server;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.boot.autoconfigure.AutoConfigureBefore;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
||||
import org.springframework.cloud.client.loadbalancer.LoadBalancerClient;
|
||||
import org.springframework.cloud.netflix.ribbon.RibbonAutoConfiguration;
|
||||
import org.springframework.cloud.netflix.ribbon.RibbonLoadBalancerClient;
|
||||
import org.springframework.cloud.netflix.ribbon.SpringClientFactory;
|
||||
import org.springframework.cloud.zookeeper.discovery.ConditionalOnRibbonZookeeper;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
/**
|
||||
*
|
||||
* Provides LoadBalancerClient that at runtime can pick proper load balancing strategy
|
||||
* basing on the Zookeeper dependencies from properties
|
||||
*
|
||||
* @author Marcin Grzejszczak, 4financeIT
|
||||
*/
|
||||
@AutoConfigureBefore(RibbonAutoConfiguration.class)
|
||||
@ConditionalOnRibbonZookeeper
|
||||
@Configuration
|
||||
@Slf4j
|
||||
public class DependencyRibbonAutoConfiguration {
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
@ConditionalOnDependenciesPassed
|
||||
public LoadBalancerClient loadBalancerClient(SpringClientFactory springClientFactory) {
|
||||
return new RibbonLoadBalancerClient(springClientFactory) {
|
||||
@Override
|
||||
protected Server getServer(String serviceId) {
|
||||
ILoadBalancer loadBalancer = this.getLoadBalancer(serviceId);
|
||||
return loadBalancer == null ? null : chooseServerByServiceIdOrDefault(loadBalancer, serviceId);
|
||||
}
|
||||
|
||||
private Server chooseServerByServiceIdOrDefault(ILoadBalancer loadBalancer, String serviceId) {
|
||||
log.debug("Dependencies are set - will try to load balance via provided load balancer [{}] for key [{}]", loadBalancer, serviceId);
|
||||
Server server = loadBalancer.chooseServer(serviceId);
|
||||
log.debug("Retrieved server [{}] via load balancer", server);
|
||||
return server != null ? server : loadBalancer.chooseServer("default");
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
package org.springframework.cloud.zookeeper.discovery.dependency;
|
||||
|
||||
import com.netflix.client.config.IClientConfig;
|
||||
import com.netflix.loadbalancer.AbstractLoadBalancerRule;
|
||||
import com.netflix.loadbalancer.IRule;
|
||||
import com.netflix.loadbalancer.Server;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
|
||||
/**
|
||||
* Load balancing rule that returns always the same instance.
|
||||
*
|
||||
* Ported from {@link org.apache.curator.x.discovery.strategies.StickyStrategy}
|
||||
*
|
||||
* author: Marcin Grzejszczak, 4financeIT
|
||||
*/
|
||||
@Slf4j
|
||||
public class StickyRule extends AbstractLoadBalancerRule {
|
||||
private final IRule masterStrategy;
|
||||
private final AtomicReference<Server> ourInstance = new AtomicReference<>(null);
|
||||
private final AtomicInteger instanceNumber = new AtomicInteger(-1);
|
||||
|
||||
public StickyRule(IRule masterStrategy) {
|
||||
this.masterStrategy = masterStrategy;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initWithNiwsConfig(IClientConfig iClientConfig) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public Server choose(Object key) {
|
||||
final List<Server> instances = getLoadBalancer().getServerList(true);
|
||||
log.debug("Instances taken from load balancer {}", instances);
|
||||
Server localOurInstance = ourInstance.get();
|
||||
log.debug("Current saved instance [{}]", localOurInstance);
|
||||
if (!instances.contains(localOurInstance)) {
|
||||
ourInstance.compareAndSet(localOurInstance, null);
|
||||
}
|
||||
if (ourInstance.get() == null) {
|
||||
Server instance = masterStrategy.choose(key);
|
||||
if (ourInstance.compareAndSet(null, instance)) {
|
||||
instanceNumber.incrementAndGet();
|
||||
}
|
||||
}
|
||||
return ourInstance.get();
|
||||
}
|
||||
|
||||
/**
|
||||
* Each time a new instance is picked, an internal counter is incremented. This way you
|
||||
* can track when/if the instance changes. The instance can change when the selected instance
|
||||
* is not in the current list of instances returned by the instance provider
|
||||
*
|
||||
* @return instance number
|
||||
*/
|
||||
public int getInstanceNumber() {
|
||||
return instanceNumber.get();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -56,7 +56,7 @@ public class ZookeeperDependencies {
|
||||
|
||||
private String path;
|
||||
|
||||
private LoadBalancerType loadBalancerType;
|
||||
private LoadBalancerType loadBalancerType = LoadBalancerType.ROUND_ROBIN;
|
||||
|
||||
private String contentTypeTemplate;
|
||||
|
||||
@@ -65,6 +65,7 @@ public class ZookeeperDependencies {
|
||||
private Map<String, String> headers;
|
||||
|
||||
private boolean required;
|
||||
|
||||
}
|
||||
|
||||
public Collection<ZookeeperDependency> getDependencyConfigurations() {
|
||||
@@ -75,6 +76,15 @@ public class ZookeeperDependencies {
|
||||
return !dependencies.isEmpty();
|
||||
}
|
||||
|
||||
public ZookeeperDependency getDependencyForPath(final String path) {
|
||||
for (Map.Entry<String, ZookeeperDependency> zookeeperDependencyEntry : dependencies.entrySet()) {
|
||||
if (zookeeperDependencyEntry.getValue().getPath().equals(path)) {
|
||||
return zookeeperDependencyEntry.getValue();
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public ZookeeperDependency getDependencyForAlias(final String alias) {
|
||||
for (Map.Entry<String, ZookeeperDependency> zookeeperDependencyEntry : dependencies.entrySet()) {
|
||||
if (zookeeperDependencyEntry.getKey().equals(alias)) {
|
||||
|
||||
@@ -17,11 +17,9 @@ package org.springframework.cloud.zookeeper.discovery.dependency;
|
||||
|
||||
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||
import org.springframework.cloud.zookeeper.ZookeeperAutoConfiguration;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Conditional;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
/**
|
||||
@@ -34,8 +32,7 @@ import org.springframework.context.annotation.Configuration;
|
||||
*/
|
||||
@Configuration
|
||||
@EnableConfigurationProperties
|
||||
@Conditional(DependenciesPassedCondition.class)
|
||||
@ConditionalOnProperty(value = "spring.cloud.zookeeper.dependencies.enabled", matchIfMissing = true)
|
||||
@ConditionalOnDependenciesPassed
|
||||
@AutoConfigureAfter(ZookeeperAutoConfiguration.class)
|
||||
public class ZookeeperDependenciesAutoConfiguration {
|
||||
|
||||
|
||||
@@ -15,24 +15,22 @@
|
||||
*/
|
||||
package org.springframework.cloud.zookeeper.discovery.watcher;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||
import org.springframework.cloud.zookeeper.discovery.ZookeeperServiceDiscovery;
|
||||
import org.springframework.cloud.zookeeper.discovery.dependency.DependenciesPassedCondition;
|
||||
import org.springframework.cloud.zookeeper.discovery.dependency.ConditionalOnDependenciesPassed;
|
||||
import org.springframework.cloud.zookeeper.discovery.dependency.ZookeeperDependencies;
|
||||
import org.springframework.cloud.zookeeper.discovery.dependency.ZookeeperDependenciesAutoConfiguration;
|
||||
import org.springframework.cloud.zookeeper.discovery.watcher.presence.DefaultDependencyPresenceOnStartupVerifier;
|
||||
import org.springframework.cloud.zookeeper.discovery.watcher.presence.DependencyPresenceOnStartupVerifier;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Conditional;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Provides hooks for observing dependency lifecycle in Zookeeper.
|
||||
* Needs custom dependencies to be set in order to work.
|
||||
@@ -43,8 +41,7 @@ import org.springframework.context.annotation.Configuration;
|
||||
*/
|
||||
@Configuration
|
||||
@EnableConfigurationProperties
|
||||
@Conditional(DependenciesPassedCondition.class)
|
||||
@ConditionalOnProperty(value = "spring.cloud.zookeeper.dependencies.enabled", matchIfMissing = true)
|
||||
@ConditionalOnDependenciesPassed
|
||||
@AutoConfigureAfter(ZookeeperDependenciesAutoConfiguration.class)
|
||||
public class DependencyWatcherAutoConfiguration {
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
# Auto Configuration
|
||||
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
|
||||
org.springframework.cloud.zookeeper.discovery.dependency.DependencyRibbonAutoConfiguration,\
|
||||
org.springframework.cloud.zookeeper.discovery.RibbonZookeeperAutoConfiguration,\
|
||||
org.springframework.cloud.zookeeper.discovery.dependency.ZookeeperDependenciesAutoConfiguration,\
|
||||
org.springframework.cloud.zookeeper.discovery.watcher.DependencyWatcherAutoConfiguration
|
||||
|
||||
@@ -13,12 +13,10 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.cloud.zookeeper.discovery
|
||||
package org.springframework.cloud.zookeeper.common
|
||||
|
||||
import com.github.tomakehurst.wiremock.WireMockServer
|
||||
import groovy.transform.CompileStatic
|
||||
import groovy.transform.PackageScope
|
||||
import org.apache.curator.framework.CuratorFramework
|
||||
import org.apache.curator.test.TestingServer
|
||||
import org.springframework.cloud.zookeeper.ZookeeperProperties
|
||||
import org.springframework.context.annotation.Bean
|
||||
@@ -34,15 +32,6 @@ class CommonTestConfig {
|
||||
return new TestingServer(SocketUtils.findAvailableTcpPort())
|
||||
}
|
||||
|
||||
@Bean(initMethod = "start", destroyMethod = "stop")
|
||||
TestServiceRegistrar testServiceRegistrar(CuratorFramework curatorFramework) {
|
||||
return new TestServiceRegistrar(wiremockServer().port(), curatorFramework)
|
||||
}
|
||||
|
||||
@Bean(initMethod = "start", destroyMethod = "shutdown") WireMockServer wiremockServer() {
|
||||
return new WireMockServer(SocketUtils.findAvailableTcpPort())
|
||||
}
|
||||
|
||||
@Bean ZookeeperProperties zookeeperProperties() {
|
||||
return new ZookeeperProperties(connectString: "localhost:${testingServer().port}")
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
package org.springframework.cloud.zookeeper.common
|
||||
|
||||
import org.springframework.web.client.RestTemplate
|
||||
|
||||
class TestRibbonClient extends TestServiceRestClient {
|
||||
|
||||
private final String thisAppName
|
||||
|
||||
TestRibbonClient(RestTemplate restTemplate) {
|
||||
super(restTemplate)
|
||||
this.thisAppName = 'someName'
|
||||
}
|
||||
|
||||
TestRibbonClient(RestTemplate restTemplate, String thisAppName) {
|
||||
super(restTemplate)
|
||||
this.thisAppName = thisAppName
|
||||
}
|
||||
|
||||
String thisHealthCheck() {
|
||||
return restTemplate.getForObject("http://$thisAppName/health", String)
|
||||
}
|
||||
|
||||
}
|
||||
@@ -13,13 +13,11 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.cloud.zookeeper.discovery
|
||||
package org.springframework.cloud.zookeeper.common
|
||||
|
||||
import groovy.transform.PackageScope
|
||||
import groovy.transform.CompileStatic
|
||||
import org.springframework.web.client.RestTemplate
|
||||
|
||||
@PackageScope
|
||||
@CompileStatic
|
||||
class TestServiceRestClient {
|
||||
|
||||
@@ -29,11 +27,11 @@ class TestServiceRestClient {
|
||||
this.restTemplate = restTemplate
|
||||
}
|
||||
|
||||
String pingService(String alias) {
|
||||
return restTemplate.getForObject("http://$alias/ping", String)
|
||||
String callService(String alias, String endpoint) {
|
||||
return restTemplate.getForObject("http://$alias/$endpoint", String)
|
||||
}
|
||||
|
||||
String pingOnUrl(String url) {
|
||||
return new RestTemplate().getForObject("http://$url/ping", String)
|
||||
String callOnUrl(String url, String endpoint) {
|
||||
return new RestTemplate().getForObject("http://$url/$endpoint", String)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
package org.springframework.cloud.zookeeper.discovery
|
||||
|
||||
trait PollingUtils {
|
||||
Closure willPass(Closure closure) {
|
||||
return {
|
||||
try {
|
||||
closure()
|
||||
} catch (Exception e) {
|
||||
throw new AssertionError("Exception occurred while evaluating closure", e)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -25,12 +25,12 @@ import org.apache.curator.x.discovery.UriSpec
|
||||
@CompileStatic
|
||||
class TestServiceRegistrar {
|
||||
|
||||
private final int wiremockServerPort
|
||||
private final int serverPort
|
||||
private final CuratorFramework curatorFramework
|
||||
private final ServiceDiscovery serviceDiscovery
|
||||
|
||||
TestServiceRegistrar(int wiremockServerPort, CuratorFramework curatorFramework) {
|
||||
this.wiremockServerPort = wiremockServerPort
|
||||
TestServiceRegistrar(int serverPort, CuratorFramework curatorFramework) {
|
||||
this.serverPort = serverPort
|
||||
this.curatorFramework = curatorFramework
|
||||
this.serviceDiscovery = serviceDiscovery()
|
||||
}
|
||||
@@ -42,7 +42,7 @@ class TestServiceRegistrar {
|
||||
ServiceInstance serviceInstance() {
|
||||
return ServiceInstance.builder().uriSpec(new UriSpec("{scheme}://{address}:{port}/"))
|
||||
.address('localhost')
|
||||
.port(wiremockServerPort)
|
||||
.port(serverPort)
|
||||
.name('testInstance')
|
||||
.build()
|
||||
}
|
||||
|
||||
@@ -15,8 +15,6 @@
|
||||
*/
|
||||
package org.springframework.cloud.zookeeper.discovery
|
||||
|
||||
import com.github.tomakehurst.wiremock.WireMockServer
|
||||
import com.github.tomakehurst.wiremock.client.WireMock
|
||||
import groovy.json.JsonSlurper
|
||||
import org.springframework.beans.factory.annotation.Autowired
|
||||
import org.springframework.beans.factory.annotation.Value
|
||||
@@ -27,53 +25,47 @@ 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.loadbalancer.LoadBalanced
|
||||
import org.springframework.cloud.zookeeper.common.CommonTestConfig
|
||||
import org.springframework.cloud.zookeeper.common.TestRibbonClient
|
||||
import org.springframework.context.annotation.Bean
|
||||
import org.springframework.context.annotation.Configuration
|
||||
import org.springframework.context.annotation.Import
|
||||
import org.springframework.context.annotation.Profile
|
||||
import org.springframework.test.context.ActiveProfiles
|
||||
import org.springframework.test.context.ContextConfiguration
|
||||
import org.springframework.web.client.RestTemplate
|
||||
import spock.lang.Specification
|
||||
|
||||
import static com.github.tomakehurst.wiremock.client.WireMock.*
|
||||
|
||||
@ContextConfiguration(classes = Config, loader = SpringApplicationContextLoader)
|
||||
@ActiveProfiles('ribbon')
|
||||
@WebIntegrationTest(randomPort = true)
|
||||
class ZookeeperDiscoveryISpec extends Specification {
|
||||
|
||||
public static final String TEST_INSTANCE_NAME = 'testInstance'
|
||||
|
||||
@Autowired TestRibbonClient testRibbonClient
|
||||
@Autowired WireMockServer wiremockServer
|
||||
@Autowired DiscoveryClient discoveryClient
|
||||
@Autowired ZookeeperServiceDiscovery serviceDiscovery
|
||||
WireMock wireMock
|
||||
@Value('${spring.application.name}') String springAppName
|
||||
|
||||
def setup() {
|
||||
wireMock = new WireMock('localhost', wiremockServer.port())
|
||||
wireMock.register(get(urlEqualTo('/ping')).willReturn(aResponse().withBody('pong')))
|
||||
}
|
||||
|
||||
def 'should find a collaborator via Ribbon'() {
|
||||
expect:
|
||||
'pong' == testRibbonClient.pingService(TEST_INSTANCE_NAME)
|
||||
}
|
||||
|
||||
def 'should find the app by its name via Ribbon'() {
|
||||
given:
|
||||
def jsonSlurper = new JsonSlurper()
|
||||
def health = jsonSlurper.parseText(testRibbonClient.thisHealthCheck())
|
||||
expect:
|
||||
'UP' == health.status
|
||||
'UP' == registeredServiceStatusViaServiceName()
|
||||
}
|
||||
|
||||
def 'should find a collaborator via discovery client'() {
|
||||
given:
|
||||
List<ServiceInstance> instances = discoveryClient.getInstances(TEST_INSTANCE_NAME)
|
||||
List<ServiceInstance> instances = discoveryClient.getInstances(springAppName)
|
||||
ServiceInstance instance = instances.first()
|
||||
expect:
|
||||
'pong' == testRibbonClient.pingOnUrl("${instance.host}:${instance.port}")
|
||||
'UP' == registeredServiceStatus(instance)
|
||||
}
|
||||
|
||||
private String registeredServiceStatusViaServiceName() {
|
||||
return new JsonSlurper().parseText(testRibbonClient.thisHealthCheck()).status
|
||||
}
|
||||
|
||||
private String registeredServiceStatus(ServiceInstance instance) {
|
||||
return new JsonSlurper().parseText(testRibbonClient.callOnUrl("${instance.host}:${instance.port}", 'health')).status
|
||||
}
|
||||
|
||||
def 'should properly find local instance'() {
|
||||
@@ -85,6 +77,7 @@ class ZookeeperDiscoveryISpec extends Specification {
|
||||
@EnableAutoConfiguration
|
||||
@Import(CommonTestConfig)
|
||||
@EnableDiscoveryClient
|
||||
@Profile('ribbon')
|
||||
static class Config {
|
||||
|
||||
@Bean
|
||||
@@ -92,21 +85,5 @@ class ZookeeperDiscoveryISpec extends Specification {
|
||||
@Value('${spring.application.name}') String springAppName) {
|
||||
return new TestRibbonClient(restTemplate, springAppName)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static class TestRibbonClient extends TestServiceRestClient {
|
||||
|
||||
private final String thisAppName
|
||||
|
||||
TestRibbonClient(RestTemplate restTemplate, String thisAppName) {
|
||||
super(restTemplate)
|
||||
this.thisAppName = thisAppName
|
||||
}
|
||||
|
||||
String thisHealthCheck() {
|
||||
return restTemplate.getForObject("http://$thisAppName/health", String)
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,95 @@
|
||||
/*
|
||||
* Copyright 2012-2015 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.dependency
|
||||
import org.apache.curator.framework.CuratorFramework
|
||||
import org.apache.curator.test.TestingServer
|
||||
import org.springframework.beans.factory.annotation.Autowired
|
||||
import org.springframework.boot.autoconfigure.EnableAutoConfiguration
|
||||
import org.springframework.boot.test.SpringApplicationContextLoader
|
||||
import org.springframework.boot.test.WebIntegrationTest
|
||||
import org.springframework.cloud.client.discovery.DiscoveryClient
|
||||
import org.springframework.cloud.client.discovery.EnableDiscoveryClient
|
||||
import org.springframework.cloud.client.loadbalancer.LoadBalancerClient
|
||||
import org.springframework.cloud.zookeeper.ZookeeperProperties
|
||||
import org.springframework.cloud.zookeeper.discovery.PollingUtils
|
||||
import org.springframework.cloud.zookeeper.discovery.TestServiceRegistrar
|
||||
import org.springframework.context.annotation.Bean
|
||||
import org.springframework.context.annotation.Configuration
|
||||
import org.springframework.context.annotation.Profile
|
||||
import org.springframework.test.context.ActiveProfiles
|
||||
import org.springframework.test.context.ContextConfiguration
|
||||
import org.springframework.util.SocketUtils
|
||||
import spock.lang.Specification
|
||||
import spock.util.concurrent.PollingConditions
|
||||
|
||||
@ContextConfiguration(classes = Config, loader = SpringApplicationContextLoader)
|
||||
@ActiveProfiles('loadbalancerclient')
|
||||
@WebIntegrationTest(randomPort = true)
|
||||
class StickyRuleISpec extends Specification implements PollingUtils {
|
||||
|
||||
@Autowired LoadBalancerClient loadBalancerClient
|
||||
@Autowired DiscoveryClient discoveryClient
|
||||
PollingConditions conditions
|
||||
|
||||
def setup() {
|
||||
conditions = new PollingConditions()
|
||||
}
|
||||
|
||||
def 'should use sticky load balancing strategy taken from Zookeeper dependencies'() {
|
||||
expect:
|
||||
thereAreTwoRegisteredServices()
|
||||
URI uri = getUriForAlias()
|
||||
conditions.eventually willPass {
|
||||
2.times {
|
||||
assert uri == getUriForAlias()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private boolean thereAreTwoRegisteredServices() {
|
||||
return discoveryClient.getInstances('someAlias')?.size() == 2
|
||||
}
|
||||
|
||||
private URI getUriForAlias() {
|
||||
return loadBalancerClient.choose('someAlias')?.uri
|
||||
}
|
||||
|
||||
|
||||
@Configuration
|
||||
@EnableAutoConfiguration
|
||||
@EnableDiscoveryClient
|
||||
@Profile('loadbalancerclient')
|
||||
static class Config {
|
||||
|
||||
@Bean(destroyMethod = 'close')
|
||||
TestingServer testingServer() {
|
||||
return new TestingServer(SocketUtils.findAvailableTcpPort())
|
||||
}
|
||||
|
||||
@Bean ZookeeperProperties zookeeperProperties() {
|
||||
return new ZookeeperProperties(connectString: "localhost:${testingServer().port}")
|
||||
}
|
||||
|
||||
@Bean(initMethod = "start", destroyMethod = "stop") TestServiceRegistrar serviceOne(CuratorFramework curatorFramework) {
|
||||
return new TestServiceRegistrar(SocketUtils.findAvailableTcpPort(), curatorFramework)
|
||||
}
|
||||
|
||||
@Bean(initMethod = "start", destroyMethod = "stop") TestServiceRegistrar serviceTwo(CuratorFramework curatorFramework) {
|
||||
return new TestServiceRegistrar(SocketUtils.findAvailableTcpPort(), curatorFramework)
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
package org.springframework.cloud.zookeeper.discovery.dependency
|
||||
|
||||
import spock.lang.Specification
|
||||
import spock.lang.Unroll
|
||||
|
||||
class ZookeeperDependenciesSpec extends Specification {
|
||||
|
||||
private static final ZookeeperDependencies.ZookeeperDependency EXPECTED_DEPENDENCY = new ZookeeperDependencies.ZookeeperDependency(
|
||||
'id',
|
||||
'path',
|
||||
LoadBalancerType.RANDOM,
|
||||
'contentTypeTemplate',
|
||||
'version',
|
||||
[header: 'value'],
|
||||
false
|
||||
)
|
||||
private static final Map<String, ZookeeperDependencies.ZookeeperDependency> DEPENDENCIES = [
|
||||
alias: EXPECTED_DEPENDENCY
|
||||
]
|
||||
|
||||
ZookeeperDependencies zookeeperDependencies = new ZookeeperDependencies(dependencies: DEPENDENCIES)
|
||||
|
||||
@Unroll
|
||||
def "should retrieve dependency [#expectedDependency] for path [#path]"() {
|
||||
expect:
|
||||
expectedDependency == zookeeperDependencies.getDependencyForPath(path)
|
||||
where:
|
||||
path || expectedDependency
|
||||
'unknownPath' || null
|
||||
'path' || EXPECTED_DEPENDENCY
|
||||
}
|
||||
|
||||
@Unroll
|
||||
def "should retrieve dependency [#expectedDependency] for alias [#alias]"() {
|
||||
expect:
|
||||
expectedDependency == zookeeperDependencies.getDependencyForAlias(alias)
|
||||
where:
|
||||
alias || expectedDependency
|
||||
'unknownAlias' || null
|
||||
'alias' || EXPECTED_DEPENDENCY
|
||||
}
|
||||
|
||||
@Unroll
|
||||
def "should retrieve alias [#expectedAlias] for path [#path]"() {
|
||||
expect:
|
||||
expectedAlias == zookeeperDependencies.getAliasForPath(path)
|
||||
where:
|
||||
path || expectedAlias
|
||||
'unknownPath' || ''
|
||||
'path' || 'alias'
|
||||
}
|
||||
|
||||
@Unroll
|
||||
def "should retrieve path [#expectedPath] for alias [#alias]"() {
|
||||
expect:
|
||||
expectedPath == zookeeperDependencies.getPathForAlias(alias)
|
||||
where:
|
||||
alias || expectedPath
|
||||
'unknownAlias' || ''
|
||||
'alias' || 'path'
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -13,10 +13,8 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.cloud.zookeeper.discovery
|
||||
package org.springframework.cloud.zookeeper.discovery.dependency
|
||||
|
||||
import com.github.tomakehurst.wiremock.WireMockServer
|
||||
import com.github.tomakehurst.wiremock.client.WireMock
|
||||
import org.springframework.beans.factory.annotation.Autowired
|
||||
import org.springframework.boot.autoconfigure.EnableAutoConfiguration
|
||||
import org.springframework.boot.test.SpringApplicationContextLoader
|
||||
@@ -25,39 +23,46 @@ 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.loadbalancer.LoadBalanced
|
||||
import org.springframework.cloud.zookeeper.common.CommonTestConfig
|
||||
import org.springframework.cloud.zookeeper.common.TestRibbonClient
|
||||
import org.springframework.cloud.zookeeper.discovery.PollingUtils
|
||||
import org.springframework.context.annotation.Bean
|
||||
import org.springframework.context.annotation.Configuration
|
||||
import org.springframework.context.annotation.Import
|
||||
import org.springframework.context.annotation.Profile
|
||||
import org.springframework.stereotype.Controller
|
||||
import org.springframework.test.context.ActiveProfiles
|
||||
import org.springframework.test.context.ContextConfiguration
|
||||
import org.springframework.web.bind.annotation.RequestMapping
|
||||
import org.springframework.web.client.RestTemplate
|
||||
import spock.lang.Specification
|
||||
|
||||
import static com.github.tomakehurst.wiremock.client.WireMock.*
|
||||
import spock.util.concurrent.PollingConditions
|
||||
|
||||
@ContextConfiguration(classes = Config, loader = SpringApplicationContextLoader)
|
||||
@ActiveProfiles('watcher')
|
||||
@ActiveProfiles('dependencies')
|
||||
@WebIntegrationTest(randomPort = true)
|
||||
class ZookeeperDiscoveryWithDependenciesISpec extends Specification {
|
||||
class ZookeeperDiscoveryWithDependenciesISpec extends Specification implements PollingUtils {
|
||||
|
||||
@Autowired TestRibbonClient testRibbonClient
|
||||
@Autowired WireMockServer wiremockServer
|
||||
@Autowired DiscoveryClient discoveryClient
|
||||
WireMock wireMock
|
||||
PollingConditions conditions
|
||||
|
||||
def setup() {
|
||||
wireMock = new WireMock('localhost', wiremockServer.port())
|
||||
wireMock.register(get(urlEqualTo('/ping')).willReturn(aResponse().withBody('pong')))
|
||||
conditions = new PollingConditions()
|
||||
}
|
||||
|
||||
def 'should find an instance via path when alias is not found'() {
|
||||
expect:
|
||||
!discoveryClient.getInstances('some/name/without/alias').empty
|
||||
conditions.eventually willPass {
|
||||
assert !discoveryClient.getInstances('nameWithoutAlias').empty
|
||||
}
|
||||
}
|
||||
|
||||
def 'should find a collaborator via Ribbon by using its alias from dependencies'() {
|
||||
expect:
|
||||
'pong' == testRibbonClient.pingService('someAlias')
|
||||
conditions.eventually willPass {
|
||||
assert callingServiceAtBeansEndpointIsNotEmpty()
|
||||
}
|
||||
}
|
||||
|
||||
def 'should find a collaborator via discovery client'() {
|
||||
@@ -65,13 +70,24 @@ class ZookeeperDiscoveryWithDependenciesISpec extends Specification {
|
||||
List<ServiceInstance> instances = discoveryClient.getInstances('someAlias')
|
||||
ServiceInstance instance = instances.first()
|
||||
expect:
|
||||
'pong' == testRibbonClient.pingOnUrl("${instance.host}:${instance.port}")
|
||||
conditions.eventually willPass {
|
||||
assert callingServiceViaUrlOnBeansEndpointIsNotEmpty(instance)
|
||||
}
|
||||
}
|
||||
|
||||
private boolean callingServiceAtBeansEndpointIsNotEmpty() {
|
||||
return !testRibbonClient.callService('someAlias', 'beans').empty
|
||||
}
|
||||
|
||||
private boolean callingServiceViaUrlOnBeansEndpointIsNotEmpty(ServiceInstance instance) {
|
||||
return !testRibbonClient.callOnUrl("${instance.host}:${instance.port}", 'beans').empty
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@EnableAutoConfiguration
|
||||
@Import(CommonTestConfig)
|
||||
@EnableDiscoveryClient
|
||||
@Profile('dependencies')
|
||||
static class Config {
|
||||
|
||||
@Bean
|
||||
@@ -81,10 +97,12 @@ class ZookeeperDiscoveryWithDependenciesISpec extends Specification {
|
||||
|
||||
}
|
||||
|
||||
static class TestRibbonClient extends TestServiceRestClient {
|
||||
@Controller
|
||||
@Profile('dependencies')
|
||||
class PingController {
|
||||
|
||||
TestRibbonClient(RestTemplate restTemplate) {
|
||||
super(restTemplate)
|
||||
@RequestMapping('/ping') String ping() {
|
||||
return 'pong'
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -14,7 +14,6 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.cloud.zookeeper.discovery.watcher
|
||||
|
||||
import org.apache.curator.framework.CuratorFramework
|
||||
import org.apache.curator.framework.CuratorFrameworkFactory
|
||||
import org.apache.curator.retry.ExponentialBackoffRetry
|
||||
@@ -26,11 +25,13 @@ import org.apache.curator.x.discovery.UriSpec
|
||||
import org.springframework.beans.factory.annotation.Autowired
|
||||
import org.springframework.boot.autoconfigure.EnableAutoConfiguration
|
||||
import org.springframework.boot.test.SpringApplicationContextLoader
|
||||
import org.springframework.cloud.zookeeper.discovery.PollingUtils
|
||||
import org.springframework.cloud.zookeeper.discovery.ZookeeperServiceDiscovery
|
||||
import org.springframework.cloud.zookeeper.discovery.watcher.presence.DependencyPresenceOnStartupVerifier
|
||||
import org.springframework.cloud.zookeeper.discovery.watcher.presence.LogMissingDependencyChecker
|
||||
import org.springframework.context.annotation.Bean
|
||||
import org.springframework.context.annotation.Configuration
|
||||
import org.springframework.context.annotation.Profile
|
||||
import org.springframework.context.support.PropertySourcesPlaceholderConfigurer
|
||||
import org.springframework.test.context.ActiveProfiles
|
||||
import org.springframework.test.context.ContextConfiguration
|
||||
@@ -42,12 +43,16 @@ import javax.annotation.PreDestroy
|
||||
|
||||
@ContextConfiguration(classes = Config, loader = SpringApplicationContextLoader)
|
||||
@ActiveProfiles('watcher')
|
||||
class DefaultDependencyWatcherSpringISpec extends Specification {
|
||||
class DefaultDependencyWatcherSpringISpec extends Specification implements PollingUtils {
|
||||
|
||||
@Autowired AssertableDependencyPresenceOnStartupVerifier dependencyPresenceOnStartupVerifier
|
||||
@Autowired AssertableDependencyWatcherListener dependencyWatcherListener
|
||||
@Autowired
|
||||
ZookeeperServiceDiscovery serviceDiscovery
|
||||
@Autowired ZookeeperServiceDiscovery serviceDiscovery
|
||||
PollingConditions conditions
|
||||
|
||||
def setup() {
|
||||
conditions = new PollingConditions()
|
||||
}
|
||||
|
||||
def 'should verify that presence of a dependency has been checked'() {
|
||||
expect:
|
||||
@@ -56,15 +61,17 @@ class DefaultDependencyWatcherSpringISpec extends Specification {
|
||||
|
||||
def 'should verify that dependency watcher listener is successfully registered and operational'() {
|
||||
when:
|
||||
serviceDiscovery.serviceDiscovery.unregisterService(serviceDiscovery.serviceInstance)
|
||||
serviceDiscovery.serviceDiscovery.unregisterService(serviceDiscovery.serviceInstance)
|
||||
|
||||
then:
|
||||
new PollingConditions().eventually {
|
||||
dependencyWatcherListener.dependencyState == DependencyState.DISCONNECTED
|
||||
conditions.eventually willPass {
|
||||
assert dependencyWatcherListener.dependencyState == DependencyState.DISCONNECTED
|
||||
}
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@EnableAutoConfiguration
|
||||
@Profile('watcher')
|
||||
static class Config {
|
||||
|
||||
@Bean
|
||||
@@ -79,9 +86,7 @@ class DefaultDependencyWatcherSpringISpec extends Specification {
|
||||
|
||||
@Bean
|
||||
ZookeeperServiceDiscovery zookeeperServiceDiscovery() {
|
||||
return new MyZookeeperServiceDiscovery(curatorFramework()) {
|
||||
|
||||
}
|
||||
return new MyZookeeperServiceDiscovery(curatorFramework())
|
||||
}
|
||||
|
||||
@Bean(initMethod = 'start', destroyMethod = 'close')
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
spring.application.name: nameWithoutAlias
|
||||
spring.cloud.zookeeper:
|
||||
dependencies:
|
||||
someAlias:
|
||||
id: someId
|
||||
path: nameWithoutAlias
|
||||
loadBalancerType: ROUND_ROBIN
|
||||
contentTypeTemplate: application/vnd.newsletter.$version+json
|
||||
version: v1
|
||||
headers:
|
||||
header1: value1
|
||||
header2: value2
|
||||
required: false
|
||||
testInstance2:
|
||||
id: someId2
|
||||
path: somePath2
|
||||
loadBalancerType: ROUND_ROBIN
|
||||
contentTypeTemplate: application/vnd.newsletter.$version+json2
|
||||
version: v1
|
||||
required: false
|
||||
@@ -0,0 +1,7 @@
|
||||
spring.application.name: loadbalancerclient
|
||||
spring.cloud.zookeeper:
|
||||
dependencies:
|
||||
someAlias:
|
||||
id: someId
|
||||
path: testInstance
|
||||
loadBalancerType: STICKY
|
||||
@@ -0,0 +1,5 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<configuration>
|
||||
<include resource="org/springframework/boot/logging/logback/base.xml"/>
|
||||
<logger name="org.springframework.cloud.zookeeper" level="DEBUG"/>
|
||||
</configuration>
|
||||
Reference in New Issue
Block a user