diff --git a/spring-cloud-cloudfoundry-discovery/src/main/java/org/springframework/cloud/cloudfoundry/discovery/CloudFoundryAppServiceDiscoveryClient.java b/spring-cloud-cloudfoundry-discovery/src/main/java/org/springframework/cloud/cloudfoundry/discovery/CloudFoundryAppServiceDiscoveryClient.java
new file mode 100644
index 0000000..c782a69
--- /dev/null
+++ b/spring-cloud-cloudfoundry-discovery/src/main/java/org/springframework/cloud/cloudfoundry/discovery/CloudFoundryAppServiceDiscoveryClient.java
@@ -0,0 +1,70 @@
+package org.springframework.cloud.cloudfoundry.discovery;
+
+import java.util.HashMap;
+import java.util.List;
+
+import org.cloudfoundry.operations.CloudFoundryOperations;
+import org.cloudfoundry.operations.applications.ApplicationDetail;
+import org.cloudfoundry.operations.applications.InstanceDetail;
+import org.springframework.cloud.client.DefaultServiceInstance;
+import org.springframework.cloud.client.ServiceInstance;
+import org.springframework.cloud.cloudfoundry.CloudFoundryService;
+
+/**
+ *
+ * Discovery Client implementation using Cloud Foundry's Native DNS based Service
+ * Discovery
+ *
+ * @see CF App Service
+ * Discovery Release
+ * @see Polyglot
+ * Service Discovery for Container Networking in Cloud Foundry
+ *
+ * @author Toshiaki Maki
+ */
+public class CloudFoundryAppServiceDiscoveryClient extends CloudFoundryDiscoveryClient {
+
+ private static final String INTERNAL_DOMAIN = "apps.internal";
+
+ CloudFoundryAppServiceDiscoveryClient(CloudFoundryOperations cloudFoundryOperations,
+ CloudFoundryService svc, CloudFoundryDiscoveryProperties cloudFoundryDiscoveryProperties) {
+ super(cloudFoundryOperations, svc, cloudFoundryDiscoveryProperties);
+ }
+
+ @Override
+ public String description() {
+ return "CF App Service Discovery Client";
+ }
+
+ @Override
+ public List getInstances(String serviceId) {
+ return getCloudFoundryService()
+ .getApplicationInstances(serviceId)
+ .filter(tuple -> tuple.getT1().getUrls().stream()
+ .anyMatch(this::isInternalDomain))
+ .map(tuple -> {
+ ApplicationDetail applicationDetail = tuple.getT1();
+ InstanceDetail instanceDetail = tuple.getT2();
+ String applicationId = applicationDetail.getId();
+ String applicationIndex = instanceDetail.getIndex();
+ String name = applicationDetail.getName();
+ String url = applicationDetail.getUrls().stream()
+ .filter(this::isInternalDomain)
+ .findFirst()
+ .map(x -> instanceDetail.getIndex() + "." + x)
+ .get();
+ HashMap metadata = new HashMap<>();
+ metadata.put("applicationId", applicationId);
+ metadata.put("instanceId", applicationIndex);
+ return (ServiceInstance) new DefaultServiceInstance(name, url, 8080,
+ false, metadata);
+ })
+ .collectList()
+ .block();
+ }
+
+ private boolean isInternalDomain(String url) {
+ return url != null && url.endsWith(INTERNAL_DOMAIN);
+ }
+}
diff --git a/spring-cloud-cloudfoundry-discovery/src/main/java/org/springframework/cloud/cloudfoundry/discovery/CloudFoundryDiscoveryClient.java b/spring-cloud-cloudfoundry-discovery/src/main/java/org/springframework/cloud/cloudfoundry/discovery/CloudFoundryDiscoveryClient.java
index 4d72b0e..1e19c88 100644
--- a/spring-cloud-cloudfoundry-discovery/src/main/java/org/springframework/cloud/cloudfoundry/discovery/CloudFoundryDiscoveryClient.java
+++ b/spring-cloud-cloudfoundry-discovery/src/main/java/org/springframework/cloud/cloudfoundry/discovery/CloudFoundryDiscoveryClient.java
@@ -98,4 +98,7 @@ public class CloudFoundryDiscoveryClient implements DiscoveryClient {
return this.properties.getOrder();
}
+ CloudFoundryService getCloudFoundryService() {
+ return this.cloudFoundryService;
+ }
}
diff --git a/spring-cloud-cloudfoundry-discovery/src/main/java/org/springframework/cloud/cloudfoundry/discovery/CloudFoundryDiscoveryClientConfiguration.java b/spring-cloud-cloudfoundry-discovery/src/main/java/org/springframework/cloud/cloudfoundry/discovery/CloudFoundryDiscoveryClientConfiguration.java
index 3ce8aa2..1db38c9 100644
--- a/spring-cloud-cloudfoundry-discovery/src/main/java/org/springframework/cloud/cloudfoundry/discovery/CloudFoundryDiscoveryClientConfiguration.java
+++ b/spring-cloud-cloudfoundry-discovery/src/main/java/org/springframework/cloud/cloudfoundry/discovery/CloudFoundryDiscoveryClientConfiguration.java
@@ -17,12 +17,15 @@
package org.springframework.cloud.cloudfoundry.discovery;
import org.cloudfoundry.operations.CloudFoundryOperations;
-
+import org.springframework.beans.factory.ObjectProvider;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
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.cloudfoundry.CloudFoundryService;
+import org.springframework.cloud.cloudfoundry.discovery.SimpleDnsBasedDiscoveryClient.ServiceIdToHostnameConverter;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@@ -35,15 +38,46 @@ import org.springframework.context.annotation.Configuration;
@EnableConfigurationProperties(CloudFoundryDiscoveryProperties.class)
public class CloudFoundryDiscoveryClientConfiguration {
- @Bean
- @ConditionalOnMissingBean(CloudFoundryDiscoveryClient.class)
- public CloudFoundryDiscoveryClient cloudFoundryDiscoveryClient(
- CloudFoundryOperations cf, CloudFoundryService svc,
- CloudFoundryDiscoveryProperties cloudFoundryDiscoveryProperties) {
- return new CloudFoundryDiscoveryClient(cf, svc, cloudFoundryDiscoveryProperties);
+ @Configuration
+ @ConditionalOnProperty(value = "spring.cloud.cloudfoundry.discovery.use-dns", havingValue = "false", matchIfMissing = true)
+ public static class CloudFoundryDiscoveryClientConfig {
+ @Bean
+ @ConditionalOnMissingBean(DiscoveryClient.class)
+ public DiscoveryClient cloudFoundryDiscoveryClient(CloudFoundryOperations cf,
+ CloudFoundryService svc,
+ CloudFoundryDiscoveryProperties cloudFoundryDiscoveryProperties) {
+ return new CloudFoundryDiscoveryClient(cf, svc,
+ cloudFoundryDiscoveryProperties);
+ }
+ }
+
+ @Configuration
+ @ConditionalOnProperty(value = "spring.cloud.cloudfoundry.discovery.use-dns", havingValue = "true")
+ public static class DnsBasedCloudFoundryDiscoveryClientConfig {
+
+ @Bean
+ @ConditionalOnProperty(value = "spring.cloud.cloudfoundry.discovery.use-container-ip", havingValue = "true")
+ @ConditionalOnMissingBean(DiscoveryClient.class)
+ public DiscoveryClient discoveryClient(
+ ObjectProvider provider) {
+ ServiceIdToHostnameConverter converter = provider.getIfAvailable();
+ return converter == null ? new SimpleDnsBasedDiscoveryClient()
+ : new SimpleDnsBasedDiscoveryClient(converter);
+ }
+
+ @Bean
+ @ConditionalOnProperty(value = "spring.cloud.cloudfoundry.discovery.use-container-ip", havingValue = "false", matchIfMissing = true)
+ @ConditionalOnMissingBean(DiscoveryClient.class)
+ public DiscoveryClient cloudFoundryDiscoveryClient(CloudFoundryOperations cf,
+ CloudFoundryService svc,
+ CloudFoundryDiscoveryProperties cloudFoundryDiscoveryProperties) {
+ return new CloudFoundryAppServiceDiscoveryClient(cf, svc,
+ cloudFoundryDiscoveryProperties);
+ }
}
@Bean
+ @ConditionalOnBean(CloudFoundryDiscoveryClient.class)
public CloudFoundryHeartbeatSender cloudFoundryHeartbeatSender(
CloudFoundryDiscoveryClient client) {
return new CloudFoundryHeartbeatSender(client);
diff --git a/spring-cloud-cloudfoundry-discovery/src/main/java/org/springframework/cloud/cloudfoundry/discovery/SimpleDnsBasedDiscoveryClient.java b/spring-cloud-cloudfoundry-discovery/src/main/java/org/springframework/cloud/cloudfoundry/discovery/SimpleDnsBasedDiscoveryClient.java
new file mode 100644
index 0000000..6ff3468
--- /dev/null
+++ b/spring-cloud-cloudfoundry-discovery/src/main/java/org/springframework/cloud/cloudfoundry/discovery/SimpleDnsBasedDiscoveryClient.java
@@ -0,0 +1,80 @@
+
+package org.springframework.cloud.cloudfoundry.discovery;
+
+import java.net.InetAddress;
+import java.net.UnknownHostException;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.cloud.client.DefaultServiceInstance;
+import org.springframework.cloud.client.ServiceInstance;
+import org.springframework.cloud.client.discovery.DiscoveryClient;
+
+/**
+ * Discovery Client implementation using Cloud Foundry's Native DNS based Service
+ * Discovery
+ *
+ * @see Polyglot
+ * Service Discovery for Container Networking in Cloud Foundry
+ *
+ * @author Toshiaki Maki
+ */
+public class SimpleDnsBasedDiscoveryClient implements DiscoveryClient {
+ public static final String INTERNAL_DOMAIN = "apps.internal";
+ private final Logger log = LoggerFactory
+ .getLogger(SimpleDnsBasedDiscoveryClient.class);
+ private final ServiceIdToHostnameConverter serviceIdToHostnameConverter;
+
+ private static final ServiceIdToHostnameConverter DEFAULT_CONVERTER = serviceId -> serviceId
+ + "." + INTERNAL_DOMAIN;
+
+ public SimpleDnsBasedDiscoveryClient(
+ ServiceIdToHostnameConverter serviceIdToHostnameConverter) {
+ this.serviceIdToHostnameConverter = serviceIdToHostnameConverter;
+ }
+
+ public SimpleDnsBasedDiscoveryClient() {
+ this(DEFAULT_CONVERTER);
+ }
+
+ @Override
+ public String description() {
+ return "DNS Based CF Service Discovery Client";
+ }
+
+ @Override
+ public List getInstances(String serviceId) {
+ String hostname = this.serviceIdToHostnameConverter.toHostname(serviceId);
+ try {
+ List serviceInstances = new ArrayList<>();
+ InetAddress[] addresses = InetAddress.getAllByName(hostname);
+ if (addresses != null) {
+ for (InetAddress address : addresses) {
+ DefaultServiceInstance serviceInstance = new DefaultServiceInstance(
+ serviceId, address.getHostAddress(), 8080, false);
+ serviceInstances.add(serviceInstance);
+ }
+ }
+ return serviceInstances;
+ }
+ catch (UnknownHostException e) {
+ log.warn("{}", e.getMessage());
+ return Collections.emptyList();
+ }
+ }
+
+ @Override
+ public List getServices() {
+ log.warn("getServices is not supported");
+ return Collections.emptyList();
+ }
+
+ @FunctionalInterface
+ public interface ServiceIdToHostnameConverter {
+ String toHostname(String serviceId);
+ }
+}
\ No newline at end of file
diff --git a/spring-cloud-cloudfoundry-discovery/src/main/resources/META-INF/additional-spring-configuration-metadata.json b/spring-cloud-cloudfoundry-discovery/src/main/resources/META-INF/additional-spring-configuration-metadata.json
new file mode 100644
index 0000000..8041ebf
--- /dev/null
+++ b/spring-cloud-cloudfoundry-discovery/src/main/resources/META-INF/additional-spring-configuration-metadata.json
@@ -0,0 +1,16 @@
+{
+ "properties": [
+ {
+ "name": "spring.cloud.cloudfoundry.discovery.use-dns",
+ "type": "java.lang.Boolean",
+ "description": "Whether to use BOSH DNS for the discovery. In order to use this feature, your Cloud Foundry installation must support Service Discovery.",
+ "defaultValue": false
+ },
+ {
+ "name": "spring.cloud.cloudfoundry.discovery.use-container-ip",
+ "type": "java.lang.Boolean",
+ "description": "Whether to resolve hostname when BOSH DNS is used. In order to use this feature, spring.cloud.cloudfoundry.discovery.use-dns must be true.",
+ "defaultValue": false
+ }
+ ]
+}
\ No newline at end of file
diff --git a/spring-cloud-cloudfoundry-discovery/src/test/java/org/springframework/cloud/cloudfoundry/discovery/CloudFoundryAppServiceDiscoveryClientTest.java b/spring-cloud-cloudfoundry-discovery/src/test/java/org/springframework/cloud/cloudfoundry/discovery/CloudFoundryAppServiceDiscoveryClientTest.java
new file mode 100644
index 0000000..ab4cbb3
--- /dev/null
+++ b/spring-cloud-cloudfoundry-discovery/src/test/java/org/springframework/cloud/cloudfoundry/discovery/CloudFoundryAppServiceDiscoveryClientTest.java
@@ -0,0 +1,116 @@
+package org.springframework.cloud.cloudfoundry.discovery;
+
+import java.util.HashMap;
+import java.util.List;
+
+import org.cloudfoundry.operations.CloudFoundryOperations;
+import org.cloudfoundry.operations.applications.ApplicationDetail;
+import org.cloudfoundry.operations.applications.InstanceDetail;
+import org.junit.Before;
+import org.junit.Test;
+import org.springframework.cloud.client.DefaultServiceInstance;
+import org.springframework.cloud.client.ServiceInstance;
+import org.springframework.cloud.cloudfoundry.CloudFoundryService;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.mockito.BDDMockito.given;
+import static org.mockito.Mockito.mock;
+
+import reactor.core.publisher.Flux;
+import reactor.util.function.Tuples;
+
+/**
+ * @author Toshiaki Maki
+ */
+public class CloudFoundryAppServiceDiscoveryClientTest {
+ private CloudFoundryAppServiceDiscoveryClient discoveryClient;
+ private CloudFoundryOperations cloudFoundryOperations;
+ private CloudFoundryService cloudFoundryService;
+
+ @Before
+ public void setUp() {
+ this.cloudFoundryOperations = mock(CloudFoundryOperations.class);
+ this.cloudFoundryService = mock(CloudFoundryService.class);
+ this.discoveryClient = new CloudFoundryAppServiceDiscoveryClient(
+ this.cloudFoundryOperations, this.cloudFoundryService,
+ new CloudFoundryDiscoveryProperties());
+ }
+
+ @Test
+ public void getInstancesOneInstance() {
+ String serviceId = "billing";
+ ApplicationDetail applicationDetail = ApplicationDetail.builder().id("billing1")
+ .name("billing").instances(1).memoryLimit(1024).stack("cflinux2")
+ .diskQuota(1024).requestedState("Running").runningInstances(1)
+ .url("billing.apps.example.com", "billing.apps.internal").build();
+ given(this.cloudFoundryService.getApplicationInstances(serviceId))
+ .willReturn(Flux.just(Tuples.of(applicationDetail,
+ InstanceDetail.builder().index("0").build())));
+ List instances = this.discoveryClient.getInstances(serviceId);
+
+ assertThat(instances).hasSize(1);
+ assertThat(instances.get(0)).isEqualTo(new DefaultServiceInstance(serviceId,
+ "0.billing.apps.internal", 8080, false, new HashMap() {
+ {
+ put("applicationId", "billing1");
+ put("instanceId", "0");
+ }
+ }));
+ }
+
+ @Test
+ public void getInstancesThreeInstance() {
+ String serviceId = "billing";
+ ApplicationDetail applicationDetail = ApplicationDetail.builder().id("billing-id")
+ .name("billing").instances(3).memoryLimit(1024).stack("cflinux2")
+ .diskQuota(1024).requestedState("Running").runningInstances(3)
+ .url("billing.apps.example.com", "billing.apps.internal").build();
+ given(this.cloudFoundryService.getApplicationInstances(serviceId))
+ .willReturn(Flux.just(
+ Tuples.of(applicationDetail,
+ InstanceDetail.builder().index("0").build()),
+ Tuples.of(applicationDetail,
+ InstanceDetail.builder().index("1").build()),
+ Tuples.of(applicationDetail,
+ InstanceDetail.builder().index("2").build())));
+ List instances = this.discoveryClient.getInstances(serviceId);
+
+ assertThat(instances).hasSize(3);
+ assertThat(instances.get(0)).isEqualTo(new DefaultServiceInstance(serviceId,
+ "0.billing.apps.internal", 8080, false, new HashMap() {
+ {
+ put("applicationId", "billing-id");
+ put("instanceId", "0");
+ }
+ }));
+ assertThat(instances.get(1)).isEqualTo(new DefaultServiceInstance(serviceId,
+ "1.billing.apps.internal", 8080, false, new HashMap() {
+ {
+ put("applicationId", "billing-id");
+ put("instanceId", "1");
+ }
+ }));
+ assertThat(instances.get(2)).isEqualTo(new DefaultServiceInstance(serviceId,
+ "2.billing.apps.internal", 8080, false, new HashMap() {
+ {
+ put("applicationId", "billing-id");
+ put("instanceId", "2");
+ }
+ }));
+ }
+
+ @Test
+ public void getInstancesEmpty() {
+ String serviceId = "billing";
+ ApplicationDetail applicationDetail = ApplicationDetail.builder().id("billing1")
+ .name("billing").instances(1).memoryLimit(1024).stack("cflinux2")
+ .diskQuota(1024).requestedState("Running").runningInstances(1)
+ .url("billing.apps.example.com").build();
+ given(this.cloudFoundryService.getApplicationInstances(serviceId))
+ .willReturn(Flux.just(Tuples.of(applicationDetail,
+ InstanceDetail.builder().index("0").build())));
+ List instances = this.discoveryClient.getInstances(serviceId);
+
+ assertThat(instances).isEmpty();
+ }
+}
\ No newline at end of file
diff --git a/spring-cloud-cloudfoundry-discovery/src/test/java/org/springframework/cloud/cloudfoundry/discovery/CloudFoundryDiscoveryClientConfigurationTest.java b/spring-cloud-cloudfoundry-discovery/src/test/java/org/springframework/cloud/cloudfoundry/discovery/CloudFoundryDiscoveryClientConfigurationTest.java
new file mode 100644
index 0000000..aff67f5
--- /dev/null
+++ b/spring-cloud-cloudfoundry-discovery/src/test/java/org/springframework/cloud/cloudfoundry/discovery/CloudFoundryDiscoveryClientConfigurationTest.java
@@ -0,0 +1,98 @@
+package org.springframework.cloud.cloudfoundry.discovery;
+
+import org.cloudfoundry.operations.CloudFoundryOperations;
+import org.junit.Test;
+import org.mockito.Mockito;
+import org.springframework.boot.autoconfigure.AutoConfigurations;
+import org.springframework.boot.test.context.runner.ApplicationContextRunner;
+import org.springframework.cloud.client.discovery.DiscoveryClient;
+import org.springframework.cloud.cloudfoundry.CloudFoundryService;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+/**
+ * Tests for {@link CloudFoundryDiscoveryClientConfiguration}.
+ * @author Toshiaki Maki
+ */
+public class CloudFoundryDiscoveryClientConfigurationTest {
+
+ private final ApplicationContextRunner contextRunner = new ApplicationContextRunner()
+ .withConfiguration(AutoConfigurations
+ .of(CloudFoundryDiscoveryClientConfiguration.class));
+
+ @Test
+ public void testDefault() {
+ this.contextRunner.withUserConfiguration(CloudFoundryConfig.class)
+ .run((context) -> {
+ DiscoveryClient discoveryClient = context
+ .getBean(DiscoveryClient.class);
+ assertThat(discoveryClient.getClass())
+ .isEqualTo(CloudFoundryDiscoveryClient.class);
+ });
+ }
+
+ @Test
+ public void testUseDnsTrue() {
+ this.contextRunner.withUserConfiguration(CloudFoundryConfig.class)
+ .withPropertyValues("spring.cloud.cloudfoundry.discovery.use-dns=true")
+ .run((context) -> {
+ DiscoveryClient discoveryClient = context
+ .getBean(DiscoveryClient.class);
+ assertThat(discoveryClient.getClass())
+ .isEqualTo(CloudFoundryAppServiceDiscoveryClient.class);
+ });
+ }
+
+ @Test
+ public void testUseDnsFalse() {
+ this.contextRunner.withUserConfiguration(CloudFoundryConfig.class)
+ .withPropertyValues("spring.cloud.cloudfoundry.discovery.use-dns=false")
+ .run((context) -> {
+ DiscoveryClient discoveryClient = context
+ .getBean(DiscoveryClient.class);
+ assertThat(discoveryClient.getClass())
+ .isEqualTo(CloudFoundryDiscoveryClient.class);
+ });
+ }
+
+ @Test
+ public void testUseContainerIpFalse() {
+ this.contextRunner.withUserConfiguration(CloudFoundryConfig.class)
+ .withPropertyValues("spring.cloud.cloudfoundry.discovery.use-dns=true",
+ "spring.cloud.cloudfoundry.discovery.use-container-ip=false")
+ .run((context) -> {
+ DiscoveryClient discoveryClient = context
+ .getBean(DiscoveryClient.class);
+ assertThat(discoveryClient.getClass())
+ .isEqualTo(CloudFoundryAppServiceDiscoveryClient.class);
+ });
+ }
+
+ @Test
+ public void testUseContainerIpTrue() {
+ this.contextRunner
+ .withPropertyValues("spring.cloud.cloudfoundry.discovery.use-dns=true",
+ "spring.cloud.cloudfoundry.discovery.use-container-ip=true")
+ .run((context) -> {
+ DiscoveryClient discoveryClient = context
+ .getBean(DiscoveryClient.class);
+ assertThat(discoveryClient.getClass())
+ .isEqualTo(SimpleDnsBasedDiscoveryClient.class);
+ });
+ }
+
+ @Configuration
+ public static class CloudFoundryConfig {
+ @Bean
+ public CloudFoundryOperations cloudFoundryOperations() {
+ return Mockito.mock(CloudFoundryOperations.class);
+ }
+
+ @Bean
+ public CloudFoundryService cloudFoundryService() {
+ return Mockito.mock(CloudFoundryService.class);
+ }
+ }
+}
\ No newline at end of file
diff --git a/spring-cloud-cloudfoundry-sample/src/main/java/org/springframework/cloud/cloudfoundry/sample/CloudFoundryApplication.java b/spring-cloud-cloudfoundry-sample/src/main/java/org/springframework/cloud/cloudfoundry/sample/CloudFoundryApplication.java
index 729a4e4..e9c4cea 100644
--- a/spring-cloud-cloudfoundry-sample/src/main/java/org/springframework/cloud/cloudfoundry/sample/CloudFoundryApplication.java
+++ b/spring-cloud-cloudfoundry-sample/src/main/java/org/springframework/cloud/cloudfoundry/sample/CloudFoundryApplication.java
@@ -22,7 +22,7 @@ import org.apache.commons.logging.LogFactory;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
-import org.springframework.cloud.cloudfoundry.discovery.CloudFoundryDiscoveryClient;
+import org.springframework.cloud.client.discovery.DiscoveryClient;
import org.springframework.cloud.cloudfoundry.discovery.EnableCloudFoundryClient;
import org.springframework.context.annotation.Bean;
@@ -55,7 +55,7 @@ public class CloudFoundryApplication {
}
@Bean
- CommandLineRunner demo(CloudFoundryDiscoveryClient discoveryClient) {
+ CommandLineRunner demo(DiscoveryClient discoveryClient) {
Log log = LogFactory.getLog(getClass());
return args -> discoveryClient.getServices().forEach(svc -> {
log.info("service = " + svc);