diff --git a/docker/spring-cloud-contract-stub-runner-docker/pom.xml b/docker/spring-cloud-contract-stub-runner-docker/pom.xml
index 7901a9a844..cc2dd708fa 100644
--- a/docker/spring-cloud-contract-stub-runner-docker/pom.xml
+++ b/docker/spring-cloud-contract-stub-runner-docker/pom.xml
@@ -40,6 +40,13 @@
pom
runtime
+
+ org.springframework.cloud
+ spring-cloud-netflix-dependencies
+ ${spring-cloud-netflix.version}
+ pom
+ runtime
+
org.springframework.cloud
spring-cloud-zookeeper-dependencies
diff --git a/pom.xml b/pom.xml
index 23c702d75e..6438ba9e64 100644
--- a/pom.xml
+++ b/pom.xml
@@ -30,6 +30,7 @@
4.0.0-SNAPSHOT
4.0.0-SNAPSHOT
4.0.0-SNAPSHOT
+ 4.0.0-SNAPSHOT
4.0.0-SNAPSHOT
4.0.0-SNAPSHOT
5.0.4
@@ -274,6 +275,13 @@
pom
import
+
+ org.springframework.cloud
+ spring-cloud-netflix-dependencies
+ ${spring-cloud-netflix.version}
+ pom
+ import
+
org.springframework.cloud
spring-cloud-stream-dependencies
diff --git a/spring-cloud-contract-stub-runner/pom.xml b/spring-cloud-contract-stub-runner/pom.xml
index 03a7a37e84..50aa6d7cca 100644
--- a/spring-cloud-contract-stub-runner/pom.xml
+++ b/spring-cloud-contract-stub-runner/pom.xml
@@ -211,6 +211,11 @@
curator-test
test
+
+ org.springframework.cloud
+ spring-cloud-netflix-eureka-server
+ true
+
org.springframework.cloud
spring-cloud-starter-consul-discovery
diff --git a/spring-cloud-contract-stub-runner/src/main/java/org/springframework/cloud/contract/stubrunner/spring/cloud/eureka/ConditionalOnEurekaEnabled.java b/spring-cloud-contract-stub-runner/src/main/java/org/springframework/cloud/contract/stubrunner/spring/cloud/eureka/ConditionalOnEurekaEnabled.java
new file mode 100644
index 0000000000..2ad62d3eb5
--- /dev/null
+++ b/spring-cloud-contract-stub-runner/src/main/java/org/springframework/cloud/contract/stubrunner/spring/cloud/eureka/ConditionalOnEurekaEnabled.java
@@ -0,0 +1,40 @@
+/*
+ * 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.eureka;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
+
+/**
+ * Conditional that checks if Eureka is enabled.
+ *
+ * @author Marcin Grzejszczak
+ *
+ * @since 1.0.0
+ */
+@Retention(RetentionPolicy.RUNTIME)
+@Target({ ElementType.TYPE, ElementType.METHOD })
+@Documented
+@ConditionalOnProperty(value = "eureka.client.enabled", havingValue = "true", matchIfMissing = true)
+@interface ConditionalOnEurekaEnabled {
+
+}
diff --git a/spring-cloud-contract-stub-runner/src/main/java/org/springframework/cloud/contract/stubrunner/spring/cloud/eureka/EurekaStubsRegistrar.java b/spring-cloud-contract-stub-runner/src/main/java/org/springframework/cloud/contract/stubrunner/spring/cloud/eureka/EurekaStubsRegistrar.java
new file mode 100644
index 0000000000..ad13b2e6c0
--- /dev/null
+++ b/spring-cloud-contract-stub-runner/src/main/java/org/springframework/cloud/contract/stubrunner/spring/cloud/eureka/EurekaStubsRegistrar.java
@@ -0,0 +1,163 @@
+/*
+ * 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.eureka;
+
+import java.lang.invoke.MethodHandles;
+import java.net.InetAddress;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+
+import com.netflix.appinfo.ApplicationInfoManager;
+import com.netflix.appinfo.InstanceInfo;
+import com.netflix.discovery.AbstractDiscoveryClientOptionalArgs;
+import com.netflix.discovery.EurekaClient;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+import org.springframework.beans.BeansException;
+import org.springframework.cloud.client.serviceregistry.ServiceRegistry;
+import org.springframework.cloud.commons.util.InetUtils;
+import org.springframework.cloud.contract.stubrunner.StubConfiguration;
+import org.springframework.cloud.contract.stubrunner.StubRunning;
+import org.springframework.cloud.contract.stubrunner.spring.cloud.StubMapperProperties;
+import org.springframework.cloud.contract.stubrunner.spring.cloud.StubsRegistrar;
+import org.springframework.cloud.netflix.eureka.CloudEurekaClient;
+import org.springframework.cloud.netflix.eureka.EurekaClientConfigBean;
+import org.springframework.cloud.netflix.eureka.EurekaInstanceConfigBean;
+import org.springframework.cloud.netflix.eureka.InstanceInfoFactory;
+import org.springframework.cloud.netflix.eureka.serviceregistry.EurekaRegistration;
+import org.springframework.context.ApplicationContext;
+import org.springframework.util.StringUtils;
+
+/**
+ * Registers all stubs in Eureka Service Discovery.
+ *
+ * @author Marcin Grzejszczak
+ * @since 1.0.0
+ */
+public class EurekaStubsRegistrar implements StubsRegistrar {
+
+ private static final Log log = LogFactory.getLog(MethodHandles.lookup().lookupClass());
+
+ private final StubRunning stubRunning;
+
+ private final StubMapperProperties stubMapperProperties;
+
+ private final InetUtils inetUtils;
+
+ private final EurekaInstanceConfigBean eurekaInstanceConfigBean;
+
+ private final EurekaClientConfigBean eurekaClientConfigBean;
+
+ private final List registrations = new LinkedList<>();
+
+ private final ServiceRegistry serviceRegistry;
+
+ private final ApplicationContext context;
+
+ public EurekaStubsRegistrar(StubRunning stubRunning, ServiceRegistry serviceRegistry,
+ StubMapperProperties stubMapperProperties, InetUtils inetUtils,
+ EurekaInstanceConfigBean eurekaInstanceConfigBean, EurekaClientConfigBean eurekaClientConfigBean,
+ ApplicationContext context) {
+ this.stubRunning = stubRunning;
+ this.stubMapperProperties = stubMapperProperties;
+ this.serviceRegistry = serviceRegistry;
+ this.inetUtils = inetUtils;
+ this.eurekaInstanceConfigBean = eurekaInstanceConfigBean;
+ this.eurekaClientConfigBean = eurekaClientConfigBean;
+ this.context = context;
+ }
+
+ @Override
+ public void registerStubs() {
+ Map activeStubs = this.stubRunning.runStubs().validNamesAndPorts();
+ for (Map.Entry entry : activeStubs.entrySet()) {
+ EurekaInstanceConfigBean instance = registration(entry);
+ log.info("Will register stub in Eureka " + "[" + instance.getAppname() + ", " + instance.getHostname()
+ + ", " + instance.getNonSecurePort() + ", " + instance.getInstanceId() + "]");
+ InstanceInfo instanceInfo = new InstanceInfoFactory().create(instance);
+ ApplicationInfoManager applicationInfoManager = new ApplicationInfoManager(instance, instanceInfo);
+ AbstractDiscoveryClientOptionalArgs args = args();
+ EurekaClient client = new CloudEurekaClient(applicationInfoManager, this.eurekaClientConfigBean, args,
+ this.context);
+ EurekaRegistration registration = EurekaRegistration.builder(instance)
+ .with(this.eurekaClientConfigBean, this.context).with(client).build();
+ this.registrations.add(registration);
+ try {
+ this.serviceRegistry.register(registration);
+ log.info("Successfully registered stub " + "[" + entry.getKey().toColonSeparatedDependencyNotation()
+ + "] in Service Discovery");
+ }
+ catch (Exception e) {
+ log.warn("Exception occurred while trying to register a stub ["
+ + entry.getKey().toColonSeparatedDependencyNotation() + "] in Service Discovery", e);
+ }
+ }
+ }
+
+ private AbstractDiscoveryClientOptionalArgs args() {
+ try {
+ return this.context.getBean(AbstractDiscoveryClientOptionalArgs.class);
+ }
+ catch (BeansException e) {
+ return null;
+ }
+ }
+
+ private EurekaInstanceConfigBean registration(Map.Entry entry) {
+ EurekaInstanceConfigBean config = new EurekaInstanceConfigBean(this.inetUtils);
+ String appName = name(entry.getKey());
+ config.setInstanceEnabledOnit(true);
+ InetAddress address = this.inetUtils.findFirstNonLoopbackAddress();
+ config.setIpAddress(address.getHostAddress());
+ config.setHostname(StringUtils.hasText(hostName(entry)) ? hostName(entry) : address.getHostName());
+ config.setAppname(appName);
+ config.setVirtualHostName(appName);
+ config.setSecureVirtualHostName(appName);
+ int port = port(entry);
+ config.setNonSecurePort(port);
+ config.setInstanceId(address.getHostAddress() + ":" + entry.getKey().getArtifactId() + ":" + port);
+ config.setLeaseRenewalIntervalInSeconds(1);
+ return config;
+ }
+
+ protected String hostName(Map.Entry entry) {
+ return this.eurekaInstanceConfigBean.getHostname();
+ }
+
+ protected int port(Map.Entry entry) {
+ return entry.getValue();
+ }
+
+ private String name(StubConfiguration stubConfiguration) {
+ String resolvedName = this.stubMapperProperties
+ .fromIvyNotationToId(stubConfiguration.toColonSeparatedDependencyNotation());
+ if (StringUtils.hasText(resolvedName)) {
+ return resolvedName;
+ }
+ return stubConfiguration.getArtifactId();
+ }
+
+ @Override
+ public void close() throws Exception {
+ for (EurekaRegistration registration : this.registrations) {
+ this.serviceRegistry.deregister(registration);
+ }
+ }
+
+}
diff --git a/spring-cloud-contract-stub-runner/src/main/java/org/springframework/cloud/contract/stubrunner/spring/cloud/eureka/StubRunnerSpringCloudEurekaAutoConfiguration.java b/spring-cloud-contract-stub-runner/src/main/java/org/springframework/cloud/contract/stubrunner/spring/cloud/eureka/StubRunnerSpringCloudEurekaAutoConfiguration.java
new file mode 100644
index 0000000000..2b40a56d20
--- /dev/null
+++ b/spring-cloud-contract-stub-runner/src/main/java/org/springframework/cloud/contract/stubrunner/spring/cloud/eureka/StubRunnerSpringCloudEurekaAutoConfiguration.java
@@ -0,0 +1,112 @@
+/*
+ * 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.eureka;
+
+import java.util.Map;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.autoconfigure.AutoConfigureAfter;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
+import org.springframework.cloud.client.serviceregistry.ServiceRegistry;
+import org.springframework.cloud.commons.util.InetUtils;
+import org.springframework.cloud.contract.stubrunner.StubConfiguration;
+import org.springframework.cloud.contract.stubrunner.StubRunning;
+import org.springframework.cloud.contract.stubrunner.spring.StubRunnerConfiguration;
+import org.springframework.cloud.contract.stubrunner.spring.cloud.ConditionalOnStubbedDiscoveryDisabled;
+import org.springframework.cloud.contract.stubrunner.spring.cloud.StubMapperProperties;
+import org.springframework.cloud.contract.stubrunner.spring.cloud.StubsRegistrar;
+import org.springframework.cloud.netflix.eureka.CloudEurekaClient;
+import org.springframework.cloud.netflix.eureka.EurekaClientAutoConfiguration;
+import org.springframework.cloud.netflix.eureka.EurekaClientConfigBean;
+import org.springframework.cloud.netflix.eureka.EurekaInstanceConfigBean;
+import org.springframework.cloud.netflix.eureka.serviceregistry.EurekaRegistration;
+import org.springframework.context.ApplicationContext;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.context.annotation.Profile;
+import org.springframework.core.env.Environment;
+
+/**
+ * Autoconfiguration for registering stubs in a Eureka Service discovery.
+ *
+ * @author Marcin Grzejszczak
+ * @since 1.0.0
+ */
+@Configuration(proxyBeanMethods = false)
+@AutoConfigureAfter({ StubRunnerConfiguration.class, EurekaClientAutoConfiguration.class })
+@ConditionalOnClass(CloudEurekaClient.class)
+@ConditionalOnStubbedDiscoveryDisabled
+@ConditionalOnEurekaEnabled
+@ConditionalOnProperty(value = "stubrunner.cloud.eureka.enabled", matchIfMissing = true)
+public class StubRunnerSpringCloudEurekaAutoConfiguration {
+
+ @Profile("!cloud")
+ @Configuration(proxyBeanMethods = false)
+ protected static class NonCloudConfig {
+
+ @Bean(initMethod = "registerStubs")
+ public StubsRegistrar stubsRegistrar(StubRunning stubRunning,
+ ServiceRegistry serviceRegistry, ApplicationContext context,
+ StubMapperProperties stubMapperProperties, InetUtils inetUtils,
+ EurekaInstanceConfigBean eurekaInstanceConfigBean, EurekaClientConfigBean eurekaClientConfigBean) {
+ return new EurekaStubsRegistrar(stubRunning, serviceRegistry, stubMapperProperties, inetUtils,
+ eurekaInstanceConfigBean, eurekaClientConfigBean, context);
+ }
+
+ }
+
+ @Profile("cloud")
+ @Configuration(proxyBeanMethods = false)
+ protected static class CloudConfig {
+
+ private static final int DEFAULT_PORT = 80;
+
+ private static final Log log = LogFactory.getLog(CloudConfig.class);
+
+ @Autowired
+ Environment environment;
+
+ @Bean(initMethod = "registerStubs")
+ public StubsRegistrar stubsRegistrar(StubRunning stubRunning,
+ ServiceRegistry serviceRegistry, ApplicationContext context,
+ StubMapperProperties stubMapperProperties, InetUtils inetUtils,
+ EurekaInstanceConfigBean eurekaInstanceConfigBean, EurekaClientConfigBean eurekaClientConfigBean) {
+ return new EurekaStubsRegistrar(stubRunning, serviceRegistry, stubMapperProperties, inetUtils,
+ eurekaInstanceConfigBean, eurekaClientConfigBean, context) {
+ @Override
+ protected String hostName(Map.Entry entry) {
+ String hostname = CloudConfig.this.environment.getProperty("application.hostname") + "-"
+ + entry.getValue() + "." + CloudConfig.this.environment.getProperty("application.domain");
+ log.info("Registering stub [" + entry.getKey().getArtifactId() + "] with hostname [" + hostname
+ + "]");
+ return hostname;
+ }
+
+ @Override
+ protected int port(Map.Entry entry) {
+ return DEFAULT_PORT;
+ }
+ };
+ }
+
+ }
+
+}
diff --git a/spring-cloud-contract-stub-runner/src/test/groovy/org/springframework/cloud/contract/stubrunner/serverexamples/StubRunnerBootEurekaExample.java b/spring-cloud-contract-stub-runner/src/test/groovy/org/springframework/cloud/contract/stubrunner/serverexamples/StubRunnerBootEurekaExample.java
new file mode 100644
index 0000000000..5158dda531
--- /dev/null
+++ b/spring-cloud-contract-stub-runner/src/test/groovy/org/springframework/cloud/contract/stubrunner/serverexamples/StubRunnerBootEurekaExample.java
@@ -0,0 +1,64 @@
+/*
+ * 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.serverexamples;
+
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+import org.springframework.cloud.contract.stubrunner.server.EnableStubRunnerServer;
+import org.springframework.cloud.contract.stubrunner.spring.AutoConfigureStubRunner;
+import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
+
+/**
+ * @author Marcin Grzejszczak
+ */
+// tag::stubrunnereureka[]
+@SpringBootApplication
+@EnableStubRunnerServer
+@EnableEurekaClient
+@AutoConfigureStubRunner
+public class StubRunnerBootEurekaExample {
+
+ public static void main(String[] args) {
+ SpringApplication.run(StubRunnerBootEurekaExample.class, args);
+ }
+
+}
+// end::stubrunnereureka[]
+
+/*
+ *
+ * // tag::stubrunnereureka_args[]
+ * -Dstubrunner.repositoryRoot=https://repo.spring.io/snapshot (1)
+ * -Dstubrunner.cloud.stubbed.discovery.enabled=false (2)
+ * -Dstubrunner.ids=org.springframework.cloud.contract.verifier.stubs:loanIssuance,org.
+ * springframework.cloud.contract.verifier.stubs:fraudDetectionServer,org.springframework.
+ * cloud.contract.verifier.stubs:bootService (3)
+ * -Dstubrunner.idsToServiceIds.fraudDetectionServer=
+ * someNameThatShouldMapFraudDetectionServer (4)
+ *
+ * (1) - we tell Stub Runner where all the stubs reside (2) - we don't want the default
+ * behaviour where the discovery service is stubbed. That's why the stub registration will
+ * be picked (3) - we provide a list of stubs to download (4) - we provide a list of
+ * artifactId to serviceId mapping // end::stubrunnereureka_args[]
+ *
+ *
+ * -Dstubrunner.cloud.eureka.enabled=true
+ * -Dstubrunner.repositoryRoot=classpath:m2repo/repository/
+ * -Dstubrunner.camel.enabled=false -Dspring.cloud.zookeeper.enabled=false
+ * -Dspring.cloud.zookeeper.discovery.enabled=false -Ddebug=true
+ *
+ */
diff --git a/spring-cloud-contract-stub-runner/src/test/groovy/org/springframework/cloud/contract/stubrunner/spring/cloud/StubRunnerSpringCloudAutoConfigurationSpec.groovy b/spring-cloud-contract-stub-runner/src/test/groovy/org/springframework/cloud/contract/stubrunner/spring/cloud/StubRunnerSpringCloudAutoConfigurationSpec.groovy
new file mode 100644
index 0000000000..aafcf1e6ff
--- /dev/null
+++ b/spring-cloud-contract-stub-runner/src/test/groovy/org/springframework/cloud/contract/stubrunner/spring/cloud/StubRunnerSpringCloudAutoConfigurationSpec.groovy
@@ -0,0 +1,96 @@
+/*
+ * 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 spock.lang.Specification
+
+import org.springframework.beans.factory.annotation.Autowired
+import org.springframework.boot.autoconfigure.EnableAutoConfiguration
+import org.springframework.boot.test.context.SpringBootTest
+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.web.client.RestTemplate
+
+/**
+ * @author Marcin Grzejszczak
+ */
+@SpringBootTest(classes = Config)
+@ActiveProfiles("cloudtest")
+// 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 StubRunnerSpringCloudAutoConfigurationSpec extends Specification {
+
+ @Autowired
+ StubFinder stubFinder
+ @Autowired
+ @LoadBalanced
+ RestTemplate restTemplate
+ @Autowired
+ LoadBalancerClientFactory loadBalancerClientFactory;
+
+ void setupSpec() {
+ System.clearProperty("stubrunner.repository.root")
+ System.clearProperty("stubrunner.classifier")
+ }
+
+ void cleanupSpec() {
+ setupSpec()
+ }
+
+ 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'
+ restTemplate.getForObject('http://loanIssuance/name', String) == 'loanIssuance'
+ restTemplate.getForObject('http://someNameThatShouldMapFraudDetectionServer/name', String) == 'fraudDetectionServer'
+ }
+ // end::test[]
+
+ @Configuration
+ @EnableAutoConfiguration(exclude = [EurekaClientAutoConfiguration,
+ ConsulAutoConfiguration, ZookeeperAutoConfiguration])
+ static class Config {
+
+ @Bean
+ @LoadBalanced
+ RestTemplate restTemplate() {
+ return new RestTemplate()
+ }
+ }
+}
diff --git a/spring-cloud-contract-stub-runner/src/test/groovy/org/springframework/cloud/contract/stubrunner/spring/cloud/StubRunnerSpringCloudReactiveAutoConfigurationSpec.groovy b/spring-cloud-contract-stub-runner/src/test/groovy/org/springframework/cloud/contract/stubrunner/spring/cloud/StubRunnerSpringCloudReactiveAutoConfigurationSpec.groovy
new file mode 100644
index 0000000000..1cd17eacab
--- /dev/null
+++ b/spring-cloud-contract-stub-runner/src/test/groovy/org/springframework/cloud/contract/stubrunner/spring/cloud/StubRunnerSpringCloudReactiveAutoConfigurationSpec.groovy
@@ -0,0 +1,97 @@
+/*
+ * 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.SpringBootTest
+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.web.client.RestTemplate
+
+/**
+ * @author Marcin Grzejszczak
+ */
+@SpringBootTest(classes = Config)
+@ActiveProfiles("cloudtest")
+@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/")
+class StubRunnerSpringCloudReactiveAutoConfigurationSpec 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()
+ }
+ }
+}
diff --git a/tests/spring-cloud-contract-stub-runner-boot-eureka/pom.xml b/tests/spring-cloud-contract-stub-runner-boot-eureka/pom.xml
new file mode 100644
index 0000000000..5e4dc31ec6
--- /dev/null
+++ b/tests/spring-cloud-contract-stub-runner-boot-eureka/pom.xml
@@ -0,0 +1,92 @@
+
+
+ 4.0.0
+
+ org.springframework.cloud
+ spring-cloud-contract-tests
+ 4.0.0-SNAPSHOT
+ ..
+
+ spring-cloud-contract-stub-runner-boot-eureka
+ jar
+ Spring Cloud Contract Stub Runner Boot Eureka
+ Spring Cloud Contract Stub Runner Boot Eureka
+
+ 1.19.1
+
+
+
+ org.springframework.cloud
+ spring-cloud-contract-stub-runner
+
+
+ com.sun.jersey.contribs
+ jersey-apache-client4
+ ${jersey-apache-client4.version}
+ true
+
+
+ org.springframework.cloud
+ spring-cloud-starter-contract-stub-runner-jetty
+ test
+
+
+ org.springframework.cloud
+ spring-cloud-starter-netflix-eureka-client
+ true
+
+
+ org.springframework.cloud
+ spring-cloud-starter-netflix-eureka-server
+ true
+
+
+ junit
+ junit
+ test
+
+
+ org.codehaus.groovy
+ groovy
+
+
+ org.spockframework
+ spock-core
+ test
+
+
+ org.spockframework
+ spock-spring
+ test
+
+
+ org.springframework.boot
+ spring-boot-starter-test
+ test
+
+
+ org.springframework.boot
+ spring-boot-starter-web
+ test
+
+
+ org.springframework.boot
+ spring-boot-starter-webflux
+ test
+
+
+
+
+
+ org.codehaus.gmavenplus
+ gmavenplus-plugin
+
+
+ org.apache.maven.plugins
+ maven-surefire-plugin
+
+
+
+
diff --git a/tests/spring-cloud-contract-stub-runner-boot-eureka/src/test/groovy/org/springframework/cloud/contract/stubrunner/spring/cloud/eureka/StubRunnerSpringCloudEurekaAutoConfigurationSpec.groovy b/tests/spring-cloud-contract-stub-runner-boot-eureka/src/test/groovy/org/springframework/cloud/contract/stubrunner/spring/cloud/eureka/StubRunnerSpringCloudEurekaAutoConfigurationSpec.groovy
new file mode 100644
index 0000000000..207c8c44b0
--- /dev/null
+++ b/tests/spring-cloud-contract-stub-runner-boot-eureka/src/test/groovy/org/springframework/cloud/contract/stubrunner/spring/cloud/eureka/StubRunnerSpringCloudEurekaAutoConfigurationSpec.groovy
@@ -0,0 +1,173 @@
+/*
+ * 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.eureka
+
+import javax.servlet.Filter
+import javax.servlet.FilterChain
+import javax.servlet.FilterConfig
+import javax.servlet.ServletException
+import javax.servlet.ServletRequest
+import javax.servlet.ServletResponse
+
+import spock.lang.AutoCleanup
+import spock.lang.Shared
+import spock.lang.Specification
+import spock.util.concurrent.PollingConditions
+
+import org.springframework.beans.factory.annotation.Autowired
+import org.springframework.boot.SpringApplication
+import org.springframework.boot.autoconfigure.EnableAutoConfiguration
+import org.springframework.boot.test.context.SpringBootContextLoader
+import org.springframework.boot.test.context.SpringBootTest
+import org.springframework.cloud.client.loadbalancer.LoadBalanced
+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.netflix.eureka.EnableEurekaClient
+import org.springframework.cloud.netflix.eureka.server.EnableEurekaServer
+import org.springframework.context.ConfigurableApplicationContext
+import org.springframework.context.annotation.Bean
+import org.springframework.context.annotation.Configuration
+import org.springframework.http.HttpMethod
+import org.springframework.http.client.ClientHttpResponse
+import org.springframework.test.context.ContextConfiguration
+import org.springframework.web.client.DefaultResponseErrorHandler
+import org.springframework.web.client.RequestCallback
+import org.springframework.web.client.ResponseExtractor
+import org.springframework.web.client.RestClientException
+import org.springframework.web.client.RestTemplate
+
+/**
+ * @author Marcin Grzejszczak
+ */
+//TODO: Speed up this test somehow (move it out of Spring Cloud Contract core to samples)
+@SpringBootTest(classes = Config, webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT,
+ properties = ["stubrunner.cloud.eureka.enabled=true",
+ "stubrunner.cloud.stubbed.discovery.enabled=false",
+ "stubrunner.cloud.ribbon.enabled=false",
+ "eureka.client.enabled=true",
+ "eureka.instance.leaseRenewalIntervalInSeconds=1",
+ "ribbon.ServerListRefreshInterval=100"])
+@AutoConfigureStubRunner(ids =
+["org.springframework.cloud.contract.verifier.stubs:loanIssuance",
+ "org.springframework.cloud.contract.verifier.stubs:fraudDetectionServer",
+ "org.springframework.cloud.contract.verifier.stubs:bootService"] ,
+repositoryRoot = "classpath:m2repo/repository/" ,
+stubsMode = StubRunnerProperties.StubsMode.REMOTE )
+class StubRunnerSpringCloudEurekaAutoConfigurationSpec extends Specification {
+
+ @Autowired
+ StubFinder stubFinder
+ @Autowired
+ @LoadBalanced
+ RestTemplate restTemplate
+ @Shared
+ @AutoCleanup
+ ConfigurableApplicationContext eurekaServer
+
+ void setupSpec() {
+ System.clearProperty("stubrunner.stubs.repository.root")
+ System.clearProperty("stubrunner.stubs.classifier")
+ eurekaServer = SpringApplication.run(EurekaServer,
+ "--stubrunner.cloud.eureka.enabled=true",
+ "--stubrunner.cloud.stubbed.discovery.enabled=false",
+ "--eureka.client.enabled=true",
+ "--server.port=8761",
+ "--spring.profiles.active=eureka")
+ }
+
+ void cleanupSpec() {
+ System.clearProperty("stubrunner.stubs.repository.root")
+ System.clearProperty("stubrunner.stubs.classifier")
+ }
+
+ PollingConditions conditions = new PollingConditions(timeout: 240, delay: 1)
+
+ 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'
+ conditions.eventually {
+ assert restTemplate.getForObject('http://loanIssuance/name', String) == 'loanIssuance'
+ }
+ restTemplate.getForObject('http://someNameThatShouldMapFraudDetectionServer/name', String) == 'fraudDetectionServer'
+ }
+
+ @Configuration
+ @EnableAutoConfiguration
+ @EnableEurekaClient
+ static class Config {
+
+ @Bean
+ @LoadBalanced
+ RestTemplate restTemplate() {
+ def template = new RestTemplate() {
+
+ @Override
+ protected T doExecute(URI url, HttpMethod method, RequestCallback requestCallback, ResponseExtractor responseExtractor) throws RestClientException {
+ try {
+ return super.doExecute(url, method, requestCallback, responseExtractor)
+ }
+ catch (Exception e) {
+ throw new AssertionError(e)
+ }
+ }
+ }
+ template.errorHandler = new DefaultResponseErrorHandler() {
+ @Override
+ void handleError(ClientHttpResponse response) throws IOException {
+ try {
+ super.handleError(response)
+ }
+ catch (Exception e) {
+ throw new AssertionError(e)
+ }
+ }
+ }
+ return template
+ }
+ }
+
+ @Configuration
+ @EnableAutoConfiguration
+ @EnableEurekaServer
+ static class EurekaServer {
+
+ @Bean
+ Filter httpTraceFilter() {
+ return new Filter() {
+ @Override
+ void init(FilterConfig filterConfig) throws ServletException {
+
+ }
+
+ @Override
+ void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
+ filterChain.doFilter(servletRequest, servletResponse)
+ }
+
+ @Override
+ void destroy() {
+
+ }
+ }
+ }
+
+
+ }
+}
diff --git a/tests/spring-cloud-contract-stub-runner-boot-eureka/src/test/resources/application.yml b/tests/spring-cloud-contract-stub-runner-boot-eureka/src/test/resources/application.yml
new file mode 100644
index 0000000000..2b91381a2c
--- /dev/null
+++ b/tests/spring-cloud-contract-stub-runner-boot-eureka/src/test/resources/application.yml
@@ -0,0 +1,5 @@
+stubrunner:
+ idsToServiceIds:
+ fraudDetectionServer: someNameThatShouldMapFraudDetectionServer
+server:
+ port: 0
diff --git a/tests/spring-cloud-contract-stub-runner-boot-eureka/src/test/resources/logback.xml b/tests/spring-cloud-contract-stub-runner-boot-eureka/src/test/resources/logback.xml
new file mode 100644
index 0000000000..96d75b755b
--- /dev/null
+++ b/tests/spring-cloud-contract-stub-runner-boot-eureka/src/test/resources/logback.xml
@@ -0,0 +1,25 @@
+
+
+
+
+
+
+
+
+
+
+
diff --git a/tests/spring-cloud-contract-stub-runner-boot-eureka/src/test/resources/m2repo/repository/org/springframework/cloud/contract/verifier/stubs/bootService/0.0.1-SNAPSHOT/bootService-0.0.1-SNAPSHOT-stubs.jar b/tests/spring-cloud-contract-stub-runner-boot-eureka/src/test/resources/m2repo/repository/org/springframework/cloud/contract/verifier/stubs/bootService/0.0.1-SNAPSHOT/bootService-0.0.1-SNAPSHOT-stubs.jar
new file mode 100644
index 0000000000..c57c836db4
Binary files /dev/null and b/tests/spring-cloud-contract-stub-runner-boot-eureka/src/test/resources/m2repo/repository/org/springframework/cloud/contract/verifier/stubs/bootService/0.0.1-SNAPSHOT/bootService-0.0.1-SNAPSHOT-stubs.jar differ
diff --git a/tests/spring-cloud-contract-stub-runner-boot-eureka/src/test/resources/m2repo/repository/org/springframework/cloud/contract/verifier/stubs/bootService/0.0.1-SNAPSHOT/bootService-0.0.1-SNAPSHOT.pom b/tests/spring-cloud-contract-stub-runner-boot-eureka/src/test/resources/m2repo/repository/org/springframework/cloud/contract/verifier/stubs/bootService/0.0.1-SNAPSHOT/bootService-0.0.1-SNAPSHOT.pom
new file mode 100644
index 0000000000..dd16575a19
--- /dev/null
+++ b/tests/spring-cloud-contract-stub-runner-boot-eureka/src/test/resources/m2repo/repository/org/springframework/cloud/contract/verifier/stubs/bootService/0.0.1-SNAPSHOT/bootService-0.0.1-SNAPSHOT.pom
@@ -0,0 +1,27 @@
+
+
+
+
+ 4.0.0
+ org.springframework.cloud.contract.verifier.stubs
+ bootService
+ 0.0.1-SNAPSHOT
+ pom
+
diff --git a/tests/spring-cloud-contract-stub-runner-boot-eureka/src/test/resources/m2repo/repository/org/springframework/cloud/contract/verifier/stubs/bootService/maven-metadata.xml b/tests/spring-cloud-contract-stub-runner-boot-eureka/src/test/resources/m2repo/repository/org/springframework/cloud/contract/verifier/stubs/bootService/maven-metadata.xml
new file mode 100644
index 0000000000..8461d4b2a6
--- /dev/null
+++ b/tests/spring-cloud-contract-stub-runner-boot-eureka/src/test/resources/m2repo/repository/org/springframework/cloud/contract/verifier/stubs/bootService/maven-metadata.xml
@@ -0,0 +1,28 @@
+
+
+
+
+ org.springframework.cloud.contract.verifier.stubs
+ bootService
+ 0.0.1-SNAPSHOT
+
+
+ 0.0.1-SNAPSHOT
+
+ 20160409062112
+
+
diff --git a/tests/spring-cloud-contract-stub-runner-boot-eureka/src/test/resources/m2repo/repository/org/springframework/cloud/contract/verifier/stubs/fraudDetectionServer/0.0.1-SNAPSHOT/fraudDetectionServer-0.0.1-SNAPSHOT-stubs.jar b/tests/spring-cloud-contract-stub-runner-boot-eureka/src/test/resources/m2repo/repository/org/springframework/cloud/contract/verifier/stubs/fraudDetectionServer/0.0.1-SNAPSHOT/fraudDetectionServer-0.0.1-SNAPSHOT-stubs.jar
new file mode 100644
index 0000000000..fdb70e383f
Binary files /dev/null and b/tests/spring-cloud-contract-stub-runner-boot-eureka/src/test/resources/m2repo/repository/org/springframework/cloud/contract/verifier/stubs/fraudDetectionServer/0.0.1-SNAPSHOT/fraudDetectionServer-0.0.1-SNAPSHOT-stubs.jar differ
diff --git a/tests/spring-cloud-contract-stub-runner-boot-eureka/src/test/resources/m2repo/repository/org/springframework/cloud/contract/verifier/stubs/fraudDetectionServer/0.0.1-SNAPSHOT/fraudDetectionServer-0.0.1-SNAPSHOT.pom b/tests/spring-cloud-contract-stub-runner-boot-eureka/src/test/resources/m2repo/repository/org/springframework/cloud/contract/verifier/stubs/fraudDetectionServer/0.0.1-SNAPSHOT/fraudDetectionServer-0.0.1-SNAPSHOT.pom
new file mode 100644
index 0000000000..33e8194127
--- /dev/null
+++ b/tests/spring-cloud-contract-stub-runner-boot-eureka/src/test/resources/m2repo/repository/org/springframework/cloud/contract/verifier/stubs/fraudDetectionServer/0.0.1-SNAPSHOT/fraudDetectionServer-0.0.1-SNAPSHOT.pom
@@ -0,0 +1,27 @@
+
+
+
+
+ 4.0.0
+ org.springframework.cloud.contract.verifier.stubs
+ fraudDetectionServer
+ 0.0.1-SNAPSHOT
+ pom
+
diff --git a/tests/spring-cloud-contract-stub-runner-boot-eureka/src/test/resources/m2repo/repository/org/springframework/cloud/contract/verifier/stubs/fraudDetectionServer/maven-metadata.xml b/tests/spring-cloud-contract-stub-runner-boot-eureka/src/test/resources/m2repo/repository/org/springframework/cloud/contract/verifier/stubs/fraudDetectionServer/maven-metadata.xml
new file mode 100644
index 0000000000..75dc4041ba
--- /dev/null
+++ b/tests/spring-cloud-contract-stub-runner-boot-eureka/src/test/resources/m2repo/repository/org/springframework/cloud/contract/verifier/stubs/fraudDetectionServer/maven-metadata.xml
@@ -0,0 +1,28 @@
+
+
+
+
+ org.springframework.cloud.contract.verifier.stubs
+ fraudDetectionServer
+ 0.0.1-SNAPSHOT
+
+
+ 0.0.1-SNAPSHOT
+
+ 20160409062112
+
+
diff --git a/tests/spring-cloud-contract-stub-runner-boot-eureka/src/test/resources/m2repo/repository/org/springframework/cloud/contract/verifier/stubs/loanIssuance/0.0.1-SNAPSHOT/loanIssuance-0.0.1-SNAPSHOT-stubs.jar b/tests/spring-cloud-contract-stub-runner-boot-eureka/src/test/resources/m2repo/repository/org/springframework/cloud/contract/verifier/stubs/loanIssuance/0.0.1-SNAPSHOT/loanIssuance-0.0.1-SNAPSHOT-stubs.jar
new file mode 100644
index 0000000000..e322590636
Binary files /dev/null and b/tests/spring-cloud-contract-stub-runner-boot-eureka/src/test/resources/m2repo/repository/org/springframework/cloud/contract/verifier/stubs/loanIssuance/0.0.1-SNAPSHOT/loanIssuance-0.0.1-SNAPSHOT-stubs.jar differ
diff --git a/tests/spring-cloud-contract-stub-runner-boot-eureka/src/test/resources/m2repo/repository/org/springframework/cloud/contract/verifier/stubs/loanIssuance/0.0.1-SNAPSHOT/loanIssuance-0.0.1-SNAPSHOT.pom b/tests/spring-cloud-contract-stub-runner-boot-eureka/src/test/resources/m2repo/repository/org/springframework/cloud/contract/verifier/stubs/loanIssuance/0.0.1-SNAPSHOT/loanIssuance-0.0.1-SNAPSHOT.pom
new file mode 100644
index 0000000000..ae3efaf070
--- /dev/null
+++ b/tests/spring-cloud-contract-stub-runner-boot-eureka/src/test/resources/m2repo/repository/org/springframework/cloud/contract/verifier/stubs/loanIssuance/0.0.1-SNAPSHOT/loanIssuance-0.0.1-SNAPSHOT.pom
@@ -0,0 +1,27 @@
+
+
+
+
+ 4.0.0
+ org.springframework.cloud.contract.verifier.stubs
+ loanIssuance
+ 0.0.1-SNAPSHOT
+ pom
+
diff --git a/tests/spring-cloud-contract-stub-runner-boot-eureka/src/test/resources/m2repo/repository/org/springframework/cloud/contract/verifier/stubs/loanIssuance/maven-metadata.xml b/tests/spring-cloud-contract-stub-runner-boot-eureka/src/test/resources/m2repo/repository/org/springframework/cloud/contract/verifier/stubs/loanIssuance/maven-metadata.xml
new file mode 100644
index 0000000000..4533bc03e8
--- /dev/null
+++ b/tests/spring-cloud-contract-stub-runner-boot-eureka/src/test/resources/m2repo/repository/org/springframework/cloud/contract/verifier/stubs/loanIssuance/maven-metadata.xml
@@ -0,0 +1,28 @@
+
+
+
+
+ org.springframework.cloud.contract.verifier.stubs
+ loanIssuance
+ 0.0.1-SNAPSHOT
+
+
+ 0.0.1-SNAPSHOT
+
+ 20160409062111
+
+