Change /routes endpoint to use EndpointMvcAdapter and AbstractEndpoint (#1802)
Change /routes endpoint to use EndpointMvcAdapter and AbstractEndpoint. Fixes #1797
This commit is contained in:
committed by
Spencer Gibb
parent
d402587ab2
commit
62cb0ae28f
@@ -1589,7 +1589,8 @@ If you are using `@EnableZuulProxy` with tha Spring Boot Actuator you
|
||||
will enable (by default) an additional endpoint, available via HTTP as
|
||||
`/routes`. A GET to this endpoint will return a list of the mapped
|
||||
routes. A POST will force a refresh of the existing routes (e.g. in
|
||||
case there have been changes in the service catalog).
|
||||
case there have been changes in the service catalog). You can disable
|
||||
this endpoint by setting `endpoints.routes.enabled` to `false`.
|
||||
|
||||
NOTE: the routes should respond automatically to changes in the
|
||||
service catalog, but the POST to /routes is a way to force the change
|
||||
|
||||
@@ -20,74 +20,43 @@ import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.actuate.endpoint.Endpoint;
|
||||
import org.springframework.boot.actuate.endpoint.mvc.MvcEndpoint;
|
||||
import org.springframework.boot.actuate.endpoint.AbstractEndpoint;
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
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.jmx.export.annotation.ManagedAttribute;
|
||||
import org.springframework.jmx.export.annotation.ManagedOperation;
|
||||
import org.springframework.jmx.export.annotation.ManagedResource;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMethod;
|
||||
import org.springframework.web.bind.annotation.ResponseBody;
|
||||
|
||||
/**
|
||||
* Endpoint to display and reset the zuul proxy routes
|
||||
* Endpoint to display the zuul proxy routes
|
||||
*
|
||||
* @author Spencer Gibb
|
||||
* @author Dave Syer
|
||||
* @author Ryan Baxter
|
||||
*/
|
||||
@ManagedResource(description = "Can be used to list and reset the reverse proxy routes")
|
||||
public class RoutesEndpoint implements MvcEndpoint, ApplicationEventPublisherAware {
|
||||
@ManagedResource(description = "Can be used to list the reverse proxy routes")
|
||||
@ConfigurationProperties(prefix = "endpoints.routes")
|
||||
public class RoutesEndpoint extends AbstractEndpoint<Map<String, String>> {
|
||||
|
||||
private static final String ID = "routes";
|
||||
|
||||
private RouteLocator routes;
|
||||
|
||||
private ApplicationEventPublisher publisher;
|
||||
|
||||
@Override
|
||||
public void setApplicationEventPublisher(ApplicationEventPublisher publisher) {
|
||||
this.publisher = publisher;
|
||||
}
|
||||
|
||||
@Autowired
|
||||
public RoutesEndpoint(RouteLocator routes) {
|
||||
super(ID, true);
|
||||
this.routes = routes;
|
||||
}
|
||||
|
||||
@RequestMapping(method = RequestMethod.POST)
|
||||
@ResponseBody
|
||||
@ManagedOperation
|
||||
public Map<String, String> reset() {
|
||||
this.publisher.publishEvent(new RoutesRefreshedEvent(this.routes));
|
||||
return getRoutes();
|
||||
}
|
||||
|
||||
@RequestMapping(method = RequestMethod.GET)
|
||||
@ResponseBody
|
||||
@ManagedAttribute
|
||||
public Map<String, String> getRoutes() {
|
||||
public Map<String, String> invoke() {
|
||||
Map<String, String> map = new LinkedHashMap<>();
|
||||
for (Route route : this.routes.getRoutes()) {
|
||||
map.put(route.getFullPath(), route.getLocation());
|
||||
}
|
||||
return map;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getPath() {
|
||||
return "/routes";
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isSensitive() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<? extends Endpoint<?>> getEndpointType() {
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,58 @@
|
||||
/*
|
||||
*
|
||||
* * 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 org.springframework.cloud.netflix.zuul;
|
||||
|
||||
import org.springframework.boot.actuate.endpoint.mvc.EndpointMvcAdapter;
|
||||
import org.springframework.cloud.netflix.zuul.filters.RouteLocator;
|
||||
import org.springframework.context.ApplicationEventPublisher;
|
||||
import org.springframework.context.ApplicationEventPublisherAware;
|
||||
import org.springframework.jmx.export.annotation.ManagedOperation;
|
||||
import org.springframework.jmx.export.annotation.ManagedResource;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMethod;
|
||||
import org.springframework.web.bind.annotation.ResponseBody;
|
||||
|
||||
/**
|
||||
* Endpoint used to reset the reverse proxy routes
|
||||
* @author Ryan Baxter
|
||||
*/
|
||||
@ManagedResource(description = "Can be used to reset the reverse proxy routes")
|
||||
public class RoutesMvcEndpoint extends EndpointMvcAdapter implements ApplicationEventPublisherAware {
|
||||
|
||||
private RouteLocator routes;
|
||||
private ApplicationEventPublisher publisher;
|
||||
|
||||
public RoutesMvcEndpoint(RoutesEndpoint endpoint, RouteLocator routes) {
|
||||
super(endpoint);
|
||||
this.routes = routes;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher) {
|
||||
this.publisher = applicationEventPublisher;
|
||||
}
|
||||
|
||||
@RequestMapping(method = RequestMethod.POST)
|
||||
@ResponseBody
|
||||
@ManagedOperation
|
||||
public Object reset() {
|
||||
this.publisher.publishEvent(new RoutesRefreshedEvent(this.routes));
|
||||
return super.invoke();
|
||||
}
|
||||
}
|
||||
@@ -139,6 +139,11 @@ public class ZuulProxyConfiguration extends ZuulConfiguration {
|
||||
return new RoutesEndpoint(routeLocator);
|
||||
}
|
||||
|
||||
@Bean
|
||||
public RoutesMvcEndpoint zuulMvcEndpoint(RouteLocator routeLocator, RoutesEndpoint endpoint) {
|
||||
return new RoutesMvcEndpoint(endpoint, routeLocator);
|
||||
}
|
||||
|
||||
@Bean
|
||||
public ProxyRequestHelper proxyRequestHelper(ZuulProperties zuulProperties) {
|
||||
TraceProxyRequestHelper helper = new TraceProxyRequestHelper();
|
||||
|
||||
@@ -61,7 +61,7 @@ public class ContextPathZuulProxyApplicationTests {
|
||||
private DiscoveryClientRouteLocator routes;
|
||||
|
||||
@Autowired
|
||||
private RoutesEndpoint endpoint;
|
||||
private RoutesMvcEndpoint endpoint;
|
||||
|
||||
@Before
|
||||
public void setTestRequestContext() {
|
||||
|
||||
@@ -0,0 +1,85 @@
|
||||
/*
|
||||
*
|
||||
* * 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 org.springframework.cloud.netflix.zuul;
|
||||
|
||||
import java.util.Map;
|
||||
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.web.client.TestRestTemplate;
|
||||
import org.springframework.context.ApplicationListener;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
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.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
/**
|
||||
* @author Ryan Baxter
|
||||
*/
|
||||
@RunWith(SpringJUnit4ClassRunner.class)
|
||||
@SpringBootTest(
|
||||
webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT,
|
||||
value = {"zuul.routes.sslservice.url=https://localhost:8443", "management.security.enabled=false"})
|
||||
@DirtiesContext
|
||||
public class RoutesEndpointIntegrationTests {
|
||||
|
||||
@Autowired
|
||||
private TestRestTemplate restTemplate;
|
||||
|
||||
@Autowired
|
||||
private SimpleZuulProxyApplication.RoutesRefreshListener refreshListener;
|
||||
|
||||
@Test
|
||||
public void getRoutesTest() {
|
||||
Map<String, String> routes = restTemplate.getForObject("/admin/routes", Map.class);
|
||||
assertEquals("https://localhost:8443", routes.get("/sslservice/**"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void postRoutesTest() {
|
||||
Map<String, String> routes = restTemplate.postForObject("/admin/routes", null, Map.class);
|
||||
assertEquals("https://localhost:8443", routes.get("/sslservice/**"));
|
||||
assertTrue(refreshListener.wasCalled());
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@EnableAutoConfiguration
|
||||
@RestController
|
||||
@EnableZuulProxy
|
||||
static class SimpleZuulProxyApplication {
|
||||
@Component
|
||||
static class RoutesRefreshListener implements ApplicationListener<RoutesRefreshedEvent> {
|
||||
private boolean called = false;
|
||||
@Override
|
||||
public void onApplicationEvent(RoutesRefreshedEvent routesRefreshedEvent) {
|
||||
called = true;
|
||||
}
|
||||
|
||||
public boolean wasCalled() {
|
||||
return called;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,86 @@
|
||||
/*
|
||||
*
|
||||
* * 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 org.springframework.cloud.netflix.zuul;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.springframework.cloud.netflix.zuul.filters.Route;
|
||||
import org.springframework.cloud.netflix.zuul.filters.RouteLocator;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
/**
|
||||
* @author Ryan Baxter
|
||||
*/
|
||||
public class RoutesEndpointTests {
|
||||
|
||||
private RouteLocator locator;
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
this.locator = new RouteLocator() {
|
||||
@Override
|
||||
public Collection<String> getIgnoredPaths() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
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));
|
||||
return routes;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Route getMatchingRoute(String path) {
|
||||
return null;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testInvoke() {
|
||||
RoutesEndpoint endpoint = new RoutesEndpoint(locator);
|
||||
Map<String, String> result = new HashMap<String, String>();
|
||||
for(Route r : locator.getRoutes()) {
|
||||
result.put(r.getFullPath(), r.getLocation());
|
||||
}
|
||||
assertEquals(result , endpoint.invoke());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testId() {
|
||||
RoutesEndpoint endpoint = new RoutesEndpoint(locator);
|
||||
assertEquals("routes", endpoint.getId());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testIsSensitive() {
|
||||
RoutesEndpoint endpoint = new RoutesEndpoint(locator);
|
||||
assertTrue(endpoint.isSensitive());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,91 @@
|
||||
/*
|
||||
*
|
||||
* * 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 org.springframework.cloud.netflix.zuul;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.runners.MockitoJUnitRunner;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
import org.springframework.cloud.netflix.zuul.filters.Route;
|
||||
import org.springframework.cloud.netflix.zuul.filters.RouteLocator;
|
||||
import org.springframework.context.ApplicationEventPublisher;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.mockito.Matchers.isA;
|
||||
import static org.mockito.Mockito.spy;
|
||||
import static org.mockito.Mockito.times;
|
||||
import static org.mockito.Mockito.verify;
|
||||
|
||||
/**
|
||||
* @author Ryan Baxter
|
||||
*/
|
||||
@SpringBootTest
|
||||
@RunWith(MockitoJUnitRunner.class)
|
||||
public class RoutesMvcEndpointTests {
|
||||
private RouteLocator locator;
|
||||
private RoutesEndpoint endpoint;
|
||||
@Mock
|
||||
private ApplicationEventPublisher publisher;
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
this.locator = new RouteLocator() {
|
||||
@Override
|
||||
public Collection<String> getIgnoredPaths() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
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));
|
||||
return routes;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Route getMatchingRoute(String path) {
|
||||
return null;
|
||||
}
|
||||
};
|
||||
endpoint = spy(new RoutesEndpoint(locator));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void reset() throws Exception {
|
||||
RoutesMvcEndpoint mvcEndpoint = new RoutesMvcEndpoint(endpoint, locator);
|
||||
mvcEndpoint.setApplicationEventPublisher(publisher);
|
||||
Map<String, String> result = new HashMap<String, String>();
|
||||
for(Route r : locator.getRoutes()) {
|
||||
result.put(r.getFullPath(), r.getLocation());
|
||||
}
|
||||
assertEquals(result , mvcEndpoint.reset());
|
||||
verify(endpoint, times(1)).invoke();
|
||||
verify(publisher, times(1)).publishEvent(isA(RoutesRefreshedEvent.class));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -66,7 +66,7 @@ public class ServletPathZuulProxyApplicationTests {
|
||||
private DiscoveryClientRouteLocator routes;
|
||||
|
||||
@Autowired
|
||||
private RoutesEndpoint endpoint;
|
||||
private RoutesMvcEndpoint endpoint;
|
||||
|
||||
@Before
|
||||
public void setTestRequestContext() {
|
||||
|
||||
@@ -66,7 +66,7 @@ public class SimpleZuulProxyApplicationTests {
|
||||
private DiscoveryClientRouteLocator routes;
|
||||
|
||||
@Autowired
|
||||
private RoutesEndpoint endpoint;
|
||||
private RoutesMvcEndpoint endpoint;
|
||||
|
||||
@Before
|
||||
public void setTestRequestContext() {
|
||||
|
||||
@@ -23,7 +23,7 @@ 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.cloud.netflix.zuul.EnableZuulProxy;
|
||||
import org.springframework.cloud.netflix.zuul.RoutesEndpoint;
|
||||
import org.springframework.cloud.netflix.zuul.RoutesMvcEndpoint;
|
||||
import org.springframework.cloud.netflix.zuul.ZuulProxyConfiguration;
|
||||
import org.springframework.cloud.netflix.zuul.filters.discovery.DiscoveryClientRouteLocator;
|
||||
import org.springframework.cloud.netflix.zuul.filters.route.SimpleHostRoutingFilter;
|
||||
@@ -59,7 +59,7 @@ public class CustomHostRoutingFilterTests {
|
||||
private DiscoveryClientRouteLocator routes;
|
||||
|
||||
@Autowired
|
||||
private RoutesEndpoint endpoint;
|
||||
private RoutesMvcEndpoint endpoint;
|
||||
|
||||
@Before
|
||||
public void setTestRequestcontext() {
|
||||
|
||||
@@ -22,7 +22,7 @@ import org.springframework.cloud.client.discovery.DiscoveryClient;
|
||||
import org.springframework.cloud.netflix.ribbon.RibbonClient;
|
||||
import org.springframework.cloud.netflix.ribbon.StaticServerList;
|
||||
import org.springframework.cloud.netflix.zuul.EnableZuulProxy;
|
||||
import org.springframework.cloud.netflix.zuul.RoutesEndpoint;
|
||||
import org.springframework.cloud.netflix.zuul.RoutesMvcEndpoint;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.http.HttpEntity;
|
||||
@@ -59,7 +59,7 @@ public class PatternServiceRouteMapperIntegrationTests {
|
||||
private DiscoveryClientRouteLocator routes;
|
||||
|
||||
@Autowired
|
||||
private RoutesEndpoint endpoint;
|
||||
private RoutesMvcEndpoint endpoint;
|
||||
|
||||
@Before
|
||||
public void setTestRequestcontext() {
|
||||
|
||||
@@ -37,7 +37,7 @@ import org.springframework.boot.autoconfigure.web.ErrorAttributes;
|
||||
import org.springframework.boot.autoconfigure.web.ErrorProperties;
|
||||
import org.springframework.boot.test.web.client.TestRestTemplate;
|
||||
import org.springframework.cloud.netflix.ribbon.StaticServerList;
|
||||
import org.springframework.cloud.netflix.zuul.RoutesEndpoint;
|
||||
import org.springframework.cloud.netflix.zuul.RoutesMvcEndpoint;
|
||||
import org.springframework.cloud.netflix.zuul.filters.Route;
|
||||
import org.springframework.cloud.netflix.zuul.filters.ZuulProperties;
|
||||
import org.springframework.cloud.netflix.zuul.filters.discovery.DiscoveryClientRouteLocator;
|
||||
@@ -88,7 +88,7 @@ public abstract class ZuulProxyTestBase {
|
||||
protected DiscoveryClientRouteLocator routes;
|
||||
|
||||
@Autowired
|
||||
protected RoutesEndpoint endpoint;
|
||||
protected RoutesMvcEndpoint endpoint;
|
||||
|
||||
@Autowired
|
||||
protected RibbonCommandFactory<?> ribbonCommandFactory;
|
||||
|
||||
Reference in New Issue
Block a user