Added support for ReactiveDiscoveryClient; fixes gh-1392
This commit is contained in:
@@ -16,7 +16,6 @@
|
||||
|
||||
package org.springframework.cloud.contract.stubrunner.spring.cloud;
|
||||
|
||||
import java.lang.invoke.MethodHandles;
|
||||
import java.net.URI;
|
||||
import java.net.URL;
|
||||
import java.util.ArrayList;
|
||||
@@ -41,8 +40,7 @@ import org.springframework.util.StringUtils;
|
||||
*/
|
||||
class StubRunnerDiscoveryClient implements DiscoveryClient {
|
||||
|
||||
private static final Log log = LogFactory
|
||||
.getLog(MethodHandles.lookup().lookupClass());
|
||||
private static final Log log = LogFactory.getLog(StubRunnerDiscoveryClient.class);
|
||||
|
||||
private final DiscoveryClient delegate;
|
||||
|
||||
@@ -51,7 +49,7 @@ class StubRunnerDiscoveryClient implements DiscoveryClient {
|
||||
private final StubMapperProperties stubMapperProperties;
|
||||
|
||||
StubRunnerDiscoveryClient(DiscoveryClient delegate, StubFinder stubFinder,
|
||||
StubMapperProperties stubMapperProperties, String springAppName) {
|
||||
StubMapperProperties stubMapperProperties) {
|
||||
this.delegate = delegate instanceof StubRunnerDiscoveryClient
|
||||
? noOpDiscoveryClient() : delegate;
|
||||
if (log.isDebugEnabled()) {
|
||||
@@ -63,7 +61,7 @@ class StubRunnerDiscoveryClient implements DiscoveryClient {
|
||||
}
|
||||
|
||||
StubRunnerDiscoveryClient(StubFinder stubFinder,
|
||||
StubMapperProperties stubMapperProperties, String springAppName) {
|
||||
StubMapperProperties stubMapperProperties) {
|
||||
this.delegate = noOpDiscoveryClient();
|
||||
if (log.isDebugEnabled()) {
|
||||
log.debug("Will delegate calls to discovery service [" + this.delegate
|
||||
@@ -97,13 +95,13 @@ class StubRunnerDiscoveryClient implements DiscoveryClient {
|
||||
String serviceToFind = StringUtils.hasText(ivyNotation) ? ivyNotation : serviceId;
|
||||
URL stubUrl = this.stubFinder.findStubUrl(serviceToFind);
|
||||
log.info("Resolved from ivy [" + ivyNotation + "] service to find ["
|
||||
+ serviceToFind + "]. " + "Found stub is available under URL [" + stubUrl
|
||||
+ serviceToFind + "]. Found stub is available under URL [" + stubUrl
|
||||
+ "]");
|
||||
if (stubUrl == null) {
|
||||
return getInstancesFromDelegate(serviceId);
|
||||
}
|
||||
return Collections.<ServiceInstance>singletonList(new StubRunnerServiceInstance(
|
||||
serviceId, stubUrl.getHost(), stubUrl.getPort(), toUri(stubUrl)));
|
||||
return Collections.singletonList(new StubRunnerServiceInstance(serviceId,
|
||||
stubUrl.getHost(), stubUrl.getPort(), toUri(stubUrl)));
|
||||
}
|
||||
|
||||
private List<ServiceInstance> getInstancesFromDelegate(String serviceId) {
|
||||
|
||||
@@ -0,0 +1,125 @@
|
||||
/*
|
||||
* 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.
|
||||
* 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;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import reactor.core.publisher.Flux;
|
||||
|
||||
import org.springframework.cloud.client.ServiceInstance;
|
||||
import org.springframework.cloud.client.discovery.DiscoveryClient;
|
||||
import org.springframework.cloud.client.discovery.ReactiveDiscoveryClient;
|
||||
import org.springframework.cloud.contract.stubrunner.StubFinder;
|
||||
|
||||
/**
|
||||
* Custom version of {@link DiscoveryClient} that tries to find an instance in one of the
|
||||
* started WireMock servers.
|
||||
*
|
||||
* @author Marcin Grzejszczak
|
||||
* @since 2.2.4
|
||||
*/
|
||||
class StubRunnerReactiveDiscoveryClient implements ReactiveDiscoveryClient {
|
||||
|
||||
private static final Log log = LogFactory
|
||||
.getLog(StubRunnerReactiveDiscoveryClient.class);
|
||||
|
||||
private final ReactiveDiscoveryClient delegate;
|
||||
|
||||
private final StubFinder stubFinder;
|
||||
|
||||
private final StubMapperProperties stubMapperProperties;
|
||||
|
||||
StubRunnerReactiveDiscoveryClient(ReactiveDiscoveryClient delegate,
|
||||
StubFinder stubFinder, StubMapperProperties stubMapperProperties) {
|
||||
this.delegate = delegate instanceof StubRunnerDiscoveryClient
|
||||
? noOpDiscoveryClient() : delegate;
|
||||
if (log.isDebugEnabled()) {
|
||||
log.debug("Will delegate calls to discovery service [" + this.delegate
|
||||
+ "] if a stub is not found");
|
||||
}
|
||||
this.stubFinder = stubFinder;
|
||||
this.stubMapperProperties = stubMapperProperties;
|
||||
}
|
||||
|
||||
StubRunnerReactiveDiscoveryClient(StubFinder stubFinder,
|
||||
StubMapperProperties stubMapperProperties) {
|
||||
this.delegate = noOpDiscoveryClient();
|
||||
if (log.isDebugEnabled()) {
|
||||
log.debug("Will delegate calls to discovery service [" + this.delegate
|
||||
+ "] if a stub is not found");
|
||||
}
|
||||
this.stubFinder = stubFinder;
|
||||
this.stubMapperProperties = stubMapperProperties;
|
||||
}
|
||||
|
||||
private StubRunnerNoOpReactiveDiscoveryClient noOpDiscoveryClient() {
|
||||
return new StubRunnerNoOpReactiveDiscoveryClient();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String description() {
|
||||
try {
|
||||
return this.delegate.description();
|
||||
}
|
||||
catch (Exception e) {
|
||||
if (log.isDebugEnabled()) {
|
||||
log.debug("Failed to fetch description from delegate", e);
|
||||
}
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
@Override
|
||||
public Flux<ServiceInstance> getInstances(String serviceId) {
|
||||
return Flux.just(client()).flatMapIterable(c -> c.getInstances(serviceId));
|
||||
}
|
||||
|
||||
private StubRunnerDiscoveryClient client() {
|
||||
return new StubRunnerDiscoveryClient(this.stubFinder, this.stubMapperProperties);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Flux<String> getServices() {
|
||||
return Flux.just(client())
|
||||
.flatMapIterable(StubRunnerDiscoveryClient::getServices);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getOrder() {
|
||||
return this.delegate.getOrder();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class StubRunnerNoOpReactiveDiscoveryClient implements ReactiveDiscoveryClient {
|
||||
|
||||
@Override
|
||||
public String description() {
|
||||
return "Spring Cloud Stub Runner No-op ReactiveDiscoveryClient";
|
||||
}
|
||||
|
||||
@Override
|
||||
public Flux<ServiceInstance> getInstances(String serviceId) {
|
||||
return Flux.empty();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Flux<String> getServices() {
|
||||
return Flux.empty();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -18,13 +18,13 @@ package org.springframework.cloud.contract.stubrunner.spring.cloud;
|
||||
|
||||
import org.springframework.beans.BeansException;
|
||||
import org.springframework.beans.factory.BeanFactory;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.beans.factory.config.BeanPostProcessor;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
|
||||
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.client.discovery.DiscoveryClient;
|
||||
import org.springframework.cloud.client.discovery.ReactiveDiscoveryClient;
|
||||
import org.springframework.cloud.contract.stubrunner.StubFinder;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
@@ -55,10 +55,18 @@ public class StubRunnerSpringCloudAutoConfiguration {
|
||||
@ConditionalOnProperty(value = "stubrunner.cloud.delegate.enabled",
|
||||
havingValue = "false", matchIfMissing = true)
|
||||
public DiscoveryClient noOpStubRunnerDiscoveryClient(StubFinder stubFinder,
|
||||
StubMapperProperties stubMapperProperties,
|
||||
@Value("${spring.application.name:unknown}") String springAppName) {
|
||||
return new StubRunnerDiscoveryClient(stubFinder, stubMapperProperties,
|
||||
springAppName);
|
||||
StubMapperProperties stubMapperProperties) {
|
||||
return new StubRunnerDiscoveryClient(stubFinder, stubMapperProperties);
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean(ReactiveDiscoveryClient.class)
|
||||
@ConditionalOnStubbedDiscoveryEnabled
|
||||
@ConditionalOnProperty(value = "stubrunner.cloud.delegate.enabled",
|
||||
havingValue = "false", matchIfMissing = true)
|
||||
public ReactiveDiscoveryClient noOpStubRunnerReactiveDiscoveryClient(
|
||||
StubFinder stubFinder, StubMapperProperties stubMapperProperties) {
|
||||
return new StubRunnerReactiveDiscoveryClient(stubFinder, stubMapperProperties);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -97,10 +105,9 @@ class StubRunnerDiscoveryClientWrapper implements BeanPostProcessor {
|
||||
}
|
||||
if (isCloudDelegateEnabled()) {
|
||||
return new StubRunnerDiscoveryClient((DiscoveryClient) bean, stubFinder(),
|
||||
stubMapperProperties(), springAppName());
|
||||
stubMapperProperties());
|
||||
}
|
||||
return new StubRunnerDiscoveryClient(stubFinder(), stubMapperProperties(),
|
||||
springAppName());
|
||||
return new StubRunnerDiscoveryClient(stubFinder(), stubMapperProperties());
|
||||
}
|
||||
return bean;
|
||||
}
|
||||
@@ -120,14 +127,6 @@ class StubRunnerDiscoveryClientWrapper implements BeanPostProcessor {
|
||||
return this.stubMapperProperties;
|
||||
}
|
||||
|
||||
String springAppName() {
|
||||
if (this.springAppName == null) {
|
||||
this.springAppName = this.beanFactory.getBean(Environment.class)
|
||||
.getProperty("spring.application.name", "unknown");
|
||||
}
|
||||
return this.springAppName;
|
||||
}
|
||||
|
||||
boolean isStubbedDiscoveryEnabled() {
|
||||
if (this.stubbedDiscoveryEnabled == null) {
|
||||
this.stubbedDiscoveryEnabled = Boolean
|
||||
|
||||
@@ -0,0 +1,99 @@
|
||||
/*
|
||||
* 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.
|
||||
* 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
|
||||
|
||||
import org.junit.AfterClass
|
||||
import org.junit.BeforeClass
|
||||
import spock.lang.Specification
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired
|
||||
import org.springframework.boot.autoconfigure.EnableAutoConfiguration
|
||||
import org.springframework.boot.test.context.SpringBootContextLoader
|
||||
import org.springframework.cloud.client.ServiceInstance
|
||||
import org.springframework.cloud.client.discovery.ReactiveDiscoveryClient
|
||||
import org.springframework.cloud.client.loadbalancer.LoadBalanced
|
||||
import org.springframework.cloud.consul.ConsulAutoConfiguration
|
||||
import org.springframework.cloud.contract.stubrunner.StubFinder
|
||||
import org.springframework.cloud.contract.stubrunner.spring.AutoConfigureStubRunner
|
||||
import org.springframework.cloud.contract.stubrunner.spring.StubRunnerProperties
|
||||
import org.springframework.cloud.contract.stubrunner.spring.cloud.loadbalancer.StubRunnerLoadBalancerClientFactory
|
||||
import org.springframework.cloud.loadbalancer.support.LoadBalancerClientFactory
|
||||
import org.springframework.cloud.netflix.eureka.EurekaClientAutoConfiguration
|
||||
import org.springframework.cloud.zookeeper.ZookeeperAutoConfiguration
|
||||
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
|
||||
*/
|
||||
@ContextConfiguration(classes = Config, loader = SpringBootContextLoader)
|
||||
@ActiveProfiles("cloudtest-no-ribbon")
|
||||
// tag::autoconfigure[]
|
||||
@AutoConfigureStubRunner(
|
||||
ids = ["org.springframework.cloud.contract.verifier.stubs:loanIssuance",
|
||||
"org.springframework.cloud.contract.verifier.stubs:fraudDetectionServer",
|
||||
"org.springframework.cloud.contract.verifier.stubs:bootService"],
|
||||
stubsMode = StubRunnerProperties.StubsMode.REMOTE,
|
||||
repositoryRoot = "classpath:m2repo/repository/")
|
||||
// end::autoconfigure[]
|
||||
class StubRunnerSpringCloudReactiveNoRibbonAutoConfigurationSpec extends Specification {
|
||||
@Autowired
|
||||
StubFinder stubFinder
|
||||
@Autowired
|
||||
ReactiveDiscoveryClient reactiveDiscoveryClient;
|
||||
@Autowired
|
||||
LoadBalancerClientFactory loadBalancerClientFactory;
|
||||
RestTemplate restTemplate = new RestTemplate()
|
||||
|
||||
@BeforeClass
|
||||
@AfterClass
|
||||
static void setupProps() {
|
||||
System.clearProperty("stubrunner.repository.root")
|
||||
System.clearProperty("stubrunner.classifier")
|
||||
}
|
||||
|
||||
def setup() {
|
||||
assert loadBalancerClientFactory instanceof StubRunnerLoadBalancerClientFactory
|
||||
}
|
||||
|
||||
// tag::test[]
|
||||
def 'should make service discovery work'() {
|
||||
expect: 'WireMocks are running'
|
||||
"${stubFinder.findStubUrl('loanIssuance').toString()}/name".toURL().text == 'loanIssuance'
|
||||
"${stubFinder.findStubUrl('fraudDetectionServer').toString()}/name".toURL().text == 'fraudDetectionServer'
|
||||
and: 'Stubs can be reached via load service discovery'
|
||||
ServiceInstance loanIssuance = reactiveDiscoveryClient.getInstances('loanIssuance').blockFirst()
|
||||
restTemplate.getForObject(loanIssuance.uri.toString() + '/name', String) == 'loanIssuance'
|
||||
ServiceInstance fraudDetection = reactiveDiscoveryClient.getInstances('someNameThatShouldMapFraudDetectionServer').blockFirst()
|
||||
restTemplate.getForObject(fraudDetection.uri.toString() + '/name', String)== 'fraudDetectionServer'
|
||||
}
|
||||
// end::test[]
|
||||
|
||||
@Configuration
|
||||
@EnableAutoConfiguration(exclude = [EurekaClientAutoConfiguration,
|
||||
ConsulAutoConfiguration, ZookeeperAutoConfiguration])
|
||||
static class Config {
|
||||
|
||||
@Bean
|
||||
@LoadBalanced
|
||||
RestTemplate restTemplate() {
|
||||
return new RestTemplate()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
stubrunner:
|
||||
cloud.stubbed.discovery.enabled: true
|
||||
spring.cloud:
|
||||
zookeeper:
|
||||
enabled: false
|
||||
discovery.enabled: false
|
||||
consul:
|
||||
enabled: false
|
||||
discovery.enabled: false
|
||||
service-registry.enabled: false
|
||||
eureka.client.enabled: false
|
||||
ribbon.eureka.enabled: false
|
||||
spring:
|
||||
cloud:
|
||||
loadbalancer:
|
||||
ribbon:
|
||||
enabled: false # Use Reactive Load Balancer
|
||||
Reference in New Issue
Block a user