Commit 62cb0ae2 by Ryan Baxter Committed by Spencer Gibb

Change /routes endpoint to use EndpointMvcAdapter and AbstractEndpoint (#1802)

Change /routes endpoint to use EndpointMvcAdapter and AbstractEndpoint. Fixes #1797
parent d402587a
...@@ -1589,7 +1589,8 @@ If you are using `@EnableZuulProxy` with tha Spring Boot Actuator you ...@@ -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 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 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 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 NOTE: the routes should respond automatically to changes in the
service catalog, but the POST to /routes is a way to force the change service catalog, but the POST to /routes is a way to force the change
......
...@@ -20,74 +20,43 @@ import java.util.LinkedHashMap; ...@@ -20,74 +20,43 @@ import java.util.LinkedHashMap;
import java.util.Map; import java.util.Map;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.actuate.endpoint.Endpoint; import org.springframework.boot.actuate.endpoint.AbstractEndpoint;
import org.springframework.boot.actuate.endpoint.mvc.MvcEndpoint; import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.cloud.netflix.zuul.filters.Route; import org.springframework.cloud.netflix.zuul.filters.Route;
import org.springframework.cloud.netflix.zuul.filters.RouteLocator; import org.springframework.cloud.netflix.zuul.filters.RouteLocator;
import org.springframework.context.ApplicationEventPublisher; import org.springframework.context.ApplicationEventPublisher;
import org.springframework.context.ApplicationEventPublisherAware;
import org.springframework.jmx.export.annotation.ManagedAttribute; import org.springframework.jmx.export.annotation.ManagedAttribute;
import org.springframework.jmx.export.annotation.ManagedOperation;
import org.springframework.jmx.export.annotation.ManagedResource; 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 Spencer Gibb
* @author Dave Syer * @author Dave Syer
* @author Ryan Baxter
*/ */
@ManagedResource(description = "Can be used to list and reset the reverse proxy routes") @ManagedResource(description = "Can be used to list the reverse proxy routes")
public class RoutesEndpoint implements MvcEndpoint, ApplicationEventPublisherAware { @ConfigurationProperties(prefix = "endpoints.routes")
public class RoutesEndpoint extends AbstractEndpoint<Map<String, String>> {
private static final String ID = "routes";
private RouteLocator routes; private RouteLocator routes;
private ApplicationEventPublisher publisher; private ApplicationEventPublisher publisher;
@Override
public void setApplicationEventPublisher(ApplicationEventPublisher publisher) {
this.publisher = publisher;
}
@Autowired @Autowired
public RoutesEndpoint(RouteLocator routes) { public RoutesEndpoint(RouteLocator routes) {
super(ID, true);
this.routes = routes; 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 @ManagedAttribute
public Map<String, String> getRoutes() { public Map<String, String> invoke() {
Map<String, String> map = new LinkedHashMap<>(); Map<String, String> map = new LinkedHashMap<>();
for (Route route : this.routes.getRoutes()) { for (Route route : this.routes.getRoutes()) {
map.put(route.getFullPath(), route.getLocation()); map.put(route.getFullPath(), route.getLocation());
} }
return map; return map;
} }
@Override
public String getPath() {
return "/routes";
}
@Override
public boolean isSensitive() {
return true;
}
@Override
public Class<? extends Endpoint<?>> getEndpointType() {
return null;
}
} }
/*
*
* * 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();
}
}
...@@ -140,6 +140,11 @@ public class ZuulProxyConfiguration extends ZuulConfiguration { ...@@ -140,6 +140,11 @@ public class ZuulProxyConfiguration extends ZuulConfiguration {
} }
@Bean @Bean
public RoutesMvcEndpoint zuulMvcEndpoint(RouteLocator routeLocator, RoutesEndpoint endpoint) {
return new RoutesMvcEndpoint(endpoint, routeLocator);
}
@Bean
public ProxyRequestHelper proxyRequestHelper(ZuulProperties zuulProperties) { public ProxyRequestHelper proxyRequestHelper(ZuulProperties zuulProperties) {
TraceProxyRequestHelper helper = new TraceProxyRequestHelper(); TraceProxyRequestHelper helper = new TraceProxyRequestHelper();
if (this.traces != null) { if (this.traces != null) {
......
...@@ -61,7 +61,7 @@ public class ContextPathZuulProxyApplicationTests { ...@@ -61,7 +61,7 @@ public class ContextPathZuulProxyApplicationTests {
private DiscoveryClientRouteLocator routes; private DiscoveryClientRouteLocator routes;
@Autowired @Autowired
private RoutesEndpoint endpoint; private RoutesMvcEndpoint endpoint;
@Before @Before
public void setTestRequestContext() { public void setTestRequestContext() {
......
/*
*
* * 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;
}
}
}
}
/*
*
* * 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());
}
}
\ No newline at end of file
/*
*
* * 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));
}
}
\ No newline at end of file
...@@ -66,7 +66,7 @@ public class ServletPathZuulProxyApplicationTests { ...@@ -66,7 +66,7 @@ public class ServletPathZuulProxyApplicationTests {
private DiscoveryClientRouteLocator routes; private DiscoveryClientRouteLocator routes;
@Autowired @Autowired
private RoutesEndpoint endpoint; private RoutesMvcEndpoint endpoint;
@Before @Before
public void setTestRequestContext() { public void setTestRequestContext() {
......
...@@ -66,7 +66,7 @@ public class SimpleZuulProxyApplicationTests { ...@@ -66,7 +66,7 @@ public class SimpleZuulProxyApplicationTests {
private DiscoveryClientRouteLocator routes; private DiscoveryClientRouteLocator routes;
@Autowired @Autowired
private RoutesEndpoint endpoint; private RoutesMvcEndpoint endpoint;
@Before @Before
public void setTestRequestContext() { public void setTestRequestContext() {
......
...@@ -23,7 +23,7 @@ import org.springframework.boot.test.context.SpringBootTest; ...@@ -23,7 +23,7 @@ import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.context.SpringBootTest.WebEnvironment; import org.springframework.boot.test.context.SpringBootTest.WebEnvironment;
import org.springframework.boot.test.web.client.TestRestTemplate; import org.springframework.boot.test.web.client.TestRestTemplate;
import org.springframework.cloud.netflix.zuul.EnableZuulProxy; 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.ZuulProxyConfiguration;
import org.springframework.cloud.netflix.zuul.filters.discovery.DiscoveryClientRouteLocator; import org.springframework.cloud.netflix.zuul.filters.discovery.DiscoveryClientRouteLocator;
import org.springframework.cloud.netflix.zuul.filters.route.SimpleHostRoutingFilter; import org.springframework.cloud.netflix.zuul.filters.route.SimpleHostRoutingFilter;
...@@ -59,7 +59,7 @@ public class CustomHostRoutingFilterTests { ...@@ -59,7 +59,7 @@ public class CustomHostRoutingFilterTests {
private DiscoveryClientRouteLocator routes; private DiscoveryClientRouteLocator routes;
@Autowired @Autowired
private RoutesEndpoint endpoint; private RoutesMvcEndpoint endpoint;
@Before @Before
public void setTestRequestcontext() { public void setTestRequestcontext() {
......
...@@ -22,7 +22,7 @@ import org.springframework.cloud.client.discovery.DiscoveryClient; ...@@ -22,7 +22,7 @@ import org.springframework.cloud.client.discovery.DiscoveryClient;
import org.springframework.cloud.netflix.ribbon.RibbonClient; import org.springframework.cloud.netflix.ribbon.RibbonClient;
import org.springframework.cloud.netflix.ribbon.StaticServerList; import org.springframework.cloud.netflix.ribbon.StaticServerList;
import org.springframework.cloud.netflix.zuul.EnableZuulProxy; 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.Bean;
import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpEntity; import org.springframework.http.HttpEntity;
...@@ -59,7 +59,7 @@ public class PatternServiceRouteMapperIntegrationTests { ...@@ -59,7 +59,7 @@ public class PatternServiceRouteMapperIntegrationTests {
private DiscoveryClientRouteLocator routes; private DiscoveryClientRouteLocator routes;
@Autowired @Autowired
private RoutesEndpoint endpoint; private RoutesMvcEndpoint endpoint;
@Before @Before
public void setTestRequestcontext() { public void setTestRequestcontext() {
......
...@@ -37,7 +37,7 @@ import org.springframework.boot.autoconfigure.web.ErrorAttributes; ...@@ -37,7 +37,7 @@ import org.springframework.boot.autoconfigure.web.ErrorAttributes;
import org.springframework.boot.autoconfigure.web.ErrorProperties; import org.springframework.boot.autoconfigure.web.ErrorProperties;
import org.springframework.boot.test.web.client.TestRestTemplate; import org.springframework.boot.test.web.client.TestRestTemplate;
import org.springframework.cloud.netflix.ribbon.StaticServerList; 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.Route;
import org.springframework.cloud.netflix.zuul.filters.ZuulProperties; import org.springframework.cloud.netflix.zuul.filters.ZuulProperties;
import org.springframework.cloud.netflix.zuul.filters.discovery.DiscoveryClientRouteLocator; import org.springframework.cloud.netflix.zuul.filters.discovery.DiscoveryClientRouteLocator;
...@@ -88,7 +88,7 @@ public abstract class ZuulProxyTestBase { ...@@ -88,7 +88,7 @@ public abstract class ZuulProxyTestBase {
protected DiscoveryClientRouteLocator routes; protected DiscoveryClientRouteLocator routes;
@Autowired @Autowired
protected RoutesEndpoint endpoint; protected RoutesMvcEndpoint endpoint;
@Autowired @Autowired
protected RibbonCommandFactory<?> ribbonCommandFactory; protected RibbonCommandFactory<?> ribbonCommandFactory;
......
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