Commit 60f5bb16 authored by Scott Frederick's avatar Scott Frederick

Remove health actuator code deprecated in 2.2

This partially re-applies the deprecation removal from commit
https://github.com/spring-projects/spring-boot/commit/df1837a16bc39a9314e34e5e89c76e1ddf56372c,
without removing CompositeHealthIndicator, HealthAggregator, and related
configuration that is required by Spring Cloud.
parent e64a145e
/*
* Copyright 2012-2019 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
*
* https://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.HealthStatusHttpMapper;
import org.springframework.boot.actuate.health.HttpCodeStatusMapper;
import org.springframework.boot.actuate.health.Status;
/**
* Adapter class to convert a legacy {@link HealthStatusHttpMapper} to a
* {@link HttpCodeStatusMapper}.
*
* @author Phillip Webb
*/
@SuppressWarnings("deprecation")
class HealthStatusHttpMapperHttpCodeStatusMapperAdapter implements HttpCodeStatusMapper {
private final HealthStatusHttpMapper healthStatusHttpMapper;
HealthStatusHttpMapperHttpCodeStatusMapperAdapter(HealthStatusHttpMapper healthStatusHttpMapper) {
this.healthStatusHttpMapper = healthStatusHttpMapper;
}
@Override
public int getStatusCode(Status status) {
return this.healthStatusHttpMapper.mapStatus(status);
}
}
/*
* Copyright 2012-2019 the original author or authors.
* Copyright 2012-2020 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.
......@@ -16,7 +16,6 @@
package org.springframework.boot.actuate.autoconfigure.health;
import org.springframework.boot.actuate.health.HttpCodeStatusMapper;
import org.springframework.boot.actuate.health.StatusAggregator;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.context.annotation.Bean;
......@@ -26,6 +25,7 @@ import org.springframework.context.annotation.Configuration;
* Configuration to adapt legacy deprecated health endpoint classes and interfaces.
*
* @author Phillip Webb
* @author Scott Frederick
* @see HealthEndpointAutoConfiguration
*/
@Configuration(proxyBeanMethods = false)
......@@ -39,11 +39,4 @@ class LegacyHealthEndpointAdaptersConfiguration {
return new HealthAggregatorStatusAggregatorAdapter(healthAggregator);
}
@Bean
@ConditionalOnBean(org.springframework.boot.actuate.health.HealthStatusHttpMapper.class)
HttpCodeStatusMapper healthStatusHttpMapperHttpCodeStatusMapperAdapter(
org.springframework.boot.actuate.health.HealthStatusHttpMapper healthStatusHttpMapper) {
return new HealthStatusHttpMapperHttpCodeStatusMapperAdapter(healthStatusHttpMapper);
}
}
/*
* Copyright 2012-2019 the original author or authors.
* Copyright 2012-2020 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.
......@@ -21,7 +21,6 @@ import reactor.core.publisher.Mono;
import org.springframework.boot.actuate.health.HealthAggregator;
import org.springframework.boot.actuate.health.HealthContributorRegistry;
import org.springframework.boot.actuate.health.HealthIndicatorRegistry;
import org.springframework.boot.actuate.health.HealthStatusHttpMapper;
import org.springframework.boot.actuate.health.OrderedHealthAggregator;
import org.springframework.boot.actuate.health.ReactiveHealthContributorRegistry;
import org.springframework.boot.actuate.health.ReactiveHealthIndicatorRegistry;
......@@ -36,6 +35,7 @@ import org.springframework.util.CollectionUtils;
* Configuration to adapt legacy deprecated health endpoint classes and interfaces.
*
* @author Phillip Webb
* @author Scott Frederick
* @see HealthEndpointAutoConfiguration
*/
@Configuration(proxyBeanMethods = false)
......@@ -53,16 +53,6 @@ class LegacyHealthEndpointCompatibilityConfiguration {
return aggregator;
}
@Bean
@ConditionalOnMissingBean
HealthStatusHttpMapper healthStatusHttpMapper(HealthIndicatorProperties healthIndicatorProperties) {
HealthStatusHttpMapper mapper = new HealthStatusHttpMapper();
if (!CollectionUtils.isEmpty(healthIndicatorProperties.getHttpMapping())) {
mapper.setStatusMapping(healthIndicatorProperties.getHttpMapping());
}
return mapper;
}
@Bean
@ConditionalOnMissingBean(HealthIndicatorRegistry.class)
HealthContributorRegistryHealthIndicatorRegistryAdapter healthIndicatorRegistry(
......
/*
* Copyright 2012-2019 the original author or authors.
* Copyright 2012-2020 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.
......@@ -39,7 +39,6 @@ import org.springframework.boot.actuate.health.HealthEndpoint;
import org.springframework.boot.actuate.health.HealthEndpointGroups;
import org.springframework.boot.actuate.health.HealthEndpointWebExtension;
import org.springframework.boot.actuate.health.HealthIndicator;
import org.springframework.boot.actuate.health.HealthStatusHttpMapper;
import org.springframework.boot.actuate.health.HttpCodeStatusMapper;
import org.springframework.boot.actuate.health.NamedContributor;
import org.springframework.boot.actuate.health.ReactiveHealthContributorRegistry;
......@@ -65,6 +64,7 @@ import static org.mockito.Mockito.mock;
* @author Phillip Webb
* @author Andy Wilkinson
* @author Stephane Nicoll
* @author Scott Frederick
*/
@SuppressWarnings("deprecation")
class HealthEndpointAutoConfigurationTests {
......@@ -99,14 +99,6 @@ class HealthEndpointAutoConfigurationTests {
});
}
@Test
void runWhenHasHealthStatusHttpMapperAdaptsToHttpCodeStatusMapper() {
this.contextRunner.withUserConfiguration(HealthStatusHttpMapperConfiguration.class).run((context) -> {
HttpCodeStatusMapper mapper = context.getBean(HttpCodeStatusMapper.class);
assertThat(mapper.getStatusCode(Status.UP)).isEqualTo(123);
});
}
@Test
void runCreatesStatusAggregatorFromProperties() {
this.contextRunner.withPropertyValues("management.endpoint.health.status.order=up,down").run((context) -> {
......@@ -298,14 +290,6 @@ class HealthEndpointAutoConfigurationTests {
});
}
@Test // gh-18354
void runCreatesLegacyHealthStatusHttpMapper() {
this.contextRunner.run((context) -> {
HealthStatusHttpMapper mapper = context.getBean(HealthStatusHttpMapper.class);
assertThat(mapper.mapStatus(Status.DOWN)).isEqualTo(503);
});
}
@Test
void runWhenReactorAvailableCreatesReactiveHealthIndicatorRegistryBean() {
this.contextRunner.run((context) -> assertThat(context).hasSingleBean(ReactiveHealthIndicatorRegistry.class));
......@@ -354,23 +338,6 @@ class HealthEndpointAutoConfigurationTests {
}
@Configuration(proxyBeanMethods = false)
static class HealthStatusHttpMapperConfiguration {
@Bean
HealthStatusHttpMapper healthStatusHttpMapper() {
return new HealthStatusHttpMapper() {
@Override
public int mapStatus(Status status) {
return 123;
}
};
}
}
@Configuration(proxyBeanMethods = false)
static class StatusAggregatorConfiguration {
......
/*
* Copyright 2012-2019 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
*
* https://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.junit.jupiter.api.Test;
import org.springframework.boot.actuate.health.HealthStatusHttpMapper;
import org.springframework.boot.actuate.health.HttpCodeStatusMapper;
import org.springframework.boot.actuate.health.Status;
import static org.assertj.core.api.Assertions.assertThat;
/**
* Tests for {@link HealthStatusHttpMapperHttpCodeStatusMapperAdapter}.
*
* @author Phillip Webb
*/
@SuppressWarnings("deprecation")
class HealthStatusHttpMapperHttpCodeStatusMapperAdapterTests {
@Test
void getStatusCodeDelegatesToHealthStatusHttpMapper() {
HttpCodeStatusMapper adapter = new HealthStatusHttpMapperHttpCodeStatusMapperAdapter(
new TestHealthStatusHttpMapper());
assertThat(adapter.getStatusCode(Status.UP)).isEqualTo(123);
}
static class TestHealthStatusHttpMapper extends HealthStatusHttpMapper {
@Override
public int mapStatus(Status status) {
return 123;
}
}
}
/*
* Copyright 2012-2019 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
*
* https://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.elasticsearch;
import java.util.List;
import org.elasticsearch.action.admin.cluster.health.ClusterHealthRequest;
import org.elasticsearch.action.admin.cluster.health.ClusterHealthResponse;
import org.elasticsearch.client.Client;
import org.elasticsearch.client.Requests;
import org.springframework.boot.actuate.health.AbstractHealthIndicator;
import org.springframework.boot.actuate.health.Health;
import org.springframework.boot.actuate.health.HealthIndicator;
import org.springframework.util.ObjectUtils;
import org.springframework.util.StringUtils;
/**
* {@link HealthIndicator} for an Elasticsearch cluster.
*
* @author Binwei Yang
* @author Andy Wilkinson
* @since 2.0.0
* @deprecated since 2.2.0 as {@literal org.elasticsearch.client:transport} has been
* deprecated upstream
*/
@Deprecated
public class ElasticsearchHealthIndicator extends AbstractHealthIndicator {
private static final String[] ALL_INDICES = { "_all" };
private final Client client;
private final String[] indices;
private final long responseTimeout;
/**
* Create a new {@link ElasticsearchHealthIndicator} instance.
* @param client the Elasticsearch client
* @param responseTimeout the request timeout in milliseconds
* @param indices the indices to check
*/
public ElasticsearchHealthIndicator(Client client, long responseTimeout, List<String> indices) {
this(client, responseTimeout, (indices != null) ? StringUtils.toStringArray(indices) : null);
}
/**
* Create a new {@link ElasticsearchHealthIndicator} instance.
* @param client the Elasticsearch client
* @param responseTimeout the request timeout in milliseconds
* @param indices the indices to check
*/
public ElasticsearchHealthIndicator(Client client, long responseTimeout, String... indices) {
super("Elasticsearch health check failed");
this.client = client;
this.responseTimeout = responseTimeout;
this.indices = indices;
}
@Override
protected void doHealthCheck(Health.Builder builder) throws Exception {
ClusterHealthRequest request = Requests
.clusterHealthRequest(ObjectUtils.isEmpty(this.indices) ? ALL_INDICES : this.indices);
ClusterHealthResponse response = this.client.admin().cluster().health(request).actionGet(this.responseTimeout);
switch (response.getStatus()) {
case GREEN:
case YELLOW:
builder.up();
break;
case RED:
default:
builder.down();
break;
}
builder.withDetail("clusterName", response.getClusterName());
builder.withDetail("numberOfNodes", response.getNumberOfNodes());
builder.withDetail("numberOfDataNodes", response.getNumberOfDataNodes());
builder.withDetail("activePrimaryShards", response.getActivePrimaryShards());
builder.withDetail("activeShards", response.getActiveShards());
builder.withDetail("relocatingShards", response.getRelocatingShards());
builder.withDetail("initializingShards", response.getInitializingShards());
builder.withDetail("unassignedShards", response.getUnassignedShards());
}
}
/*
* Copyright 2012-2019 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
*
* https://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;
/**
* Default implementation of {@link HealthIndicator} that returns {@link Status#UP}.
*
* @author Dave Syer
* @author Christian Dupuis
* @since 1.2.0
* @see Status#UP
* @deprecated since 2.2 in favor of {@link PingHealthIndicator}.
*/
@Deprecated
public class ApplicationHealthIndicator extends AbstractHealthIndicator {
public ApplicationHealthIndicator() {
super("Application health check failed");
}
@Override
protected void doHealthCheck(Health.Builder builder) throws Exception {
builder.up();
}
}
/*
* Copyright 2012-2019 the original author or authors.
* Copyright 2012-2020 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.
......@@ -33,6 +33,7 @@ import org.springframework.boot.actuate.endpoint.http.ApiVersion;
* @author Christian Dupuis
* @author Andy Wilkinson
* @author Stephane Nicoll
* @author Scott Frederick
* @since 2.0.0
*/
@Endpoint(id = "health")
......@@ -40,17 +41,6 @@ public class HealthEndpoint extends HealthEndpointSupport<HealthContributor, Hea
private static final String[] EMPTY_PATH = {};
/**
* Create a new {@link HealthEndpoint} instance that will use the given {@code
* healthIndicator} to generate its response.
* @param healthIndicator the health indicator
* @deprecated since 2.2.0 in favor of
* {@link #HealthEndpoint(HealthContributorRegistry, HealthEndpointGroups)}
*/
@Deprecated
public HealthEndpoint(HealthIndicator healthIndicator) {
}
/**
* Create a new {@link HealthEndpoint} instance.
* @param registry the health contributor registry
......
/*
* Copyright 2012-2019 the original author or authors.
* Copyright 2012-2020 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.
......@@ -31,6 +31,7 @@ import org.springframework.util.Assert;
* @param <C> the contributor type
* @param <T> the contributed health component type
* @author Phillip Webb
* @author Scott Frederick
*/
abstract class HealthEndpointSupport<C, T> {
......@@ -40,16 +41,6 @@ abstract class HealthEndpointSupport<C, T> {
private final HealthEndpointGroups groups;
/**
* Throw a new {@link IllegalStateException} to indicate a constructor has been
* deprecated.
* @deprecated since 2.2.0 in order to support deprecated subclass constructors
*/
@Deprecated
HealthEndpointSupport() {
throw new IllegalStateException("Unable to create " + getClass() + " using deprecated constructor");
}
/**
* Create a new {@link HealthEndpointSupport} instance.
* @param registry the health contributor registry
......
/*
* Copyright 2012-2019 the original author or authors.
* Copyright 2012-2020 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.
......@@ -38,6 +38,7 @@ import org.springframework.boot.actuate.endpoint.web.annotation.EndpointWebExten
* @author Eddú Meléndez
* @author Madhura Bhave
* @author Stephane Nicoll
* @author Scott Frederick
* @since 2.0.0
*/
@EndpointWebExtension(endpoint = HealthEndpoint.class)
......@@ -45,17 +46,6 @@ public class HealthEndpointWebExtension extends HealthEndpointSupport<HealthCont
private static final String[] NO_PATH = {};
/**
* Create a new {@link HealthEndpointWebExtension} instance using a delegate endpoint.
* @param delegate the delegate endpoint
* @param responseMapper the response mapper
* @deprecated since 2.2.0 in favor of
* {@link #HealthEndpointWebExtension(HealthContributorRegistry, HealthEndpointGroups)}
*/
@Deprecated
public HealthEndpointWebExtension(HealthEndpoint delegate, HealthWebEndpointResponseMapper responseMapper) {
}
/**
* Create a new {@link HealthEndpointWebExtension} instance.
* @param registry the health contributor registry
......
/*
* Copyright 2012-2019 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
*
* https://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.Collections;
import java.util.HashMap;
import java.util.Map;
import org.springframework.boot.actuate.endpoint.web.WebEndpointResponse;
import org.springframework.util.Assert;
/**
* Map a {@link Status} to an HTTP status code.
*
* @author Stephane Nicoll
* @since 2.0.0
* @deprecated since 2.2.0 in favor of {@link HttpCodeStatusMapper} or
* {@link SimpleHttpCodeStatusMapper}
*/
@Deprecated
public class HealthStatusHttpMapper {
private Map<String, Integer> statusMapping = new HashMap<>();
/**
* Create a new instance.
*/
public HealthStatusHttpMapper() {
setupDefaultStatusMapping();
}
private void setupDefaultStatusMapping() {
addStatusMapping(Status.DOWN, WebEndpointResponse.STATUS_SERVICE_UNAVAILABLE);
addStatusMapping(Status.OUT_OF_SERVICE, WebEndpointResponse.STATUS_SERVICE_UNAVAILABLE);
}
/**
* Set specific status mappings.
* @param statusMapping a map of health status code to HTTP status code
*/
public void setStatusMapping(Map<String, Integer> statusMapping) {
Assert.notNull(statusMapping, "StatusMapping must not be null");
this.statusMapping = new HashMap<>(statusMapping);
}
/**
* Add specific status mappings to the existing set.
* @param statusMapping a map of health status code to HTTP status code
*/
public void addStatusMapping(Map<String, Integer> statusMapping) {
Assert.notNull(statusMapping, "StatusMapping must not be null");
this.statusMapping.putAll(statusMapping);
}
/**
* Add a status mapping to the existing set.
* @param status the status to map
* @param httpStatus the http status
*/
public void addStatusMapping(Status status, Integer httpStatus) {
Assert.notNull(status, "Status must not be null");
Assert.notNull(httpStatus, "HttpStatus must not be null");
addStatusMapping(status.getCode(), httpStatus);
}
/**
* Add a status mapping to the existing set.
* @param statusCode the status code to map
* @param httpStatus the http status
*/
public void addStatusMapping(String statusCode, Integer httpStatus) {
Assert.notNull(statusCode, "StatusCode must not be null");
Assert.notNull(httpStatus, "HttpStatus must not be null");
this.statusMapping.put(statusCode, httpStatus);
}
/**
* Return an immutable view of the status mapping.
* @return the http status codes mapped by status name
*/
public Map<String, Integer> getStatusMapping() {
return Collections.unmodifiableMap(this.statusMapping);
}
/**
* Map the specified {@link Status} to an HTTP status code.
* @param status the health {@link Status}
* @return the corresponding HTTP status code
*/
public int mapStatus(Status status) {
String code = getUniformValue(status.getCode());
if (code != null) {
return this.statusMapping.entrySet().stream()
.filter((entry) -> code.equals(getUniformValue(entry.getKey()))).map(Map.Entry::getValue)
.findFirst().orElse(WebEndpointResponse.STATUS_OK);
}
return WebEndpointResponse.STATUS_OK;
}
private String getUniformValue(String code) {
if (code == null) {
return null;
}
StringBuilder builder = new StringBuilder();
for (char ch : code.toCharArray()) {
if (Character.isAlphabetic(ch) || Character.isDigit(ch)) {
builder.append(Character.toLowerCase(ch));
}
}
return builder.toString();
}
}
/*
* Copyright 2012-2019 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
*
* https://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.security.Principal;
import java.util.Set;
import java.util.function.Supplier;
import org.springframework.boot.actuate.endpoint.SecurityContext;
import org.springframework.boot.actuate.endpoint.web.WebEndpointResponse;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.util.ClassUtils;
import org.springframework.util.CollectionUtils;
/**
* Maps a {@link Health} to a {@link WebEndpointResponse}.
*
* @author Andy Wilkinson
* @since 2.0.0
* @deprecated since 2.2.0 in favor of {@link HealthEndpointWebExtension} or
* {@link ReactiveHealthEndpointWebExtension}
*/
@Deprecated
public class HealthWebEndpointResponseMapper {
private final HealthStatusHttpMapper statusHttpMapper;
private final ShowDetails showDetails;
private final Set<String> authorizedRoles;
public HealthWebEndpointResponseMapper(HealthStatusHttpMapper statusHttpMapper, ShowDetails showDetails,
Set<String> authorizedRoles) {
this.statusHttpMapper = statusHttpMapper;
this.showDetails = showDetails;
this.authorizedRoles = authorizedRoles;
}
/**
* Maps the given {@code health} details to a {@link WebEndpointResponse}, honouring
* the mapper's default {@link ShowDetails} using the given {@code securityContext}.
* <p>
* If the current user does not have the right to see the details, the
* {@link Supplier} is not invoked and a 404 response is returned instead.
* @param health the provider of health details, invoked if the current user has the
* right to see them
* @param securityContext the security context
* @return the mapped response
*/
public WebEndpointResponse<Health> mapDetails(Supplier<Health> health, SecurityContext securityContext) {
if (canSeeDetails(securityContext, this.showDetails)) {
Health healthDetails = health.get();
if (healthDetails != null) {
return createWebEndpointResponse(healthDetails);
}
}
return new WebEndpointResponse<>(WebEndpointResponse.STATUS_NOT_FOUND);
}
/**
* Maps the given {@code health} to a {@link WebEndpointResponse}, honouring the
* mapper's default {@link ShowDetails} using the given {@code securityContext}.
* @param health the health to map
* @param securityContext the security context
* @return the mapped response
*/
public WebEndpointResponse<Health> map(Health health, SecurityContext securityContext) {
return map(health, securityContext, this.showDetails);
}
/**
* Maps the given {@code health} to a {@link WebEndpointResponse}, honouring the given
* {@code showDetails} using the given {@code securityContext}.
* @param health the health to map
* @param securityContext the security context
* @param showDetails when to show details in the response
* @return the mapped response
*/
public WebEndpointResponse<Health> map(Health health, SecurityContext securityContext, ShowDetails showDetails) {
if (!canSeeDetails(securityContext, showDetails)) {
health = Health.status(health.getStatus()).build();
}
return createWebEndpointResponse(health);
}
private WebEndpointResponse<Health> createWebEndpointResponse(Health health) {
int status = this.statusHttpMapper.mapStatus(health.getStatus());
return new WebEndpointResponse<>(health, status);
}
private boolean canSeeDetails(SecurityContext securityContext, ShowDetails showDetails) {
return showDetails != ShowDetails.NEVER && (showDetails != ShowDetails.WHEN_AUTHORIZED
|| (securityContext.getPrincipal() != null && isUserInRole(securityContext)));
}
private boolean isUserInRole(SecurityContext securityContext) {
if (CollectionUtils.isEmpty(this.authorizedRoles)) {
return true;
}
Principal principal = securityContext.getPrincipal();
boolean checkAuthorities = isSpringSecurityAuthentication(principal);
for (String role : this.authorizedRoles) {
if (securityContext.isUserInRole(role)) {
return true;
}
if (checkAuthorities) {
Authentication authentication = (Authentication) principal;
for (GrantedAuthority authority : authentication.getAuthorities()) {
String name = authority.getAuthority();
if (role.equals(name)) {
return true;
}
}
}
}
return false;
}
private boolean isSpringSecurityAuthentication(Principal principal) {
return ClassUtils.isPresent("org.springframework.security.core.Authentication", null)
&& (principal instanceof Authentication);
}
}
/*
* Copyright 2012-2019 the original author or authors.
* Copyright 2012-2020 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.
......@@ -37,6 +37,7 @@ import org.springframework.boot.actuate.endpoint.web.annotation.EndpointWebExten
*
* @author Stephane Nicoll
* @author Phillip Webb
* @author Scott Frederick
* @since 2.0.0
*/
@EndpointWebExtension(endpoint = HealthEndpoint.class)
......@@ -45,18 +46,6 @@ public class ReactiveHealthEndpointWebExtension
private static final String[] NO_PATH = {};
/**
* Create a new {@link ReactiveHealthEndpointWebExtension} instance.
* @param delegate the delegate health indicator
* @param responseMapper the response mapper
* @deprecated since 2.2.0 in favor of
* {@link #ReactiveHealthEndpointWebExtension(ReactiveHealthContributorRegistry, HealthEndpointGroups)}
*/
@Deprecated
public ReactiveHealthEndpointWebExtension(ReactiveHealthIndicator delegate,
HealthWebEndpointResponseMapper responseMapper) {
}
/**
* Create a new {@link ReactiveHealthEndpointWebExtension} instance.
* @param registry the health contributor registry
......
/*
* Copyright 2012-2019 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
*
* https://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;
/**
* Options for showing details in responses from the {@link HealthEndpoint} web
* extensions.
*
* @author Andy Wilkinson
* @since 2.0.0
* @deprecated since 2.2.0 in favor of {@code HealthEndpointProperties.ShowDetails}
*/
@Deprecated
public enum ShowDetails {
/**
* Never show details in the response.
*/
NEVER,
/**
* Show details in the response when accessed by an authorized user.
*/
WHEN_AUTHORIZED,
/**
* Always show details in the response.
*/
ALWAYS
}
/*
* Copyright 2012-2019 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
*
* https://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.elasticsearch;
import java.util.Map;
import org.elasticsearch.ElasticsearchException;
import org.elasticsearch.ElasticsearchTimeoutException;
import org.elasticsearch.action.admin.cluster.health.ClusterHealthRequest;
import org.elasticsearch.action.admin.cluster.health.ClusterHealthResponse;
import org.elasticsearch.action.support.PlainActionFuture;
import org.elasticsearch.client.AdminClient;
import org.elasticsearch.client.Client;
import org.elasticsearch.client.ClusterAdminClient;
import org.elasticsearch.cluster.ClusterState;
import org.elasticsearch.cluster.block.ClusterBlocks;
import org.elasticsearch.cluster.health.ClusterHealthStatus;
import org.elasticsearch.cluster.node.DiscoveryNodes;
import org.elasticsearch.cluster.routing.RoutingTable;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.springframework.boot.actuate.health.Health;
import org.springframework.boot.actuate.health.Status;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.BDDMockito.given;
/**
* Test for {@link ElasticsearchHealthIndicator}.
*
* @author Andy Wilkinson
*/
@Deprecated
class ElasticsearchHealthIndicatorTests {
@Mock
private Client client;
@Mock
private AdminClient admin;
@Mock
private ClusterAdminClient cluster;
private ElasticsearchHealthIndicator indicator;
@BeforeEach
void setUp() {
MockitoAnnotations.initMocks(this);
given(this.client.admin()).willReturn(this.admin);
given(this.admin.cluster()).willReturn(this.cluster);
this.indicator = new ElasticsearchHealthIndicator(this.client, 100L);
}
@Test
void defaultConfigurationQueriesAllIndicesWith100msTimeout() {
TestActionFuture responseFuture = new TestActionFuture();
responseFuture.onResponse(new StubClusterHealthResponse());
ArgumentCaptor<ClusterHealthRequest> requestCaptor = ArgumentCaptor.forClass(ClusterHealthRequest.class);
given(this.cluster.health(requestCaptor.capture())).willReturn(responseFuture);
Health health = this.indicator.health();
assertThat(responseFuture.getTimeout).isEqualTo(100L);
assertThat(requestCaptor.getValue().indices()).contains("_all");
assertThat(health.getStatus()).isEqualTo(Status.UP);
}
@Test
void certainIndices() {
this.indicator = new ElasticsearchHealthIndicator(this.client, 100L, "test-index-1", "test-index-2");
PlainActionFuture<ClusterHealthResponse> responseFuture = new PlainActionFuture<>();
responseFuture.onResponse(new StubClusterHealthResponse());
ArgumentCaptor<ClusterHealthRequest> requestCaptor = ArgumentCaptor.forClass(ClusterHealthRequest.class);
given(this.cluster.health(requestCaptor.capture())).willReturn(responseFuture);
Health health = this.indicator.health();
assertThat(requestCaptor.getValue().indices()).contains("test-index-1", "test-index-2");
assertThat(health.getStatus()).isEqualTo(Status.UP);
}
@Test
void customTimeout() {
this.indicator = new ElasticsearchHealthIndicator(this.client, 1000L);
TestActionFuture responseFuture = new TestActionFuture();
responseFuture.onResponse(new StubClusterHealthResponse());
ArgumentCaptor<ClusterHealthRequest> requestCaptor = ArgumentCaptor.forClass(ClusterHealthRequest.class);
given(this.cluster.health(requestCaptor.capture())).willReturn(responseFuture);
this.indicator.health();
assertThat(responseFuture.getTimeout).isEqualTo(1000L);
}
@Test
void healthDetails() {
PlainActionFuture<ClusterHealthResponse> responseFuture = new PlainActionFuture<>();
responseFuture.onResponse(new StubClusterHealthResponse());
given(this.cluster.health(any(ClusterHealthRequest.class))).willReturn(responseFuture);
Health health = this.indicator.health();
assertThat(health.getStatus()).isEqualTo(Status.UP);
Map<String, Object> details = health.getDetails();
assertDetail(details, "clusterName", "test-cluster");
assertDetail(details, "activeShards", 1);
assertDetail(details, "relocatingShards", 2);
assertDetail(details, "activePrimaryShards", 3);
assertDetail(details, "initializingShards", 4);
assertDetail(details, "unassignedShards", 5);
assertDetail(details, "numberOfNodes", 6);
assertDetail(details, "numberOfDataNodes", 7);
}
@Test
void redResponseMapsToDown() {
PlainActionFuture<ClusterHealthResponse> responseFuture = new PlainActionFuture<>();
responseFuture.onResponse(new StubClusterHealthResponse(ClusterHealthStatus.RED));
given(this.cluster.health(any(ClusterHealthRequest.class))).willReturn(responseFuture);
assertThat(this.indicator.health().getStatus()).isEqualTo(Status.DOWN);
}
@Test
void yellowResponseMapsToUp() {
PlainActionFuture<ClusterHealthResponse> responseFuture = new PlainActionFuture<>();
responseFuture.onResponse(new StubClusterHealthResponse(ClusterHealthStatus.YELLOW));
given(this.cluster.health(any(ClusterHealthRequest.class))).willReturn(responseFuture);
assertThat(this.indicator.health().getStatus()).isEqualTo(Status.UP);
}
@Test
void responseTimeout() {
PlainActionFuture<ClusterHealthResponse> responseFuture = new PlainActionFuture<>();
given(this.cluster.health(any(ClusterHealthRequest.class))).willReturn(responseFuture);
Health health = this.indicator.health();
assertThat(health.getStatus()).isEqualTo(Status.DOWN);
assertThat((String) health.getDetails().get("error")).contains(ElasticsearchTimeoutException.class.getName());
}
@SuppressWarnings("unchecked")
private <T> void assertDetail(Map<String, Object> details, String detail, T value) {
assertThat((T) details.get(detail)).isEqualTo(value);
}
private static final class StubClusterHealthResponse extends ClusterHealthResponse {
private final ClusterHealthStatus status;
private StubClusterHealthResponse() {
this(ClusterHealthStatus.GREEN);
}
private StubClusterHealthResponse(ClusterHealthStatus status) {
super("test-cluster", new String[0], new ClusterState(null, 0, null, null, RoutingTable.builder().build(),
DiscoveryNodes.builder().build(), ClusterBlocks.builder().build(), null, 1, false));
this.status = status;
}
@Override
public int getActiveShards() {
return 1;
}
@Override
public int getRelocatingShards() {
return 2;
}
@Override
public int getActivePrimaryShards() {
return 3;
}
@Override
public int getInitializingShards() {
return 4;
}
@Override
public int getUnassignedShards() {
return 5;
}
@Override
public int getNumberOfNodes() {
return 6;
}
@Override
public int getNumberOfDataNodes() {
return 7;
}
@Override
public ClusterHealthStatus getStatus() {
return this.status;
}
}
static class TestActionFuture extends PlainActionFuture<ClusterHealthResponse> {
private long getTimeout = -1L;
@Override
public ClusterHealthResponse actionGet(long timeoutMillis) throws ElasticsearchException {
this.getTimeout = timeoutMillis;
return super.actionGet(timeoutMillis);
}
}
}
/*
* Copyright 2012-2019 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
*
* https://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.junit.jupiter.api.Test;
import static org.assertj.core.api.Assertions.assertThat;
/**
* Tests for {@link ApplicationHealthIndicator}.
*
* @author Phillip Webb
*/
@Deprecated
class ApplicationHealthIndicatorTests {
@Test
void indicatesUp() {
ApplicationHealthIndicator healthIndicator = new ApplicationHealthIndicator();
assertThat(healthIndicator.health().getStatus()).isEqualTo(Status.UP);
}
}
/*
* Copyright 2012-2019 the original author or authors.
* Copyright 2012-2020 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.
......@@ -24,26 +24,17 @@ import org.junit.jupiter.api.Test;
import org.springframework.boot.actuate.health.HealthEndpointSupport.HealthResult;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatIllegalStateException;
import static org.mockito.Mockito.mock;
/**
* Tests for {@link HealthEndpoint}.
*
* @author Phillip Webb
* @author Scott Frederick
*/
class HealthEndpointTests
extends HealthEndpointSupportTests<HealthContributorRegistry, HealthContributor, HealthComponent> {
@Test
@SuppressWarnings("deprecation")
void createWhenUsingDeprecatedConstructorThrowsException() {
HealthIndicator healthIndicator = mock(HealthIndicator.class);
assertThatIllegalStateException().isThrownBy(() -> new HealthEndpoint(healthIndicator))
.withMessage("Unable to create class org.springframework.boot.actuate.health.HealthEndpoint "
+ "using deprecated constructor");
}
@Test
void healthReturnsSystemHealth() {
this.registry.registerContributor("test", createContributor(this.up));
......
/*
* Copyright 2012-2019 the original author or authors.
* Copyright 2012-2020 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.
......@@ -27,27 +27,17 @@ import org.springframework.boot.actuate.endpoint.web.WebEndpointResponse;
import org.springframework.boot.actuate.health.HealthEndpointSupport.HealthResult;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatIllegalStateException;
import static org.mockito.Mockito.mock;
/**
* Tests for {@link HealthEndpointWebExtension}.
*
* @author Phillip Webb
* @author Scott Frederick
*/
class HealthEndpointWebExtensionTests
extends HealthEndpointSupportTests<HealthContributorRegistry, HealthContributor, HealthComponent> {
@Test
@SuppressWarnings("deprecation")
void createWhenUsingDeprecatedConstructorThrowsException() {
HealthEndpoint delegate = mock(HealthEndpoint.class);
HealthWebEndpointResponseMapper responseMapper = mock(HealthWebEndpointResponseMapper.class);
assertThatIllegalStateException().isThrownBy(() -> new HealthEndpointWebExtension(delegate, responseMapper))
.withMessage("Unable to create class org.springframework.boot.actuate."
+ "health.HealthEndpointWebExtension using deprecated constructor");
}
@Test
void healthReturnsSystemHealth() {
this.registry.registerContributor("test", createContributor(this.up));
......
/*
* Copyright 2012-2019 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
*
* https://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.security.Principal;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Set;
import java.util.function.Supplier;
import org.junit.jupiter.api.Test;
import org.mockito.stubbing.Answer;
import org.springframework.boot.actuate.endpoint.SecurityContext;
import org.springframework.boot.actuate.endpoint.web.WebEndpointResponse;
import org.springframework.http.HttpStatus;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.BDDMockito.given;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoInteractions;
/**
* Tests for {@link HealthWebEndpointResponseMapper}.
*
* @author Stephane Nicoll
*/
@Deprecated
class HealthWebEndpointResponseMapperTests {
private final HealthStatusHttpMapper statusHttpMapper = new HealthStatusHttpMapper();
private Set<String> authorizedRoles = Collections.singleton("ACTUATOR");
@Test
void mapDetailsWithDisableDetailsDoesNotInvokeSupplier() {
HealthWebEndpointResponseMapper mapper = createMapper(ShowDetails.NEVER);
Supplier<Health> supplier = mockSupplier();
SecurityContext securityContext = mock(SecurityContext.class);
WebEndpointResponse<Health> response = mapper.mapDetails(supplier, securityContext);
assertThat(response.getStatus()).isEqualTo(HttpStatus.NOT_FOUND.value());
verifyNoInteractions(supplier);
verifyNoInteractions(securityContext);
}
@Test
void mapDetailsWithUnauthorizedUserDoesNotInvokeSupplier() {
HealthWebEndpointResponseMapper mapper = createMapper(ShowDetails.WHEN_AUTHORIZED);
Supplier<Health> supplier = mockSupplier();
SecurityContext securityContext = mockSecurityContext("USER");
WebEndpointResponse<Health> response = mapper.mapDetails(supplier, securityContext);
assertThat(response.getStatus()).isEqualTo(HttpStatus.NOT_FOUND.value());
assertThat(response.getBody()).isNull();
verifyNoInteractions(supplier);
verify(securityContext).isUserInRole("ACTUATOR");
}
@Test
void mapDetailsWithAuthorizedUserInvokesSupplier() {
HealthWebEndpointResponseMapper mapper = createMapper(ShowDetails.WHEN_AUTHORIZED);
Supplier<Health> supplier = mockSupplier();
given(supplier.get()).willReturn(Health.down().build());
SecurityContext securityContext = mockSecurityContext("ACTUATOR");
WebEndpointResponse<Health> response = mapper.mapDetails(supplier, securityContext);
assertThat(response.getStatus()).isEqualTo(HttpStatus.SERVICE_UNAVAILABLE.value());
assertThat(response.getBody().getStatus()).isEqualTo(Status.DOWN);
verify(supplier).get();
verify(securityContext).isUserInRole("ACTUATOR");
}
@Test
void mapDetailsWithRightAuthoritiesInvokesSupplier() {
HealthWebEndpointResponseMapper mapper = createMapper(ShowDetails.WHEN_AUTHORIZED);
Supplier<Health> supplier = mockSupplier();
given(supplier.get()).willReturn(Health.down().build());
SecurityContext securityContext = getSecurityContext("ACTUATOR");
WebEndpointResponse<Health> response = mapper.mapDetails(supplier, securityContext);
assertThat(response.getStatus()).isEqualTo(HttpStatus.SERVICE_UNAVAILABLE.value());
assertThat(response.getBody().getStatus()).isEqualTo(Status.DOWN);
verify(supplier).get();
}
@Test
void mapDetailsWithOtherAuthoritiesShouldNotInvokeSupplier() {
HealthWebEndpointResponseMapper mapper = createMapper(ShowDetails.WHEN_AUTHORIZED);
Supplier<Health> supplier = mockSupplier();
given(supplier.get()).willReturn(Health.down().build());
SecurityContext securityContext = getSecurityContext("OTHER");
WebEndpointResponse<Health> response = mapper.mapDetails(supplier, securityContext);
assertThat(response.getStatus()).isEqualTo(HttpStatus.NOT_FOUND.value());
assertThat(response.getBody()).isNull();
verifyNoInteractions(supplier);
}
private SecurityContext getSecurityContext(String other) {
SecurityContext securityContext = mock(SecurityContext.class);
Authentication principal = mock(Authentication.class);
given(securityContext.getPrincipal()).willReturn(principal);
given(principal.getAuthorities())
.willAnswer((invocation) -> Collections.singleton(new SimpleGrantedAuthority(other)));
return securityContext;
}
@Test
void mapDetailsWithUnavailableHealth() {
HealthWebEndpointResponseMapper mapper = createMapper(ShowDetails.ALWAYS);
Supplier<Health> supplier = mockSupplier();
SecurityContext securityContext = mock(SecurityContext.class);
WebEndpointResponse<Health> response = mapper.mapDetails(supplier, securityContext);
assertThat(response.getStatus()).isEqualTo(HttpStatus.NOT_FOUND.value());
assertThat(response.getBody()).isNull();
verify(supplier).get();
verifyNoInteractions(securityContext);
}
@SuppressWarnings("unchecked")
private Supplier<Health> mockSupplier() {
return mock(Supplier.class);
}
private SecurityContext mockSecurityContext(String... roles) {
List<String> associatedRoles = Arrays.asList(roles);
SecurityContext securityContext = mock(SecurityContext.class);
given(securityContext.getPrincipal()).willReturn(mock(Principal.class));
given(securityContext.isUserInRole(anyString())).will((Answer<Boolean>) (invocation) -> {
String expectedRole = invocation.getArgument(0);
return associatedRoles.contains(expectedRole);
});
return securityContext;
}
private HealthWebEndpointResponseMapper createMapper(ShowDetails showDetails) {
return new HealthWebEndpointResponseMapper(this.statusHttpMapper, showDetails, this.authorizedRoles);
}
}
/*
* Copyright 2012-2019 the original author or authors.
* Copyright 2012-2020 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.
......@@ -28,28 +28,17 @@ import org.springframework.boot.actuate.endpoint.web.WebEndpointResponse;
import org.springframework.boot.actuate.health.HealthEndpointSupport.HealthResult;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatIllegalStateException;
import static org.mockito.Mockito.mock;
/**
* Tests for {@link ReactiveHealthEndpointWebExtension}.
*
* @author Phillip Webb
* @author Scott Frederick
*/
class ReactiveHealthEndpointWebExtensionTests extends
HealthEndpointSupportTests<ReactiveHealthContributorRegistry, ReactiveHealthContributor, Mono<? extends HealthComponent>> {
@Test
@SuppressWarnings("deprecation")
void createWhenUsingDeprecatedConstructorThrowsException() {
ReactiveHealthIndicator delegate = mock(ReactiveHealthIndicator.class);
HealthWebEndpointResponseMapper responseMapper = mock(HealthWebEndpointResponseMapper.class);
assertThatIllegalStateException()
.isThrownBy(() -> new ReactiveHealthEndpointWebExtension(delegate, responseMapper)).withMessage(
"Unable to create class org.springframework.boot.actuate.health.ReactiveHealthEndpointWebExtension "
+ "using deprecated constructor");
}
@Test
void healthReturnsSystemHealth() {
this.registry.registerContributor("test", createContributor(this.up));
......
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