From c108629311085c0a41c12ddac062d3dc552effdd Mon Sep 17 00:00:00 2001 From: HaiTao Zhang Date: Tue, 2 Jul 2019 18:18:29 -0700 Subject: [PATCH] Provide links for actuators at / when using a separate management port See gh-17418 --- ...ndpointManagementContextConfiguration.java | 8 +++- ...ndpointManagementContextConfiguration.java | 9 ++++- ...ndpointManagementContextConfiguration.java | 8 +++- .../actuate/endpoint/web/EndpointMapping.java | 14 ++++++- .../jersey/JerseyEndpointResourceFactory.java | 2 +- ...AbstractWebFluxEndpointHandlerMapping.java | 2 +- .../AbstractWebMvcEndpointHandlerMapping.java | 2 +- .../endpoint/web/EndpointMappingTests.java | 5 +++ ...entPortSampleActuatorApplicationTests.java | 37 +++++++++++++++++++ ...entPortSampleActuatorApplicationTests.java | 35 ++++++++++++++++++ ...entPortSampleActuatorApplicationTests.java | 35 ++++++++++++++++++ 11 files changed, 147 insertions(+), 10 deletions(-) create mode 100644 spring-boot-tests/spring-boot-smoke-tests/spring-boot-smoke-test-actuator/src/test/java/smoketest/actuator/ManagementDifferentPortSampleActuatorApplicationTests.java create mode 100644 spring-boot-tests/spring-boot-smoke-tests/spring-boot-smoke-test-jersey/src/test/java/smoketest/jersey/JerseyDifferentPortSampleActuatorApplicationTests.java create mode 100644 spring-boot-tests/spring-boot-smoke-tests/spring-boot-smoke-test-webflux/src/test/java/smoketest/webflux/WebFluxDifferentPortSampleActuatorApplicationTests.java diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/endpoint/web/jersey/JerseyWebEndpointManagementContextConfiguration.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/endpoint/web/jersey/JerseyWebEndpointManagementContextConfiguration.java index 729f2141f93..0d7612f4885 100644 --- a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/endpoint/web/jersey/JerseyWebEndpointManagementContextConfiguration.java +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/endpoint/web/jersey/JerseyWebEndpointManagementContextConfiguration.java @@ -26,6 +26,7 @@ import org.glassfish.jersey.server.ResourceConfig; import org.springframework.boot.actuate.autoconfigure.endpoint.web.WebEndpointProperties; import org.springframework.boot.actuate.autoconfigure.web.ManagementContextConfiguration; +import org.springframework.boot.actuate.autoconfigure.web.server.ManagementPortType; import org.springframework.boot.actuate.endpoint.ExposableEndpoint; import org.springframework.boot.actuate.endpoint.annotation.Endpoint; import org.springframework.boot.actuate.endpoint.web.EndpointLinksResolver; @@ -42,6 +43,7 @@ import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplicat import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication.Type; import org.springframework.boot.autoconfigure.jersey.ResourceConfigCustomizer; import org.springframework.context.annotation.Bean; +import org.springframework.core.env.Environment; /** * {@link ManagementContextConfiguration @ManagementContextConfiguration} for Jersey @@ -62,14 +64,16 @@ class JerseyWebEndpointManagementContextConfiguration { @Bean ResourceConfigCustomizer webEndpointRegistrar(WebEndpointsSupplier webEndpointsSupplier, ServletEndpointsSupplier servletEndpointsSupplier, EndpointMediaTypes endpointMediaTypes, - WebEndpointProperties webEndpointProperties) { + WebEndpointProperties webEndpointProperties, Environment environment) { List> allEndpoints = new ArrayList<>(); allEndpoints.addAll(webEndpointsSupplier.getEndpoints()); allEndpoints.addAll(servletEndpointsSupplier.getEndpoints()); return (resourceConfig) -> { JerseyEndpointResourceFactory resourceFactory = new JerseyEndpointResourceFactory(); String basePath = webEndpointProperties.getBasePath(); - EndpointMapping endpointMapping = new EndpointMapping(basePath); + ManagementPortType type = ManagementPortType.get(environment); + Boolean samePort = type == ManagementPortType.SAME; + EndpointMapping endpointMapping = new EndpointMapping(basePath, samePort); Collection webEndpoints = Collections .unmodifiableCollection(webEndpointsSupplier.getEndpoints()); resourceConfig.registerResources(new HashSet<>(resourceFactory.createEndpointResources(endpointMapping, diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/endpoint/web/reactive/WebFluxEndpointManagementContextConfiguration.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/endpoint/web/reactive/WebFluxEndpointManagementContextConfiguration.java index e0ef4be2ac0..8e5111571f7 100644 --- a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/endpoint/web/reactive/WebFluxEndpointManagementContextConfiguration.java +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/endpoint/web/reactive/WebFluxEndpointManagementContextConfiguration.java @@ -23,6 +23,7 @@ import java.util.List; import org.springframework.boot.actuate.autoconfigure.endpoint.web.CorsEndpointProperties; import org.springframework.boot.actuate.autoconfigure.endpoint.web.WebEndpointProperties; import org.springframework.boot.actuate.autoconfigure.web.ManagementContextConfiguration; +import org.springframework.boot.actuate.autoconfigure.web.server.ManagementPortType; import org.springframework.boot.actuate.endpoint.ExposableEndpoint; import org.springframework.boot.actuate.endpoint.annotation.Endpoint; import org.springframework.boot.actuate.endpoint.web.EndpointLinksResolver; @@ -40,6 +41,7 @@ import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplicat import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication.Type; import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.context.annotation.Bean; +import org.springframework.core.env.Environment; import org.springframework.http.server.reactive.HttpHandler; import org.springframework.web.reactive.DispatcherHandler; @@ -62,8 +64,11 @@ public class WebFluxEndpointManagementContextConfiguration { @ConditionalOnMissingBean public WebFluxEndpointHandlerMapping webEndpointReactiveHandlerMapping(WebEndpointsSupplier webEndpointsSupplier, ControllerEndpointsSupplier controllerEndpointsSupplier, EndpointMediaTypes endpointMediaTypes, - CorsEndpointProperties corsProperties, WebEndpointProperties webEndpointProperties) { - EndpointMapping endpointMapping = new EndpointMapping(webEndpointProperties.getBasePath()); + CorsEndpointProperties corsProperties, WebEndpointProperties webEndpointProperties, + Environment environment) { + ManagementPortType type = ManagementPortType.get(environment); + Boolean samePort = type == ManagementPortType.SAME; + EndpointMapping endpointMapping = new EndpointMapping(webEndpointProperties.getBasePath(), samePort); Collection endpoints = webEndpointsSupplier.getEndpoints(); List> allEndpoints = new ArrayList<>(); allEndpoints.addAll(endpoints); diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/endpoint/web/servlet/WebMvcEndpointManagementContextConfiguration.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/endpoint/web/servlet/WebMvcEndpointManagementContextConfiguration.java index 023d65c8504..910de4927b8 100644 --- a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/endpoint/web/servlet/WebMvcEndpointManagementContextConfiguration.java +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/endpoint/web/servlet/WebMvcEndpointManagementContextConfiguration.java @@ -23,6 +23,7 @@ import java.util.List; import org.springframework.boot.actuate.autoconfigure.endpoint.web.CorsEndpointProperties; import org.springframework.boot.actuate.autoconfigure.endpoint.web.WebEndpointProperties; import org.springframework.boot.actuate.autoconfigure.web.ManagementContextConfiguration; +import org.springframework.boot.actuate.autoconfigure.web.server.ManagementPortType; import org.springframework.boot.actuate.endpoint.ExposableEndpoint; import org.springframework.boot.actuate.endpoint.annotation.Endpoint; import org.springframework.boot.actuate.endpoint.web.EndpointLinksResolver; @@ -41,6 +42,7 @@ import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplicat import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication.Type; import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.context.annotation.Bean; +import org.springframework.core.env.Environment; import org.springframework.web.servlet.DispatcherServlet; /** @@ -63,13 +65,15 @@ public class WebMvcEndpointManagementContextConfiguration { public WebMvcEndpointHandlerMapping webEndpointServletHandlerMapping(WebEndpointsSupplier webEndpointsSupplier, ServletEndpointsSupplier servletEndpointsSupplier, ControllerEndpointsSupplier controllerEndpointsSupplier, EndpointMediaTypes endpointMediaTypes, CorsEndpointProperties corsProperties, - WebEndpointProperties webEndpointProperties) { + WebEndpointProperties webEndpointProperties, Environment environment) { List> allEndpoints = new ArrayList<>(); Collection webEndpoints = webEndpointsSupplier.getEndpoints(); allEndpoints.addAll(webEndpoints); allEndpoints.addAll(servletEndpointsSupplier.getEndpoints()); allEndpoints.addAll(controllerEndpointsSupplier.getEndpoints()); - EndpointMapping endpointMapping = new EndpointMapping(webEndpointProperties.getBasePath()); + ManagementPortType type = ManagementPortType.get(environment); + Boolean samePort = type == ManagementPortType.SAME; + EndpointMapping endpointMapping = new EndpointMapping(webEndpointProperties.getBasePath(), samePort); return new WebMvcEndpointHandlerMapping(endpointMapping, webEndpoints, endpointMediaTypes, corsProperties.toCorsConfiguration(), new EndpointLinksResolver(allEndpoints, webEndpointProperties.getBasePath())); diff --git a/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/web/EndpointMapping.java b/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/web/EndpointMapping.java index c106cb4025c..c70fb9e3c71 100644 --- a/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/web/EndpointMapping.java +++ b/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/web/EndpointMapping.java @@ -28,12 +28,20 @@ public class EndpointMapping { private final String path; + private final Boolean samePort; + /** * Creates a new {@code EndpointMapping} using the given {@code path}. * @param path the path + * @param samePort states true or false for same port as server */ - public EndpointMapping(String path) { + public EndpointMapping(String path, Boolean samePort) { this.path = normalizePath(path); + this.samePort = samePort; + } + + public EndpointMapping(String path) { + this(path, true); } /** @@ -44,6 +52,10 @@ public class EndpointMapping { return this.path; } + public boolean isSamePort() { + return this.samePort; + } + public String createSubPath(String path) { return this.path + normalizePath(path); } diff --git a/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/web/jersey/JerseyEndpointResourceFactory.java b/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/web/jersey/JerseyEndpointResourceFactory.java index 425f08c983c..54ee86e7f80 100644 --- a/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/web/jersey/JerseyEndpointResourceFactory.java +++ b/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/web/jersey/JerseyEndpointResourceFactory.java @@ -79,7 +79,7 @@ public class JerseyEndpointResourceFactory { List resources = new ArrayList<>(); endpoints.stream().flatMap((endpoint) -> endpoint.getOperations().stream()) .map((operation) -> createResource(endpointMapping, operation)).forEach(resources::add); - if (StringUtils.hasText(endpointMapping.getPath())) { + if (StringUtils.hasText(endpointMapping.getPath()) || !endpointMapping.isSamePort()) { Resource resource = createEndpointLinksResource(endpointMapping.getPath(), endpointMediaTypes, linksResolver); resources.add(resource); diff --git a/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/web/reactive/AbstractWebFluxEndpointHandlerMapping.java b/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/web/reactive/AbstractWebFluxEndpointHandlerMapping.java index 8f1fb5128f5..8e0799ee338 100644 --- a/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/web/reactive/AbstractWebFluxEndpointHandlerMapping.java +++ b/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/web/reactive/AbstractWebFluxEndpointHandlerMapping.java @@ -120,7 +120,7 @@ public abstract class AbstractWebFluxEndpointHandlerMapping extends RequestMappi registerMappingForOperation(endpoint, operation); } } - if (StringUtils.hasText(this.endpointMapping.getPath())) { + if (StringUtils.hasText(this.endpointMapping.getPath()) || !this.endpointMapping.isSamePort()) { registerLinksMapping(); } } diff --git a/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/web/servlet/AbstractWebMvcEndpointHandlerMapping.java b/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/web/servlet/AbstractWebMvcEndpointHandlerMapping.java index e579f19e3ae..92136dde17c 100644 --- a/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/web/servlet/AbstractWebMvcEndpointHandlerMapping.java +++ b/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/web/servlet/AbstractWebMvcEndpointHandlerMapping.java @@ -123,7 +123,7 @@ public abstract class AbstractWebMvcEndpointHandlerMapping extends RequestMappin registerMappingForOperation(endpoint, operation); } } - if (StringUtils.hasText(this.endpointMapping.getPath())) { + if (StringUtils.hasText(this.endpointMapping.getPath()) || !this.endpointMapping.isSamePort()) { registerLinksMapping(); } } diff --git a/spring-boot-project/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/endpoint/web/EndpointMappingTests.java b/spring-boot-project/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/endpoint/web/EndpointMappingTests.java index 243b4932a84..70758878583 100644 --- a/spring-boot-project/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/endpoint/web/EndpointMappingTests.java +++ b/spring-boot-project/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/endpoint/web/EndpointMappingTests.java @@ -77,4 +77,9 @@ class EndpointMappingTests { assertThat(new EndpointMapping("/test").createSubPath("one/")).isEqualTo("/test/one"); } + @Test + void setDifferentPort() { + assertThat(new EndpointMapping("/test", false).isSamePort()).isFalse(); + } + } diff --git a/spring-boot-tests/spring-boot-smoke-tests/spring-boot-smoke-test-actuator/src/test/java/smoketest/actuator/ManagementDifferentPortSampleActuatorApplicationTests.java b/spring-boot-tests/spring-boot-smoke-tests/spring-boot-smoke-test-actuator/src/test/java/smoketest/actuator/ManagementDifferentPortSampleActuatorApplicationTests.java new file mode 100644 index 00000000000..a9025f0ad02 --- /dev/null +++ b/spring-boot-tests/spring-boot-smoke-tests/spring-boot-smoke-test-actuator/src/test/java/smoketest/actuator/ManagementDifferentPortSampleActuatorApplicationTests.java @@ -0,0 +1,37 @@ +package smoketest.actuator; + +import org.junit.jupiter.api.Test; +import org.springframework.boot.actuate.autoconfigure.web.server.LocalManagementPort; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.boot.test.web.client.TestRestTemplate; +import org.springframework.boot.web.server.LocalServerPort; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; + +import static org.assertj.core.api.Assertions.assertThat; + +@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, + properties = { "management.endpoints.web.base-path=/","management.server.port=0"}) +public class ManagementDifferentPortSampleActuatorApplicationTests { + + @LocalServerPort + private int port; + + @LocalManagementPort + private int managementPort; + + @Test + void testDifferentServerPort(){ + if(this.managementPort != this.port) { + ResponseEntity entity = new TestRestTemplate("user", getPassword()) + .getForEntity("http://localhost:" + this.managementPort + "/", String.class); + assertThat(entity.getStatusCode()).isEqualTo(HttpStatus.OK); + assertThat(entity.getBody()).contains("\"_links\""); + } + } + + private String getPassword(){ + return "password"; + } + +} diff --git a/spring-boot-tests/spring-boot-smoke-tests/spring-boot-smoke-test-jersey/src/test/java/smoketest/jersey/JerseyDifferentPortSampleActuatorApplicationTests.java b/spring-boot-tests/spring-boot-smoke-tests/spring-boot-smoke-test-jersey/src/test/java/smoketest/jersey/JerseyDifferentPortSampleActuatorApplicationTests.java new file mode 100644 index 00000000000..8e1577aee20 --- /dev/null +++ b/spring-boot-tests/spring-boot-smoke-tests/spring-boot-smoke-test-jersey/src/test/java/smoketest/jersey/JerseyDifferentPortSampleActuatorApplicationTests.java @@ -0,0 +1,35 @@ +package smoketest.jersey; + +import org.junit.jupiter.api.Test; +import org.springframework.boot.actuate.autoconfigure.web.server.LocalManagementPort; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.boot.test.web.client.TestRestTemplate; +import org.springframework.boot.web.server.LocalServerPort; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; + +import static org.assertj.core.api.Assertions.assertThat; + +@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, properties = {"management.server.port=0","management.endpoints.web.base-path=/"}) +public class JerseyDifferentPortSampleActuatorApplicationTests { + + @LocalManagementPort + private int managementPort; + + @LocalServerPort + private int port; + + @Test + void testDifferentServerPort(){ + if(this.managementPort != this.port) { + ResponseEntity entity = new TestRestTemplate("user", getPassword()) + .getForEntity("http://localhost:" + this.managementPort + "/", String.class); + assertThat(entity.getStatusCode()).isEqualTo(HttpStatus.OK); + assertThat(entity.getBody()).contains("\"_links\""); + } + } + + private String getPassword(){ + return "password"; + } +} diff --git a/spring-boot-tests/spring-boot-smoke-tests/spring-boot-smoke-test-webflux/src/test/java/smoketest/webflux/WebFluxDifferentPortSampleActuatorApplicationTests.java b/spring-boot-tests/spring-boot-smoke-tests/spring-boot-smoke-test-webflux/src/test/java/smoketest/webflux/WebFluxDifferentPortSampleActuatorApplicationTests.java new file mode 100644 index 00000000000..e7fe948190f --- /dev/null +++ b/spring-boot-tests/spring-boot-smoke-tests/spring-boot-smoke-test-webflux/src/test/java/smoketest/webflux/WebFluxDifferentPortSampleActuatorApplicationTests.java @@ -0,0 +1,35 @@ +package smoketest.webflux; + +import org.junit.jupiter.api.Test; +import org.springframework.boot.actuate.autoconfigure.web.server.LocalManagementPort; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.boot.test.web.client.TestRestTemplate; +import org.springframework.boot.web.server.LocalServerPort; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; + +import static org.assertj.core.api.Assertions.assertThat; + +@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, properties = {"management.server.port=0","management.endpoints.web.base-path=/"}) +public class WebFluxDifferentPortSampleActuatorApplicationTests { + + @LocalManagementPort + private int managementPort; + + @LocalServerPort + private int port; + + @Test + void testDifferentServerPort(){ + if(this.managementPort != this.port) { + ResponseEntity entity = new TestRestTemplate("user", getPassword()) + .getForEntity("http://localhost:" + this.managementPort + "/", String.class); + assertThat(entity.getStatusCode()).isEqualTo(HttpStatus.OK); + assertThat(entity.getBody()).contains("\"_links\""); + } + } + + private String getPassword(){ + return "password"; + } +}