diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/availability/AvailabilityProbesAutoConfiguration.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/availability/AvailabilityProbesAutoConfiguration.java index 5b130eee37..aea64fd36e 100644 --- a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/availability/AvailabilityProbesAutoConfiguration.java +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/availability/AvailabilityProbesAutoConfiguration.java @@ -20,7 +20,6 @@ import org.springframework.boot.actuate.autoconfigure.availability.AvailabilityP import org.springframework.boot.actuate.autoconfigure.health.ConditionalOnEnabledHealthIndicator; import org.springframework.boot.actuate.availability.LivenessStateHealthIndicator; import org.springframework.boot.actuate.availability.ReadinessStateHealthIndicator; -import org.springframework.boot.actuate.health.HealthEndpointGroupsRegistryCustomizer; import org.springframework.boot.autoconfigure.AutoConfigureAfter; import org.springframework.boot.autoconfigure.EnableAutoConfiguration; import org.springframework.boot.autoconfigure.availability.ApplicationAvailabilityAutoConfiguration; @@ -38,11 +37,10 @@ import org.springframework.core.env.Environment; import org.springframework.core.type.AnnotatedTypeMetadata; /** - * {@link EnableAutoConfiguration Auto-configuration} for - * {@link LivenessStateHealthIndicator}, {@link ReadinessStateHealthIndicator} and - * {@link HealthEndpointGroupsRegistryCustomizer}. + * {@link EnableAutoConfiguration Auto-configuration} for availability probes. * * @author Brian Clozel + * @author Phillip Webb * @since 2.3.0 */ @Configuration(proxyBeanMethods = false) @@ -66,8 +64,8 @@ public class AvailabilityProbesAutoConfiguration { } @Bean - public HealthEndpointGroupsRegistryCustomizer probesRegistryCustomizer() { - return new AvailabilityProbesHealthEndpointGroupsRegistrar(); + public AvailabilityProbesHealthEndpointGroupsPostProcessor availabilityProbesHealthEndpointGroupsPostProcessor() { + return new AvailabilityProbesHealthEndpointGroupsPostProcessor(); } /** @@ -75,15 +73,17 @@ public class AvailabilityProbesAutoConfiguration { */ static class ProbesCondition extends SpringBootCondition { + private static final String ENABLED_PROPERTY = "management.health.probes.enabled"; + @Override public ConditionOutcome getMatchOutcome(ConditionContext context, AnnotatedTypeMetadata metadata) { Environment environment = context.getEnvironment(); - ConditionMessage.Builder message = ConditionMessage.forCondition("Health probes"); - String enabled = environment.getProperty("management.health.probes.enabled"); + ConditionMessage.Builder message = ConditionMessage.forCondition("Health availability"); + String enabled = environment.getProperty(ENABLED_PROPERTY); if (enabled != null) { boolean match = !"false".equalsIgnoreCase(enabled); return new ConditionOutcome(match, - message.because("'management.health.probes.enabled' set to '" + enabled + "'")); + message.because("'" + ENABLED_PROPERTY + "' set to '" + enabled + "'")); } if (CloudPlatform.getActive(environment) == CloudPlatform.KUBERNETES) { return ConditionOutcome.match(message.because("running on Kubernetes")); diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/availability/AvailabilityProbesHealthEndpointGroup.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/availability/AvailabilityProbesHealthEndpointGroup.java new file mode 100644 index 0000000000..470ba31ef1 --- /dev/null +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/availability/AvailabilityProbesHealthEndpointGroup.java @@ -0,0 +1,67 @@ +/* + * 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. + * 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.availability; + +import java.util.Arrays; +import java.util.HashSet; +import java.util.Set; + +import org.springframework.boot.actuate.endpoint.SecurityContext; +import org.springframework.boot.actuate.health.HealthEndpointGroup; +import org.springframework.boot.actuate.health.HttpCodeStatusMapper; +import org.springframework.boot.actuate.health.StatusAggregator; + +/** + * {@link HealthEndpointGroup} used to support availability probes. + * + * @author Phillip Webb + * @author Brian Clozel + */ +class AvailabilityProbesHealthEndpointGroup implements HealthEndpointGroup { + + private final Set members; + + AvailabilityProbesHealthEndpointGroup(String... members) { + this.members = new HashSet<>(Arrays.asList(members)); + } + + @Override + public boolean isMember(String name) { + return this.members.contains(name); + } + + @Override + public boolean showComponents(SecurityContext securityContext) { + return false; + } + + @Override + public boolean showDetails(SecurityContext securityContext) { + return false; + } + + @Override + public StatusAggregator getStatusAggregator() { + return StatusAggregator.DEFAULT; + } + + @Override + public HttpCodeStatusMapper getHttpCodeStatusMapper() { + return HttpCodeStatusMapper.DEFAULT; + } + +} diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/availability/AvailabilityProbesHealthEndpointGroups.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/availability/AvailabilityProbesHealthEndpointGroups.java new file mode 100644 index 0000000000..0da69e7b5e --- /dev/null +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/availability/AvailabilityProbesHealthEndpointGroups.java @@ -0,0 +1,80 @@ +/* + * 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. + * 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.availability; + +import java.util.Collections; +import java.util.LinkedHashMap; +import java.util.LinkedHashSet; +import java.util.Map; +import java.util.Set; + +import org.springframework.boot.actuate.health.HealthEndpointGroup; +import org.springframework.boot.actuate.health.HealthEndpointGroups; +import org.springframework.util.Assert; + +/** + * {@link HealthEndpointGroups} decorator to support availability probes. + * + * @author Phillip Webb + * @author Brian Clozel + */ +class AvailabilityProbesHealthEndpointGroups implements HealthEndpointGroups { + + private static final Map GROUPS; + static { + Map 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 Set names; + + AvailabilityProbesHealthEndpointGroups(HealthEndpointGroups groups) { + Assert.notNull(groups, "Groups must not be null"); + this.groups = groups; + Set names = new LinkedHashSet<>(groups.getNames()); + names.addAll(GROUPS.keySet()); + this.names = Collections.unmodifiableSet(names); + } + + @Override + public HealthEndpointGroup getPrimary() { + return this.groups.getPrimary(); + } + + @Override + public Set getNames() { + return this.names; + } + + @Override + public HealthEndpointGroup get(String name) { + HealthEndpointGroup group = this.groups.get(name); + if (group == null) { + group = GROUPS.get(name); + } + return group; + } + + static boolean containsAllProbeGroups(HealthEndpointGroups groups) { + return groups.getNames().containsAll(GROUPS.keySet()); + } + +} diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/availability/AvailabilityProbesHealthEndpointGroupsPostProcessor.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/availability/AvailabilityProbesHealthEndpointGroupsPostProcessor.java new file mode 100644 index 0000000000..fcd36c99d9 --- /dev/null +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/availability/AvailabilityProbesHealthEndpointGroupsPostProcessor.java @@ -0,0 +1,41 @@ +/* + * 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. + * 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.availability; + +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; + +/** + * {@link HealthEndpointGroupsPostProcessor} to add + * {@link AvailabilityProbesHealthEndpointGroups}. + * + * @author Phillip Webb + */ +@Order(Ordered.LOWEST_PRECEDENCE) +class AvailabilityProbesHealthEndpointGroupsPostProcessor implements HealthEndpointGroupsPostProcessor { + + @Override + public HealthEndpointGroups postProcessHealthEndpointGroups(HealthEndpointGroups groups) { + if (AvailabilityProbesHealthEndpointGroups.containsAllProbeGroups(groups)) { + return groups; + } + return new AvailabilityProbesHealthEndpointGroups(groups); + } + +} diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/availability/AvailabilityProbesHealthEndpointGroupsRegistrar.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/availability/AvailabilityProbesHealthEndpointGroupsRegistrar.java deleted file mode 100644 index 6b997809a6..0000000000 --- a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/availability/AvailabilityProbesHealthEndpointGroupsRegistrar.java +++ /dev/null @@ -1,50 +0,0 @@ -/* - * 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. - * 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.availability; - -import org.springframework.boot.actuate.health.HealthEndpointGroup; -import org.springframework.boot.actuate.health.HealthEndpointGroupsRegistry; -import org.springframework.boot.actuate.health.HealthEndpointGroupsRegistryCustomizer; - -/** - * {@link HealthEndpointGroupsRegistryCustomizer} that registers {@code "liveness"} and - * {@code "readiness"} {@link HealthEndpointGroup groups} if they don't exist already. - * - * @author Brian Clozel - * @since 2.3.0 - */ -public class AvailabilityProbesHealthEndpointGroupsRegistrar implements HealthEndpointGroupsRegistryCustomizer { - - private static final String LIVENESS_GROUP_NAME = "liveness"; - - private static final String READINESS_GROUP_NAME = "readiness"; - - private static final String LIVENESS_PROBE_INDICATOR = "livenessProbe"; - - private static final String READINESS_PROBE_INDICATOR = "readinessProbe"; - - @Override - public void customize(HealthEndpointGroupsRegistry registry) { - if (registry.get(LIVENESS_GROUP_NAME) == null) { - registry.add(LIVENESS_GROUP_NAME, (configurer) -> configurer.include(LIVENESS_PROBE_INDICATOR)); - } - if (registry.get(READINESS_GROUP_NAME) == null) { - registry.add(READINESS_GROUP_NAME, (configurer) -> configurer.include(READINESS_PROBE_INDICATOR)); - } - } - -} diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/health/DefaultHealthEndpointGroup.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/health/AutoConfiguredHealthEndpointGroup.java similarity index 91% rename from spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/health/DefaultHealthEndpointGroup.java rename to spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/health/AutoConfiguredHealthEndpointGroup.java index a87591fbd1..efabc71529 100644 --- a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/health/DefaultHealthEndpointGroup.java +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/health/AutoConfiguredHealthEndpointGroup.java @@ -20,6 +20,7 @@ import java.security.Principal; import java.util.Collection; 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.HealthEndpointGroup; import org.springframework.boot.actuate.health.HttpCodeStatusMapper; @@ -30,12 +31,12 @@ import org.springframework.util.ClassUtils; import org.springframework.util.CollectionUtils; /** - * Auto-configured {@link HealthEndpointGroup}. + * Auto-configured {@link HealthEndpointGroup} backed by {@link HealthProperties}. * * @author Phillip Webb * @author Andy Wilkinson */ -class DefaultHealthEndpointGroup implements HealthEndpointGroup { +class AutoConfiguredHealthEndpointGroup implements HealthEndpointGroup { private final Predicate members; @@ -50,7 +51,7 @@ class DefaultHealthEndpointGroup implements HealthEndpointGroup { private final Collection roles; /** - * Create a new {@link DefaultHealthEndpointGroup} instance. + * Create a new {@link AutoConfiguredHealthEndpointGroup} instance. * @param members a predicate used to test for group membership * @param statusAggregator the status aggregator to use * @param httpCodeStatusMapper the HTTP code status mapper to use @@ -58,7 +59,7 @@ class DefaultHealthEndpointGroup implements HealthEndpointGroup { * @param showDetails the show details setting * @param roles the roles to match */ - DefaultHealthEndpointGroup(Predicate members, StatusAggregator statusAggregator, + AutoConfiguredHealthEndpointGroup(Predicate members, StatusAggregator statusAggregator, HttpCodeStatusMapper httpCodeStatusMapper, Show showComponents, Show showDetails, Collection roles) { this.members = members; diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/health/AutoConfiguredHealthEndpointGroupsRegistry.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/health/AutoConfiguredHealthEndpointGroups.java similarity index 51% rename from spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/health/AutoConfiguredHealthEndpointGroupsRegistry.java rename to spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/health/AutoConfiguredHealthEndpointGroups.java index 9dac900925..e89fc5ba0a 100644 --- a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/health/AutoConfiguredHealthEndpointGroupsRegistry.java +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/health/AutoConfiguredHealthEndpointGroups.java @@ -22,7 +22,6 @@ import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.Set; -import java.util.function.Consumer; import java.util.function.Predicate; import java.util.function.Supplier; @@ -32,12 +31,10 @@ import org.springframework.beans.factory.ListableBeanFactory; import org.springframework.beans.factory.NoSuchBeanDefinitionException; 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.HealthEndpointGroup; -import org.springframework.boot.actuate.health.HealthEndpointGroup.Show; -import org.springframework.boot.actuate.health.HealthEndpointGroupConfigurer; import org.springframework.boot.actuate.health.HealthEndpointGroups; -import org.springframework.boot.actuate.health.HealthEndpointGroupsRegistry; import org.springframework.boot.actuate.health.HttpCodeStatusMapper; import org.springframework.boot.actuate.health.SimpleHttpCodeStatusMapper; import org.springframework.boot.actuate.health.SimpleStatusAggregator; @@ -48,113 +45,71 @@ import org.springframework.util.CollectionUtils; import org.springframework.util.ObjectUtils; /** - * Auto-configured {@link HealthEndpointGroupsRegistry}. + * Auto-configured {@link HealthEndpointGroups}. * * @author Phillip Webb - * @author Brian Clozel */ -class AutoConfiguredHealthEndpointGroupsRegistry implements HealthEndpointGroupsRegistry { +class AutoConfiguredHealthEndpointGroups implements HealthEndpointGroups { private static Predicate ALL = (name) -> true; - private final StatusAggregator defaultStatusAggregator; - - private final HttpCodeStatusMapper defaultHttpCodeStatusMapper; - - private final Show defaultShowComponents; - - private final Show defaultShowDetails; - - private final Set defaultRoles; - - private HealthEndpointGroup primaryGroup; + private final HealthEndpointGroup primaryGroup; private final Map groups; /** - * Create a new {@link AutoConfiguredHealthEndpointGroupsRegistry} instance. + * Create a new {@link AutoConfiguredHealthEndpointGroups} instance. * @param applicationContext the application context used to check for override beans * @param properties the health endpoint properties */ - AutoConfiguredHealthEndpointGroupsRegistry(ApplicationContext applicationContext, - HealthEndpointProperties properties) { + AutoConfiguredHealthEndpointGroups(ApplicationContext applicationContext, HealthEndpointProperties properties) { ListableBeanFactory beanFactory = (applicationContext instanceof ConfigurableApplicationContext) ? ((ConfigurableApplicationContext) applicationContext).getBeanFactory() : applicationContext; - this.defaultShowComponents = convertVisibility(properties.getShowComponents()); - this.defaultShowDetails = convertVisibility(properties.getShowDetails()); - this.defaultRoles = properties.getRoles(); - + Show showComponents = properties.getShowComponents(); + Show showDetails = properties.getShowDetails(); + Set roles = properties.getRoles(); StatusAggregator statusAggregator = getNonQualifiedBean(beanFactory, StatusAggregator.class); if (statusAggregator == null) { statusAggregator = new SimpleStatusAggregator(properties.getStatus().getOrder()); } - this.defaultStatusAggregator = statusAggregator; - HttpCodeStatusMapper httpCodeStatusMapper = getNonQualifiedBean(beanFactory, HttpCodeStatusMapper.class); if (httpCodeStatusMapper == null) { httpCodeStatusMapper = new SimpleHttpCodeStatusMapper(properties.getStatus().getHttpMapping()); } - this.defaultHttpCodeStatusMapper = httpCodeStatusMapper; - - this.primaryGroup = new DefaultHealthEndpointGroup(ALL, statusAggregator, httpCodeStatusMapper, - this.defaultShowComponents, this.defaultShowDetails, this.defaultRoles); - - this.groups = createGroups(properties.getGroup(), beanFactory); + this.primaryGroup = new AutoConfiguredHealthEndpointGroup(ALL, statusAggregator, httpCodeStatusMapper, + showComponents, showDetails, roles); + this.groups = createGroups(properties.getGroup(), beanFactory, statusAggregator, httpCodeStatusMapper, + showComponents, showDetails, roles); } - @Override - public HealthEndpointGroupsRegistry add(String groupName, Consumer consumer) { - DefaultHealthEndpointGroupConfigurer groupConfigurer = new DefaultHealthEndpointGroupConfigurer( - this.defaultStatusAggregator, this.defaultHttpCodeStatusMapper, this.defaultShowComponents, - this.defaultShowDetails, this.defaultRoles); - consumer.accept(groupConfigurer); - this.groups.put(groupName, groupConfigurer.toHealthEndpointGroup()); - return this; - } - - @Override - public HealthEndpointGroupsRegistry remove(String groupName) { - this.groups.remove(groupName); - return this; - } - - @Override - public HealthEndpointGroups toGroups() { - return HealthEndpointGroups.of(this.primaryGroup, Collections.unmodifiableMap(this.groups)); - } - - private Map createGroups(Map groupProperties, BeanFactory beanFactory) { + private Map createGroups(Map groupProperties, BeanFactory beanFactory, + StatusAggregator defaultStatusAggregator, HttpCodeStatusMapper defaultHttpCodeStatusMapper, + Show defaultShowComponents, Show defaultShowDetails, Set defaultRoles) { Map groups = new LinkedHashMap<>(); - groupProperties - .forEach((groupName, group) -> groups.put(groupName, createGroup(groupName, group, beanFactory))); - return groups; - } - - private HealthEndpointGroup createGroup(String groupName, Group groupProperties, BeanFactory beanFactory) { - Status status = groupProperties.getStatus(); - Show showComponents = (groupProperties.getShowComponents() != null) - ? convertVisibility(groupProperties.getShowComponents()) : this.defaultShowComponents; - Show showDetails = (groupProperties.getShowDetails() != null) - ? convertVisibility(groupProperties.getShowDetails()) : this.defaultShowDetails; - Set roles = !CollectionUtils.isEmpty(groupProperties.getRoles()) ? groupProperties.getRoles() - : this.defaultRoles; - StatusAggregator statusAggregator = getQualifiedBean(beanFactory, StatusAggregator.class, groupName, () -> { - if (!CollectionUtils.isEmpty(status.getOrder())) { - return new SimpleStatusAggregator(status.getOrder()); - } - return this.defaultStatusAggregator; + groupProperties.forEach((groupName, group) -> { + Status status = group.getStatus(); + Show showComponents = (group.getShowComponents() != null) ? group.getShowComponents() + : defaultShowComponents; + Show showDetails = (group.getShowDetails() != null) ? group.getShowDetails() : defaultShowDetails; + Set roles = !CollectionUtils.isEmpty(group.getRoles()) ? group.getRoles() : defaultRoles; + StatusAggregator statusAggregator = getQualifiedBean(beanFactory, StatusAggregator.class, groupName, () -> { + if (!CollectionUtils.isEmpty(status.getOrder())) { + return new SimpleStatusAggregator(status.getOrder()); + } + return defaultStatusAggregator; + }); + HttpCodeStatusMapper httpCodeStatusMapper = getQualifiedBean(beanFactory, HttpCodeStatusMapper.class, + groupName, () -> { + if (!CollectionUtils.isEmpty(status.getHttpMapping())) { + return new SimpleHttpCodeStatusMapper(status.getHttpMapping()); + } + return defaultHttpCodeStatusMapper; + }); + Predicate members = new IncludeExcludeGroupMemberPredicate(group.getInclude(), group.getExclude()); + groups.put(groupName, new AutoConfiguredHealthEndpointGroup(members, statusAggregator, httpCodeStatusMapper, + showComponents, showDetails, roles)); }); - HttpCodeStatusMapper httpCodeStatusMapper = getQualifiedBean(beanFactory, HttpCodeStatusMapper.class, groupName, - () -> { - if (!CollectionUtils.isEmpty(status.getHttpMapping())) { - return new SimpleHttpCodeStatusMapper(status.getHttpMapping()); - } - return this.defaultHttpCodeStatusMapper; - }); - Predicate members = new IncludeExcludeGroupMemberPredicate(groupProperties.getInclude(), - groupProperties.getExclude()); - return new DefaultHealthEndpointGroup(members, statusAggregator, httpCodeStatusMapper, showComponents, - showDetails, roles); + return Collections.unmodifiableMap(groups); } private T getNonQualifiedBean(ListableBeanFactory beanFactory, Class type) { @@ -185,21 +140,6 @@ class AutoConfiguredHealthEndpointGroupsRegistry implements HealthEndpointGroups } } - private Show convertVisibility(HealthProperties.Show show) { - if (show == null) { - return null; - } - switch (show) { - case ALWAYS: - return Show.ALWAYS; - case NEVER: - return Show.NEVER; - case WHEN_AUTHORIZED: - return Show.WHEN_AUTHORIZED; - } - throw new IllegalStateException("Unsupported 'show' value " + show); - } - @Override public HealthEndpointGroup getPrimary() { return this.primaryGroup; diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/health/DefaultHealthEndpointGroupConfigurer.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/health/DefaultHealthEndpointGroupConfigurer.java deleted file mode 100644 index 122c4f25c2..0000000000 --- a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/health/DefaultHealthEndpointGroupConfigurer.java +++ /dev/null @@ -1,110 +0,0 @@ -/* - * 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. - * 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 java.util.Arrays; -import java.util.HashSet; -import java.util.Set; - -import org.springframework.boot.actuate.health.HealthEndpointGroup; -import org.springframework.boot.actuate.health.HealthEndpointGroup.Show; -import org.springframework.boot.actuate.health.HealthEndpointGroupConfigurer; -import org.springframework.boot.actuate.health.HttpCodeStatusMapper; -import org.springframework.boot.actuate.health.StatusAggregator; - -/** - * Mutable {@link HealthEndpointGroupConfigurer configurer} for - * {@link HealthEndpointGroup}. - * - * @author Brian Clozel - */ -class DefaultHealthEndpointGroupConfigurer implements HealthEndpointGroupConfigurer { - - Set includedIndicators; - - Set excludedIndicators; - - private StatusAggregator statusAggregator; - - private HttpCodeStatusMapper httpCodeStatusMapper; - - private Show showComponents; - - private Show showDetails; - - private Set roles; - - DefaultHealthEndpointGroupConfigurer(StatusAggregator defaultStatusAggregator, - HttpCodeStatusMapper defaultHttpCodeStatusMapper, Show defaultShowComponents, Show defaultShowDetails, - Set defaultRoles) { - this.statusAggregator = defaultStatusAggregator; - this.httpCodeStatusMapper = defaultHttpCodeStatusMapper; - this.showComponents = defaultShowComponents; - this.showDetails = defaultShowDetails; - this.roles = new HashSet<>(defaultRoles); - } - - @Override - public HealthEndpointGroupConfigurer include(String... indicators) { - this.includedIndicators = new HashSet<>(Arrays.asList(indicators)); - return this; - } - - @Override - public HealthEndpointGroupConfigurer exclude(String... exclude) { - this.excludedIndicators = new HashSet<>(Arrays.asList(exclude)); - return this; - } - - @Override - public HealthEndpointGroupConfigurer statusAggregator(StatusAggregator statusAggregator) { - this.statusAggregator = statusAggregator; - return this; - } - - @Override - public HealthEndpointGroupConfigurer httpCodeStatusMapper(HttpCodeStatusMapper httpCodeStatusMapper) { - this.httpCodeStatusMapper = httpCodeStatusMapper; - return this; - } - - @Override - public HealthEndpointGroupConfigurer showComponents(Show showComponents) { - this.showComponents = showComponents; - return this; - } - - @Override - public HealthEndpointGroupConfigurer showDetails(Show showDetails) { - this.showDetails = showDetails; - return this; - } - - @Override - public HealthEndpointGroupConfigurer roles(String... roles) { - this.roles = new HashSet<>(Arrays.asList(roles)); - return this; - } - - HealthEndpointGroup toHealthEndpointGroup() { - IncludeExcludeGroupMemberPredicate predicate = new IncludeExcludeGroupMemberPredicate(this.includedIndicators, - this.excludedIndicators); - return new DefaultHealthEndpointGroup(predicate, this.statusAggregator, this.httpCodeStatusMapper, - this.showComponents, this.showDetails, this.roles); - } - -} diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/health/HealthEndpointConfiguration.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/health/HealthEndpointConfiguration.java index d224e99944..88d6b0e7f9 100644 --- a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/health/HealthEndpointConfiguration.java +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/health/HealthEndpointConfiguration.java @@ -21,7 +21,9 @@ import java.util.Iterator; import java.util.LinkedHashMap; import java.util.Map; +import org.springframework.beans.BeansException; import org.springframework.beans.factory.ObjectProvider; +import org.springframework.beans.factory.config.BeanPostProcessor; import org.springframework.boot.actuate.health.CompositeHealthContributor; import org.springframework.boot.actuate.health.CompositeReactiveHealthContributor; import org.springframework.boot.actuate.health.Health; @@ -29,7 +31,7 @@ import org.springframework.boot.actuate.health.HealthContributor; 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.HealthEndpointGroupsRegistryCustomizer; +import org.springframework.boot.actuate.health.HealthEndpointGroupsPostProcessor; import org.springframework.boot.actuate.health.HealthIndicator; import org.springframework.boot.actuate.health.HttpCodeStatusMapper; import org.springframework.boot.actuate.health.NamedContributor; @@ -68,11 +70,8 @@ class HealthEndpointConfiguration { @Bean @ConditionalOnMissingBean HealthEndpointGroups healthEndpointGroups(ApplicationContext applicationContext, - HealthEndpointProperties properties, ObjectProvider customizers) { - AutoConfiguredHealthEndpointGroupsRegistry registry = new AutoConfiguredHealthEndpointGroupsRegistry( - applicationContext, properties); - customizers.orderedStream().forEach((customizer) -> customizer.customize(registry)); - return registry.toGroups(); + HealthEndpointProperties properties) { + return new AutoConfiguredHealthEndpointGroups(applicationContext, properties); } @Bean @@ -93,6 +92,42 @@ class HealthEndpointConfiguration { return new HealthEndpoint(registry, groups); } + @Bean + static HealthEndpointGroupsBeanPostProcessor healthEndpointGroupsBeanPostProcessor( + ObjectProvider healthEndpointGroupsPostProcessors) { + return new HealthEndpointGroupsBeanPostProcessor(healthEndpointGroupsPostProcessors); + } + + /** + * {@link BeanPostProcessor} to invoke {@link HealthEndpointGroupsPostProcessor} + * beans. + */ + private static class HealthEndpointGroupsBeanPostProcessor implements BeanPostProcessor { + + private final ObjectProvider postProcessors; + + HealthEndpointGroupsBeanPostProcessor(ObjectProvider postProcessors) { + this.postProcessors = postProcessors; + } + + @Override + public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { + if (bean instanceof HealthEndpointGroups) { + return applyPostProcessors((HealthEndpointGroups) bean); + } + return bean; + } + + private Object applyPostProcessors(HealthEndpointGroups bean) { + for (HealthEndpointGroupsPostProcessor postProcessor : this.postProcessors.orderedStream() + .toArray(HealthEndpointGroupsPostProcessor[]::new)) { + bean = postProcessor.postProcessHealthEndpointGroups(bean); + } + return bean; + } + + } + /** * Adapter to expose {@link ReactiveHealthContributor} beans as * {@link HealthContributor} instances. diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/availability/AvailabilityProbesAutoConfigurationTests.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/availability/AvailabilityProbesAutoConfigurationTests.java index 939edbbc86..7958919964 100644 --- a/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/availability/AvailabilityProbesAutoConfigurationTests.java +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/availability/AvailabilityProbesAutoConfigurationTests.java @@ -20,7 +20,6 @@ import org.junit.jupiter.api.Test; import org.springframework.boot.actuate.availability.LivenessStateHealthIndicator; import org.springframework.boot.actuate.availability.ReadinessStateHealthIndicator; -import org.springframework.boot.actuate.health.HealthEndpointGroupsRegistryCustomizer; import org.springframework.boot.autoconfigure.AutoConfigurations; import org.springframework.boot.autoconfigure.availability.ApplicationAvailabilityAutoConfiguration; import org.springframework.boot.availability.ApplicationAvailability; @@ -43,7 +42,7 @@ class AvailabilityProbesAutoConfigurationTests { this.contextRunner.run((context) -> assertThat(context).hasSingleBean(ApplicationAvailability.class) .doesNotHaveBean(LivenessStateHealthIndicator.class) .doesNotHaveBean(ReadinessStateHealthIndicator.class) - .doesNotHaveBean(HealthEndpointGroupsRegistryCustomizer.class)); + .doesNotHaveBean(AvailabilityProbesHealthEndpointGroupsPostProcessor.class)); } @Test @@ -52,7 +51,7 @@ class AvailabilityProbesAutoConfigurationTests { .run((context) -> assertThat(context).hasSingleBean(ApplicationAvailability.class) .hasSingleBean(LivenessStateHealthIndicator.class) .hasSingleBean(ReadinessStateHealthIndicator.class) - .hasSingleBean(HealthEndpointGroupsRegistryCustomizer.class)); + .hasSingleBean(AvailabilityProbesHealthEndpointGroupsPostProcessor.class)); } @Test @@ -61,7 +60,7 @@ class AvailabilityProbesAutoConfigurationTests { .run((context) -> assertThat(context).hasSingleBean(ApplicationAvailability.class) .hasSingleBean(LivenessStateHealthIndicator.class) .hasSingleBean(ReadinessStateHealthIndicator.class) - .hasSingleBean(HealthEndpointGroupsRegistryCustomizer.class)); + .hasSingleBean(AvailabilityProbesHealthEndpointGroupsPostProcessor.class)); } @Test @@ -71,7 +70,7 @@ class AvailabilityProbesAutoConfigurationTests { .run((context) -> assertThat(context).hasSingleBean(ApplicationAvailability.class) .doesNotHaveBean(LivenessStateHealthIndicator.class) .doesNotHaveBean(ReadinessStateHealthIndicator.class) - .doesNotHaveBean(HealthEndpointGroupsRegistryCustomizer.class)); + .doesNotHaveBean(AvailabilityProbesHealthEndpointGroupsPostProcessor.class)); } } diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/availability/AvailabilityProbesHealthEndpointGroupTests.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/availability/AvailabilityProbesHealthEndpointGroupTests.java new file mode 100644 index 0000000000..84f99fe9ac --- /dev/null +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/availability/AvailabilityProbesHealthEndpointGroupTests.java @@ -0,0 +1,68 @@ +/* + * 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. + * 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.availability; + +import org.junit.jupiter.api.Test; + +import org.springframework.boot.actuate.endpoint.SecurityContext; +import org.springframework.boot.actuate.health.HttpCodeStatusMapper; +import org.springframework.boot.actuate.health.StatusAggregator; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Mockito.mock; + +/** + * Tests for {@link AvailabilityProbesHealthEndpointGroup}. + * + * @author Phillip Webb + */ +class AvailabilityProbesHealthEndpointGroupTests { + + private AvailabilityProbesHealthEndpointGroup group = new AvailabilityProbesHealthEndpointGroup("a", "b"); + + @Test + void isMemberWhenMemberReturnsTrue() { + assertThat(this.group.isMember("a")).isTrue(); + assertThat(this.group.isMember("b")).isTrue(); + } + + @Test + void isMemberWhenNotMemberReturnsFalse() { + assertThat(this.group.isMember("c")).isFalse(); + } + + @Test + void showComponentsReturnsFalse() { + assertThat(this.group.showComponents(mock(SecurityContext.class))).isFalse(); + } + + @Test + void showDetailsReturnsFalse() { + assertThat(this.group.showDetails(mock(SecurityContext.class))).isFalse(); + } + + @Test + void getStatusAggregattorReturnsDefaultStatusAggregator() { + assertThat(this.group.getStatusAggregator()).isEqualTo(StatusAggregator.DEFAULT); + } + + @Test + void getHttpCodeStatusMapperReturnsDefaultHttpCodeStatusMapper() { + assertThat(this.group.getHttpCodeStatusMapper()).isEqualTo(HttpCodeStatusMapper.DEFAULT); + } + +} diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/availability/AvailabilityProbesHealthEndpointGroupsPostProcessorTests.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/availability/AvailabilityProbesHealthEndpointGroupsPostProcessorTests.java new file mode 100644 index 0000000000..d7994b896a --- /dev/null +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/availability/AvailabilityProbesHealthEndpointGroupsPostProcessorTests.java @@ -0,0 +1,74 @@ +/* + * 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. + * 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.availability; + +import java.util.LinkedHashSet; +import java.util.Set; + +import org.junit.jupiter.api.Test; + +import org.springframework.boot.actuate.health.HealthEndpointGroups; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.BDDMockito.given; +import static org.mockito.Mockito.mock; + +/** + * Tests for {@link AvailabilityProbesHealthEndpointGroupsPostProcessor}. + * + * @author Phillip Webb + */ +class AvailabilityProbesHealthEndpointGroupsPostProcessorTests { + + private AvailabilityProbesHealthEndpointGroupsPostProcessor postProcessor = new AvailabilityProbesHealthEndpointGroupsPostProcessor(); + + @Test + void postProcessHealthEndpointGroupsWhenGroupsAlreadyContainedReturnsOriginal() { + HealthEndpointGroups groups = mock(HealthEndpointGroups.class); + Set names = new LinkedHashSet<>(); + names.add("test"); + names.add("readiness"); + names.add("liveness"); + given(groups.getNames()).willReturn(names); + assertThat(this.postProcessor.postProcessHealthEndpointGroups(groups)).isSameAs(groups); + } + + @Test + void postProcessHealthEndpointGroupsWhenGroupContainsOneReturnsPostProcessed() { + HealthEndpointGroups groups = mock(HealthEndpointGroups.class); + Set names = new LinkedHashSet<>(); + names.add("test"); + names.add("readiness"); + given(groups.getNames()).willReturn(names); + assertThat(this.postProcessor.postProcessHealthEndpointGroups(groups)) + .isInstanceOf(AvailabilityProbesHealthEndpointGroups.class); + } + + @Test + void postProcessHealthEndpointGroupsWhenGroupsContainsNoneReturnsProcessed() { + HealthEndpointGroups groups = mock(HealthEndpointGroups.class); + Set names = new LinkedHashSet<>(); + names.add("test"); + names.add("spring"); + names.add("boot"); + given(groups.getNames()).willReturn(names); + assertThat(this.postProcessor.postProcessHealthEndpointGroups(groups)) + .isInstanceOf(AvailabilityProbesHealthEndpointGroups.class); + + } + +} diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/availability/AvailabilityProbesHealthEndpointGroupsRegistrarTests.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/availability/AvailabilityProbesHealthEndpointGroupsRegistrarTests.java deleted file mode 100644 index 962e9befe4..0000000000 --- a/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/availability/AvailabilityProbesHealthEndpointGroupsRegistrarTests.java +++ /dev/null @@ -1,65 +0,0 @@ -/* - * 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. - * 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.availability; - -import org.junit.jupiter.api.Test; - -import org.springframework.boot.actuate.autoconfigure.availability.AvailabilityProbesHealthEndpointGroupsRegistrar; -import org.springframework.boot.actuate.health.HealthEndpointGroup; -import org.springframework.boot.actuate.health.HealthEndpointGroupsRegistry; -import org.springframework.boot.actuate.health.TestHealthEndpointGroupsRegistry; - -import static org.assertj.core.api.Assertions.assertThat; - -/** - * Tests for {@code ProbesHealthEndpointGroupsRegistrar} - * - * @author Brian Clozel - */ -class AvailabilityProbesHealthEndpointGroupsRegistrarTests { - - private AvailabilityProbesHealthEndpointGroupsRegistrar registrar = new AvailabilityProbesHealthEndpointGroupsRegistrar(); - - @Test - void shouldAddKubernetesProbes() { - HealthEndpointGroupsRegistry registry = new TestHealthEndpointGroupsRegistry(); - this.registrar.customize(registry); - HealthEndpointGroup liveness = registry.get("liveness"); - assertThat(liveness).isNotNull(); - assertThat(liveness.isMember("livenessProbe")).isTrue(); - HealthEndpointGroup readiness = registry.get("readiness"); - assertThat(readiness).isNotNull(); - assertThat(readiness.isMember("readinessProbe")).isTrue(); - } - - @Test - void shouldNotAddProbeIfGroupExists() { - HealthEndpointGroupsRegistry registry = new TestHealthEndpointGroupsRegistry(); - registry.add("readiness", (configurer) -> configurer.include("test")); - this.registrar.customize(registry); - - HealthEndpointGroup liveness = registry.get("liveness"); - assertThat(liveness).isNotNull(); - assertThat(liveness.isMember("livenessProbe")).isTrue(); - - HealthEndpointGroup readiness = registry.get("readiness"); - assertThat(readiness).isNotNull(); - assertThat(readiness.isMember("readinessProbe")).isFalse(); - assertThat(readiness.isMember("test")).isTrue(); - } - -} diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/availability/AvailabilityProbesHealthEndpointGroupsTests.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/availability/AvailabilityProbesHealthEndpointGroupsTests.java new file mode 100644 index 0000000000..5bda628037 --- /dev/null +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/availability/AvailabilityProbesHealthEndpointGroupsTests.java @@ -0,0 +1,124 @@ +/* + * 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. + * 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.availability; + +import java.util.Arrays; +import java.util.Collections; +import java.util.LinkedHashSet; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import org.springframework.boot.actuate.health.HealthEndpointGroup; +import org.springframework.boot.actuate.health.HealthEndpointGroups; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException; +import static org.mockito.BDDMockito.given; +import static org.mockito.Mockito.mock; + +/** + * Tests for {@link AvailabilityProbesHealthEndpointGroups}. + * + * @author Phillip Webb + */ +class AvailabilityProbesHealthEndpointGroupsTests { + + private HealthEndpointGroups delegate; + + private HealthEndpointGroup group; + + @BeforeEach + void setup() { + this.delegate = mock(HealthEndpointGroups.class); + this.group = mock(HealthEndpointGroup.class); + } + + @Test + void createWhenGroupsIsNullThrowsException() { + assertThatIllegalArgumentException().isThrownBy(() -> new AvailabilityProbesHealthEndpointGroups(null)) + .withMessage("Groups must not be null"); + } + + @Test + void getPrimaryDelegatesToGroups() { + given(this.delegate.getPrimary()).willReturn(this.group); + HealthEndpointGroups availabilityProbes = new AvailabilityProbesHealthEndpointGroups(this.delegate); + assertThat(availabilityProbes.getPrimary()).isEqualTo(this.group); + } + + @Test + void getNamesIncludesAvailabilityProbeGroups() { + given(this.delegate.getNames()).willReturn(Collections.singleton("test")); + HealthEndpointGroups availabilityProbes = new AvailabilityProbesHealthEndpointGroups(this.delegate); + assertThat(availabilityProbes.getNames()).containsExactly("test", "liveness", "readiness"); + } + + @Test + void getWhenProbeInDelegateReturnsGroupFromDelegate() { + given(this.delegate.get("liveness")).willReturn(this.group); + HealthEndpointGroups availabilityProbes = new AvailabilityProbesHealthEndpointGroups(this.delegate); + assertThat(availabilityProbes.get("liveness")).isEqualTo(this.group); + } + + @Test + void getWhenProbeNotInDelegateReturnsProbeGroup() { + HealthEndpointGroups availabilityProbes = new AvailabilityProbesHealthEndpointGroups(this.delegate); + assertThat(availabilityProbes.get("liveness")).isInstanceOf(AvailabilityProbesHealthEndpointGroup.class); + } + + @Test + void getWhenNotProbeAndNotInDelegateReturnsNull() { + HealthEndpointGroups availabilityProbes = new AvailabilityProbesHealthEndpointGroups(this.delegate); + assertThat(availabilityProbes.get("mygroup")).isNull(); + } + + @Test + void getLivenessProbeHasOnlyLivenessStateAsMember() { + HealthEndpointGroups availabilityProbes = new AvailabilityProbesHealthEndpointGroups(this.delegate); + HealthEndpointGroup probeGroup = availabilityProbes.get("liveness"); + assertThat(probeGroup.isMember("livenessState")).isTrue(); + assertThat(probeGroup.isMember("readinessState")).isFalse(); + } + + @Test + void getRedinessProbeHasOnlyReadinessStateAsMember() { + HealthEndpointGroups availabilityProbes = new AvailabilityProbesHealthEndpointGroups(this.delegate); + HealthEndpointGroup probeGroup = availabilityProbes.get("readiness"); + assertThat(probeGroup.isMember("livenessState")).isFalse(); + assertThat(probeGroup.isMember("readinessState")).isTrue(); + } + + @Test + void containsAllWhenContainsAllReturnTrue() { + given(this.delegate.getNames()).willReturn(new LinkedHashSet<>(Arrays.asList("test", "liveness", "readiness"))); + assertThat(AvailabilityProbesHealthEndpointGroups.containsAllProbeGroups(this.delegate)).isTrue(); + } + + @Test + void containsAllWhenContainsOneReturnFalse() { + given(this.delegate.getNames()).willReturn(new LinkedHashSet<>(Arrays.asList("test", "liveness"))); + assertThat(AvailabilityProbesHealthEndpointGroups.containsAllProbeGroups(this.delegate)).isFalse(); + } + + @Test + void containsAllWhenContainsNoneReturnFalse() { + given(this.delegate.getNames()).willReturn(new LinkedHashSet<>(Arrays.asList("test", "spring"))); + assertThat(AvailabilityProbesHealthEndpointGroups.containsAllProbeGroups(this.delegate)).isFalse(); + } + +} diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/health/DefaultHealthEndpointGroupTests.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/health/AutoConfiguredHealthEndpointGroupTests.java similarity index 61% rename from spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/health/DefaultHealthEndpointGroupTests.java rename to spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/health/AutoConfiguredHealthEndpointGroupTests.java index b9841e8376..c7797017bd 100644 --- a/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/health/DefaultHealthEndpointGroupTests.java +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/health/AutoConfiguredHealthEndpointGroupTests.java @@ -25,8 +25,8 @@ import org.junit.jupiter.api.Test; import org.mockito.Mock; import org.mockito.MockitoAnnotations; +import org.springframework.boot.actuate.autoconfigure.health.HealthProperties.Show; import org.springframework.boot.actuate.endpoint.SecurityContext; -import org.springframework.boot.actuate.health.HealthEndpointGroup.Show; import org.springframework.boot.actuate.health.HttpCodeStatusMapper; import org.springframework.boot.actuate.health.StatusAggregator; import org.springframework.security.core.Authentication; @@ -37,11 +37,11 @@ import static org.mockito.BDDMockito.given; import static org.mockito.Mockito.mock; /** - * Tests for {@link DefaultHealthEndpointGroup}. + * Tests for {@link AutoConfiguredHealthEndpointGroup}. * * @author Phillip Webb */ -class DefaultHealthEndpointGroupTests { +class AutoConfiguredHealthEndpointGroupTests { @Mock private StatusAggregator statusAggregator; @@ -62,7 +62,7 @@ class DefaultHealthEndpointGroupTests { @Test void isMemberWhenMemberPredicateMatchesAcceptsTrue() { - DefaultHealthEndpointGroup group = new DefaultHealthEndpointGroup((name) -> name.startsWith("a"), + AutoConfiguredHealthEndpointGroup group = new AutoConfiguredHealthEndpointGroup((name) -> name.startsWith("a"), this.statusAggregator, this.httpCodeStatusMapper, null, Show.ALWAYS, Collections.emptySet()); assertThat(group.isMember("albert")).isTrue(); assertThat(group.isMember("arnold")).isTrue(); @@ -70,7 +70,7 @@ class DefaultHealthEndpointGroupTests { @Test void isMemberWhenMemberPredicateRejectsReturnsTrue() { - DefaultHealthEndpointGroup group = new DefaultHealthEndpointGroup((name) -> name.startsWith("a"), + AutoConfiguredHealthEndpointGroup group = new AutoConfiguredHealthEndpointGroup((name) -> name.startsWith("a"), this.statusAggregator, this.httpCodeStatusMapper, null, Show.ALWAYS, Collections.emptySet()); assertThat(group.isMember("bert")).isFalse(); assertThat(group.isMember("ernie")).isFalse(); @@ -78,38 +78,39 @@ class DefaultHealthEndpointGroupTests { @Test void showDetailsWhenShowDetailsIsNeverReturnsFalse() { - DefaultHealthEndpointGroup group = new DefaultHealthEndpointGroup((name) -> true, this.statusAggregator, - this.httpCodeStatusMapper, null, Show.NEVER, Collections.emptySet()); + AutoConfiguredHealthEndpointGroup group = new AutoConfiguredHealthEndpointGroup((name) -> true, + this.statusAggregator, this.httpCodeStatusMapper, null, Show.NEVER, Collections.emptySet()); assertThat(group.showDetails(SecurityContext.NONE)).isFalse(); } @Test void showDetailsWhenShowDetailsIsAlwaysReturnsTrue() { - DefaultHealthEndpointGroup group = new DefaultHealthEndpointGroup((name) -> true, this.statusAggregator, - this.httpCodeStatusMapper, null, Show.ALWAYS, Collections.emptySet()); + AutoConfiguredHealthEndpointGroup group = new AutoConfiguredHealthEndpointGroup((name) -> true, + this.statusAggregator, this.httpCodeStatusMapper, null, Show.ALWAYS, Collections.emptySet()); assertThat(group.showDetails(SecurityContext.NONE)).isTrue(); } @Test void showDetailsWhenShowDetailsIsWhenAuthorizedAndPrincipalIsNullReturnsFalse() { - DefaultHealthEndpointGroup group = new DefaultHealthEndpointGroup((name) -> true, this.statusAggregator, - this.httpCodeStatusMapper, null, Show.WHEN_AUTHORIZED, Collections.emptySet()); + AutoConfiguredHealthEndpointGroup group = new AutoConfiguredHealthEndpointGroup((name) -> true, + this.statusAggregator, this.httpCodeStatusMapper, null, Show.WHEN_AUTHORIZED, Collections.emptySet()); given(this.securityContext.getPrincipal()).willReturn(null); assertThat(group.showDetails(this.securityContext)).isFalse(); } @Test void showDetailsWhenShowDetailsIsWhenAuthorizedAndRolesAreEmptyReturnsTrue() { - DefaultHealthEndpointGroup group = new DefaultHealthEndpointGroup((name) -> true, this.statusAggregator, - this.httpCodeStatusMapper, null, Show.WHEN_AUTHORIZED, Collections.emptySet()); + AutoConfiguredHealthEndpointGroup group = new AutoConfiguredHealthEndpointGroup((name) -> true, + this.statusAggregator, this.httpCodeStatusMapper, null, Show.WHEN_AUTHORIZED, Collections.emptySet()); given(this.securityContext.getPrincipal()).willReturn(this.principal); assertThat(group.showDetails(this.securityContext)).isTrue(); } @Test void showDetailsWhenShowDetailsIsWhenAuthorizedAndUseIsInRoleReturnsTrue() { - DefaultHealthEndpointGroup group = new DefaultHealthEndpointGroup((name) -> true, this.statusAggregator, - this.httpCodeStatusMapper, null, Show.WHEN_AUTHORIZED, Arrays.asList("admin", "root", "bossmode")); + AutoConfiguredHealthEndpointGroup group = new AutoConfiguredHealthEndpointGroup((name) -> true, + this.statusAggregator, this.httpCodeStatusMapper, null, Show.WHEN_AUTHORIZED, + Arrays.asList("admin", "root", "bossmode")); given(this.securityContext.getPrincipal()).willReturn(this.principal); given(this.securityContext.isUserInRole("root")).willReturn(true); assertThat(group.showDetails(this.securityContext)).isTrue(); @@ -117,8 +118,9 @@ class DefaultHealthEndpointGroupTests { @Test void showDetailsWhenShowDetailsIsWhenAuthorizedAndUseIsNotInRoleReturnsFalse() { - DefaultHealthEndpointGroup group = new DefaultHealthEndpointGroup((name) -> true, this.statusAggregator, - this.httpCodeStatusMapper, null, Show.WHEN_AUTHORIZED, Arrays.asList("admin", "rot", "bossmode")); + AutoConfiguredHealthEndpointGroup group = new AutoConfiguredHealthEndpointGroup((name) -> true, + this.statusAggregator, this.httpCodeStatusMapper, null, Show.WHEN_AUTHORIZED, + Arrays.asList("admin", "rot", "bossmode")); given(this.securityContext.getPrincipal()).willReturn(this.principal); given(this.securityContext.isUserInRole("root")).willReturn(true); assertThat(group.showDetails(this.securityContext)).isFalse(); @@ -126,8 +128,9 @@ class DefaultHealthEndpointGroupTests { @Test void showDetailsWhenShowDetailsIsWhenAuthorizedAndUserHasRightAuthorityReturnsTrue() { - DefaultHealthEndpointGroup group = new DefaultHealthEndpointGroup((name) -> true, this.statusAggregator, - this.httpCodeStatusMapper, null, Show.WHEN_AUTHORIZED, Arrays.asList("admin", "root", "bossmode")); + AutoConfiguredHealthEndpointGroup group = new AutoConfiguredHealthEndpointGroup((name) -> true, + this.statusAggregator, this.httpCodeStatusMapper, null, Show.WHEN_AUTHORIZED, + Arrays.asList("admin", "root", "bossmode")); Authentication principal = mock(Authentication.class); given(principal.getAuthorities()) .willAnswer((invocation) -> Collections.singleton(new SimpleGrantedAuthority("admin"))); @@ -137,8 +140,9 @@ class DefaultHealthEndpointGroupTests { @Test void showDetailsWhenShowDetailsIsWhenAuthorizedAndUserDoesNotHaveRightAuthoritiesReturnsFalse() { - DefaultHealthEndpointGroup group = new DefaultHealthEndpointGroup((name) -> true, this.statusAggregator, - this.httpCodeStatusMapper, null, Show.WHEN_AUTHORIZED, Arrays.asList("admin", "rot", "bossmode")); + AutoConfiguredHealthEndpointGroup group = new AutoConfiguredHealthEndpointGroup((name) -> true, + this.statusAggregator, this.httpCodeStatusMapper, null, Show.WHEN_AUTHORIZED, + Arrays.asList("admin", "rot", "bossmode")); Authentication principal = mock(Authentication.class); given(principal.getAuthorities()) .willAnswer((invocation) -> Collections.singleton(new SimpleGrantedAuthority("other"))); @@ -148,48 +152,50 @@ class DefaultHealthEndpointGroupTests { @Test void showComponentsWhenShowComponentsIsNullDelegatesToShowDetails() { - DefaultHealthEndpointGroup alwaysGroup = new DefaultHealthEndpointGroup((name) -> true, this.statusAggregator, - this.httpCodeStatusMapper, null, Show.ALWAYS, Collections.emptySet()); + AutoConfiguredHealthEndpointGroup alwaysGroup = new AutoConfiguredHealthEndpointGroup((name) -> true, + this.statusAggregator, this.httpCodeStatusMapper, null, Show.ALWAYS, Collections.emptySet()); assertThat(alwaysGroup.showComponents(SecurityContext.NONE)).isTrue(); - DefaultHealthEndpointGroup neverGroup = new DefaultHealthEndpointGroup((name) -> true, this.statusAggregator, - this.httpCodeStatusMapper, null, Show.NEVER, Collections.emptySet()); + AutoConfiguredHealthEndpointGroup neverGroup = new AutoConfiguredHealthEndpointGroup((name) -> true, + this.statusAggregator, this.httpCodeStatusMapper, null, Show.NEVER, Collections.emptySet()); assertThat(neverGroup.showComponents(SecurityContext.NONE)).isFalse(); } @Test void showComponentsWhenShowComponentsIsNeverReturnsFalse() { - DefaultHealthEndpointGroup group = new DefaultHealthEndpointGroup((name) -> true, this.statusAggregator, - this.httpCodeStatusMapper, Show.NEVER, Show.ALWAYS, Collections.emptySet()); + AutoConfiguredHealthEndpointGroup group = new AutoConfiguredHealthEndpointGroup((name) -> true, + this.statusAggregator, this.httpCodeStatusMapper, Show.NEVER, Show.ALWAYS, Collections.emptySet()); assertThat(group.showComponents(SecurityContext.NONE)).isFalse(); } @Test void showComponentsWhenShowComponentsIsAlwaysReturnsTrue() { - DefaultHealthEndpointGroup group = new DefaultHealthEndpointGroup((name) -> true, this.statusAggregator, - this.httpCodeStatusMapper, Show.ALWAYS, Show.NEVER, Collections.emptySet()); + AutoConfiguredHealthEndpointGroup group = new AutoConfiguredHealthEndpointGroup((name) -> true, + this.statusAggregator, this.httpCodeStatusMapper, Show.ALWAYS, Show.NEVER, Collections.emptySet()); assertThat(group.showComponents(SecurityContext.NONE)).isTrue(); } @Test void showComponentsWhenShowComponentsIsWhenAuthorizedAndPrincipalIsNullReturnsFalse() { - DefaultHealthEndpointGroup group = new DefaultHealthEndpointGroup((name) -> true, this.statusAggregator, - this.httpCodeStatusMapper, Show.WHEN_AUTHORIZED, Show.NEVER, Collections.emptySet()); + AutoConfiguredHealthEndpointGroup group = new AutoConfiguredHealthEndpointGroup((name) -> true, + this.statusAggregator, this.httpCodeStatusMapper, Show.WHEN_AUTHORIZED, Show.NEVER, + Collections.emptySet()); given(this.securityContext.getPrincipal()).willReturn(null); assertThat(group.showComponents(this.securityContext)).isFalse(); } @Test void showComponentsWhenShowComponentsIsWhenAuthorizedAndRolesAreEmptyReturnsTrue() { - DefaultHealthEndpointGroup group = new DefaultHealthEndpointGroup((name) -> true, this.statusAggregator, - this.httpCodeStatusMapper, Show.WHEN_AUTHORIZED, Show.NEVER, Collections.emptySet()); + AutoConfiguredHealthEndpointGroup group = new AutoConfiguredHealthEndpointGroup((name) -> true, + this.statusAggregator, this.httpCodeStatusMapper, Show.WHEN_AUTHORIZED, Show.NEVER, + Collections.emptySet()); given(this.securityContext.getPrincipal()).willReturn(this.principal); assertThat(group.showComponents(this.securityContext)).isTrue(); } @Test void showComponentsWhenShowComponentsIsWhenAuthorizedAndUseIsInRoleReturnsTrue() { - DefaultHealthEndpointGroup group = new DefaultHealthEndpointGroup((name) -> true, this.statusAggregator, - this.httpCodeStatusMapper, Show.WHEN_AUTHORIZED, Show.NEVER, + AutoConfiguredHealthEndpointGroup group = new AutoConfiguredHealthEndpointGroup((name) -> true, + this.statusAggregator, this.httpCodeStatusMapper, Show.WHEN_AUTHORIZED, Show.NEVER, Arrays.asList("admin", "root", "bossmode")); given(this.securityContext.getPrincipal()).willReturn(this.principal); given(this.securityContext.isUserInRole("root")).willReturn(true); @@ -198,8 +204,9 @@ class DefaultHealthEndpointGroupTests { @Test void showComponentsWhenShowComponentsIsWhenAuthorizedAndUseIsNotInRoleReturnsFalse() { - DefaultHealthEndpointGroup group = new DefaultHealthEndpointGroup((name) -> true, this.statusAggregator, - this.httpCodeStatusMapper, Show.WHEN_AUTHORIZED, Show.NEVER, Arrays.asList("admin", "rot", "bossmode")); + AutoConfiguredHealthEndpointGroup group = new AutoConfiguredHealthEndpointGroup((name) -> true, + this.statusAggregator, this.httpCodeStatusMapper, Show.WHEN_AUTHORIZED, Show.NEVER, + Arrays.asList("admin", "rot", "bossmode")); given(this.securityContext.getPrincipal()).willReturn(this.principal); given(this.securityContext.isUserInRole("root")).willReturn(true); assertThat(group.showComponents(this.securityContext)).isFalse(); @@ -207,8 +214,8 @@ class DefaultHealthEndpointGroupTests { @Test void showComponentsWhenShowComponentsIsWhenAuthorizedAndUserHasRightAuthoritiesReturnsTrue() { - DefaultHealthEndpointGroup group = new DefaultHealthEndpointGroup((name) -> true, this.statusAggregator, - this.httpCodeStatusMapper, Show.WHEN_AUTHORIZED, Show.NEVER, + AutoConfiguredHealthEndpointGroup group = new AutoConfiguredHealthEndpointGroup((name) -> true, + this.statusAggregator, this.httpCodeStatusMapper, Show.WHEN_AUTHORIZED, Show.NEVER, Arrays.asList("admin", "root", "bossmode")); Authentication principal = mock(Authentication.class); given(principal.getAuthorities()) @@ -219,8 +226,9 @@ class DefaultHealthEndpointGroupTests { @Test void showComponentsWhenShowComponentsIsWhenAuthorizedAndUserDoesNotHaveRightAuthoritiesReturnsFalse() { - DefaultHealthEndpointGroup group = new DefaultHealthEndpointGroup((name) -> true, this.statusAggregator, - this.httpCodeStatusMapper, Show.WHEN_AUTHORIZED, Show.NEVER, Arrays.asList("admin", "rot", "bossmode")); + AutoConfiguredHealthEndpointGroup group = new AutoConfiguredHealthEndpointGroup((name) -> true, + this.statusAggregator, this.httpCodeStatusMapper, Show.WHEN_AUTHORIZED, Show.NEVER, + Arrays.asList("admin", "rot", "bossmode")); Authentication principal = mock(Authentication.class); given(principal.getAuthorities()) .willAnswer((invocation) -> Collections.singleton(new SimpleGrantedAuthority("other"))); @@ -230,15 +238,15 @@ class DefaultHealthEndpointGroupTests { @Test void getStatusAggregatorReturnsStatusAggregator() { - DefaultHealthEndpointGroup group = new DefaultHealthEndpointGroup((name) -> true, this.statusAggregator, - this.httpCodeStatusMapper, null, Show.ALWAYS, Collections.emptySet()); + AutoConfiguredHealthEndpointGroup group = new AutoConfiguredHealthEndpointGroup((name) -> true, + this.statusAggregator, this.httpCodeStatusMapper, null, Show.ALWAYS, Collections.emptySet()); assertThat(group.getStatusAggregator()).isSameAs(this.statusAggregator); } @Test void getHttpCodeStatusMapperReturnsHttpCodeStatusMapper() { - DefaultHealthEndpointGroup group = new DefaultHealthEndpointGroup((name) -> true, this.statusAggregator, - this.httpCodeStatusMapper, null, Show.ALWAYS, Collections.emptySet()); + AutoConfiguredHealthEndpointGroup group = new AutoConfiguredHealthEndpointGroup((name) -> true, + this.statusAggregator, this.httpCodeStatusMapper, null, Show.ALWAYS, Collections.emptySet()); assertThat(group.getHttpCodeStatusMapper()).isSameAs(this.httpCodeStatusMapper); } diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/health/AutoConfiguredHealthEndpointGroupsBuilderTests.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/health/AutoConfiguredHealthEndpointGroupsTests.java similarity index 97% rename from spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/health/AutoConfiguredHealthEndpointGroupsBuilderTests.java rename to spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/health/AutoConfiguredHealthEndpointGroupsTests.java index 04f9b75f9e..f3b6b1244f 100644 --- a/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/health/AutoConfiguredHealthEndpointGroupsBuilderTests.java +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/health/AutoConfiguredHealthEndpointGroupsTests.java @@ -40,11 +40,11 @@ import org.springframework.context.annotation.Primary; import static org.assertj.core.api.Assertions.assertThat; /** - * Tests for {@link AutoConfiguredHealthEndpointGroupsRegistry}. + * Tests for {@link AutoConfiguredHealthEndpointGroups}. * * @author Phillip Webb */ -class AutoConfiguredHealthEndpointGroupsBuilderTests { +class AutoConfiguredHealthEndpointGroupsTests { private final ApplicationContextRunner contextRunner = new ApplicationContextRunner() .withConfiguration(AutoConfigurations.of(AutoConfiguredHealthEndpointGroupsTestConfiguration.class)); @@ -313,9 +313,9 @@ class AutoConfiguredHealthEndpointGroupsBuilderTests { static class AutoConfiguredHealthEndpointGroupsTestConfiguration { @Bean - AutoConfiguredHealthEndpointGroupsRegistry healthEndpointGroups( - ConfigurableApplicationContext applicationContext, HealthEndpointProperties properties) { - return new AutoConfiguredHealthEndpointGroupsRegistry(applicationContext, properties); + AutoConfiguredHealthEndpointGroups healthEndpointGroups(ConfigurableApplicationContext applicationContext, + HealthEndpointProperties properties) { + return new AutoConfiguredHealthEndpointGroups(applicationContext, properties); } } diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/health/HealthEndpointAutoConfigurationTests.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/health/HealthEndpointAutoConfigurationTests.java index a69d18cea2..17eeb0d948 100644 --- a/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/health/HealthEndpointAutoConfigurationTests.java +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/health/HealthEndpointAutoConfigurationTests.java @@ -37,7 +37,7 @@ import org.springframework.boot.actuate.health.HealthComponent; 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.HealthEndpointGroupsRegistryCustomizer; +import org.springframework.boot.actuate.health.HealthEndpointGroupsPostProcessor; import org.springframework.boot.actuate.health.HealthEndpointWebExtension; import org.springframework.boot.actuate.health.HealthIndicator; import org.springframework.boot.actuate.health.HttpCodeStatusMapper; @@ -56,6 +56,7 @@ import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatExceptionOfType; import static org.mockito.BDDMockito.given; import static org.mockito.Mockito.mock; @@ -155,6 +156,7 @@ class HealthEndpointAutoConfigurationTests { void runCreatesHealthEndpointGroups() { this.contextRunner.withPropertyValues("management.endpoint.health.group.ready.include=*").run((context) -> { HealthEndpointGroups groups = context.getBean(HealthEndpointGroups.class); + assertThat(groups).isInstanceOf(AutoConfiguredHealthEndpointGroups.class); assertThat(groups.getNames()).containsOnly("ready"); }); } @@ -302,12 +304,13 @@ class HealthEndpointAutoConfigurationTests { } @Test - void runWhenHealthEndpointGroupsRegistryCustomizerAddsHealthEndpointGroup() { - this.contextRunner.withUserConfiguration(HealthEndpointGroupsRegistryCustomizerConfig.class).run((context) -> { - assertThat(context).hasSingleBean(HealthEndpointGroupsRegistryCustomizer.class); - HealthEndpointGroups groups = context.getBean(HealthEndpointGroups.class); - assertThat(groups.getNames()).contains("test"); - }); + void runWhenHasHealthEndpointGroupsPostProcessorPerformsProcessing() { + this.contextRunner.withPropertyValues("management.endpoint.health.group.ready.include=*").withUserConfiguration( + HealthEndpointGroupsConfiguration.class, TestHealthEndpointGroupsPostProcessor.class).run((context) -> { + HealthEndpointGroups groups = context.getBean(HealthEndpointGroups.class); + assertThatExceptionOfType(RuntimeException.class).isThrownBy(() -> groups.get("test")) + .withMessage("postprocessed"); + }); } @Configuration(proxyBeanMethods = false) @@ -429,12 +432,12 @@ class HealthEndpointAutoConfigurationTests { } - @Configuration(proxyBeanMethods = false) - static class HealthEndpointGroupsRegistryCustomizerConfig { + static class TestHealthEndpointGroupsPostProcessor implements HealthEndpointGroupsPostProcessor { - @Bean - HealthEndpointGroupsRegistryCustomizer customHealthEndpointGroup() { - return (registry) -> registry.add("test", (configurer) -> configurer.include("ping")); + @Override + public HealthEndpointGroups postProcessHealthEndpointGroups(HealthEndpointGroups groups) { + given(groups.get("test")).willThrow(new RuntimeException("postprocessed")); + return groups; } } diff --git a/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/health/HealthEndpointGroup.java b/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/health/HealthEndpointGroup.java index c3a54b1be0..0fa53c5df2 100644 --- a/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/health/HealthEndpointGroup.java +++ b/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/health/HealthEndpointGroup.java @@ -23,7 +23,6 @@ import org.springframework.boot.actuate.endpoint.SecurityContext; * by the {@link HealthEndpoint}. * * @author Phillip Webb - * @author Brian Clozel * @since 2.2.0 */ public interface HealthEndpointGroup { @@ -63,27 +62,4 @@ public interface HealthEndpointGroup { */ HttpCodeStatusMapper getHttpCodeStatusMapper(); - /** - * Options for showing items in responses from the {@link HealthEndpointGroup} web - * extensions. - */ - enum Show { - - /** - * Never show the item in the response. - */ - NEVER, - - /** - * Show the item in the response when accessed by an authorized user. - */ - WHEN_AUTHORIZED, - - /** - * Always show the item in the response. - */ - ALWAYS - - } - } diff --git a/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/health/HealthEndpointGroupConfigurer.java b/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/health/HealthEndpointGroupConfigurer.java deleted file mode 100644 index 3c150d8014..0000000000 --- a/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/health/HealthEndpointGroupConfigurer.java +++ /dev/null @@ -1,84 +0,0 @@ -/* - * 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. - * 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.springframework.boot.actuate.health.HealthEndpointGroup.Show; - -/** - * A configurer for customizing an {@link HealthEndpointGroup} being built. - * - * @author Brian Clozel - * @since 2.3.0 - */ -public interface HealthEndpointGroupConfigurer { - - /** - * Configure the indicator endpoint ids to include in this group. - * @param indicators the indicator endpoint ids - * @return the configurer instance - */ - HealthEndpointGroupConfigurer include(String... indicators); - - /** - * Configure the indicator endpoint ids to exclude from this group. - * @param indicators the indicator endpoint ids - * @return the configurer instance - */ - HealthEndpointGroupConfigurer exclude(String... indicators); - - /** - * Configure the {@link StatusAggregator} to use for this group. - *

- * If none set, this will default to the globally configured {@link StatusAggregator}. - * @param statusAggregator the status aggregator - * @return the configurer instance - */ - HealthEndpointGroupConfigurer statusAggregator(StatusAggregator statusAggregator); - - /** - * Configure the {@link HttpCodeStatusMapper} to use for this group. - *

- * If none set, this will default to the globally configured - * {@link HttpCodeStatusMapper}. - * @param httpCodeStatusMapper the status code mapper - * @return the configurer instance - */ - HealthEndpointGroupConfigurer httpCodeStatusMapper(HttpCodeStatusMapper httpCodeStatusMapper); - - /** - * Configure the {@link Show visibility option} for showing components of this group. - * @param showComponents the components visibility - * @return the configurer instance - */ - HealthEndpointGroupConfigurer showComponents(Show showComponents); - - /** - * Configure the {@link Show visibility option} for showing details of this group. - * @param showDetails the details visibility - * @return the configurer instance - */ - HealthEndpointGroupConfigurer showDetails(Show showDetails); - - /** - * Configure roles used to determine whether or not a user is authorized to be shown - * details. - * @param roles the roles - * @return the configurer instance - */ - HealthEndpointGroupConfigurer roles(String... roles); - -} diff --git a/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/health/HealthEndpointGroupsRegistryCustomizer.java b/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/health/HealthEndpointGroupsPostProcessor.java similarity index 58% rename from spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/health/HealthEndpointGroupsRegistryCustomizer.java rename to spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/health/HealthEndpointGroupsPostProcessor.java index 4d3a49d38a..8bc802a6a7 100644 --- a/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/health/HealthEndpointGroupsRegistryCustomizer.java +++ b/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/health/HealthEndpointGroupsPostProcessor.java @@ -17,19 +17,22 @@ package org.springframework.boot.actuate.health; /** - * Callback interface that can be used to customize a - * {@link HealthEndpointGroupsRegistry}. + * Hook that allows for custom modification of {@link HealthEndpointGroups} — for + * example, automatically adding additional auto-configured groups. * + * @author Phillip Webb * @author Brian Clozel * @since 2.3.0 */ @FunctionalInterface -public interface HealthEndpointGroupsRegistryCustomizer { +public interface HealthEndpointGroupsPostProcessor { /** - * Callback to customize a {@link HealthEndpointGroupsRegistry} instance. - * @param healthEndpointGroupsRegistry the registry to customize + * Post-process the given {@link HealthEndpointGroups} instance. + * @param groups the existing groups instance + * @return a post-processed groups instance, or the original instance if not + * post-processing was required */ - void customize(HealthEndpointGroupsRegistry healthEndpointGroupsRegistry); + HealthEndpointGroups postProcessHealthEndpointGroups(HealthEndpointGroups groups); } diff --git a/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/health/HealthEndpointGroupsRegistry.java b/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/health/HealthEndpointGroupsRegistry.java deleted file mode 100644 index a9f69e95a7..0000000000 --- a/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/health/HealthEndpointGroupsRegistry.java +++ /dev/null @@ -1,50 +0,0 @@ -/* - * 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. - * 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.function.Consumer; - -/** - * Builder for an {@link HealthEndpointGroups} immutable instance. - * - * @author Brian Clozel - * @since 2.3.0 - */ -public interface HealthEndpointGroupsRegistry extends HealthEndpointGroups { - - /** - * Add a new {@link HealthEndpointGroup}. - * @param groupName the name of the group to add - * @param builder the group to add - * @return the builder instance - */ - HealthEndpointGroupsRegistry add(String groupName, Consumer builder); - - /** - * Remove an existing {@link HealthEndpointGroup}. - * @param groupName the name of the group to remove - * @return the builder instance - */ - HealthEndpointGroupsRegistry remove(String groupName); - - /** - * Build an immutable {@link HealthEndpointGroups}. - * @return the {@link HealthEndpointGroups} - */ - HealthEndpointGroups toGroups(); - -} diff --git a/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/health/HttpCodeStatusMapper.java b/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/health/HttpCodeStatusMapper.java index 2e809040e6..10d9d291d4 100644 --- a/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/health/HttpCodeStatusMapper.java +++ b/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/health/HttpCodeStatusMapper.java @@ -1,5 +1,5 @@ /* - * 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. @@ -26,6 +26,12 @@ package org.springframework.boot.actuate.health; @FunctionalInterface public interface HttpCodeStatusMapper { + /** + * A {@link HttpCodeStatusMapper} instance using default mappings. + * @since 2.3.0 + */ + HttpCodeStatusMapper DEFAULT = new SimpleHttpCodeStatusMapper(); + /** * Return the HTTP status code that corresponds to the given {@link Status health * status}. diff --git a/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/health/StatusAggregator.java b/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/health/StatusAggregator.java index e83e183492..91a76b93a7 100644 --- a/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/health/StatusAggregator.java +++ b/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/health/StatusAggregator.java @@ -1,5 +1,5 @@ /* - * 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. @@ -32,6 +32,12 @@ import java.util.Set; @FunctionalInterface public interface StatusAggregator { + /** + * A {@link StatusAggregator} instance using default ordering rules. + * @since 2.3.0 + */ + StatusAggregator DEFAULT = new SimpleStatusAggregator(); + /** * Return the aggregate status for the given set of statuses. * @param statuses the statuses to aggregate diff --git a/spring-boot-project/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/health/TestHealthEndpointGroupsRegistry.java b/spring-boot-project/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/health/TestHealthEndpointGroupsRegistry.java deleted file mode 100644 index 30db51a576..0000000000 --- a/spring-boot-project/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/health/TestHealthEndpointGroupsRegistry.java +++ /dev/null @@ -1,124 +0,0 @@ -/* - * 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. - * 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.Arrays; -import java.util.HashMap; -import java.util.Map; -import java.util.Set; -import java.util.function.Consumer; -import java.util.function.Predicate; - -/** - * Test implementation for {@link HealthEndpointGroupsRegistry} - * - * @author Brian Clozel - */ -public class TestHealthEndpointGroupsRegistry implements HealthEndpointGroupsRegistry { - - private TestHealthEndpointGroup primary = new TestHealthEndpointGroup(); - - private Map groups = new HashMap<>(); - - @Override - public HealthEndpointGroupsRegistry add(String groupName, Consumer builder) { - TestHealthEndpointGroupConfigurer configurer = new TestHealthEndpointGroupConfigurer(); - builder.accept(configurer); - this.groups.put(groupName, configurer.toHealthEndpointGroup()); - return this; - } - - @Override - public HealthEndpointGroupsRegistry remove(String groupName) { - this.groups.remove(groupName); - return this; - } - - @Override - public HealthEndpointGroups toGroups() { - return this; - } - - @Override - public HealthEndpointGroup getPrimary() { - return this.primary; - } - - @Override - public Set getNames() { - return this.groups.keySet(); - } - - @Override - public HealthEndpointGroup get(String name) { - return this.groups.get(name); - } - - class TestHealthEndpointGroupConfigurer implements HealthEndpointGroupConfigurer { - - private Predicate predicate = (name) -> true; - - @Override - public HealthEndpointGroupConfigurer include(String... indicators) { - Predicate included = Arrays.asList(indicators).stream() - .map((group) -> (Predicate) (s) -> s.equals(group)).reduce(Predicate::or) - .orElse((s) -> true); - this.predicate = this.predicate.and(included); - return this; - } - - @Override - public HealthEndpointGroupConfigurer exclude(String... indicators) { - Predicate excluded = Arrays.asList(indicators).stream() - .map((group) -> (Predicate) (s) -> !s.equals(group)).reduce(Predicate::or) - .orElse((s) -> true); - this.predicate = this.predicate.and(excluded); - return this; - } - - @Override - public HealthEndpointGroupConfigurer statusAggregator(StatusAggregator statusAggregator) { - throw new UnsupportedOperationException(); - } - - @Override - public HealthEndpointGroupConfigurer httpCodeStatusMapper(HttpCodeStatusMapper httpCodeStatusMapper) { - throw new UnsupportedOperationException(); - } - - @Override - public HealthEndpointGroupConfigurer showComponents(HealthEndpointGroup.Show showComponents) { - throw new UnsupportedOperationException(); - } - - @Override - public HealthEndpointGroupConfigurer showDetails(HealthEndpointGroup.Show showDetails) { - throw new UnsupportedOperationException(); - } - - @Override - public HealthEndpointGroupConfigurer roles(String... roles) { - throw new UnsupportedOperationException(); - } - - TestHealthEndpointGroup toHealthEndpointGroup() { - return new TestHealthEndpointGroup(this.predicate); - } - - } - -}