diff --git a/docs/src/main/asciidoc/spring-cloud-netflix.adoc b/docs/src/main/asciidoc/spring-cloud-netflix.adoc index ee54f879d..49a240455 100644 --- a/docs/src/main/asciidoc/spring-cloud-netflix.adoc +++ b/docs/src/main/asciidoc/spring-cloud-netflix.adoc @@ -680,6 +680,29 @@ Spring Cloud provides a `spring-cloud-starter-netflix-turbine` that has all the NOTE: By default, Spring Cloud lets Turbine use the host and port to allow multiple processes per host, per cluster. If you want the native Netflix behavior built into Turbine to _not_ allow multiple processes per host, per cluster (the key to the instance ID is the hostname), set `turbine.combineHostPort=false`. +==== Clusters Endpoint + +In some situations it might be useful for other applications to know what custers have been configured +in Turbine. To support this you can use the `/clusters` endpoint which will return a JSON array of +all the configured clusters. + +.GET /clusters +[source,json] +---- +[ + { + "name": "RACES", + "link": "http://localhost:8383/turbine.stream?cluster=RACES" + }, + { + "name": "WEB", + "link": "http://localhost:8383/turbine.stream?cluster=WEB" + } +] +---- + +This endpoint can be disabled by setting `turbine.endpoints.clusters.enabled` to `false`. + === Turbine Stream In some environments (such as in a PaaS setting), the classic Turbine model of pulling metrics from all the distributed Hystrix commands does not work. diff --git a/spring-cloud-netflix-turbine/src/main/java/org/springframework/cloud/netflix/turbine/ClusterInformation.java b/spring-cloud-netflix-turbine/src/main/java/org/springframework/cloud/netflix/turbine/ClusterInformation.java index e869153d8..92414a595 100644 --- a/spring-cloud-netflix-turbine/src/main/java/org/springframework/cloud/netflix/turbine/ClusterInformation.java +++ b/spring-cloud-netflix-turbine/src/main/java/org/springframework/cloud/netflix/turbine/ClusterInformation.java @@ -4,8 +4,10 @@ import java.util.Objects; public class ClusterInformation { - private final String name; - private final String link; + private String name; + private String link; + + public ClusterInformation(){}; public ClusterInformation(String name, String link) { this.name = name; @@ -20,6 +22,14 @@ public class ClusterInformation { return link; } + public void setName(String name) { + this.name = name; + } + + public void setLink(String link) { + this.link = link; + } + @Override public boolean equals(Object o) { if (this == o) return true; diff --git a/spring-cloud-netflix-turbine/src/main/java/org/springframework/cloud/netflix/turbine/TurbineHttpConfiguration.java b/spring-cloud-netflix-turbine/src/main/java/org/springframework/cloud/netflix/turbine/TurbineHttpConfiguration.java index 23ca5969d..53b89301e 100644 --- a/spring-cloud-netflix-turbine/src/main/java/org/springframework/cloud/netflix/turbine/TurbineHttpConfiguration.java +++ b/spring-cloud-netflix-turbine/src/main/java/org/springframework/cloud/netflix/turbine/TurbineHttpConfiguration.java @@ -20,6 +20,7 @@ import com.netflix.turbine.monitor.cluster.ClusterMonitorFactory; import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingClass; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.boot.web.servlet.ServletRegistrationBean; import org.springframework.cloud.client.actuator.HasFeatures; @@ -55,6 +56,18 @@ public class TurbineHttpConfiguration { return new TurbineProperties(); } + @Bean + @ConditionalOnMissingBean + public TurbineInformationService turbineInformationService() { + return new TurbineInformationService(); + } + + @Bean + @ConditionalOnProperty(value = "turbine.endpoints.clusters.enabled", matchIfMissing = true) + public TurbineController turbineController(TurbineInformationService service) { + return new TurbineController(service); + } + @Bean @ConditionalOnMissingBean public TurbineAggregatorProperties turbineAggregatorProperties() { diff --git a/spring-cloud-netflix-turbine/src/test/java/org/springframework/cloud/netflix/turbine/TurbineHttpTests.java b/spring-cloud-netflix-turbine/src/test/java/org/springframework/cloud/netflix/turbine/TurbineHttpTests.java index 77ea0e6e5..262596d9a 100644 --- a/spring-cloud-netflix-turbine/src/test/java/org/springframework/cloud/netflix/turbine/TurbineHttpTests.java +++ b/spring-cloud-netflix-turbine/src/test/java/org/springframework/cloud/netflix/turbine/TurbineHttpTests.java @@ -16,13 +16,23 @@ package org.springframework.cloud.netflix.turbine; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import javax.servlet.http.HttpServletRequest; import org.junit.Test; import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.autoconfigure.EnableAutoConfiguration; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.boot.test.context.SpringBootTest.WebEnvironment; +import org.springframework.boot.test.web.client.TestRestTemplate; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; +import static org.junit.Assert.assertEquals; + /** * @author Spencer Gibb */ @@ -30,12 +40,38 @@ import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; @SpringBootTest(classes = TurbineHttpTests.TurbineHttpSampleApplication.class, webEnvironment = WebEnvironment.RANDOM_PORT) public class TurbineHttpTests { + private static final ClusterInformation foo = new ClusterInformation("foo", "http://foo"); + private static final ClusterInformation bar = new ClusterInformation("bar", "http://bar"); + + @Autowired + TestRestTemplate rest; + @EnableAutoConfiguration @EnableTurbine public static class TurbineHttpSampleApplication { + + @Configuration + static class MyConfig { + @Bean + TurbineInformationService myInfoService() { + return new TurbineInformationService() { + @Override + public Collection getClusterInformations(HttpServletRequest request) { + List clusterInformationList = new ArrayList(); + clusterInformationList.add(foo); + clusterInformationList.add(bar); + return clusterInformationList; + } + }; + } + } } @Test public void contextLoads() { + ClusterInformation[] clusters = rest.getForObject("/clusters", ClusterInformation[].class); + assertEquals(2, clusters.length); + assertEquals(foo, clusters[0]); + assertEquals(bar, clusters[1]); } }