Drop status endpoint

Drop the status endpoint and merge functionality back into the health
endpoint. The `management.endpoint.health.show-details` property can
be used to change if full details, or just the status is displayed.

Fixes gh-11113
pull/11128/head
Phillip Webb 7 years ago
parent d99625fa78
commit 31025d9f6c

@ -66,5 +66,4 @@ include::endpoints/prometheus.adoc[leveloffset=+1]
include::endpoints/scheduledtasks.adoc[leveloffset=+1]
include::endpoints/sessions.adoc[leveloffset=+1]
include::endpoints/shutdown.adoc[leveloffset=+1]
include::endpoints/status.adoc[leveloffset=+1]
include::endpoints/threaddump.adoc[leveloffset=+1]

@ -105,7 +105,7 @@ public class WebEndpointAutoConfiguration {
public ExposeExcludePropertyEndpointFilter<WebOperation> webIncludeExcludePropertyEndpointFilter() {
return new ExposeExcludePropertyEndpointFilter<>(
WebAnnotationEndpointDiscoverer.class, this.properties.getExpose(),
this.properties.getExclude(), "info", "status");
this.properties.getExclude(), "info", "health");
}
}

@ -27,9 +27,9 @@ import org.springframework.boot.actuate.health.HealthEndpoint;
import org.springframework.boot.actuate.health.HealthIndicator;
import org.springframework.boot.actuate.health.OrderedHealthAggregator;
import org.springframework.boot.actuate.health.ReactiveHealthIndicator;
import org.springframework.boot.actuate.health.StatusEndpoint;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@ -44,6 +44,7 @@ import org.springframework.util.ClassUtils;
* @since 2.0.0
*/
@Configuration
@EnableConfigurationProperties(HealthEndpointProperties.class)
public class HealthEndpointAutoConfiguration {
private final HealthIndicator healthIndicator;
@ -69,15 +70,8 @@ public class HealthEndpointAutoConfiguration {
@Bean
@ConditionalOnMissingBean
@ConditionalOnEnabledEndpoint
public HealthEndpoint healthEndpoint() {
return new HealthEndpoint(this.healthIndicator);
}
@Bean
@ConditionalOnMissingBean
@ConditionalOnEnabledEndpoint
public StatusEndpoint statusEndpoint() {
return new StatusEndpoint(this.healthIndicator);
public HealthEndpoint healthEndpoint(HealthEndpointProperties properties) {
return new HealthEndpoint(this.healthIndicator, properties.isShowDetails());
}
private static class ReactiveHealthIndicators {

@ -0,0 +1,43 @@
/*
* Copyright 2012-2017 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.boot.actuate.autoconfigure.health;
import org.springframework.boot.actuate.health.HealthEndpoint;
import org.springframework.boot.context.properties.ConfigurationProperties;
/**
* Configuration properties for {@link HealthEndpoint}.
*
* @author Phillip Webb
*/
@ConfigurationProperties("management.endpoint.health")
public class HealthEndpointProperties {
/**
* Whether to show full health details instead of just the status.
*/
private boolean showDetails;
public boolean isShowDetails() {
return this.showDetails;
}
public void setShowDetails(boolean showDetails) {
this.showDetails = showDetails;
}
}

@ -31,9 +31,6 @@ import org.springframework.boot.actuate.health.HealthStatusHttpMapper;
import org.springframework.boot.actuate.health.OrderedHealthAggregator;
import org.springframework.boot.actuate.health.ReactiveHealthEndpointWebExtension;
import org.springframework.boot.actuate.health.ReactiveHealthIndicator;
import org.springframework.boot.actuate.health.ReactiveStatusEndpointWebExtension;
import org.springframework.boot.actuate.health.StatusEndpoint;
import org.springframework.boot.actuate.health.StatusEndpointWebExtension;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication;
@ -85,19 +82,10 @@ public class HealthWebEndpointManagementContextConfiguration {
@ConditionalOnEnabledEndpoint
@ConditionalOnBean(HealthEndpoint.class)
public ReactiveHealthEndpointWebExtension reactiveHealthEndpointWebExtension(
HealthStatusHttpMapper healthStatusHttpMapper) {
HealthStatusHttpMapper healthStatusHttpMapper,
HealthEndpointProperties properties) {
return new ReactiveHealthEndpointWebExtension(this.reactiveHealthIndicator,
healthStatusHttpMapper);
}
@Bean
@ConditionalOnMissingBean
@ConditionalOnEnabledEndpoint
@ConditionalOnBean(StatusEndpoint.class)
public ReactiveStatusEndpointWebExtension reactiveStatusEndpointWebExtension(
HealthStatusHttpMapper healthStatusHttpMapper) {
return new ReactiveStatusEndpointWebExtension(this.reactiveHealthIndicator,
healthStatusHttpMapper);
healthStatusHttpMapper, properties.isShowDetails());
}
}
@ -115,15 +103,6 @@ public class HealthWebEndpointManagementContextConfiguration {
return new HealthEndpointWebExtension(delegate, healthStatusHttpMapper);
}
@Bean
@ConditionalOnMissingBean
@ConditionalOnEnabledEndpoint
@ConditionalOnBean(StatusEndpoint.class)
public StatusEndpointWebExtension statusEndpointWebExtension(
StatusEndpoint delegate, HealthStatusHttpMapper healthStatusHttpMapper) {
return new StatusEndpointWebExtension(delegate, healthStatusHttpMapper);
}
}
}

@ -57,7 +57,8 @@ public class HealthEndpointDocumentationTests extends AbstractEndpointDocumentat
fieldWithPath("status").description(
"Overall status of the application."),
fieldWithPath("details")
.description("Details of the health of the application."),
.description("Details of the health of the application "
+ "(only included when `management.endpoint.health.show-details` is `true`)."),
fieldWithPath("details.*.status").description(
"Status of a specific part of the application."),
subsectionWithPath("details.*.details").description(
@ -73,7 +74,7 @@ public class HealthEndpointDocumentationTests extends AbstractEndpointDocumentat
@Bean
public HealthEndpoint endpoint(Map<String, HealthIndicator> healthIndicators) {
return new HealthEndpoint(new CompositeHealthIndicator(
new OrderedHealthAggregator(), healthIndicators));
new OrderedHealthAggregator(), healthIndicators), true);
}
@Bean

@ -1,82 +0,0 @@
/*
* Copyright 2012-2017 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.boot.actuate.autoconfigure.endpoint.web.documentation;
import java.io.File;
import java.util.Map;
import javax.sql.DataSource;
import org.junit.Test;
import org.springframework.boot.actuate.health.CompositeHealthIndicator;
import org.springframework.boot.actuate.health.HealthIndicator;
import org.springframework.boot.actuate.health.OrderedHealthAggregator;
import org.springframework.boot.actuate.health.StatusEndpoint;
import org.springframework.boot.actuate.jdbc.DataSourceHealthIndicator;
import org.springframework.boot.actuate.system.DiskSpaceHealthIndicator;
import org.springframework.boot.autoconfigure.ImportAutoConfiguration;
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import static org.springframework.restdocs.mockmvc.MockMvcRestDocumentation.document;
import static org.springframework.restdocs.payload.PayloadDocumentation.fieldWithPath;
import static org.springframework.restdocs.payload.PayloadDocumentation.responseFields;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
/**
* Tests for generating documentation describing the {@link StatusEndpoint}.
*
* @author Andy Wilkinson
*/
public class StatusEndpointDocumentationTests extends AbstractEndpointDocumentationTests {
@Test
public void health() throws Exception {
this.mockMvc.perform(get("/application/status")).andExpect(status().isOk())
.andDo(document("status", responseFields(fieldWithPath("status")
.description("Overall status of the application."))));
}
@Configuration
@Import(BaseDocumentationConfiguration.class)
@ImportAutoConfiguration(DataSourceAutoConfiguration.class)
static class TestConfiguration {
@Bean
public StatusEndpoint endpoint(Map<String, HealthIndicator> healthIndicators) {
return new StatusEndpoint(new CompositeHealthIndicator(
new OrderedHealthAggregator(), healthIndicators));
}
@Bean
public DiskSpaceHealthIndicator diskSpaceHealthIndicator() {
return new DiskSpaceHealthIndicator(new File("."), 1024 * 1024 * 10);
}
@Bean
public DataSourceHealthIndicator dataSourceHealthIndicator(
DataSource dataSource) {
return new DataSourceHealthIndicator(dataSource);
}
}
}

@ -24,7 +24,6 @@ import org.springframework.boot.actuate.health.HealthEndpoint;
import org.springframework.boot.actuate.health.HealthIndicator;
import org.springframework.boot.actuate.health.ReactiveHealthIndicator;
import org.springframework.boot.actuate.health.Status;
import org.springframework.boot.actuate.health.StatusEndpoint;
import org.springframework.boot.autoconfigure.AutoConfigurations;
import org.springframework.boot.test.context.runner.ApplicationContextRunner;
import org.springframework.context.annotation.Bean;
@ -48,9 +47,25 @@ public class HealthEndpointAutoConfigurationTests {
.withConfiguration(
AutoConfigurations.of(HealthEndpointAutoConfiguration.class));
@Test
public void healthEndpointShowDetailsDefault() {
this.contextRunner
.withUserConfiguration(ReactiveHealthIndicatorConfiguration.class)
.run((context) -> {
ReactiveHealthIndicator indicator = context.getBean(
"reactiveHealthIndicator", ReactiveHealthIndicator.class);
verify(indicator, times(0)).health();
Health health = context.getBean(HealthEndpoint.class).health();
assertThat(health.getStatus()).isEqualTo(Status.UP);
assertThat(health.getDetails()).isEmpty();
verify(indicator, times(1)).health();
});
}
@Test
public void healthEndpointAdaptReactiveHealthIndicator() {
this.contextRunner
.withPropertyValues("management.endpoint.health.show-details=true")
.withUserConfiguration(ReactiveHealthIndicatorConfiguration.class)
.run((context) -> {
ReactiveHealthIndicator indicator = context.getBean(
@ -65,8 +80,11 @@ public class HealthEndpointAutoConfigurationTests {
@Test
public void healthEndpointMergeRegularAndReactive() {
this.contextRunner.withUserConfiguration(HealthIndicatorConfiguration.class,
ReactiveHealthIndicatorConfiguration.class).run((context) -> {
this.contextRunner
.withPropertyValues("management.endpoint.health.show-details=true")
.withUserConfiguration(HealthIndicatorConfiguration.class,
ReactiveHealthIndicatorConfiguration.class)
.run((context) -> {
HealthIndicator indicator = context.getBean("simpleHealthIndicator",
HealthIndicator.class);
ReactiveHealthIndicator reactiveHealthIndicator = context.getBean(
@ -82,22 +100,6 @@ public class HealthEndpointAutoConfigurationTests {
});
}
@Test
public void runShouldHaveStatusEndpointBeanEvenIfDefaultIsDisabled() {
// FIXME
this.contextRunner.withPropertyValues("management.endpoint.default.enabled:false")
.run((context) -> assertThat(context)
.hasSingleBean(StatusEndpoint.class));
}
@Test
public void runWhenEnabledPropertyIsFalseShouldNotHaveStatusEndpointBean()
throws Exception {
this.contextRunner.withPropertyValues("management.endpoint.status.enabled:false")
.run((context) -> assertThat(context)
.doesNotHaveBean(StatusEndpoint.class));
}
@Configuration
static class HealthIndicatorConfiguration {

@ -22,7 +22,6 @@ import org.junit.Test;
import org.springframework.boot.actuate.health.HealthStatusHttpMapper;
import org.springframework.boot.actuate.health.ReactiveHealthEndpointWebExtension;
import org.springframework.boot.actuate.health.ReactiveStatusEndpointWebExtension;
import org.springframework.boot.test.context.runner.ReactiveWebApplicationContextRunner;
import org.springframework.test.util.ReflectionTestUtils;
@ -46,7 +45,6 @@ public class HealthWebEndpointReactiveManagementContextConfigurationTests {
@Test
public void runShouldCreateExtensionBeans() throws Exception {
this.contextRunner.run((context) -> assertThat(context)
.hasSingleBean(ReactiveStatusEndpointWebExtension.class)
.hasSingleBean(ReactiveHealthEndpointWebExtension.class));
}
@ -58,19 +56,10 @@ public class HealthWebEndpointReactiveManagementContextConfigurationTests {
.doesNotHaveBean(ReactiveHealthEndpointWebExtension.class));
}
@Test
public void runWhenStatusEndpointIsDisabledShouldNotCreateExtensionBeans()
throws Exception {
this.contextRunner.withPropertyValues("management.endpoint.status.enabled:false")
.run((context) -> assertThat(context)
.doesNotHaveBean(ReactiveStatusEndpointWebExtension.class));
}
@Test
public void runWithCustomHealthMappingShouldMapStatusCode() throws Exception {
this.contextRunner
.withPropertyValues(
"management.health.status.http-mapping.CUSTOM=500")
.withPropertyValues("management.health.status.http-mapping.CUSTOM=500")
.run((context) -> {
Object extension = context
.getBean(ReactiveHealthEndpointWebExtension.class);
@ -83,21 +72,4 @@ public class HealthWebEndpointReactiveManagementContextConfigurationTests {
});
}
@Test
public void runWithCustomStatusMappingShouldMapStatusCode() throws Exception {
this.contextRunner
.withPropertyValues(
"management.health.status.http-mapping.CUSTOM=500")
.run((context) -> {
Object extension = context
.getBean(ReactiveStatusEndpointWebExtension.class);
HealthStatusHttpMapper mapper = (HealthStatusHttpMapper) ReflectionTestUtils
.getField(extension, "statusHttpMapper");
Map<String, Integer> statusMappings = mapper.getStatusMapping();
assertThat(statusMappings).containsEntry("DOWN", 503);
assertThat(statusMappings).containsEntry("OUT_OF_SERVICE", 503);
assertThat(statusMappings).containsEntry("CUSTOM", 500);
});
}
}

@ -22,7 +22,6 @@ import org.junit.Test;
import org.springframework.boot.actuate.health.HealthEndpointWebExtension;
import org.springframework.boot.actuate.health.HealthStatusHttpMapper;
import org.springframework.boot.actuate.health.StatusEndpointWebExtension;
import org.springframework.boot.test.context.runner.WebApplicationContextRunner;
import org.springframework.test.util.ReflectionTestUtils;
@ -46,7 +45,6 @@ public class HealthWebEndpointServletManagementContextConfigurationTests {
@Test
public void runShouldCreateExtensionBeans() throws Exception {
this.contextRunner.run((context) -> assertThat(context)
.hasSingleBean(StatusEndpointWebExtension.class)
.hasSingleBean(HealthEndpointWebExtension.class));
}
@ -58,14 +56,6 @@ public class HealthWebEndpointServletManagementContextConfigurationTests {
.doesNotHaveBean(HealthEndpointWebExtension.class));
}
@Test
public void runWhenStatusEndpointIsDisabledShouldNotCreateExtensionBeans()
throws Exception {
this.contextRunner.withPropertyValues("management.endpoint.status.enabled:false")
.run((context) -> assertThat(context)
.doesNotHaveBean(StatusEndpointWebExtension.class));
}
@Test
public void runWithCustomHealthMappingShouldMapStatusCode() throws Exception {
this.contextRunner
@ -81,19 +71,4 @@ public class HealthWebEndpointServletManagementContextConfigurationTests {
});
}
@Test
public void runWithCustomStatusMappingShouldMapStatusCode() throws Exception {
this.contextRunner
.withPropertyValues("management.health.status.http-mapping.CUSTOM=500")
.run((context) -> {
Object extension = context.getBean(StatusEndpointWebExtension.class);
HealthStatusHttpMapper mapper = (HealthStatusHttpMapper) ReflectionTestUtils
.getField(extension, "statusHttpMapper");
Map<String, Integer> statusMappings = mapper.getStatusMapping();
assertThat(statusMappings).containsEntry("DOWN", 503);
assertThat(statusMappings).containsEntry("OUT_OF_SERVICE", 503);
assertThat(statusMappings).containsEntry("CUSTOM", 500);
});
}
}

@ -55,7 +55,7 @@ public class JmxEndpointIntegrationTests {
MBeanServer mBeanServer = context.getBean(MBeanServer.class);
checkEndpointMBeans(mBeanServer,
new String[] { "beans", "conditions", "configprops", "env", "health",
"info", "mappings", "status", "threaddump", "trace" },
"info", "mappings", "threaddump", "trace" },
new String[] { "shutdown" });
});
}

@ -21,8 +21,6 @@ import org.junit.Test;
import org.springframework.boot.SpringBootConfiguration;
import org.springframework.boot.actuate.health.HealthEndpointWebExtension;
import org.springframework.boot.actuate.health.ReactiveHealthEndpointWebExtension;
import org.springframework.boot.actuate.health.ReactiveStatusEndpointWebExtension;
import org.springframework.boot.actuate.health.StatusEndpointWebExtension;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.cassandra.CassandraAutoConfiguration;
import org.springframework.boot.autoconfigure.data.cassandra.CassandraDataAutoConfiguration;
@ -62,24 +60,12 @@ public class WebEndpointsAutoConfigurationIntegrationTests {
.hasSingleBean(HealthEndpointWebExtension.class));
}
@Test
public void statusEndpointWebExtensionIsAutoConfigured() {
servletWebRunner().run((context) -> assertThat(context)
.hasSingleBean(StatusEndpointWebExtension.class));
}
@Test
public void healthEndpointReactiveWebExtensionIsAutoConfigured() {
reactiveWebRunner().run((context) -> assertThat(context)
.hasSingleBean(ReactiveHealthEndpointWebExtension.class));
}
@Test
public void statusEndpointReactiveWebExtensionIsAutoConfigured() {
reactiveWebRunner().run((context) -> assertThat(context)
.hasSingleBean(ReactiveStatusEndpointWebExtension.class));
}
private WebApplicationContextRunner servletWebRunner() {
return new WebApplicationContextRunner()
.withConfiguration(

@ -69,11 +69,10 @@ public class WebMvcEndpointExposureIntegrationTests {
assertThat(isExposed(mvc, HttpMethod.GET, "conditions")).isFalse();
assertThat(isExposed(mvc, HttpMethod.GET, "configprops")).isFalse();
assertThat(isExposed(mvc, HttpMethod.GET, "env")).isFalse();
assertThat(isExposed(mvc, HttpMethod.GET, "health")).isFalse();
assertThat(isExposed(mvc, HttpMethod.GET, "health")).isTrue();
assertThat(isExposed(mvc, HttpMethod.GET, "info")).isTrue();
assertThat(isExposed(mvc, HttpMethod.GET, "mappings")).isFalse();
assertThat(isExposed(mvc, HttpMethod.POST, "shutdown")).isFalse();
assertThat(isExposed(mvc, HttpMethod.GET, "status")).isTrue();
assertThat(isExposed(mvc, HttpMethod.GET, "threaddump")).isFalse();
assertThat(isExposed(mvc, HttpMethod.GET, "trace")).isFalse();
});
@ -93,7 +92,6 @@ public class WebMvcEndpointExposureIntegrationTests {
assertThat(isExposed(mvc, HttpMethod.GET, "info")).isTrue();
assertThat(isExposed(mvc, HttpMethod.GET, "mappings")).isTrue();
assertThat(isExposed(mvc, HttpMethod.POST, "shutdown")).isFalse();
assertThat(isExposed(mvc, HttpMethod.GET, "status")).isTrue();
assertThat(isExposed(mvc, HttpMethod.GET, "threaddump")).isTrue();
assertThat(isExposed(mvc, HttpMethod.GET, "trace")).isTrue();
});
@ -113,7 +111,6 @@ public class WebMvcEndpointExposureIntegrationTests {
assertThat(isExposed(mvc, HttpMethod.GET, "info")).isFalse();
assertThat(isExposed(mvc, HttpMethod.GET, "mappings")).isFalse();
assertThat(isExposed(mvc, HttpMethod.POST, "shutdown")).isFalse();
assertThat(isExposed(mvc, HttpMethod.GET, "status")).isFalse();
assertThat(isExposed(mvc, HttpMethod.GET, "threaddump")).isFalse();
assertThat(isExposed(mvc, HttpMethod.GET, "trace")).isFalse();
});
@ -134,7 +131,6 @@ public class WebMvcEndpointExposureIntegrationTests {
assertThat(isExposed(mvc, HttpMethod.GET, "info")).isTrue();
assertThat(isExposed(mvc, HttpMethod.GET, "mappings")).isTrue();
assertThat(isExposed(mvc, HttpMethod.POST, "shutdown")).isFalse();
assertThat(isExposed(mvc, HttpMethod.GET, "status")).isTrue();
assertThat(isExposed(mvc, HttpMethod.GET, "threaddump")).isTrue();
assertThat(isExposed(mvc, HttpMethod.GET, "trace")).isTrue();
});

@ -20,7 +20,7 @@ import org.springframework.boot.actuate.endpoint.annotation.Endpoint;
import org.springframework.boot.actuate.endpoint.annotation.ReadOperation;
/**
* {@link Endpoint} to expose application health.
* {@link Endpoint} to expose application health information.
*
* @author Dave Syer
* @author Christian Dupuis
@ -32,17 +32,25 @@ public class HealthEndpoint {
private final HealthIndicator healthIndicator;
private final boolean showDetails;
/**
* Create a new {@link HealthEndpoint} instance.
* @param healthIndicator the health indicator
* @param showDetails if full details should be returned instead of just the status
*/
public HealthEndpoint(HealthIndicator healthIndicator) {
public HealthEndpoint(HealthIndicator healthIndicator, boolean showDetails) {
this.healthIndicator = healthIndicator;
this.showDetails = showDetails;
}
@ReadOperation
public Health health() {
return this.healthIndicator.health();
Health health = this.healthIndicator.health();
if (this.showDetails) {
return health;
}
return Health.status(health.getStatus()).build();
}
}

@ -35,16 +35,22 @@ public class ReactiveHealthEndpointWebExtension {
private final HealthStatusHttpMapper statusHttpMapper;
private final boolean showDetails;
public ReactiveHealthEndpointWebExtension(ReactiveHealthIndicator delegate,
HealthStatusHttpMapper statusHttpMapper) {
HealthStatusHttpMapper statusHttpMapper, boolean showDetails) {
this.delegate = delegate;
this.statusHttpMapper = statusHttpMapper;
this.showDetails = showDetails;
}
@ReadOperation
public Mono<WebEndpointResponse<Health>> health() {
return this.delegate.health().map((health) -> {
Integer status = this.statusHttpMapper.mapStatus(health.getStatus());
if (!this.showDetails) {
health = Health.status(health.getStatus()).build();
}
return new WebEndpointResponse<>(health, status);
});
}

@ -1,53 +0,0 @@
/*
* Copyright 2012-2017 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.boot.actuate.health;
import reactor.core.publisher.Mono;
import org.springframework.boot.actuate.endpoint.annotation.ReadOperation;
import org.springframework.boot.actuate.endpoint.web.WebEndpointResponse;
import org.springframework.boot.actuate.endpoint.web.annotation.EndpointWebExtension;
/**
* Reactive {@link EndpointWebExtension} for the {@link StatusEndpoint}.
*
* @author Stephane Nicoll
* @since 2.0.0
*/
@EndpointWebExtension(endpoint = StatusEndpoint.class)
public class ReactiveStatusEndpointWebExtension {
private final ReactiveHealthIndicator delegate;
private final HealthStatusHttpMapper statusHttpMapper;
public ReactiveStatusEndpointWebExtension(ReactiveHealthIndicator delegate,
HealthStatusHttpMapper statusHttpMapper) {
this.delegate = delegate;
this.statusHttpMapper = statusHttpMapper;
}
@ReadOperation
public Mono<WebEndpointResponse<Health>> health() {
return this.delegate.health().map((health) -> {
Integer status = this.statusHttpMapper.mapStatus(health.getStatus());
return new WebEndpointResponse<>(Health.status(health.getStatus()).build(),
status);
});
}
}

@ -1,46 +0,0 @@
/*
* Copyright 2012-2017 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.boot.actuate.health;
import org.springframework.boot.actuate.endpoint.annotation.Endpoint;
import org.springframework.boot.actuate.endpoint.annotation.ReadOperation;
/**
* {@link Endpoint} to expose application {@link Status}.
*
* @author Stephane Nicoll
* @since 2.0.0
*/
@Endpoint(id = "status")
public class StatusEndpoint {
private final HealthIndicator healthIndicator;
/**
* Create a new {@link StatusEndpoint} instance.
* @param healthIndicator the health indicator
*/
public StatusEndpoint(HealthIndicator healthIndicator) {
this.healthIndicator = healthIndicator;
}
@ReadOperation
public Health health() {
return Health.status(this.healthIndicator.health().getStatus()).build();
}
}

@ -1,49 +0,0 @@
/*
* Copyright 2012-2017 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.boot.actuate.health;
import org.springframework.boot.actuate.endpoint.annotation.ReadOperation;
import org.springframework.boot.actuate.endpoint.web.WebEndpointResponse;
import org.springframework.boot.actuate.endpoint.web.annotation.EndpointWebExtension;
/**
* {@link EndpointWebExtension} for the {@link StatusEndpoint}.
*
* @author Stephane Nicoll
* @since 2.0.0
*/
@EndpointWebExtension(endpoint = StatusEndpoint.class)
public class StatusEndpointWebExtension {
private final StatusEndpoint delegate;
private final HealthStatusHttpMapper statusHttpMapper;
public StatusEndpointWebExtension(StatusEndpoint delegate,
HealthStatusHttpMapper statusHttpMapper) {
this.delegate = delegate;
this.statusHttpMapper = statusHttpMapper;
}
@ReadOperation
public WebEndpointResponse<Health> getHealth() {
Health health = this.delegate.health();
Integer status = this.statusHttpMapper.mapStatus(health.getStatus());
return new WebEndpointResponse<>(health, status);
}
}

@ -41,7 +41,7 @@ public class HealthEndpointTests {
healthIndicators.put("upAgain", () -> new Health.Builder().status(Status.UP)
.withDetail("second", "2").build());
HealthEndpoint endpoint = new HealthEndpoint(
createHealthIndicator(healthIndicators));
createHealthIndicator(healthIndicators), true);
Health health = endpoint.health();
assertThat(health.getStatus()).isEqualTo(Status.UP);
assertThat(health.getDetails()).containsOnlyKeys("up", "upAgain");
@ -51,6 +51,20 @@ public class HealthEndpointTests {
assertThat(upAgainHealth.getDetails()).containsOnly(entry("second", "2"));
}
@Test
public void onlyStatusIsExposed() {
Map<String, HealthIndicator> healthIndicators = new HashMap<>();
healthIndicators.put("up", () -> new Health.Builder().status(Status.UP)
.withDetail("first", "1").build());
healthIndicators.put("upAgain", () -> new Health.Builder().status(Status.UP)
.withDetail("second", "2").build());
HealthEndpoint endpoint = new HealthEndpoint(
createHealthIndicator(healthIndicators), false);
Health health = endpoint.health();
assertThat(health.getStatus()).isEqualTo(Status.UP);
assertThat(health.getDetails()).isEmpty();
}
private HealthIndicator createHealthIndicator(
Map<String, HealthIndicator> healthIndicators) {
return new CompositeHealthIndicatorFactory()

@ -67,7 +67,8 @@ public class HealthEndpointWebIntegrationTests {
Map<String, HealthIndicator> healthIndicators) {
return new HealthEndpoint(
new CompositeHealthIndicatorFactory().createHealthIndicator(
new OrderedHealthAggregator(), healthIndicators));
new OrderedHealthAggregator(), healthIndicators),
true);
}
@Bean

@ -1,53 +0,0 @@
/*
* Copyright 2012-2017 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.boot.actuate.health;
import java.util.HashMap;
import java.util.Map;
import org.junit.Test;
import static org.assertj.core.api.Assertions.assertThat;
/**
* Tests for {@link StatusEndpoint}.
*
* @author Stephane Nicoll
*/
public class StatusEndpointTests {
@Test
public void onlyStatusIsExposed() {
Map<String, HealthIndicator> healthIndicators = new HashMap<>();
healthIndicators.put("up", () -> new Health.Builder().status(Status.UP)
.withDetail("first", "1").build());
healthIndicators.put("upAgain", () -> new Health.Builder().status(Status.UP)
.withDetail("second", "2").build());
StatusEndpoint endpoint = new StatusEndpoint(
createHealthIndicator(healthIndicators));
Health health = endpoint.health();
assertThat(health.getStatus()).isEqualTo(Status.UP);
assertThat(health.getDetails()).isEmpty();
}
private HealthIndicator createHealthIndicator(
Map<String, HealthIndicator> healthIndicators) {
return new CompositeHealthIndicatorFactory()
.createHealthIndicator(new OrderedHealthAggregator(), healthIndicators);
}
}

@ -1,105 +0,0 @@
/*
* Copyright 2012-2017 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.boot.actuate.health;
import java.util.Map;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.boot.actuate.endpoint.web.test.WebEndpointRunners;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpStatus;
import org.springframework.test.web.reactive.server.WebTestClient;
/**
* Integration tests for {@link StatusEndpoint} and {@link StatusEndpointWebExtension}
* exposed by Jersey, Spring MVC, and WebFlux.
*
* @author Stephane Nicoll
*/
@RunWith(WebEndpointRunners.class)
public class StatusEndpointWebIntegrationTests {
private static WebTestClient client;
private static ConfigurableApplicationContext context;
@Test
public void whenStatusIsUp200ResponseIsReturned() throws Exception {
client.get().uri("/application/status").exchange().expectStatus().isOk()
.expectBody().json("{\"status\":\"UP\"}");
}
@Test
public void whenStatusIsDown503ResponseIsReturned() throws Exception {
context.getBean("alphaHealthIndicator", TestHealthIndicator.class)
.setHealth(Health.down().build());
client.get().uri("/application/status").exchange().expectStatus()
.isEqualTo(HttpStatus.SERVICE_UNAVAILABLE).expectBody()
.json("{\"status\":\"DOWN\"}");
}
@Configuration
public static class TestConfiguration {
@Bean
public StatusEndpoint statusEndpoint(
Map<String, HealthIndicator> healthIndicators) {
return new StatusEndpoint(
new CompositeHealthIndicatorFactory().createHealthIndicator(
new OrderedHealthAggregator(), healthIndicators));
}
@Bean
public StatusEndpointWebExtension statusWebEndpointExtension(
StatusEndpoint delegate) {
return new StatusEndpointWebExtension(delegate, new HealthStatusHttpMapper());
}
@Bean
public TestHealthIndicator alphaHealthIndicator() {
return new TestHealthIndicator();
}
@Bean
public TestHealthIndicator bravoHealthIndicator() {
return new TestHealthIndicator();
}
}
private static class TestHealthIndicator implements HealthIndicator {
private Health health = Health.up().build();
@Override
public Health health() {
Health result = this.health;
this.health = Health.up().build();
return result;
}
void setHealth(Health health) {
this.health = health;
}
}
}

@ -1165,6 +1165,7 @@ content into your application. Rather, pick only the properties that you need.
# HEALTH ENDPOINT ({sc-spring-boot-actuator}/health/HealthEndpoint.{sc-ext}[HealthEndpoint])
management.endpoint.health.cache.time-to-live=0ms # Maximum time that a response can be cached.
management.endpoint.health.enabled= # Whether to enable the health endpoint.
management.endpoint.health.show-details= # Whether to show full health details
# HEAP DUMP ENDPOINT ({sc-spring-boot-actuator}/management/HeapDumpWebEndpoint.{sc-ext}[HeapDumpWebEndpoint])
management.endpoint.heapdump.cache.time-to-live=0ms # Maximum time that a response can be cached.

@ -111,10 +111,6 @@ The following technology-agnostic endpoints are available:
|`shutdown`
|Lets the application be gracefully shutdown (not enabled by default).
|`status`
|Shows application status information (that is, `health` status with no additional
details).
|`threaddump`
|Performs a thread dump.

@ -41,7 +41,7 @@ public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
protected void configure(HttpSecurity http) throws Exception {
// @formatter:off
http.authorizeRequests()
.requestMatchers(EndpointRequest.to("status", "info")).permitAll()
.requestMatchers(EndpointRequest.to("health", "info")).permitAll()
.requestMatchers(EndpointRequest.toAnyEndpoint()).hasRole("ACTUATOR")
.requestMatchers(StaticResourceRequest.toCommonLocations()).permitAll()
.antMatchers("/foo").permitAll()

@ -1 +1,2 @@
management.endpoints.web.expose=*
management.endpoint.health.show-details=true

@ -57,9 +57,9 @@ public class ManagementPortAndPathSampleActuatorApplicationTests {
@Test
public void testSecureActuator() throws Exception {
ResponseEntity<String> entity = new TestRestTemplate()
.getForEntity("http://localhost:" + this.managementPort
+ "/management/application/health", String.class);
ResponseEntity<String> entity = new TestRestTemplate().getForEntity(
"http://localhost:" + this.managementPort + "/management/application/env",
String.class);
assertThat(entity.getStatusCode()).isEqualTo(HttpStatus.UNAUTHORIZED);
}
@ -67,7 +67,7 @@ public class ManagementPortAndPathSampleActuatorApplicationTests {
public void testInsecureActuator() throws Exception {
ResponseEntity<String> entity = new TestRestTemplate()
.getForEntity("http://localhost:" + this.managementPort
+ "/management/application/status", String.class);
+ "/management/application/health", String.class);
assertThat(entity.getStatusCode()).isEqualTo(HttpStatus.OK);
assertThat(entity.getBody()).contains("\"status\":\"UP\"");
}

@ -73,7 +73,7 @@ public class SampleActuatorCustomSecurityApplicationTests {
@Test
public void insecureActuator() throws Exception {
ResponseEntity<String> entity = this.restTemplate
.getForEntity("/application/status", String.class);
.getForEntity("/application/health", String.class);
assertThat(entity.getStatusCode()).isEqualTo(HttpStatus.OK);
assertThat(entity.getBody()).contains("\"status\":\"UP\"");
}

@ -1,2 +1,3 @@
server.error.path: /oops
management.endpoints.web.base-path: /admin
management.endpoint.health.show-details: true
management.endpoints.web.base-path: /admin

@ -61,7 +61,7 @@ public class SampleJerseyApplicationTests {
@Test
public void actuatorStatus() {
ResponseEntity<String> entity = this.restTemplate
.getForEntity("/application/status", String.class);
.getForEntity("/application/health", String.class);
assertThat(entity.getStatusCode()).isEqualTo(HttpStatus.OK);
assertThat(entity.getBody()).isEqualTo("{\"status\":\"UP\"}");
}

@ -48,7 +48,7 @@ public class SampleSecureWebFluxApplicationTests {
@Test
public void actuatorsSecureByDefault() {
this.webClient.get().uri("/application/status").accept(MediaType.APPLICATION_JSON)
this.webClient.get().uri("/application/health").accept(MediaType.APPLICATION_JSON)
.exchange().expectStatus().isUnauthorized();
}
@ -61,7 +61,7 @@ public class SampleSecureWebFluxApplicationTests {
@Test
public void actuatorsAccessibleOnLogin() {
this.webClient.get().uri("/application/status").accept(MediaType.APPLICATION_JSON)
this.webClient.get().uri("/application/health").accept(MediaType.APPLICATION_JSON)
.header("Authorization", "basic " + getBasicAuth()).exchange()
.expectBody(String.class).isEqualTo("{\"status\":\"UP\"}");
}

@ -55,7 +55,7 @@ public class SampleWebFluxApplicationTests {
@Test
public void testActuatorStatus() {
this.webClient.get().uri("/application/status").accept(MediaType.APPLICATION_JSON)
this.webClient.get().uri("/application/health").accept(MediaType.APPLICATION_JSON)
.exchange().expectStatus().isOk().expectBody()
.json("{\"status\":\"UP\"}");
}

Loading…
Cancel
Save