Added support for the Spring Cloud Load Balancer module

fixes gh-1312
This commit is contained in:
Marcin Grzejszczak
2020-01-28 19:59:08 +01:00
parent 331adf0b62
commit 21978af96c
34 changed files with 203 additions and 36 deletions

View File

@@ -11,7 +11,7 @@
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.2.2.RELEASE</version>
<version>2.2.4.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>

View File

@@ -11,7 +11,7 @@
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.2.2.RELEASE</version>
<version>2.2.4.RELEASE</version>
<relativePath /> <!-- lookup parent from repository -->
</parent>

View File

@@ -11,7 +11,7 @@
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.2.2.RELEASE</version>
<version>2.2.4.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>

View File

@@ -11,7 +11,7 @@
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.2.2.RELEASE</version>
<version>2.2.4.RELEASE</version>
<relativePath /> <!-- lookup parent from repository -->
</parent>

View File

@@ -14,7 +14,7 @@
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.2.2.RELEASE</version>
<version>2.2.4.RELEASE</version>
<relativePath/>
</parent>

View File

@@ -1,3 +1,3 @@
org.gradle.daemon=false
BOM_VERSION=Hoxton.BUILD-SNAPSHOT
bootVersion=2.2.2.RELEASE
bootVersion=2.2.4.RELEASE

View File

@@ -14,7 +14,7 @@
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.2.2.RELEASE</version>
<version>2.2.4.RELEASE</version>
<relativePath/>
</parent>

View File

@@ -1,4 +1,4 @@
org.gradle.daemon=false
verifierVersion=2.2.2.BUILD-SNAPSHOT
BOM_VERSION=Hoxton.BUILD-SNAPSHOT
bootVersion=2.2.2.RELEASE
bootVersion=2.2.4.RELEASE

View File

@@ -14,7 +14,7 @@
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.2.2.RELEASE</version>
<version>2.2.4.RELEASE</version>
<relativePath/>
</parent>

View File

@@ -1,4 +1,4 @@
org.gradle.daemon=false
verifierVersion=2.2.2.BUILD-SNAPSHOT
BOM_VERSION=Hoxton.BUILD-SNAPSHOT
bootVersion=2.2.2.RELEASE
bootVersion=2.2.4.RELEASE

View File

@@ -31,7 +31,7 @@
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.2.2.RELEASE</version>
<version>2.2.4.RELEASE</version>
<relativePath/>
</parent>

View File

@@ -1,4 +1,4 @@
org.gradle.daemon=false
verifierVersion=2.2.2.BUILD-SNAPSHOT
BOM_VERSION=Hoxton.BUILD-SNAPSHOT
bootVersion=2.2.2.RELEASE
bootVersion=2.2.4.RELEASE

View File

@@ -14,7 +14,7 @@
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.2.2.RELEASE</version>
<version>2.2.4.RELEASE</version>
<relativePath/>
</parent>

View File

@@ -1,4 +1,4 @@
org.gradle.daemon=false
verifierVersion=2.2.2.BUILD-SNAPSHOT
BOM_VERSION=Hoxton.BUILD-SNAPSHOT
bootVersion=2.2.2.RELEASE
bootVersion=2.2.4.RELEASE

View File

@@ -14,7 +14,7 @@
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.2.2.RELEASE</version>
<version>2.2.4.RELEASE</version>
<relativePath/>
</parent>

View File

@@ -1,4 +1,4 @@
org.gradle.daemon=false
verifierVersion=2.2.2.BUILD-SNAPSHOT
BOM_VERSION=Hoxton.BUILD-SNAPSHOT
bootVersion=2.2.2.RELEASE
bootVersion=2.2.4.RELEASE

View File

@@ -14,7 +14,7 @@
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.2.2.RELEASE</version>
<version>2.2.4.RELEASE</version>
<relativePath/>
</parent>

View File

@@ -14,7 +14,7 @@
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.2.2.RELEASE</version>
<version>2.2.4.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>

View File

@@ -14,7 +14,7 @@
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.2.2.RELEASE</version>
<version>2.2.4.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>

View File

@@ -14,7 +14,7 @@
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.2.2.RELEASE</version>
<version>2.2.4.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>

View File

@@ -14,7 +14,7 @@
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.2.2.RELEASE</version>
<version>2.2.4.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>

View File

@@ -14,7 +14,7 @@
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.2.2.RELEASE</version>
<version>2.2.4.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>

View File

@@ -14,7 +14,7 @@
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.2.2.RELEASE</version>
<version>2.2.4.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>

View File

@@ -0,0 +1,168 @@
/*
* Copyright 2012-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.contract.stubrunner.spring.cloud.loadbalancer;
import java.io.Closeable;
import java.net.URI;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import reactor.core.publisher.Mono;
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
import org.springframework.boot.autoconfigure.AutoConfigureBefore;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.loadbalancer.LoadBalancerClient;
import org.springframework.cloud.client.loadbalancer.reactive.DefaultResponse;
import org.springframework.cloud.client.loadbalancer.reactive.ReactiveLoadBalancer;
import org.springframework.cloud.contract.stubrunner.RunningStubs;
import org.springframework.cloud.contract.stubrunner.StubConfiguration;
import org.springframework.cloud.contract.stubrunner.StubFinder;
import org.springframework.cloud.contract.stubrunner.spring.cloud.ConditionalOnStubbedDiscoveryEnabled;
import org.springframework.cloud.contract.stubrunner.spring.cloud.StubMapperProperties;
import org.springframework.cloud.loadbalancer.annotation.LoadBalancerClientConfiguration;
import org.springframework.cloud.loadbalancer.config.LoadBalancerAutoConfiguration;
import org.springframework.cloud.loadbalancer.support.LoadBalancerClientFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.util.StringUtils;
/**
* Provides autoconfiguraion for the Spring Cloud Load Balancer module.
*
* @since 3.0.0
* @author Marcin Grzejszczak
*/
@Configuration(proxyBeanMethods = false)
@ConditionalOnClass(LoadBalancerClient.class)
@ConditionalOnProperty(value = "stubrunner.cloud.loadbalancer.enabled",
matchIfMissing = true)
@ConditionalOnBean(StubMapperProperties.class)
@AutoConfigureBefore(LoadBalancerAutoConfiguration.class)
@AutoConfigureAfter(LoadBalancerClientConfiguration.class)
@ConditionalOnStubbedDiscoveryEnabled
public class SpringCloudLoadBalancerAutoConfiguration {
@Bean
@Primary
LoadBalancerClientFactory stubRunnerLoadBalancerClientFactory(StubFinder stubFinder,
StubMapperProperties stubMapperProperties) {
return new StubRunnerLoadBalancerClientFactory(stubFinder, stubMapperProperties);
}
}
class StubRunnerLoadBalancerClientFactory extends LoadBalancerClientFactory
implements Closeable {
private final StubFinder stubFinder;
private final StubMapperProperties stubMapperProperties;
StubRunnerLoadBalancerClientFactory(StubFinder stubFinder,
StubMapperProperties stubMapperProperties) {
this.stubFinder = stubFinder;
this.stubMapperProperties = stubMapperProperties;
}
@Override
public ReactiveLoadBalancer<ServiceInstance> getInstance(String serviceId) {
return request -> Mono.just(new DefaultResponse(
new StubbedServiceInstance(stubFinder, stubMapperProperties, serviceId)));
}
@Override
public void close() {
StubbedServiceInstance.CACHE.clear();
}
}
class StubbedServiceInstance implements ServiceInstance {
private final StubFinder stubFinder;
private final StubMapperProperties stubMapperProperties;
private final String serviceId;
static final Map<String, Map.Entry<StubConfiguration, Integer>> CACHE = new ConcurrentHashMap<>();
StubbedServiceInstance(StubFinder stubFinder,
StubMapperProperties stubMapperProperties, String serviceId) {
this.stubFinder = stubFinder;
this.stubMapperProperties = stubMapperProperties;
this.serviceId = serviceId;
}
private Map.Entry<StubConfiguration, Integer> stubEntry() {
Map.Entry<StubConfiguration, Integer> entry = CACHE.get(this.serviceId);
if (entry != null) {
return entry;
}
RunningStubs runningStubs = this.stubFinder.findAllRunningStubs();
String mappedServiceName = StringUtils.hasText(
this.stubMapperProperties.fromServiceIdToIvyNotation(this.serviceId))
? this.stubMapperProperties.fromServiceIdToIvyNotation(
this.serviceId)
: this.serviceId;
entry = runningStubs.getEntry(mappedServiceName);
CACHE.put(this.serviceId, entry);
return entry;
}
@Override
public String getServiceId() {
return this.serviceId;
}
@Override
public String getHost() {
return "localhost";
}
@Override
public int getPort() {
Map.Entry<StubConfiguration, Integer> entry = stubEntry();
if (entry == null) {
return 0;
}
return entry.getValue();
}
@Override
public boolean isSecure() {
// TODO: What if the user marks a stub as secure?
return false;
}
@Override
public URI getUri() {
return URI.create(
(isSecure() ? "https://" : "http://") + getHost() + ":" + getPort());
}
@Override
public Map<String, String> getMetadata() {
return null;
}
}

View File

@@ -37,9 +37,9 @@
"defaultValue": true
},
{
"name": "stubrunner.cloud.ribbon.enabled",
"name": "stubrunner.cloud.loadbalancer.enabled",
"type": "java.lang.Boolean",
"description": "Whether to enable Stub Runner's Ribbon integration.",
"description": "Whether to enable Stub Runner's Spring Cloud Load Balancer integration.",
"defaultValue": true
},
{

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2013-2019 the original author or authors.
* Copyright 2013-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.
@@ -30,13 +30,11 @@ import org.springframework.cloud.contract.stubrunner.spring.AutoConfigureStubRun
import org.springframework.cloud.contract.stubrunner.spring.StubRunnerProperties
import org.springframework.cloud.netflix.eureka.EurekaClientAutoConfiguration
import org.springframework.cloud.zookeeper.ZookeeperAutoConfiguration
import org.springframework.cloud.zookeeper.discovery.RibbonZookeeperAutoConfiguration
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration
import org.springframework.test.context.ActiveProfiles
import org.springframework.test.context.ContextConfiguration
import org.springframework.web.client.RestTemplate
/**
* @author Marcin Grzejszczak
*/
@@ -78,7 +76,7 @@ class StubRunnerSpringCloudAutoConfigurationSpec extends Specification {
// end::test[]
@Configuration
@EnableAutoConfiguration(exclude = [RibbonZookeeperAutoConfiguration, EurekaClientAutoConfiguration,
@EnableAutoConfiguration(exclude = [EurekaClientAutoConfiguration,
ConsulAutoConfiguration, ZookeeperAutoConfiguration])
static class Config {

View File

@@ -9,3 +9,4 @@ spring.cloud:
discovery.enabled: false
service-registry.enabled: false
eureka.client.enabled: false
ribbon.eureka.enabled: false

View File

@@ -16,5 +16,5 @@
wiremockVersion=2.25.1
jsonAssertVersion=0.4.13
verifierVersion=2.2.2.BUILD-SNAPSHOT
bootVersion=2.2.2.RELEASE
bootVersion=2.2.4.RELEASE
groovyVersion=2.4.17

View File

@@ -16,5 +16,5 @@
wiremockVersion=2.25.1
jsonAssertVersion=0.4.13
verifierVersion=2.2.2.BUILD-SNAPSHOT
bootVersion=2.2.2.RELEASE
bootVersion=2.2.4.RELEASE
groovyVersion=2.4.17

View File

@@ -29,7 +29,7 @@
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.2.2.RELEASE</version>
<version>2.2.4.RELEASE</version>
</parent>
<dependencies>

View File

@@ -29,7 +29,7 @@
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.2.2.RELEASE</version>
<version>2.2.4.RELEASE</version>
</parent>
<dependencies>

View File

@@ -29,7 +29,7 @@
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.2.2.RELEASE</version>
<version>2.2.4.RELEASE</version>
</parent>
<dependencies>

View File

@@ -29,7 +29,7 @@
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.2.2.RELEASE</version>
<version>2.2.4.RELEASE</version>
</parent>
<dependencies>

View File

@@ -29,7 +29,7 @@
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.2.2.RELEASE</version>
<version>2.2.4.RELEASE</version>
</parent>
<dependencies>