Allow health groups to be configured at an additional path

Closes gh-25471

Co-authored-by: Phillip Webb <pwebb@vmware.com>
This commit is contained in:
Madhura Bhave
2021-07-13 15:13:04 -07:00
parent fdde40e4fb
commit 49c86e6e1b
48 changed files with 1659 additions and 160 deletions

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2012-2020 the original author or authors.
* Copyright 2012-2021 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.
@@ -61,8 +61,9 @@ public class AvailabilityProbesAutoConfiguration {
}
@Bean
public AvailabilityProbesHealthEndpointGroupsPostProcessor availabilityProbesHealthEndpointGroupsPostProcessor() {
return new AvailabilityProbesHealthEndpointGroupsPostProcessor();
public AvailabilityProbesHealthEndpointGroupsPostProcessor availabilityProbesHealthEndpointGroupsPostProcessor(
Environment environment) {
return new AvailabilityProbesHealthEndpointGroupsPostProcessor(environment);
}
/**

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2012-2020 the original author or authors.
* Copyright 2012-2021 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,6 +21,7 @@ import java.util.HashSet;
import java.util.Set;
import org.springframework.boot.actuate.endpoint.SecurityContext;
import org.springframework.boot.actuate.health.AdditionalHealthEndpointPath;
import org.springframework.boot.actuate.health.HealthEndpointGroup;
import org.springframework.boot.actuate.health.HttpCodeStatusMapper;
import org.springframework.boot.actuate.health.StatusAggregator;
@@ -35,8 +36,11 @@ class AvailabilityProbesHealthEndpointGroup implements HealthEndpointGroup {
private final Set<String> members;
AvailabilityProbesHealthEndpointGroup(String... members) {
private final AdditionalHealthEndpointPath additionalPath;
AvailabilityProbesHealthEndpointGroup(AdditionalHealthEndpointPath additionalPath, String... members) {
this.members = new HashSet<>(Arrays.asList(members));
this.additionalPath = additionalPath;
}
@Override
@@ -64,4 +68,9 @@ class AvailabilityProbesHealthEndpointGroup implements HealthEndpointGroup {
return HttpCodeStatusMapper.DEFAULT;
}
@Override
public AdditionalHealthEndpointPath getAdditionalPath() {
return this.additionalPath;
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2012-2020 the original author or authors.
* Copyright 2012-2021 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.
@@ -22,6 +22,8 @@ import java.util.LinkedHashSet;
import java.util.Map;
import java.util.Set;
import org.springframework.boot.actuate.endpoint.web.WebServerNamespace;
import org.springframework.boot.actuate.health.AdditionalHealthEndpointPath;
import org.springframework.boot.actuate.health.HealthEndpointGroup;
import org.springframework.boot.actuate.health.HealthEndpointGroups;
import org.springframework.util.Assert;
@@ -31,29 +33,39 @@ import org.springframework.util.Assert;
*
* @author Phillip Webb
* @author Brian Clozel
* @author Madhura Bhave
*/
class AvailabilityProbesHealthEndpointGroups implements HealthEndpointGroups {
private static final Map<String, AvailabilityProbesHealthEndpointGroup> GROUPS;
static {
Map<String, AvailabilityProbesHealthEndpointGroup> groups = new LinkedHashMap<>();
groups.put("liveness", new AvailabilityProbesHealthEndpointGroup("livenessState"));
groups.put("readiness", new AvailabilityProbesHealthEndpointGroup("readinessState"));
GROUPS = Collections.unmodifiableMap(groups);
}
private final HealthEndpointGroups groups;
private final Map<String, HealthEndpointGroup> probeGroups;
private final Set<String> names;
AvailabilityProbesHealthEndpointGroups(HealthEndpointGroups groups) {
AvailabilityProbesHealthEndpointGroups(HealthEndpointGroups groups, boolean addAdditionalPaths) {
Assert.notNull(groups, "Groups must not be null");
this.groups = groups;
this.probeGroups = createProbeGroups(addAdditionalPaths);
Set<String> names = new LinkedHashSet<>(groups.getNames());
names.addAll(GROUPS.keySet());
names.addAll(this.probeGroups.keySet());
this.names = Collections.unmodifiableSet(names);
}
private Map<String, HealthEndpointGroup> createProbeGroups(boolean addAdditionalPaths) {
Map<String, HealthEndpointGroup> probeGroups = new LinkedHashMap<>();
probeGroups.put("liveness", createProbeGroup(addAdditionalPaths, "/livez", "livenessState"));
probeGroups.put("readiness", createProbeGroup(addAdditionalPaths, "/readyz", "readinessState"));
return Collections.unmodifiableMap(probeGroups);
}
private AvailabilityProbesHealthEndpointGroup createProbeGroup(boolean addAdditionalPath, String path,
String members) {
AdditionalHealthEndpointPath additionalPath = (!addAdditionalPath) ? null
: AdditionalHealthEndpointPath.of(WebServerNamespace.SERVER, path);
return new AvailabilityProbesHealthEndpointGroup(additionalPath, members);
}
@Override
public HealthEndpointGroup getPrimary() {
return this.groups.getPrimary();
@@ -68,13 +80,14 @@ class AvailabilityProbesHealthEndpointGroups implements HealthEndpointGroups {
public HealthEndpointGroup get(String name) {
HealthEndpointGroup group = this.groups.get(name);
if (group == null) {
group = GROUPS.get(name);
group = this.probeGroups.get(name);
}
return group;
}
static boolean containsAllProbeGroups(HealthEndpointGroups groups) {
return groups.getNames().containsAll(GROUPS.keySet());
Set<String> names = groups.getNames();
return names.contains("liveness") && names.contains("readiness");
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2012-2020 the original author or authors.
* Copyright 2012-2021 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.
@@ -20,22 +20,31 @@ import org.springframework.boot.actuate.health.HealthEndpointGroups;
import org.springframework.boot.actuate.health.HealthEndpointGroupsPostProcessor;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
import org.springframework.core.env.Environment;
/**
* {@link HealthEndpointGroupsPostProcessor} to add
* {@link AvailabilityProbesHealthEndpointGroups}.
*
* @author Phillip Webb
* @author Madhura Bhave
*/
@Order(Ordered.LOWEST_PRECEDENCE)
class AvailabilityProbesHealthEndpointGroupsPostProcessor implements HealthEndpointGroupsPostProcessor {
private final boolean addAdditionalPaths;
AvailabilityProbesHealthEndpointGroupsPostProcessor(Environment environment) {
this.addAdditionalPaths = "true"
.equalsIgnoreCase(environment.getProperty("management.endpoint.health.probes.add-additional-paths"));
}
@Override
public HealthEndpointGroups postProcessHealthEndpointGroups(HealthEndpointGroups groups) {
if (AvailabilityProbesHealthEndpointGroups.containsAllProbeGroups(groups)) {
return groups;
}
return new AvailabilityProbesHealthEndpointGroups(groups);
return new AvailabilityProbesHealthEndpointGroups(groups, this.addAdditionalPaths);
}
}

View File

@@ -48,13 +48,13 @@ public class CloudFoundryReactiveHealthEndpointWebExtension {
@ReadOperation
public Mono<WebEndpointResponse<? extends HealthComponent>> health(ApiVersion apiVersion) {
return this.delegate.health(apiVersion, SecurityContext.NONE, true);
return this.delegate.health(apiVersion, null, SecurityContext.NONE, true);
}
@ReadOperation
public Mono<WebEndpointResponse<? extends HealthComponent>> health(ApiVersion apiVersion,
@Selector(match = Match.ALL_REMAINING) String... path) {
return this.delegate.health(apiVersion, SecurityContext.NONE, true, path);
return this.delegate.health(apiVersion, null, SecurityContext.NONE, true, path);
}
}

View File

@@ -46,13 +46,13 @@ public class CloudFoundryHealthEndpointWebExtension {
@ReadOperation
public WebEndpointResponse<HealthComponent> health(ApiVersion apiVersion) {
return this.delegate.health(apiVersion, SecurityContext.NONE, true);
return this.delegate.health(apiVersion, null, SecurityContext.NONE, true);
}
@ReadOperation
public WebEndpointResponse<HealthComponent> health(ApiVersion apiVersion,
@Selector(match = Match.ALL_REMAINING) String... path) {
return this.delegate.health(apiVersion, SecurityContext.NONE, true, path);
return this.delegate.health(apiVersion, null, SecurityContext.NONE, true, path);
}
}

View File

@@ -18,8 +18,11 @@ package org.springframework.boot.actuate.autoconfigure.endpoint.web.jersey;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;
import org.glassfish.jersey.server.ResourceConfig;
import org.glassfish.jersey.server.model.Resource;
@@ -27,7 +30,9 @@ import org.glassfish.jersey.server.model.Resource;
import org.springframework.boot.actuate.autoconfigure.endpoint.web.WebEndpointProperties;
import org.springframework.boot.actuate.autoconfigure.web.ManagementContextConfiguration;
import org.springframework.boot.actuate.autoconfigure.web.jersey.ManagementContextResourceConfigCustomizer;
import org.springframework.boot.actuate.autoconfigure.web.server.ConditionalOnManagementPort;
import org.springframework.boot.actuate.autoconfigure.web.server.ManagementPortType;
import org.springframework.boot.actuate.endpoint.EndpointId;
import org.springframework.boot.actuate.endpoint.ExposableEndpoint;
import org.springframework.boot.actuate.endpoint.annotation.Endpoint;
import org.springframework.boot.actuate.endpoint.web.EndpointLinksResolver;
@@ -36,8 +41,12 @@ import org.springframework.boot.actuate.endpoint.web.EndpointMediaTypes;
import org.springframework.boot.actuate.endpoint.web.ExposableServletEndpoint;
import org.springframework.boot.actuate.endpoint.web.ExposableWebEndpoint;
import org.springframework.boot.actuate.endpoint.web.WebEndpointsSupplier;
import org.springframework.boot.actuate.endpoint.web.WebServerNamespace;
import org.springframework.boot.actuate.endpoint.web.annotation.ServletEndpointsSupplier;
import org.springframework.boot.actuate.endpoint.web.jersey.JerseyEndpointResourceFactory;
import org.springframework.boot.actuate.endpoint.web.jersey.JerseyHealthEndpointAdditionalPathResourceFactory;
import org.springframework.boot.actuate.health.HealthEndpoint;
import org.springframework.boot.actuate.health.HealthEndpointGroups;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
@@ -64,6 +73,8 @@ import org.springframework.util.StringUtils;
@ConditionalOnMissingBean(type = "org.springframework.web.servlet.DispatcherServlet")
class JerseyWebEndpointManagementContextConfiguration {
private static final EndpointId HEALTH_ENDPOINT_ID = EndpointId.of("health");
@Bean
JerseyWebEndpointsResourcesRegistrar jerseyWebEndpointsResourcesRegistrar(Environment environment,
WebEndpointsSupplier webEndpointsSupplier, ServletEndpointsSupplier servletEndpointsSupplier,
@@ -74,6 +85,17 @@ class JerseyWebEndpointManagementContextConfiguration {
endpointMediaTypes, basePath, shouldRegisterLinks);
}
@Bean
@ConditionalOnBean(HealthEndpoint.class)
@ConditionalOnManagementPort(ManagementPortType.DIFFERENT)
JerseyAdditionalHealthEndpointPathsManagementResourcesRegistrar jerseyDifferentPortAdditionalHealthEndpointPathsResourcesRegistrar(
WebEndpointsSupplier webEndpointsSupplier, HealthEndpointGroups healthEndpointGroups) {
Collection<ExposableWebEndpoint> webEndpoints = webEndpointsSupplier.getEndpoints();
ExposableWebEndpoint health = webEndpoints.stream()
.filter((endpoint) -> endpoint.getEndpointId().equals(HEALTH_ENDPOINT_ID)).findFirst().get();
return new JerseyAdditionalHealthEndpointPathsManagementResourcesRegistrar(health, healthEndpointGroups);
}
private boolean shouldRegisterLinksMapping(WebEndpointProperties properties, Environment environment,
String basePath) {
return properties.getDiscovery().isEnabled() && (StringUtils.hasText(basePath)
@@ -134,4 +156,38 @@ class JerseyWebEndpointManagementContextConfiguration {
}
class JerseyAdditionalHealthEndpointPathsManagementResourcesRegistrar
implements ManagementContextResourceConfigCustomizer {
private final ExposableWebEndpoint endpoint;
private final HealthEndpointGroups groups;
JerseyAdditionalHealthEndpointPathsManagementResourcesRegistrar(ExposableWebEndpoint endpoint,
HealthEndpointGroups groups) {
this.endpoint = endpoint;
this.groups = groups;
}
@Override
public void customize(ResourceConfig config) {
register(config);
}
private void register(ResourceConfig config) {
EndpointMapping mapping = new EndpointMapping("");
JerseyHealthEndpointAdditionalPathResourceFactory resourceFactory = new JerseyHealthEndpointAdditionalPathResourceFactory(
WebServerNamespace.MANAGEMENT, this.groups);
Collection<Resource> endpointResources = resourceFactory
.createEndpointResources(mapping, Collections.singletonList(this.endpoint), null, null, false)
.stream().filter(Objects::nonNull).collect(Collectors.toList());
register(endpointResources, config);
}
private void register(Collection<Resource> resources, ResourceConfig config) {
config.registerResources(new HashSet<>(resources));
}
}
}

View File

@@ -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.ConditionalOnManagementPort;
import org.springframework.boot.actuate.autoconfigure.web.server.ManagementPortType;
import org.springframework.boot.actuate.endpoint.ExposableEndpoint;
import org.springframework.boot.actuate.endpoint.annotation.Endpoint;
@@ -31,9 +32,13 @@ import org.springframework.boot.actuate.endpoint.web.EndpointMapping;
import org.springframework.boot.actuate.endpoint.web.EndpointMediaTypes;
import org.springframework.boot.actuate.endpoint.web.ExposableWebEndpoint;
import org.springframework.boot.actuate.endpoint.web.WebEndpointsSupplier;
import org.springframework.boot.actuate.endpoint.web.WebServerNamespace;
import org.springframework.boot.actuate.endpoint.web.annotation.ControllerEndpointsSupplier;
import org.springframework.boot.actuate.endpoint.web.reactive.AdditionalHealthEndpointPathsWebFluxHandlerMapping;
import org.springframework.boot.actuate.endpoint.web.reactive.ControllerEndpointHandlerMapping;
import org.springframework.boot.actuate.endpoint.web.reactive.WebFluxEndpointHandlerMapping;
import org.springframework.boot.actuate.health.HealthEndpoint;
import org.springframework.boot.actuate.health.HealthEndpointGroups;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
@@ -84,6 +89,18 @@ public class WebFluxEndpointManagementContextConfiguration {
|| ManagementPortType.get(environment).equals(ManagementPortType.DIFFERENT));
}
@Bean
@ConditionalOnManagementPort(ManagementPortType.DIFFERENT)
@ConditionalOnBean(HealthEndpoint.class)
public AdditionalHealthEndpointPathsWebFluxHandlerMapping managementHealthEndpointWebFluxHandlerMapping(
WebEndpointsSupplier webEndpointsSupplier, HealthEndpointGroups groups) {
Collection<ExposableWebEndpoint> webEndpoints = webEndpointsSupplier.getEndpoints();
ExposableWebEndpoint health = webEndpoints.stream()
.filter((endpoint) -> endpoint.getEndpointId().equals(HealthEndpoint.ID)).findFirst().get();
return new AdditionalHealthEndpointPathsWebFluxHandlerMapping(new EndpointMapping(""), health,
groups.getAllWithAdditionalPath(WebServerNamespace.MANAGEMENT));
}
@Bean
@ConditionalOnMissingBean
public ControllerEndpointHandlerMapping controllerEndpointHandlerMapping(

View File

@@ -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.ConditionalOnManagementPort;
import org.springframework.boot.actuate.autoconfigure.web.server.ManagementPortType;
import org.springframework.boot.actuate.endpoint.ExposableEndpoint;
import org.springframework.boot.actuate.endpoint.annotation.Endpoint;
@@ -31,10 +32,14 @@ import org.springframework.boot.actuate.endpoint.web.EndpointMapping;
import org.springframework.boot.actuate.endpoint.web.EndpointMediaTypes;
import org.springframework.boot.actuate.endpoint.web.ExposableWebEndpoint;
import org.springframework.boot.actuate.endpoint.web.WebEndpointsSupplier;
import org.springframework.boot.actuate.endpoint.web.WebServerNamespace;
import org.springframework.boot.actuate.endpoint.web.annotation.ControllerEndpointsSupplier;
import org.springframework.boot.actuate.endpoint.web.annotation.ServletEndpointsSupplier;
import org.springframework.boot.actuate.endpoint.web.servlet.AdditionalHealthEndpointPathsWebMvcHandlerMapping;
import org.springframework.boot.actuate.endpoint.web.servlet.ControllerEndpointHandlerMapping;
import org.springframework.boot.actuate.endpoint.web.servlet.WebMvcEndpointHandlerMapping;
import org.springframework.boot.actuate.health.HealthEndpoint;
import org.springframework.boot.actuate.health.HealthEndpointGroups;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
@@ -86,6 +91,18 @@ public class WebMvcEndpointManagementContextConfiguration {
|| ManagementPortType.get(environment).equals(ManagementPortType.DIFFERENT));
}
@Bean
@ConditionalOnManagementPort(ManagementPortType.DIFFERENT)
@ConditionalOnBean(HealthEndpoint.class)
public AdditionalHealthEndpointPathsWebMvcHandlerMapping managementHealthEndpointWebMvcHandlerMapping(
WebEndpointsSupplier webEndpointsSupplier, HealthEndpointGroups groups) {
Collection<ExposableWebEndpoint> webEndpoints = webEndpointsSupplier.getEndpoints();
ExposableWebEndpoint health = webEndpoints.stream()
.filter((endpoint) -> endpoint.getEndpointId().equals(HealthEndpoint.ID)).findFirst().get();
return new AdditionalHealthEndpointPathsWebMvcHandlerMapping(health,
groups.getAllWithAdditionalPath(WebServerNamespace.MANAGEMENT));
}
@Bean
@ConditionalOnMissingBean
public ControllerEndpointHandlerMapping controllerEndpointHandlerMapping(

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2012-2020 the original author or authors.
* Copyright 2012-2021 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.
@@ -22,6 +22,7 @@ import java.util.function.Predicate;
import org.springframework.boot.actuate.autoconfigure.health.HealthProperties.Show;
import org.springframework.boot.actuate.endpoint.SecurityContext;
import org.springframework.boot.actuate.health.AdditionalHealthEndpointPath;
import org.springframework.boot.actuate.health.HealthEndpointGroup;
import org.springframework.boot.actuate.health.HttpCodeStatusMapper;
import org.springframework.boot.actuate.health.StatusAggregator;
@@ -35,6 +36,7 @@ import org.springframework.util.CollectionUtils;
*
* @author Phillip Webb
* @author Andy Wilkinson
* @author Madhura Bhave
*/
class AutoConfiguredHealthEndpointGroup implements HealthEndpointGroup {
@@ -50,6 +52,8 @@ class AutoConfiguredHealthEndpointGroup implements HealthEndpointGroup {
private final Collection<String> roles;
private final AdditionalHealthEndpointPath additionalPath;
/**
* Create a new {@link AutoConfiguredHealthEndpointGroup} instance.
* @param members a predicate used to test for group membership
@@ -58,16 +62,18 @@ class AutoConfiguredHealthEndpointGroup implements HealthEndpointGroup {
* @param showComponents the show components setting
* @param showDetails the show details setting
* @param roles the roles to match
* @param additionalPath the additional path to use for this group
*/
AutoConfiguredHealthEndpointGroup(Predicate<String> members, StatusAggregator statusAggregator,
HttpCodeStatusMapper httpCodeStatusMapper, Show showComponents, Show showDetails,
Collection<String> roles) {
HttpCodeStatusMapper httpCodeStatusMapper, Show showComponents, Show showDetails, Collection<String> roles,
AdditionalHealthEndpointPath additionalPath) {
this.members = members;
this.statusAggregator = statusAggregator;
this.httpCodeStatusMapper = httpCodeStatusMapper;
this.showComponents = showComponents;
this.showDetails = showDetails;
this.roles = roles;
this.additionalPath = additionalPath;
}
@Override
@@ -141,4 +147,9 @@ class AutoConfiguredHealthEndpointGroup implements HealthEndpointGroup {
return this.httpCodeStatusMapper;
}
@Override
public AdditionalHealthEndpointPath getAdditionalPath() {
return this.additionalPath;
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2012-2020 the original author or authors.
* Copyright 2012-2021 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.beans.factory.annotation.BeanFactoryAnnotationUtils;
import org.springframework.boot.actuate.autoconfigure.health.HealthEndpointProperties.Group;
import org.springframework.boot.actuate.autoconfigure.health.HealthProperties.Show;
import org.springframework.boot.actuate.autoconfigure.health.HealthProperties.Status;
import org.springframework.boot.actuate.health.AdditionalHealthEndpointPath;
import org.springframework.boot.actuate.health.HealthEndpointGroup;
import org.springframework.boot.actuate.health.HealthEndpointGroups;
import org.springframework.boot.actuate.health.HttpCodeStatusMapper;
@@ -48,6 +49,7 @@ import org.springframework.util.ObjectUtils;
* Auto-configured {@link HealthEndpointGroups}.
*
* @author Phillip Webb
* @author Madhura Bhave
*/
class AutoConfiguredHealthEndpointGroups implements HealthEndpointGroups {
@@ -77,7 +79,7 @@ class AutoConfiguredHealthEndpointGroups implements HealthEndpointGroups {
httpCodeStatusMapper = new SimpleHttpCodeStatusMapper(properties.getStatus().getHttpMapping());
}
this.primaryGroup = new AutoConfiguredHealthEndpointGroup(ALL, statusAggregator, httpCodeStatusMapper,
showComponents, showDetails, roles);
showComponents, showDetails, roles, null);
this.groups = createGroups(properties.getGroup(), beanFactory, statusAggregator, httpCodeStatusMapper,
showComponents, showDetails, roles);
}
@@ -106,8 +108,10 @@ class AutoConfiguredHealthEndpointGroups implements HealthEndpointGroups {
return defaultHttpCodeStatusMapper;
});
Predicate<String> members = new IncludeExcludeGroupMemberPredicate(group.getInclude(), group.getExclude());
AdditionalHealthEndpointPath additionalPath = (group.getAdditionalPath() != null)
? AdditionalHealthEndpointPath.from(group.getAdditionalPath()) : null;
groups.put(groupName, new AutoConfiguredHealthEndpointGroup(members, statusAggregator, httpCodeStatusMapper,
showComponents, showDetails, roles));
showComponents, showDetails, roles, additionalPath));
});
return Collections.unmodifiableMap(groups);
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2012-2020 the original author or authors.
* Copyright 2012-2021 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.
@@ -61,6 +61,10 @@ public class HealthEndpointProperties extends HealthProperties {
*/
public static class Group extends HealthProperties {
public static final String SERVER_PREFIX = "server:";
public static final String MANAGEMENT_PREFIX = "management:";
/**
* Health indicator IDs that should be included or '*' for all.
*/
@@ -77,6 +81,14 @@ public class HealthEndpointProperties extends HealthProperties {
*/
private Show showDetails;
/**
* Additional path that this group can be made available on. The additional path
* must start with a valid prefix, either `server` or `management` to indicate if
* it will be available on the main port or the management port. For instance,
* `server:/healthz` will configure the group on the main port at `/healthz`.
*/
private String additionalPath;
public Set<String> getInclude() {
return this.include;
}
@@ -102,6 +114,14 @@ public class HealthEndpointProperties extends HealthProperties {
this.showDetails = showDetails;
}
public String getAdditionalPath() {
return this.additionalPath;
}
public void setAdditionalPath(String additionalPath) {
this.additionalPath = additionalPath;
}
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2012-2019 the original author or authors.
* Copyright 2012-2021 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,6 +16,13 @@
package org.springframework.boot.actuate.autoconfigure.health;
import java.util.Collection;
import org.springframework.boot.actuate.endpoint.web.EndpointMapping;
import org.springframework.boot.actuate.endpoint.web.ExposableWebEndpoint;
import org.springframework.boot.actuate.endpoint.web.WebEndpointsSupplier;
import org.springframework.boot.actuate.endpoint.web.WebServerNamespace;
import org.springframework.boot.actuate.endpoint.web.reactive.AdditionalHealthEndpointPathsWebFluxHandlerMapping;
import org.springframework.boot.actuate.health.HealthEndpoint;
import org.springframework.boot.actuate.health.HealthEndpointGroups;
import org.springframework.boot.actuate.health.ReactiveHealthContributorRegistry;
@@ -31,6 +38,7 @@ import org.springframework.context.annotation.Configuration;
* Configuration for {@link HealthEndpoint} reactive web extensions.
*
* @author Phillip Webb
* @author Madhura Bhave
* @see HealthEndpointAutoConfiguration
*/
@Configuration(proxyBeanMethods = false)
@@ -46,4 +54,19 @@ class HealthEndpointReactiveWebExtensionConfiguration {
return new ReactiveHealthEndpointWebExtension(reactiveHealthContributorRegistry, groups);
}
@Configuration(proxyBeanMethods = false)
static class WebFluxAdditionalHealthEndpointPathsConfiguration {
@Bean
AdditionalHealthEndpointPathsWebFluxHandlerMapping healthEndpointWebFluxHandlerMapping(
WebEndpointsSupplier webEndpointsSupplier, HealthEndpointGroups groups) {
Collection<ExposableWebEndpoint> webEndpoints = webEndpointsSupplier.getEndpoints();
ExposableWebEndpoint health = webEndpoints.stream()
.filter((endpoint) -> endpoint.getEndpointId().equals(HealthEndpoint.ID)).findFirst().get();
return new AdditionalHealthEndpointPathsWebFluxHandlerMapping(new EndpointMapping(""), health,
groups.getAllWithAdditionalPath(WebServerNamespace.SERVER));
}
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2012-2019 the original author or authors.
* Copyright 2012-2021 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,21 +16,48 @@
package org.springframework.boot.actuate.autoconfigure.health;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.Objects;
import java.util.stream.Collectors;
import org.glassfish.jersey.server.ResourceConfig;
import org.glassfish.jersey.server.model.Resource;
import org.glassfish.jersey.servlet.ServletContainer;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.boot.actuate.endpoint.web.EndpointMapping;
import org.springframework.boot.actuate.endpoint.web.ExposableWebEndpoint;
import org.springframework.boot.actuate.endpoint.web.WebEndpointsSupplier;
import org.springframework.boot.actuate.endpoint.web.WebServerNamespace;
import org.springframework.boot.actuate.endpoint.web.jersey.JerseyHealthEndpointAdditionalPathResourceFactory;
import org.springframework.boot.actuate.endpoint.web.servlet.AdditionalHealthEndpointPathsWebMvcHandlerMapping;
import org.springframework.boot.actuate.health.HealthContributorRegistry;
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.autoconfigure.condition.ConditionalOnBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication;
import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication.Type;
import org.springframework.boot.autoconfigure.jersey.JerseyProperties;
import org.springframework.boot.autoconfigure.jersey.ResourceConfigCustomizer;
import org.springframework.boot.autoconfigure.web.servlet.DefaultJerseyApplicationPath;
import org.springframework.boot.autoconfigure.web.servlet.JerseyApplicationPath;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.boot.web.servlet.ServletRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.DispatcherServlet;
/**
* Configuration for {@link HealthEndpoint} web extensions.
*
* @author Phillip Webb
* @author Madhura Bhave
* @see HealthEndpointAutoConfiguration
*/
@Configuration(proxyBeanMethods = false)
@@ -46,4 +73,98 @@ class HealthEndpointWebExtensionConfiguration {
return new HealthEndpointWebExtension(healthContributorRegistry, groups);
}
private static ExposableWebEndpoint getHealthEndpoint(WebEndpointsSupplier webEndpointsSupplier) {
Collection<ExposableWebEndpoint> webEndpoints = webEndpointsSupplier.getEndpoints();
return webEndpoints.stream().filter((endpoint) -> endpoint.getEndpointId().equals(HealthEndpoint.ID))
.findFirst().get();
}
@Configuration(proxyBeanMethods = false)
@ConditionalOnBean(DispatcherServlet.class)
static class MvcAdditionalHealthEndpointPathsConfiguration {
@Bean
AdditionalHealthEndpointPathsWebMvcHandlerMapping healthEndpointWebMvcHandlerMapping(
WebEndpointsSupplier webEndpointsSupplier, HealthEndpointGroups groups) {
ExposableWebEndpoint health = getHealthEndpoint(webEndpointsSupplier);
return new AdditionalHealthEndpointPathsWebMvcHandlerMapping(health,
groups.getAllWithAdditionalPath(WebServerNamespace.SERVER));
}
}
@Configuration(proxyBeanMethods = false)
@ConditionalOnClass(ResourceConfig.class)
@ConditionalOnMissingClass("org.springframework.web.servlet.DispatcherServlet")
static class JerseyAdditionalHealthEndpointPathsConfiguration {
@Bean
JerseyAdditionalHealthEndpointPathsResourcesRegistrar jerseyAdditionalHealthEndpointPathsResourcesRegistrar(
WebEndpointsSupplier webEndpointsSupplier, HealthEndpointGroups healthEndpointGroups) {
ExposableWebEndpoint health = getHealthEndpoint(webEndpointsSupplier);
return new JerseyAdditionalHealthEndpointPathsResourcesRegistrar(health, healthEndpointGroups);
}
@Configuration(proxyBeanMethods = false)
@ConditionalOnMissingBean(ResourceConfig.class)
@EnableConfigurationProperties(JerseyProperties.class)
static class JerseyInfrastructureConfiguration {
@Bean
@ConditionalOnMissingBean(JerseyApplicationPath.class)
JerseyApplicationPath jerseyApplicationPath(JerseyProperties properties, ResourceConfig config) {
return new DefaultJerseyApplicationPath(properties.getApplicationPath(), config);
}
@Bean
ResourceConfig resourceConfig(ObjectProvider<ResourceConfigCustomizer> resourceConfigCustomizers) {
ResourceConfig resourceConfig = new ResourceConfig();
resourceConfigCustomizers.orderedStream().forEach((customizer) -> customizer.customize(resourceConfig));
return resourceConfig;
}
@Bean
ServletRegistrationBean<ServletContainer> jerseyServletRegistration(
JerseyApplicationPath jerseyApplicationPath, ResourceConfig resourceConfig) {
return new ServletRegistrationBean<>(new ServletContainer(resourceConfig),
jerseyApplicationPath.getUrlMapping());
}
}
}
static class JerseyAdditionalHealthEndpointPathsResourcesRegistrar implements ResourceConfigCustomizer {
private final ExposableWebEndpoint endpoint;
private final HealthEndpointGroups groups;
JerseyAdditionalHealthEndpointPathsResourcesRegistrar(ExposableWebEndpoint endpoint,
HealthEndpointGroups groups) {
this.endpoint = endpoint;
this.groups = groups;
}
@Override
public void customize(ResourceConfig config) {
register(config);
}
private void register(ResourceConfig config) {
EndpointMapping mapping = new EndpointMapping("");
JerseyHealthEndpointAdditionalPathResourceFactory resourceFactory = new JerseyHealthEndpointAdditionalPathResourceFactory(
WebServerNamespace.SERVER, this.groups);
Collection<Resource> endpointResources = resourceFactory
.createEndpointResources(mapping, Collections.singletonList(this.endpoint), null, null, false)
.stream().filter(Objects::nonNull).collect(Collectors.toList());
register(endpointResources, config);
}
private void register(Collection<Resource> resources, ResourceConfig config) {
config.registerResources(new HashSet<>(resources));
}
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2012-2020 the original author or authors.
* Copyright 2012-2021 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.
@@ -32,7 +32,7 @@ import static org.mockito.Mockito.mock;
*/
class AvailabilityProbesHealthEndpointGroupTests {
private AvailabilityProbesHealthEndpointGroup group = new AvailabilityProbesHealthEndpointGroup("a", "b");
private AvailabilityProbesHealthEndpointGroup group = new AvailabilityProbesHealthEndpointGroup(null, "a", "b");
@Test
void isMemberWhenMemberReturnsTrue() {

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2012-2020 the original author or authors.
* Copyright 2012-2021 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,9 @@ import java.util.Set;
import org.junit.jupiter.api.Test;
import org.springframework.boot.actuate.health.HealthEndpointGroup;
import org.springframework.boot.actuate.health.HealthEndpointGroups;
import org.springframework.mock.env.MockEnvironment;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.BDDMockito.given;
@@ -31,10 +33,12 @@ import static org.mockito.Mockito.mock;
* Tests for {@link AvailabilityProbesHealthEndpointGroupsPostProcessor}.
*
* @author Phillip Webb
* @author Madhura Bhave
*/
class AvailabilityProbesHealthEndpointGroupsPostProcessorTests {
private AvailabilityProbesHealthEndpointGroupsPostProcessor postProcessor = new AvailabilityProbesHealthEndpointGroupsPostProcessor();
private AvailabilityProbesHealthEndpointGroupsPostProcessor postProcessor = new AvailabilityProbesHealthEndpointGroupsPostProcessor(
new MockEnvironment());
@Test
void postProcessHealthEndpointGroupsWhenGroupsAlreadyContainedReturnsOriginal() {
@@ -68,7 +72,43 @@ class AvailabilityProbesHealthEndpointGroupsPostProcessorTests {
given(groups.getNames()).willReturn(names);
assertThat(this.postProcessor.postProcessHealthEndpointGroups(groups))
.isInstanceOf(AvailabilityProbesHealthEndpointGroups.class);
}
@Test
void postProcessHealthEndpointGroupsWhenAdditionalPathPropertyIsTrue() {
HealthEndpointGroups postProcessed = getPostProcessed("true");
HealthEndpointGroup liveness = postProcessed.get("liveness");
HealthEndpointGroup readiness = postProcessed.get("readiness");
assertThat(liveness.getAdditionalPath().toString()).isEqualTo("server:/livez");
assertThat(readiness.getAdditionalPath().toString()).isEqualTo("server:/readyz");
}
private HealthEndpointGroups getPostProcessed(String value) {
MockEnvironment environment = new MockEnvironment();
environment.setProperty("management.endpoint.health.probes.add-additional-paths", value);
AvailabilityProbesHealthEndpointGroupsPostProcessor postProcessor = new AvailabilityProbesHealthEndpointGroupsPostProcessor(
environment);
HealthEndpointGroups groups = mock(HealthEndpointGroups.class);
return postProcessor.postProcessHealthEndpointGroups(groups);
}
@Test
void postProcessHealthEndpointGroupsWhenAdditionalPathPropertyIsFalse() {
HealthEndpointGroups postProcessed = getPostProcessed("false");
HealthEndpointGroup liveness = postProcessed.get("liveness");
HealthEndpointGroup readiness = postProcessed.get("readiness");
assertThat(liveness.getAdditionalPath()).isNull();
assertThat(readiness.getAdditionalPath()).isNull();
}
@Test
void postProcessHealthEndpointGroupsWhenAdditionalPathPropertyIsNull() {
HealthEndpointGroups groups = mock(HealthEndpointGroups.class);
HealthEndpointGroups postProcessed = this.postProcessor.postProcessHealthEndpointGroups(groups);
HealthEndpointGroup liveness = postProcessed.get("liveness");
HealthEndpointGroup readiness = postProcessed.get("readiness");
assertThat(liveness.getAdditionalPath()).isNull();
assertThat(readiness.getAdditionalPath()).isNull();
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2012-2020 the original author or authors.
* Copyright 2012-2021 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.
@@ -50,46 +50,46 @@ class AvailabilityProbesHealthEndpointGroupsTests {
@Test
void createWhenGroupsIsNullThrowsException() {
assertThatIllegalArgumentException().isThrownBy(() -> new AvailabilityProbesHealthEndpointGroups(null))
assertThatIllegalArgumentException().isThrownBy(() -> new AvailabilityProbesHealthEndpointGroups(null, false))
.withMessage("Groups must not be null");
}
@Test
void getPrimaryDelegatesToGroups() {
given(this.delegate.getPrimary()).willReturn(this.group);
HealthEndpointGroups availabilityProbes = new AvailabilityProbesHealthEndpointGroups(this.delegate);
HealthEndpointGroups availabilityProbes = new AvailabilityProbesHealthEndpointGroups(this.delegate, false);
assertThat(availabilityProbes.getPrimary()).isEqualTo(this.group);
}
@Test
void getNamesIncludesAvailabilityProbeGroups() {
given(this.delegate.getNames()).willReturn(Collections.singleton("test"));
HealthEndpointGroups availabilityProbes = new AvailabilityProbesHealthEndpointGroups(this.delegate);
HealthEndpointGroups availabilityProbes = new AvailabilityProbesHealthEndpointGroups(this.delegate, false);
assertThat(availabilityProbes.getNames()).containsExactly("test", "liveness", "readiness");
}
@Test
void getWhenProbeInDelegateReturnsGroupFromDelegate() {
given(this.delegate.get("liveness")).willReturn(this.group);
HealthEndpointGroups availabilityProbes = new AvailabilityProbesHealthEndpointGroups(this.delegate);
HealthEndpointGroups availabilityProbes = new AvailabilityProbesHealthEndpointGroups(this.delegate, false);
assertThat(availabilityProbes.get("liveness")).isEqualTo(this.group);
}
@Test
void getWhenProbeNotInDelegateReturnsProbeGroup() {
HealthEndpointGroups availabilityProbes = new AvailabilityProbesHealthEndpointGroups(this.delegate);
HealthEndpointGroups availabilityProbes = new AvailabilityProbesHealthEndpointGroups(this.delegate, false);
assertThat(availabilityProbes.get("liveness")).isInstanceOf(AvailabilityProbesHealthEndpointGroup.class);
}
@Test
void getWhenNotProbeAndNotInDelegateReturnsNull() {
HealthEndpointGroups availabilityProbes = new AvailabilityProbesHealthEndpointGroups(this.delegate);
HealthEndpointGroups availabilityProbes = new AvailabilityProbesHealthEndpointGroups(this.delegate, false);
assertThat(availabilityProbes.get("mygroup")).isNull();
}
@Test
void getLivenessProbeHasOnlyLivenessStateAsMember() {
HealthEndpointGroups availabilityProbes = new AvailabilityProbesHealthEndpointGroups(this.delegate);
HealthEndpointGroups availabilityProbes = new AvailabilityProbesHealthEndpointGroups(this.delegate, false);
HealthEndpointGroup probeGroup = availabilityProbes.get("liveness");
assertThat(probeGroup.isMember("livenessState")).isTrue();
assertThat(probeGroup.isMember("readinessState")).isFalse();
@@ -97,7 +97,7 @@ class AvailabilityProbesHealthEndpointGroupsTests {
@Test
void getReadinessProbeHasOnlyReadinessStateAsMember() {
HealthEndpointGroups availabilityProbes = new AvailabilityProbesHealthEndpointGroups(this.delegate);
HealthEndpointGroups availabilityProbes = new AvailabilityProbesHealthEndpointGroups(this.delegate, false);
HealthEndpointGroup probeGroup = availabilityProbes.get("readiness");
assertThat(probeGroup.isMember("livenessState")).isFalse();
assertThat(probeGroup.isMember("readinessState")).isTrue();

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2012-2020 the original author or authors.
* Copyright 2012-2021 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,6 +28,7 @@ import javax.sql.DataSource;
import org.junit.jupiter.api.Test;
import org.springframework.boot.actuate.endpoint.SecurityContext;
import org.springframework.boot.actuate.health.AdditionalHealthEndpointPath;
import org.springframework.boot.actuate.health.CompositeHealthContributor;
import org.springframework.boot.actuate.health.DefaultHealthContributorRegistry;
import org.springframework.boot.actuate.health.Health;
@@ -164,6 +165,11 @@ class HealthEndpointDocumentationTests extends MockMvcEndpointDocumentationTests
return this.httpCodeStatusMapper;
}
@Override
public AdditionalHealthEndpointPath getAdditionalPath() {
return null;
}
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2012-2020 the original author or authors.
* Copyright 2012-2021 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.
@@ -59,7 +59,7 @@ class AutoConfiguredHealthEndpointGroupTests {
@Test
void isMemberWhenMemberPredicateMatchesAcceptsTrue() {
AutoConfiguredHealthEndpointGroup group = new AutoConfiguredHealthEndpointGroup((name) -> name.startsWith("a"),
this.statusAggregator, this.httpCodeStatusMapper, null, Show.ALWAYS, Collections.emptySet());
this.statusAggregator, this.httpCodeStatusMapper, null, Show.ALWAYS, Collections.emptySet(), null);
assertThat(group.isMember("albert")).isTrue();
assertThat(group.isMember("arnold")).isTrue();
}
@@ -67,7 +67,7 @@ class AutoConfiguredHealthEndpointGroupTests {
@Test
void isMemberWhenMemberPredicateRejectsReturnsTrue() {
AutoConfiguredHealthEndpointGroup group = new AutoConfiguredHealthEndpointGroup((name) -> name.startsWith("a"),
this.statusAggregator, this.httpCodeStatusMapper, null, Show.ALWAYS, Collections.emptySet());
this.statusAggregator, this.httpCodeStatusMapper, null, Show.ALWAYS, Collections.emptySet(), null);
assertThat(group.isMember("bert")).isFalse();
assertThat(group.isMember("ernie")).isFalse();
}
@@ -75,21 +75,22 @@ class AutoConfiguredHealthEndpointGroupTests {
@Test
void showDetailsWhenShowDetailsIsNeverReturnsFalse() {
AutoConfiguredHealthEndpointGroup group = new AutoConfiguredHealthEndpointGroup((name) -> true,
this.statusAggregator, this.httpCodeStatusMapper, null, Show.NEVER, Collections.emptySet());
this.statusAggregator, this.httpCodeStatusMapper, null, Show.NEVER, Collections.emptySet(), null);
assertThat(group.showDetails(SecurityContext.NONE)).isFalse();
}
@Test
void showDetailsWhenShowDetailsIsAlwaysReturnsTrue() {
AutoConfiguredHealthEndpointGroup group = new AutoConfiguredHealthEndpointGroup((name) -> true,
this.statusAggregator, this.httpCodeStatusMapper, null, Show.ALWAYS, Collections.emptySet());
this.statusAggregator, this.httpCodeStatusMapper, null, Show.ALWAYS, Collections.emptySet(), null);
assertThat(group.showDetails(SecurityContext.NONE)).isTrue();
}
@Test
void showDetailsWhenShowDetailsIsWhenAuthorizedAndPrincipalIsNullReturnsFalse() {
AutoConfiguredHealthEndpointGroup group = new AutoConfiguredHealthEndpointGroup((name) -> true,
this.statusAggregator, this.httpCodeStatusMapper, null, Show.WHEN_AUTHORIZED, Collections.emptySet());
this.statusAggregator, this.httpCodeStatusMapper, null, Show.WHEN_AUTHORIZED, Collections.emptySet(),
null);
given(this.securityContext.getPrincipal()).willReturn(null);
assertThat(group.showDetails(this.securityContext)).isFalse();
}
@@ -97,7 +98,8 @@ class AutoConfiguredHealthEndpointGroupTests {
@Test
void showDetailsWhenShowDetailsIsWhenAuthorizedAndRolesAreEmptyReturnsTrue() {
AutoConfiguredHealthEndpointGroup group = new AutoConfiguredHealthEndpointGroup((name) -> true,
this.statusAggregator, this.httpCodeStatusMapper, null, Show.WHEN_AUTHORIZED, Collections.emptySet());
this.statusAggregator, this.httpCodeStatusMapper, null, Show.WHEN_AUTHORIZED, Collections.emptySet(),
null);
given(this.securityContext.getPrincipal()).willReturn(this.principal);
assertThat(group.showDetails(this.securityContext)).isTrue();
}
@@ -106,7 +108,7 @@ class AutoConfiguredHealthEndpointGroupTests {
void showDetailsWhenShowDetailsIsWhenAuthorizedAndUseIsInRoleReturnsTrue() {
AutoConfiguredHealthEndpointGroup group = new AutoConfiguredHealthEndpointGroup((name) -> true,
this.statusAggregator, this.httpCodeStatusMapper, null, Show.WHEN_AUTHORIZED,
Arrays.asList("admin", "root", "bossmode"));
Arrays.asList("admin", "root", "bossmode"), null);
given(this.securityContext.getPrincipal()).willReturn(this.principal);
given(this.securityContext.isUserInRole("admin")).willReturn(false);
given(this.securityContext.isUserInRole("root")).willReturn(true);
@@ -117,7 +119,7 @@ class AutoConfiguredHealthEndpointGroupTests {
void showDetailsWhenShowDetailsIsWhenAuthorizedAndUserIsNotInRoleReturnsFalse() {
AutoConfiguredHealthEndpointGroup group = new AutoConfiguredHealthEndpointGroup((name) -> true,
this.statusAggregator, this.httpCodeStatusMapper, null, Show.WHEN_AUTHORIZED,
Arrays.asList("admin", "root", "bossmode"));
Arrays.asList("admin", "root", "bossmode"), null);
given(this.securityContext.getPrincipal()).willReturn(this.principal);
assertThat(group.showDetails(this.securityContext)).isFalse();
}
@@ -126,7 +128,7 @@ class AutoConfiguredHealthEndpointGroupTests {
void showDetailsWhenShowDetailsIsWhenAuthorizedAndUserHasRightAuthorityReturnsTrue() {
AutoConfiguredHealthEndpointGroup group = new AutoConfiguredHealthEndpointGroup((name) -> true,
this.statusAggregator, this.httpCodeStatusMapper, null, Show.WHEN_AUTHORIZED,
Arrays.asList("admin", "root", "bossmode"));
Arrays.asList("admin", "root", "bossmode"), null);
Authentication principal = mock(Authentication.class);
given(principal.getAuthorities())
.willAnswer((invocation) -> Collections.singleton(new SimpleGrantedAuthority("admin")));
@@ -138,7 +140,7 @@ class AutoConfiguredHealthEndpointGroupTests {
void showDetailsWhenShowDetailsIsWhenAuthorizedAndUserDoesNotHaveRightAuthoritiesReturnsFalse() {
AutoConfiguredHealthEndpointGroup group = new AutoConfiguredHealthEndpointGroup((name) -> true,
this.statusAggregator, this.httpCodeStatusMapper, null, Show.WHEN_AUTHORIZED,
Arrays.asList("admin", "rot", "bossmode"));
Arrays.asList("admin", "rot", "bossmode"), null);
Authentication principal = mock(Authentication.class);
given(principal.getAuthorities())
.willAnswer((invocation) -> Collections.singleton(new SimpleGrantedAuthority("other")));
@@ -149,24 +151,26 @@ class AutoConfiguredHealthEndpointGroupTests {
@Test
void showComponentsWhenShowComponentsIsNullDelegatesToShowDetails() {
AutoConfiguredHealthEndpointGroup alwaysGroup = new AutoConfiguredHealthEndpointGroup((name) -> true,
this.statusAggregator, this.httpCodeStatusMapper, null, Show.ALWAYS, Collections.emptySet());
this.statusAggregator, this.httpCodeStatusMapper, null, Show.ALWAYS, Collections.emptySet(), null);
assertThat(alwaysGroup.showComponents(SecurityContext.NONE)).isTrue();
AutoConfiguredHealthEndpointGroup neverGroup = new AutoConfiguredHealthEndpointGroup((name) -> true,
this.statusAggregator, this.httpCodeStatusMapper, null, Show.NEVER, Collections.emptySet());
this.statusAggregator, this.httpCodeStatusMapper, null, Show.NEVER, Collections.emptySet(), null);
assertThat(neverGroup.showComponents(SecurityContext.NONE)).isFalse();
}
@Test
void showComponentsWhenShowComponentsIsNeverReturnsFalse() {
AutoConfiguredHealthEndpointGroup group = new AutoConfiguredHealthEndpointGroup((name) -> true,
this.statusAggregator, this.httpCodeStatusMapper, Show.NEVER, Show.ALWAYS, Collections.emptySet());
this.statusAggregator, this.httpCodeStatusMapper, Show.NEVER, Show.ALWAYS, Collections.emptySet(),
null);
assertThat(group.showComponents(SecurityContext.NONE)).isFalse();
}
@Test
void showComponentsWhenShowComponentsIsAlwaysReturnsTrue() {
AutoConfiguredHealthEndpointGroup group = new AutoConfiguredHealthEndpointGroup((name) -> true,
this.statusAggregator, this.httpCodeStatusMapper, Show.ALWAYS, Show.NEVER, Collections.emptySet());
this.statusAggregator, this.httpCodeStatusMapper, Show.ALWAYS, Show.NEVER, Collections.emptySet(),
null);
assertThat(group.showComponents(SecurityContext.NONE)).isTrue();
}
@@ -174,7 +178,7 @@ class AutoConfiguredHealthEndpointGroupTests {
void showComponentsWhenShowComponentsIsWhenAuthorizedAndPrincipalIsNullReturnsFalse() {
AutoConfiguredHealthEndpointGroup group = new AutoConfiguredHealthEndpointGroup((name) -> true,
this.statusAggregator, this.httpCodeStatusMapper, Show.WHEN_AUTHORIZED, Show.NEVER,
Collections.emptySet());
Collections.emptySet(), null);
given(this.securityContext.getPrincipal()).willReturn(null);
assertThat(group.showComponents(this.securityContext)).isFalse();
}
@@ -183,7 +187,7 @@ class AutoConfiguredHealthEndpointGroupTests {
void showComponentsWhenShowComponentsIsWhenAuthorizedAndRolesAreEmptyReturnsTrue() {
AutoConfiguredHealthEndpointGroup group = new AutoConfiguredHealthEndpointGroup((name) -> true,
this.statusAggregator, this.httpCodeStatusMapper, Show.WHEN_AUTHORIZED, Show.NEVER,
Collections.emptySet());
Collections.emptySet(), null);
given(this.securityContext.getPrincipal()).willReturn(this.principal);
assertThat(group.showComponents(this.securityContext)).isTrue();
}
@@ -192,7 +196,7 @@ class AutoConfiguredHealthEndpointGroupTests {
void showComponentsWhenShowComponentsIsWhenAuthorizedAndUseIsInRoleReturnsTrue() {
AutoConfiguredHealthEndpointGroup group = new AutoConfiguredHealthEndpointGroup((name) -> true,
this.statusAggregator, this.httpCodeStatusMapper, Show.WHEN_AUTHORIZED, Show.NEVER,
Arrays.asList("admin", "root", "bossmode"));
Arrays.asList("admin", "root", "bossmode"), null);
given(this.securityContext.getPrincipal()).willReturn(this.principal);
given(this.securityContext.isUserInRole("admin")).willReturn(false);
given(this.securityContext.isUserInRole("root")).willReturn(true);
@@ -203,7 +207,7 @@ class AutoConfiguredHealthEndpointGroupTests {
void showComponentsWhenShowComponentsIsWhenAuthorizedAndUserIsNotInRoleReturnsFalse() {
AutoConfiguredHealthEndpointGroup group = new AutoConfiguredHealthEndpointGroup((name) -> true,
this.statusAggregator, this.httpCodeStatusMapper, Show.WHEN_AUTHORIZED, Show.NEVER,
Arrays.asList("admin", "rot", "bossmode"));
Arrays.asList("admin", "rot", "bossmode"), null);
given(this.securityContext.getPrincipal()).willReturn(this.principal);
assertThat(group.showComponents(this.securityContext)).isFalse();
}
@@ -212,7 +216,7 @@ class AutoConfiguredHealthEndpointGroupTests {
void showComponentsWhenShowComponentsIsWhenAuthorizedAndUserHasRightAuthoritiesReturnsTrue() {
AutoConfiguredHealthEndpointGroup group = new AutoConfiguredHealthEndpointGroup((name) -> true,
this.statusAggregator, this.httpCodeStatusMapper, Show.WHEN_AUTHORIZED, Show.NEVER,
Arrays.asList("admin", "root", "bossmode"));
Arrays.asList("admin", "root", "bossmode"), null);
Authentication principal = mock(Authentication.class);
given(principal.getAuthorities())
.willAnswer((invocation) -> Collections.singleton(new SimpleGrantedAuthority("admin")));
@@ -224,7 +228,7 @@ class AutoConfiguredHealthEndpointGroupTests {
void showComponentsWhenShowComponentsIsWhenAuthorizedAndUserDoesNotHaveRightAuthoritiesReturnsFalse() {
AutoConfiguredHealthEndpointGroup group = new AutoConfiguredHealthEndpointGroup((name) -> true,
this.statusAggregator, this.httpCodeStatusMapper, Show.WHEN_AUTHORIZED, Show.NEVER,
Arrays.asList("admin", "rot", "bossmode"));
Arrays.asList("admin", "rot", "bossmode"), null);
Authentication principal = mock(Authentication.class);
given(principal.getAuthorities())
.willAnswer((invocation) -> Collections.singleton(new SimpleGrantedAuthority("other")));
@@ -235,14 +239,14 @@ class AutoConfiguredHealthEndpointGroupTests {
@Test
void getStatusAggregatorReturnsStatusAggregator() {
AutoConfiguredHealthEndpointGroup group = new AutoConfiguredHealthEndpointGroup((name) -> true,
this.statusAggregator, this.httpCodeStatusMapper, null, Show.ALWAYS, Collections.emptySet());
this.statusAggregator, this.httpCodeStatusMapper, null, Show.ALWAYS, Collections.emptySet(), null);
assertThat(group.getStatusAggregator()).isSameAs(this.statusAggregator);
}
@Test
void getHttpCodeStatusMapperReturnsHttpCodeStatusMapper() {
AutoConfiguredHealthEndpointGroup group = new AutoConfiguredHealthEndpointGroup((name) -> true,
this.statusAggregator, this.httpCodeStatusMapper, null, Show.ALWAYS, Collections.emptySet());
this.statusAggregator, this.httpCodeStatusMapper, null, Show.ALWAYS, Collections.emptySet(), null);
assertThat(group.getHttpCodeStatusMapper()).isSameAs(this.httpCodeStatusMapper);
}

View File

@@ -22,9 +22,12 @@ import org.junit.jupiter.api.Test;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
import org.springframework.boot.actuate.autoconfigure.endpoint.EndpointAutoConfiguration;
import org.springframework.boot.actuate.autoconfigure.endpoint.web.WebEndpointAutoConfiguration;
import org.springframework.boot.actuate.endpoint.ApiVersion;
import org.springframework.boot.actuate.endpoint.SecurityContext;
import org.springframework.boot.actuate.endpoint.web.WebEndpointResponse;
import org.springframework.boot.actuate.endpoint.web.WebServerNamespace;
import org.springframework.boot.actuate.health.DefaultHealthContributorRegistry;
import org.springframework.boot.actuate.health.DefaultReactiveHealthContributorRegistry;
import org.springframework.boot.actuate.health.Health;
@@ -69,8 +72,10 @@ class HealthEndpointAutoConfigurationTests {
.of(HealthContributorAutoConfiguration.class, HealthEndpointAutoConfiguration.class));
private final ReactiveWebApplicationContextRunner reactiveContextRunner = new ReactiveWebApplicationContextRunner()
.withUserConfiguration(HealthIndicatorsConfiguration.class).withConfiguration(AutoConfigurations
.of(HealthContributorAutoConfiguration.class, HealthEndpointAutoConfiguration.class));
.withUserConfiguration(HealthIndicatorsConfiguration.class)
.withConfiguration(AutoConfigurations.of(HealthContributorAutoConfiguration.class,
HealthEndpointAutoConfiguration.class, WebEndpointAutoConfiguration.class,
EndpointAutoConfiguration.class));
@Test
void runWhenHealthEndpointIsDisabledDoesNotCreateBeans() {
@@ -208,8 +213,8 @@ class HealthEndpointAutoConfigurationTests {
void runCreatesHealthEndpointWebExtension() {
this.contextRunner.run((context) -> {
HealthEndpointWebExtension webExtension = context.getBean(HealthEndpointWebExtension.class);
WebEndpointResponse<HealthComponent> response = webExtension.health(ApiVersion.V3, SecurityContext.NONE,
true, "simple");
WebEndpointResponse<HealthComponent> response = webExtension.health(ApiVersion.V3,
WebServerNamespace.SERVER, SecurityContext.NONE, true, "simple");
Health health = (Health) response.getBody();
assertThat(response.getStatus()).isEqualTo(200);
assertThat(health.getDetails()).containsEntry("counter", 42);
@@ -220,8 +225,8 @@ class HealthEndpointAutoConfigurationTests {
void runWhenHasHealthEndpointWebExtensionBeanDoesNotCreateExtraHealthEndpointWebExtension() {
this.contextRunner.withUserConfiguration(HealthEndpointWebExtensionConfiguration.class).run((context) -> {
HealthEndpointWebExtension webExtension = context.getBean(HealthEndpointWebExtension.class);
WebEndpointResponse<HealthComponent> response = webExtension.health(ApiVersion.V3, SecurityContext.NONE,
true, "simple");
WebEndpointResponse<HealthComponent> response = webExtension.health(ApiVersion.V3,
WebServerNamespace.SERVER, SecurityContext.NONE, true, "simple");
assertThat(response).isNull();
});
}
@@ -231,7 +236,7 @@ class HealthEndpointAutoConfigurationTests {
this.reactiveContextRunner.run((context) -> {
ReactiveHealthEndpointWebExtension webExtension = context.getBean(ReactiveHealthEndpointWebExtension.class);
Mono<WebEndpointResponse<? extends HealthComponent>> response = webExtension.health(ApiVersion.V3,
SecurityContext.NONE, true, "simple");
WebServerNamespace.SERVER, SecurityContext.NONE, true, "simple");
Health health = (Health) (response.block().getBody());
assertThat(health.getDetails()).containsEntry("counter", 42);
});
@@ -244,7 +249,7 @@ class HealthEndpointAutoConfigurationTests {
ReactiveHealthEndpointWebExtension webExtension = context
.getBean(ReactiveHealthEndpointWebExtension.class);
Mono<WebEndpointResponse<? extends HealthComponent>> response = webExtension.health(ApiVersion.V3,
SecurityContext.NONE, true, "simple");
WebServerNamespace.SERVER, SecurityContext.NONE, true, "simple");
assertThat(response).isNull();
});
}

View File

@@ -0,0 +1,95 @@
/*
* Copyright 2012-2021 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.integrationtest;
import java.util.function.Consumer;
import org.junit.jupiter.api.Test;
import org.springframework.boot.autoconfigure.logging.ConditionEvaluationReportLoggingListener;
import org.springframework.boot.test.context.assertj.ApplicationContextAssertProvider;
import org.springframework.boot.test.context.runner.AbstractApplicationContextRunner;
import org.springframework.boot.test.context.runner.ContextConsumer;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.http.MediaType;
import org.springframework.test.web.reactive.server.WebTestClient;
/**
* Abstract base class for health groups with an additional path.
*
* @param <T> the runner
* @param <C> the application context type
* @param <A> the assertions
* @author Madhura Bhave
*/
abstract class AbstractHealthEndpointAdditionalPathIntegrationTests<T extends AbstractApplicationContextRunner<T, C, A>, C extends ConfigurableApplicationContext, A extends ApplicationContextAssertProvider<C>> {
private final T runner;
AbstractHealthEndpointAdditionalPathIntegrationTests(T runner) {
this.runner = runner;
}
@Test
void groupIsAvailableAtAdditionalPath() {
this.runner
.withPropertyValues("management.endpoint.health.group.live.include=diskSpace",
"management.endpoint.health.group.live.additional-path=server:/healthz",
"management.endpoint.health.group.live.show-components=always")
.run(withWebTestClient(this::testResponse, "local.server.port"));
}
@Test
void groupIsAvailableAtAdditionalPathWithoutSlash() {
this.runner
.withPropertyValues("management.endpoint.health.group.live.include=diskSpace",
"management.endpoint.health.group.live.additional-path=server:healthz",
"management.endpoint.health.group.live.show-components=always")
.run(withWebTestClient(this::testResponse, "local.server.port"));
}
@Test
void groupIsAvailableAtAdditionalPathOnManagementPort() {
this.runner.withPropertyValues("management.endpoint.health.group.live.include=diskSpace",
"management.server.port=0", "management.endpoint.health.group.live.additional-path=management:healthz",
"management.endpoint.health.group.live.show-components=always")
.run(withWebTestClient(this::testResponse, "local.management.port"));
}
@Test
void groupIsAvailableAtAdditionalPathOnServerPortWithDifferentManagementPort() {
this.runner.withPropertyValues("management.endpoint.health.group.live.include=diskSpace",
"management.server.port=0", "management.endpoint.health.group.live.additional-path=server:healthz",
"management.endpoint.health.group.live.show-components=always")
.withInitializer(new ConditionEvaluationReportLoggingListener())
.run(withWebTestClient(this::testResponse, "local.server.port"));
}
private void testResponse(WebTestClient client) {
client.get().uri("/healthz").accept(MediaType.APPLICATION_JSON).exchange().expectStatus().isOk().expectBody()
.jsonPath("status").isEqualTo("UP").jsonPath("components.diskSpace").exists();
}
private ContextConsumer<A> withWebTestClient(Consumer<WebTestClient> consumer, String property) {
return (context) -> {
String port = context.getEnvironment().getProperty(property);
WebTestClient client = WebTestClient.bindToServer().baseUrl("http://localhost:" + port).build();
consumer.accept(client);
};
}
}

View File

@@ -0,0 +1,56 @@
/*
* Copyright 2012-2021 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.integrationtest;
import org.springframework.boot.actuate.autoconfigure.endpoint.EndpointAutoConfiguration;
import org.springframework.boot.actuate.autoconfigure.endpoint.web.WebEndpointAutoConfiguration;
import org.springframework.boot.actuate.autoconfigure.health.HealthEndpointAutoConfiguration;
import org.springframework.boot.actuate.autoconfigure.system.DiskSpaceHealthContributorAutoConfiguration;
import org.springframework.boot.actuate.autoconfigure.web.server.ManagementContextAutoConfiguration;
import org.springframework.boot.actuate.autoconfigure.web.servlet.ServletManagementContextAutoConfiguration;
import org.springframework.boot.autoconfigure.AutoConfigurations;
import org.springframework.boot.autoconfigure.jackson.JacksonAutoConfiguration;
import org.springframework.boot.autoconfigure.jersey.JerseyAutoConfiguration;
import org.springframework.boot.autoconfigure.web.servlet.ServletWebServerFactoryAutoConfiguration;
import org.springframework.boot.test.context.FilteredClassLoader;
import org.springframework.boot.test.context.assertj.AssertableWebApplicationContext;
import org.springframework.boot.test.context.runner.WebApplicationContextRunner;
import org.springframework.boot.web.context.ServerPortInfoApplicationContextInitializer;
import org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext;
import org.springframework.web.context.ConfigurableWebApplicationContext;
import org.springframework.web.servlet.DispatcherServlet;
/**
* Integration tests for health groups on an additional path on Jersey.
*
* @author Madhura Bhave
*/
class JerseyHealthEndpointAdditionalPathIntegrationTests extends
AbstractHealthEndpointAdditionalPathIntegrationTests<WebApplicationContextRunner, ConfigurableWebApplicationContext, AssertableWebApplicationContext> {
JerseyHealthEndpointAdditionalPathIntegrationTests() {
super(new WebApplicationContextRunner(AnnotationConfigServletWebServerApplicationContext::new)
.withConfiguration(AutoConfigurations.of(JacksonAutoConfiguration.class, JerseyAutoConfiguration.class,
EndpointAutoConfiguration.class, ServletWebServerFactoryAutoConfiguration.class,
WebEndpointAutoConfiguration.class, JerseyAutoConfiguration.class,
ManagementContextAutoConfiguration.class, ServletManagementContextAutoConfiguration.class,
HealthEndpointAutoConfiguration.class, DiskSpaceHealthContributorAutoConfiguration.class))
.withInitializer(new ServerPortInfoApplicationContextInitializer())
.withClassLoader(new FilteredClassLoader(DispatcherServlet.class)).withPropertyValues("server.port=0"));
}
}

View File

@@ -0,0 +1,57 @@
/*
* Copyright 2012-2021 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.integrationtest;
import org.springframework.boot.actuate.autoconfigure.endpoint.EndpointAutoConfiguration;
import org.springframework.boot.actuate.autoconfigure.endpoint.web.WebEndpointAutoConfiguration;
import org.springframework.boot.actuate.autoconfigure.health.HealthEndpointAutoConfiguration;
import org.springframework.boot.actuate.autoconfigure.system.DiskSpaceHealthContributorAutoConfiguration;
import org.springframework.boot.actuate.autoconfigure.web.server.ManagementContextAutoConfiguration;
import org.springframework.boot.actuate.autoconfigure.web.servlet.ServletManagementContextAutoConfiguration;
import org.springframework.boot.autoconfigure.AutoConfigurations;
import org.springframework.boot.autoconfigure.http.HttpMessageConvertersAutoConfiguration;
import org.springframework.boot.autoconfigure.jackson.JacksonAutoConfiguration;
import org.springframework.boot.autoconfigure.web.servlet.DispatcherServletAutoConfiguration;
import org.springframework.boot.autoconfigure.web.servlet.ServletWebServerFactoryAutoConfiguration;
import org.springframework.boot.autoconfigure.web.servlet.WebMvcAutoConfiguration;
import org.springframework.boot.test.context.assertj.AssertableWebApplicationContext;
import org.springframework.boot.test.context.runner.WebApplicationContextRunner;
import org.springframework.boot.web.context.ServerPortInfoApplicationContextInitializer;
import org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext;
import org.springframework.web.context.ConfigurableWebApplicationContext;
/**
* Integration tests for MVC health groups on an additional path.
*
* @author Madhura Bhave
*/
class WebMvcHealthEndpointAdditionalPathIntegrationTests extends
AbstractHealthEndpointAdditionalPathIntegrationTests<WebApplicationContextRunner, ConfigurableWebApplicationContext, AssertableWebApplicationContext> {
WebMvcHealthEndpointAdditionalPathIntegrationTests() {
super(new WebApplicationContextRunner(AnnotationConfigServletWebServerApplicationContext::new)
.withConfiguration(AutoConfigurations.of(JacksonAutoConfiguration.class,
HttpMessageConvertersAutoConfiguration.class, ManagementContextAutoConfiguration.class,
ServletWebServerFactoryAutoConfiguration.class, WebMvcAutoConfiguration.class,
ServletManagementContextAutoConfiguration.class, WebEndpointAutoConfiguration.class,
EndpointAutoConfiguration.class, DispatcherServletAutoConfiguration.class,
HealthEndpointAutoConfiguration.class, DiskSpaceHealthContributorAutoConfiguration.class))
.withInitializer(new ServerPortInfoApplicationContextInitializer())
.withPropertyValues("server.port=0"));
}
}

View File

@@ -0,0 +1,58 @@
/*
* Copyright 2012-2021 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.integrationtest;
import org.springframework.boot.actuate.autoconfigure.beans.BeansEndpointAutoConfiguration;
import org.springframework.boot.actuate.autoconfigure.endpoint.EndpointAutoConfiguration;
import org.springframework.boot.actuate.autoconfigure.endpoint.web.WebEndpointAutoConfiguration;
import org.springframework.boot.actuate.autoconfigure.health.HealthEndpointAutoConfiguration;
import org.springframework.boot.actuate.autoconfigure.system.DiskSpaceHealthContributorAutoConfiguration;
import org.springframework.boot.actuate.autoconfigure.web.reactive.ReactiveManagementContextAutoConfiguration;
import org.springframework.boot.actuate.autoconfigure.web.server.ManagementContextAutoConfiguration;
import org.springframework.boot.autoconfigure.AutoConfigurations;
import org.springframework.boot.autoconfigure.http.codec.CodecsAutoConfiguration;
import org.springframework.boot.autoconfigure.jackson.JacksonAutoConfiguration;
import org.springframework.boot.autoconfigure.web.reactive.HttpHandlerAutoConfiguration;
import org.springframework.boot.autoconfigure.web.reactive.ReactiveWebServerFactoryAutoConfiguration;
import org.springframework.boot.autoconfigure.web.reactive.WebFluxAutoConfiguration;
import org.springframework.boot.test.context.assertj.AssertableReactiveWebApplicationContext;
import org.springframework.boot.test.context.runner.ReactiveWebApplicationContextRunner;
import org.springframework.boot.web.context.ServerPortInfoApplicationContextInitializer;
import org.springframework.boot.web.reactive.context.AnnotationConfigReactiveWebServerApplicationContext;
import org.springframework.boot.web.reactive.context.ConfigurableReactiveWebApplicationContext;
/**
* Integration tests for Webflux health groups on an additional path.
*
* @author Madhura Bhave
*/
class WebfluxHealthEndpointAdditionalPathIntegrationTests extends
AbstractHealthEndpointAdditionalPathIntegrationTests<ReactiveWebApplicationContextRunner, ConfigurableReactiveWebApplicationContext, AssertableReactiveWebApplicationContext> {
WebfluxHealthEndpointAdditionalPathIntegrationTests() {
super(new ReactiveWebApplicationContextRunner(AnnotationConfigReactiveWebServerApplicationContext::new)
.withConfiguration(AutoConfigurations.of(JacksonAutoConfiguration.class, CodecsAutoConfiguration.class,
WebFluxAutoConfiguration.class, HttpHandlerAutoConfiguration.class,
EndpointAutoConfiguration.class, HealthEndpointAutoConfiguration.class,
DiskSpaceHealthContributorAutoConfiguration.class, WebEndpointAutoConfiguration.class,
ManagementContextAutoConfiguration.class, ReactiveWebServerFactoryAutoConfiguration.class,
ReactiveManagementContextAutoConfiguration.class, BeansEndpointAutoConfiguration.class))
.withInitializer(new ServerPortInfoApplicationContextInitializer())
.withPropertyValues("server.port=0"));
}
}