Commit e9059795 by Johannes Edmeier

Allow serviceIds to be used for Turbine location

With this commit you can specify either a URL or a serviceId for the Turbine server location. Note that `spring.boot.admin.turbin.url` is replaced by `spring.boot.admin.turbine.location`. closes #373
parent 1d8d7ccc
......@@ -46,13 +46,14 @@ The Turbine module uses the hystrix-dashboard to display the metrics from a Turb
</dependency>
----
. Configure the Turbine URL and clusters
. Configure the Turbine server and clusters
+
[source,yml]
.application.yml
----
include::{samples-dir}/spring-boot-admin-sample-eureka/src/main/resources/application.yml[tags=configuration-ui-turbine]
----
<1> Configures the service with id `turbine` as Turbine server. URLs are also allowed.
.Turbine UI Module configuration options
|===
......@@ -62,9 +63,9 @@ include::{samples-dir}/spring-boot-admin-sample-eureka/src/main/resources/applic
| Enable the Spring Boot Admin backend configuration for Turbine.
| `true`
| spring.boot.admin.turbine.url
| URL to the `turbine.stream`. Must be reachable from the admin server.
|
| spring.boot.admin.turbine.location
| ServiceId or URL (without the `/turbine.stream` path) for the Turbine server. Must be reachable from admin server.
| `"turbine"`
| spring.boot.admin.turbine.clusters
| List of available Turbine clusters.
......@@ -111,4 +112,4 @@ The Login module just provides you a login page and a logout button.
<artifactId>spring-boot-admin-server-ui-login</artifactId>
<version>{project-version}</version>
</dependency>
----
\ No newline at end of file
----
......@@ -23,6 +23,6 @@ spring.boot.admin.routes.endpoints: env,metrics,trace,dump,jolokia,info,configpr
# tag::configuration-ui-turbine[]
spring.boot.admin.turbine:
clusters: default
url: http://localhost:8989/turbine.stream
location: turbine #<1>
# end::configuration-ui-turbine[]
......@@ -15,17 +15,15 @@
*/
package spring.boot.admin.turbine.config;
import java.util.Collection;
import java.util.Collections;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.AutoConfigureBefore;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.autoconfigure.web.ServerProperties;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.cloud.client.discovery.DiscoveryClient;
import org.springframework.cloud.netflix.zuul.filters.ZuulProperties;
import org.springframework.cloud.netflix.zuul.filters.ZuulProperties.ZuulRoute;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Conditional;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.annotation.Order;
......@@ -33,7 +31,7 @@ import de.codecentric.boot.admin.config.AdminServerProperties;
import de.codecentric.boot.admin.config.AdminServerWebConfiguration;
import de.codecentric.boot.admin.config.RevereseZuulProxyConfiguration;
import spring.boot.admin.turbine.web.TurbineController;
import spring.boot.admin.turbine.zuul.filters.StaticRouteLocator;
import spring.boot.admin.turbine.zuul.filters.TurbineRouteLocator;
/**
* Configures all necessary components for the Turbine view.
......@@ -43,7 +41,7 @@ import spring.boot.admin.turbine.zuul.filters.StaticRouteLocator;
@Configuration
@EnableConfigurationProperties(TurbineProperties.class)
@AutoConfigureBefore({ AdminServerWebConfiguration.class, RevereseZuulProxyConfiguration.class })
@Conditional(TurbineEnabledCondition.class)
@ConditionalOnProperty(value = "spring.boot.admin.turbine.enabled", matchIfMissing = true)
public class TurbineAutoConfiguration {
@Autowired
......@@ -62,11 +60,12 @@ public class TurbineAutoConfiguration {
@Bean
@Order(100)
public StaticRouteLocator staticRouteLocator(AdminServerProperties admin) {
Collection<ZuulRoute> routes = Collections
.singleton(new ZuulRoute(admin.getContextPath() + "/api/turbine/stream/**",
properties.getUrl().toString()));
return new StaticRouteLocator(routes, server.getServletPrefix(), zuulProperties);
public TurbineRouteLocator staticRouteLocator(AdminServerProperties admin,
DiscoveryClient discovery) {
ZuulRoute turbineRoute = new ZuulRoute(admin.getContextPath() + "/api/turbine/stream/**",
properties.getLocation());
return new TurbineRouteLocator(turbineRoute, server.getServletPrefix(), zuulProperties,
discovery);
}
}
/*
* Copyright 2013-2016 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
*
* http://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 spring.boot.admin.turbine.config;
import org.springframework.boot.autoconfigure.condition.AllNestedConditions;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
/**
* Condition for enabling the Turbine components.
*
* @author Johannes Edmeier
*/
public class TurbineEnabledCondition extends AllNestedConditions {
public TurbineEnabledCondition() {
super(ConfigurationPhase.PARSE_CONFIGURATION);
}
@ConditionalOnProperty(value = "spring.boot.admin.turbine.enabled", matchIfMissing = true)
static class EnabledProperty {
}
@ConditionalOnProperty(value = "spring.boot.admin.turbine.url", matchIfMissing = false)
static class TurbinUrlProperty {
}
}
\ No newline at end of file
......@@ -24,9 +24,10 @@ import org.springframework.boot.context.properties.ConfigurationProperties;
public class TurbineProperties {
/**
* URL to the <code>turbine.stream</code>. Must be reachable from the admin server.
* ServiceId or URL (without the "/turbine.stream") for the Turbine server. Must be reachable
* from admin server.
*/
private URI url;
private String location = "turbine";
/**
* List of available Turbine clusters.
......@@ -55,11 +56,16 @@ public class TurbineProperties {
this.enabled = enabled;
}
public URI getUrl() {
return url;
public void setLocation(String location) {
this.location = location;
}
public String getLocation() {
return location;
}
@Deprecated
public void setUrl(URI url) {
this.url = url;
this.location = url.toString().replace("/turbine.stream", "");
}
}
......@@ -15,33 +15,63 @@
*/
package spring.boot.admin.turbine.zuul.filters;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import static java.util.Collections.singletonMap;
import java.util.List;
import java.util.Map;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.discovery.DiscoveryClient;
import org.springframework.cloud.netflix.zuul.filters.Route;
import org.springframework.cloud.netflix.zuul.filters.SimpleRouteLocator;
import org.springframework.cloud.netflix.zuul.filters.ZuulProperties;
import org.springframework.cloud.netflix.zuul.filters.ZuulProperties.ZuulRoute;
/**
* RouteLocator that uses static non-configurable routes.
*
* RouteLocator for Turbine.
*
* @author Johannes Edmeier
*/
public class StaticRouteLocator extends SimpleRouteLocator {
private final Map<String, ZuulRoute> routes = new HashMap<>();
public StaticRouteLocator(Collection<ZuulRoute> routes, String servletPath,
ZuulProperties properties) {
super(servletPath, properties);
for (ZuulRoute route : routes) {
this.routes.put(route.getPath(), route);
}
public class TurbineRouteLocator extends SimpleRouteLocator {
private final Map<String, ZuulRoute> routes;
private DiscoveryClient discovery;
public TurbineRouteLocator(ZuulRoute route, String servletPath,
ZuulProperties zuulProperties, DiscoveryClient discovery) {
super(servletPath, zuulProperties);
this.routes = singletonMap(route.getPath(), route);
this.discovery = discovery;
}
@Override
protected Map<String, ZuulRoute> locateRoutes() {
return Collections.unmodifiableMap(this.routes);
return this.routes;
}
@Override
public Route getMatchingRoute(String path) {
Route route = super.getMatchingRoute(path);
if (route == null) {
return route;
}
String location = route.getLocation();
if (!(location.startsWith("http:") || location.startsWith("https:"))) {
location = resolveServiceId(location);
}
String targetPath = route.getPath() + "/turbine.stream";
return new Route(route.getId(), targetPath, location, route.getPrefix(),
route.getRetryable(), route.getSensitiveHeaders());
}
private String resolveServiceId(String location) {
List<ServiceInstance> instances = discovery.getInstances(location);
if (instances.isEmpty()) {
throw new IllegalStateException(
"No instance found for serviceId '" + location + "'");
}
return instances.get(0).getUri().toString();
}
}
......@@ -10,6 +10,7 @@ import org.springframework.boot.autoconfigure.PropertyPlaceholderAutoConfigurati
import org.springframework.boot.autoconfigure.web.ServerPropertiesAutoConfiguration;
import org.springframework.boot.autoconfigure.web.WebClientAutoConfiguration.RestTemplateConfiguration;
import org.springframework.boot.test.util.EnvironmentTestUtils;
import org.springframework.cloud.client.discovery.noop.NoopDiscoveryClientAutoConfiguration;
import org.springframework.web.context.support.AnnotationConfigWebApplicationContext;
import de.codecentric.boot.admin.config.AdminServerCoreConfiguration;
......@@ -36,12 +37,6 @@ public class TurbineAutoConfigurationTest {
}
@Test
public void test_missing_url() {
load();
assertThat(context.getBeansOfType(TurbineController.class).values(), empty());
}
@Test
public void test_enabled() {
load("spring.boot.admin.turbine.url:http://turbine.server:8989/turbine.stream");
assertThat(context.getBean(TurbineController.class), instanceOf(TurbineController.class));
......@@ -52,6 +47,7 @@ public class TurbineAutoConfigurationTest {
applicationContext.register(PropertyPlaceholderAutoConfiguration.class);
applicationContext.register(RestTemplateConfiguration.class);
applicationContext.register(ServerPropertiesAutoConfiguration.class);
applicationContext.register(NoopDiscoveryClientAutoConfiguration.class);
applicationContext.register(AdminServerCoreConfiguration.class);
applicationContext.register(AdminServerWebConfiguration.class);
applicationContext.register(RevereseZuulProxyConfiguration.class);
......
package spring.boot.admin.turbine.zuul.filters;
import static org.hamcrest.CoreMatchers.is;
import static org.hamcrest.CoreMatchers.nullValue;
import static org.junit.Assert.assertThat;
import java.util.Collections;
import java.util.List;
import org.junit.Test;
import org.springframework.cloud.netflix.zuul.filters.ZuulProperties;
import org.springframework.cloud.netflix.zuul.filters.ZuulProperties.ZuulRoute;
public class StaticRouteLocatorTest {
@Test
public void test_route() {
List<ZuulRoute> routes = Collections
.singletonList(new ZuulRoute("/path/**", "http://example.com/target"));
StaticRouteLocator locator = new StaticRouteLocator(routes, "", new ZuulProperties());
assertThat(locator.getRoutes().size(), is(1) );
assertThat(locator.getRoutes().get(0).getPath(), is("/**"));
assertThat(locator.getRoutes().get(0).getPrefix(), is("/path"));
assertThat(locator.getRoutes().get(0).getLocation(), is( "http://example.com/target"));
assertThat(locator.getMatchingRoute("/path/foo").getLocation(),
is("http://example.com/target"));
assertThat(locator.getMatchingRoute("/404/foo"), nullValue());
}
}
package spring.boot.admin.turbine.zuul.filters;
import static org.hamcrest.CoreMatchers.is;
import static org.hamcrest.CoreMatchers.nullValue;
import static org.junit.Assert.assertThat;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
import java.util.Arrays;
import org.junit.Test;
import org.springframework.cloud.client.DefaultServiceInstance;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.discovery.DiscoveryClient;
import org.springframework.cloud.client.discovery.noop.NoopDiscoveryClient;
import org.springframework.cloud.netflix.zuul.filters.Route;
import org.springframework.cloud.netflix.zuul.filters.ZuulProperties;
import org.springframework.cloud.netflix.zuul.filters.ZuulProperties.ZuulRoute;
public class TurbineRouteLocatorTest {
@Test
public void test_route_http_location() {
ZuulRoute route = new ZuulRoute("/path/**", "http://example.com/target");
TurbineRouteLocator locator = new TurbineRouteLocator(route, "", new ZuulProperties(),
null);
assertThat(locator.getRoutes().size(), is(1) );
assertThat(locator.getRoutes().get(0).getPath(), is("/**"));
assertThat(locator.getRoutes().get(0).getPrefix(), is("/path"));
assertThat(locator.getRoutes().get(0).getLocation(), is( "http://example.com/target"));
Route matchingRoute = locator.getMatchingRoute("/path/foo");
assertThat(matchingRoute.getLocation(), is("http://example.com/target"));
assertThat(matchingRoute.getPath(), is("/foo/turbine.stream"));
assertThat(locator.getMatchingRoute("/404/foo"), nullValue());
}
@Test
public void test_route_service_location() {
ZuulRoute route = new ZuulRoute("/path/**", "turbine");
DiscoveryClient discovery = mock(DiscoveryClient.class);
when(discovery.getInstances("turbine")).thenReturn(Arrays.<ServiceInstance>asList(
new DefaultServiceInstance("turbine", "example.com", 80, false)));
TurbineRouteLocator locator = new TurbineRouteLocator(route, "", new ZuulProperties(),
discovery);
Route matchingRoute = locator.getMatchingRoute("/path/foo");
assertThat(matchingRoute.getLocation(), is("http://example.com:80"));
assertThat(matchingRoute.getPath(), is("/foo/turbine.stream"));
}
@Test(expected = IllegalStateException.class)
public void test_route_noservice() {
ZuulRoute route = new ZuulRoute("/path/**", "turbine");
DiscoveryClient discovery = new NoopDiscoveryClient(null);
TurbineRouteLocator locator = new TurbineRouteLocator(route, "", new ZuulProperties(),
discovery);
locator.getMatchingRoute("/path/foo");
}
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment