Browse Source

Provide more Zuul route details in the routes actuator endpoint

Add a parameter to control the level of details returned with the
routes actuator endpoint. If requested, return all available Zuul route
details including ID, prefix, sensitive headers, etc.

Signed-off-by: Gregor Zurowski <gregor@zurowski.org>
pull/6/head
Gregor Zurowski 7 years ago
parent
commit
eb1b7baf1c
  1. 56
      spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/zuul/RoutesEndpoint.java
  2. 26
      spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/zuul/RoutesMvcEndpoint.java
  3. 23
      spring-cloud-netflix-core/src/test/java/org/springframework/cloud/netflix/zuul/RoutesEndpointIntegrationTests.java
  4. 13
      spring-cloud-netflix-core/src/test/java/org/springframework/cloud/netflix/zuul/RoutesEndpointTests.java
  5. 14
      spring-cloud-netflix-core/src/test/java/org/springframework/cloud/netflix/zuul/RoutesMvcEndpointTests.java

56
spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/zuul/RoutesEndpoint.java

@ -18,7 +18,11 @@ package org.springframework.cloud.netflix.zuul; @@ -18,7 +18,11 @@ package org.springframework.cloud.netflix.zuul;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Set;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonPropertyOrder;
import lombok.Data;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.actuate.endpoint.AbstractEndpoint;
import org.springframework.boot.context.properties.ConfigurationProperties;
@ -34,6 +38,7 @@ import org.springframework.jmx.export.annotation.ManagedResource; @@ -34,6 +38,7 @@ import org.springframework.jmx.export.annotation.ManagedResource;
* @author Spencer Gibb
* @author Dave Syer
* @author Ryan Baxter
* @author Gregor Zurowski
*/
@ManagedResource(description = "Can be used to list the reverse proxy routes")
@ConfigurationProperties(prefix = "endpoints.routes")
@ -59,4 +64,55 @@ public class RoutesEndpoint extends AbstractEndpoint<Map<String, String>> { @@ -59,4 +64,55 @@ public class RoutesEndpoint extends AbstractEndpoint<Map<String, String>> {
}
return map;
}
Map<String, RouteDetails> invokeRouteDetails() {
Map<String, RouteDetails> map = new LinkedHashMap<>();
for (Route route : this.routes.getRoutes()) {
map.put(route.getFullPath(), new RouteDetails(route));
}
return map;
}
/**
* Container for exposing Zuul {@link Route} details as JSON.
*/
@JsonPropertyOrder({ "id", "fullPath", "location" })
@JsonInclude(JsonInclude.Include.NON_EMPTY)
@Data
public static class RouteDetails {
private String id;
private String fullPath;
private String path;
private String location;
private String prefix;
private Boolean retryable;
private Set<String> sensitiveHeaders;
private boolean customSensitiveHeaders;
private boolean prefixStripped;
public RouteDetails() {
}
RouteDetails(final Route route) {
this.id = route.getId();
this.fullPath = route.getFullPath();
this.path = route.getPath();
this.location = route.getLocation();
this.prefix = route.getPrefix();
this.retryable = route.getRetryable();
this.sensitiveHeaders = route.getSensitiveHeaders();
this.customSensitiveHeaders = route.isCustomSensitiveHeaders();
this.prefixStripped = route.isPrefixStripped();
}
}
}

26
spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/zuul/RoutesMvcEndpoint.java

@ -18,28 +18,38 @@ @@ -18,28 +18,38 @@
package org.springframework.cloud.netflix.zuul;
import org.springframework.boot.actuate.endpoint.mvc.ActuatorMediaTypes;
import org.springframework.boot.actuate.endpoint.mvc.EndpointMvcAdapter;
import org.springframework.cloud.netflix.zuul.filters.Route;
import org.springframework.cloud.netflix.zuul.filters.RouteLocator;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.context.ApplicationEventPublisherAware;
import org.springframework.http.MediaType;
import org.springframework.jmx.export.annotation.ManagedOperation;
import org.springframework.jmx.export.annotation.ManagedResource;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
/**
* Endpoint used to reset the reverse proxy routes
* @author Ryan Baxter
* @author Gregor Zurowski
*/
@ManagedResource(description = "Can be used to reset the reverse proxy routes")
public class RoutesMvcEndpoint extends EndpointMvcAdapter implements ApplicationEventPublisherAware {
static final String FORMAT_DETAILS = "details";
private final RoutesEndpoint endpoint;
private RouteLocator routes;
private ApplicationEventPublisher publisher;
public RoutesMvcEndpoint(RoutesEndpoint endpoint, RouteLocator routes) {
super(endpoint);
this.endpoint = endpoint;
this.routes = routes;
}
@ -55,4 +65,18 @@ public class RoutesMvcEndpoint extends EndpointMvcAdapter implements Application @@ -55,4 +65,18 @@ public class RoutesMvcEndpoint extends EndpointMvcAdapter implements Application
this.publisher.publishEvent(new RoutesRefreshedEvent(this.routes));
return super.invoke();
}
}
/**
* Expose Zuul {@link Route} information with details.
*/
@GetMapping(params = "format", produces = { ActuatorMediaTypes.APPLICATION_ACTUATOR_V1_JSON_VALUE,
MediaType.APPLICATION_JSON_VALUE })
@ResponseBody
public Object invokeRouteDetails(@RequestParam String format) {
if (FORMAT_DETAILS.equalsIgnoreCase(format)) {
return endpoint.invokeRouteDetails();
} else {
return super.invoke();
}
}
}

23
spring-cloud-netflix-core/src/test/java/org/springframework/cloud/netflix/zuul/RoutesEndpointIntegrationTests.java

@ -27,16 +27,23 @@ import org.springframework.boot.test.context.SpringBootTest; @@ -27,16 +27,23 @@ import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.web.client.TestRestTemplate;
import org.springframework.context.ApplicationListener;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.ParameterizedTypeReference;
import org.springframework.http.HttpMethod;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Component;
import org.springframework.test.annotation.DirtiesContext;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.web.bind.annotation.RestController;
import static org.hamcrest.Matchers.is;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertThat;
import static org.junit.Assert.assertTrue;
/**
* @author Ryan Baxter
* @author Gregor Zurowski
*/
@RunWith(SpringJUnit4ClassRunner.class)
@SpringBootTest(
@ -64,6 +71,22 @@ public class RoutesEndpointIntegrationTests { @@ -64,6 +71,22 @@ public class RoutesEndpointIntegrationTests {
assertTrue(refreshListener.wasCalled());
}
@Test
public void getRouteDetailsTest() {
ResponseEntity<Map<String, RoutesEndpoint.RouteDetails>> responseEntity = restTemplate.exchange(
"/admin/routes?format=details", HttpMethod.GET, null, new ParameterizedTypeReference<Map<String, RoutesEndpoint.RouteDetails>>() {
});
assertThat(responseEntity.getStatusCode(), is(HttpStatus.OK));
RoutesEndpoint.RouteDetails details = responseEntity.getBody().get("/sslservice/**");
assertThat(details.getPath(), is("/**"));
assertThat(details.getFullPath(), is("/sslservice/**"));
assertThat(details.getLocation(), is("https://localhost:8443"));
assertThat(details.getPrefix(), is("/sslservice"));
assertTrue(details.isPrefixStripped());
}
@Configuration
@EnableAutoConfiguration
@RestController

13
spring-cloud-netflix-core/src/test/java/org/springframework/cloud/netflix/zuul/RoutesEndpointTests.java

@ -34,6 +34,7 @@ import static org.junit.Assert.assertTrue; @@ -34,6 +34,7 @@ import static org.junit.Assert.assertTrue;
/**
* @author Ryan Baxter
* @author Gregor Zurowski
*/
public class RoutesEndpointTests {
@ -51,7 +52,7 @@ public class RoutesEndpointTests { @@ -51,7 +52,7 @@ public class RoutesEndpointTests {
public List<Route> getRoutes() {
List<Route> routes = new ArrayList<>();
routes.add(new Route("foo", "foopath", "foolocation", null, true, Collections.EMPTY_SET));
routes.add(new Route("bar", "barpath", "barlocation", null, true, Collections.EMPTY_SET));
routes.add(new Route("bar", "barpath", "barlocation", "/bar-prefix", true, Collections.EMPTY_SET));
return routes;
}
@ -72,6 +73,16 @@ public class RoutesEndpointTests { @@ -72,6 +73,16 @@ public class RoutesEndpointTests {
assertEquals(result , endpoint.invoke());
}
@Test
public void testInvokeRouteDetails() {
RoutesEndpoint endpoint = new RoutesEndpoint(locator);
Map<String, RoutesEndpoint.RouteDetails> results = new HashMap<>();
for (Route route : locator.getRoutes()) {
results.put(route.getFullPath(), new RoutesEndpoint.RouteDetails(route));
}
assertEquals(results, endpoint.invokeRouteDetails());
}
@Test
public void testId() {
RoutesEndpoint endpoint = new RoutesEndpoint(locator);

14
spring-cloud-netflix-core/src/test/java/org/springframework/cloud/netflix/zuul/RoutesMvcEndpointTests.java

@ -42,6 +42,7 @@ import static org.mockito.Mockito.verify; @@ -42,6 +42,7 @@ import static org.mockito.Mockito.verify;
/**
* @author Ryan Baxter
* @author Gregor Zurowski
*/
@SpringBootTest
@RunWith(MockitoJUnitRunner.class)
@ -63,7 +64,7 @@ public class RoutesMvcEndpointTests { @@ -63,7 +64,7 @@ public class RoutesMvcEndpointTests {
public List<Route> getRoutes() {
List<Route> routes = new ArrayList<>();
routes.add(new Route("foo", "foopath", "foolocation", null, true, Collections.EMPTY_SET));
routes.add(new Route("bar", "barpath", "barlocation", null, true, Collections.EMPTY_SET));
routes.add(new Route("bar", "barpath", "barlocation", "bar-prefix", true, Collections.EMPTY_SET));
return routes;
}
@ -88,4 +89,15 @@ public class RoutesMvcEndpointTests { @@ -88,4 +89,15 @@ public class RoutesMvcEndpointTests {
verify(publisher, times(1)).publishEvent(isA(RoutesRefreshedEvent.class));
}
@Test
public void routeDetails() throws Exception {
RoutesMvcEndpoint mvcEndpoint = new RoutesMvcEndpoint(endpoint, locator);
Map<String, RoutesEndpoint.RouteDetails> results = new HashMap<>();
for (Route route : locator.getRoutes()) {
results.put(route.getFullPath(), new RoutesEndpoint.RouteDetails(route));
}
assertEquals(results, mvcEndpoint.invokeRouteDetails(RoutesMvcEndpoint.FORMAT_DETAILS));
verify(endpoint, times(1)).invokeRouteDetails();
}
}
Loading…
Cancel
Save