Rename probe health indicators

Rename `LivenessProbeHealthIndicator` to `LivenessStateHealthIndicator`
and `ReadinessProbeHealthIndicator` to `ReadinessStateHealthIndicator`.

Also introduce a general purpose `AvailabilityStateHealthIndicator`
class.

See gh-20962
This commit is contained in:
Phillip Webb
2020-04-13 10:52:09 -07:00
parent bb79c847b2
commit 71e4801e68
8 changed files with 283 additions and 43 deletions

View File

@@ -18,8 +18,8 @@ package org.springframework.boot.actuate.autoconfigure.kubernetes;
import org.springframework.boot.actuate.autoconfigure.health.ConditionalOnEnabledHealthIndicator;
import org.springframework.boot.actuate.autoconfigure.kubernetes.ProbesHealthContributorAutoConfiguration.KubernetesOrPropertyCondition;
import org.springframework.boot.actuate.availability.LivenessProbeHealthIndicator;
import org.springframework.boot.actuate.availability.ReadinessProbeHealthIndicator;
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.actuate.kubernetes.ProbesHealthEndpointGroupsRegistrar;
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
@@ -37,7 +37,7 @@ import org.springframework.context.annotation.Configuration;
/**
* {@link EnableAutoConfiguration Auto-configuration} for
* {@link LivenessProbeHealthIndicator} and {@link ReadinessProbeHealthIndicator}.
* {@link LivenessStateHealthIndicator} and {@link ReadinessStateHealthIndicator}.
*
* @author Brian Clozel
* @since 2.3.0
@@ -50,16 +50,16 @@ public class ProbesHealthContributorAutoConfiguration {
@Bean
@ConditionalOnEnabledHealthIndicator("livenessProbe")
@ConditionalOnMissingBean
public LivenessProbeHealthIndicator livenessProbeHealthIndicator(ApplicationAvailability applicationAvailability) {
return new LivenessProbeHealthIndicator(applicationAvailability);
public LivenessStateHealthIndicator livenessProbeHealthIndicator(ApplicationAvailability applicationAvailability) {
return new LivenessStateHealthIndicator(applicationAvailability);
}
@Bean
@ConditionalOnEnabledHealthIndicator("readinessProbe")
@ConditionalOnMissingBean
public ReadinessProbeHealthIndicator readinessProbeHealthIndicator(
public ReadinessStateHealthIndicator readinessProbeHealthIndicator(
ApplicationAvailability applicationAvailability) {
return new ReadinessProbeHealthIndicator(applicationAvailability);
return new ReadinessStateHealthIndicator(applicationAvailability);
}
@Bean

View File

@@ -18,8 +18,8 @@ package org.springframework.boot.actuate.autoconfigure.kubernetes;
import org.junit.jupiter.api.Test;
import org.springframework.boot.actuate.availability.LivenessProbeHealthIndicator;
import org.springframework.boot.actuate.availability.ReadinessProbeHealthIndicator;
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;
@@ -29,7 +29,7 @@ import org.springframework.boot.test.context.runner.ApplicationContextRunner;
import static org.assertj.core.api.Assertions.assertThat;
/**
* Tests fos {@link ProbesHealthContributorAutoConfiguration}.
* Tests for {@link ProbesHealthContributorAutoConfiguration}.
*
* @author Brian Clozel
*/
@@ -41,8 +41,8 @@ class ProbesHealthContributorAutoConfigurationTests {
@Test
void probesNotConfiguredIfNotKubernetes() {
this.contextRunner.run((context) -> assertThat(context).hasSingleBean(ApplicationAvailability.class)
.doesNotHaveBean(LivenessProbeHealthIndicator.class)
.doesNotHaveBean(ReadinessProbeHealthIndicator.class)
.doesNotHaveBean(LivenessStateHealthIndicator.class)
.doesNotHaveBean(ReadinessStateHealthIndicator.class)
.doesNotHaveBean(HealthEndpointGroupsRegistryCustomizer.class));
}
@@ -50,8 +50,8 @@ class ProbesHealthContributorAutoConfigurationTests {
void probesConfiguredIfProperty() {
this.contextRunner.withPropertyValues("management.health.probes.enabled=true")
.run((context) -> assertThat(context).hasSingleBean(ApplicationAvailability.class)
.hasSingleBean(LivenessProbeHealthIndicator.class)
.hasSingleBean(ReadinessProbeHealthIndicator.class)
.hasSingleBean(LivenessStateHealthIndicator.class)
.hasSingleBean(ReadinessStateHealthIndicator.class)
.hasSingleBean(HealthEndpointGroupsRegistryCustomizer.class));
}

View File

@@ -0,0 +1,122 @@
/*
* 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.availability;
import java.util.EnumSet;
import java.util.HashMap;
import java.util.Map;
import java.util.function.Consumer;
import org.springframework.boot.actuate.health.AbstractHealthIndicator;
import org.springframework.boot.actuate.health.Health.Builder;
import org.springframework.boot.actuate.health.HealthIndicator;
import org.springframework.boot.actuate.health.Status;
import org.springframework.boot.availability.ApplicationAvailability;
import org.springframework.boot.availability.AvailabilityState;
import org.springframework.util.Assert;
/**
* A {@link HealthIndicator} that checks a specific {@link AvailabilityState} of the
* application.
*
* @author Phillip Webb
* @author Brian Clozel
* @since 2.3.0
*/
public class AvailabilityStateHealthIndicator extends AbstractHealthIndicator {
private final ApplicationAvailability applicationAvailability;
private Class<? extends AvailabilityState> stateType;
private final Map<AvailabilityState, Status> statusMappings = new HashMap<>();
/**
* Create a new {@link AvailabilityStateHealthIndicator} instance.
* @param <S> the availability state type
* @param applicationAvailability the application availability
* @param stateType the availability state type
* @param statusMappings consumer used to setup the status mappings
*/
public <S extends AvailabilityState> AvailabilityStateHealthIndicator(
ApplicationAvailability applicationAvailability, Class<S> stateType,
Consumer<StatusMappings<S>> statusMappings) {
Assert.notNull(applicationAvailability, "ApplicationAvailability must not be null");
Assert.notNull(stateType, "StateType must not be null");
Assert.notNull(statusMappings, "StatusMappings must not be null");
this.applicationAvailability = applicationAvailability;
this.stateType = stateType;
statusMappings.accept(this.statusMappings::put);
assertAllEnumsMapped(stateType);
}
@SuppressWarnings({ "unchecked", "rawtypes" })
private <S extends AvailabilityState> void assertAllEnumsMapped(Class<S> stateType) {
if (!this.statusMappings.containsKey(null) && Enum.class.isAssignableFrom(stateType)) {
EnumSet elements = EnumSet.allOf((Class) stateType);
for (Object element : elements) {
Assert.isTrue(this.statusMappings.containsKey(element), "StatusMappings does not include " + element);
}
}
}
@Override
protected void doHealthCheck(Builder builder) throws Exception {
AvailabilityState state = getState(this.applicationAvailability);
Status status = this.statusMappings.get(state);
if (status == null) {
status = this.statusMappings.get(null);
}
Assert.state(status != null, "No mapping provided for " + state);
builder.status(status);
}
/**
* Return the current availability state. Subclasses can override this method if a
* different retrieval mechanism is needed.
* @param applicationAvailability the application availability
* @return the current availability state
*/
protected AvailabilityState getState(ApplicationAvailability applicationAvailability) {
return applicationAvailability.getState(this.stateType);
}
/**
* Callback used to add status mappings.
*
* @param <S> the availability state type
*/
public interface StatusMappings<S extends AvailabilityState> {
/**
* Add the status that should be used if no explicit mapping is defined.
* @param status the default status
*/
default void addDefaultStatus(Status status) {
add(null, status);
}
/**
* Add a new status mapping .
* @param availabilityState the availability state
* @param status the mapped status
*/
void add(S availabilityState, Status status);
}
}

View File

@@ -16,11 +16,10 @@
package org.springframework.boot.actuate.availability;
import org.springframework.boot.actuate.health.AbstractHealthIndicator;
import org.springframework.boot.actuate.health.Health;
import org.springframework.boot.actuate.health.HealthIndicator;
import org.springframework.boot.actuate.health.Status;
import org.springframework.boot.availability.ApplicationAvailability;
import org.springframework.boot.availability.AvailabilityState;
import org.springframework.boot.availability.LivenessState;
/**
@@ -29,18 +28,18 @@ import org.springframework.boot.availability.LivenessState;
* @author Brian Clozel
* @since 2.3.0
*/
public class LivenessProbeHealthIndicator extends AbstractHealthIndicator {
public class LivenessStateHealthIndicator extends AvailabilityStateHealthIndicator {
private final ApplicationAvailability applicationAvailability;
public LivenessProbeHealthIndicator(ApplicationAvailability applicationAvailability) {
this.applicationAvailability = applicationAvailability;
public LivenessStateHealthIndicator(ApplicationAvailability availability) {
super(availability, LivenessState.class, (statusMappings) -> {
statusMappings.add(LivenessState.CORRECT, Status.UP);
statusMappings.add(LivenessState.BROKEN, Status.DOWN);
});
}
@Override
protected void doHealthCheck(Health.Builder builder) throws Exception {
LivenessState state = this.applicationAvailability.getLivenessState();
builder.status(LivenessState.CORRECT == state ? Status.UP : Status.DOWN);
protected AvailabilityState getState(ApplicationAvailability applicationAvailability) {
return applicationAvailability.getLivenessState();
}
}

View File

@@ -16,31 +16,32 @@
package org.springframework.boot.actuate.availability;
import org.springframework.boot.actuate.health.AbstractHealthIndicator;
import org.springframework.boot.actuate.health.Health;
import org.springframework.boot.actuate.health.HealthIndicator;
import org.springframework.boot.actuate.health.Status;
import org.springframework.boot.availability.ApplicationAvailability;
import org.springframework.boot.availability.AvailabilityState;
import org.springframework.boot.availability.LivenessState;
import org.springframework.boot.availability.ReadinessState;
/**
* A {@link HealthIndicator} that checks the {@link ReadinessState} of the application.
* A {@link HealthIndicator} that checks the {@link LivenessState} of the application.
*
* @author Brian Clozel
* @author Phillip Webb
* @since 2.3.0
*/
public class ReadinessProbeHealthIndicator extends AbstractHealthIndicator {
public class ReadinessStateHealthIndicator extends AvailabilityStateHealthIndicator {
private final ApplicationAvailability applicationAvailability;
public ReadinessProbeHealthIndicator(ApplicationAvailability applicationAvailability) {
this.applicationAvailability = applicationAvailability;
public ReadinessStateHealthIndicator(ApplicationAvailability availability) {
super(availability, ReadinessState.class, (statusMappings) -> {
statusMappings.add(ReadinessState.ACCEPTING_TRAFFIC, Status.UP);
statusMappings.add(ReadinessState.REFUSING_TRAFFIC, Status.OUT_OF_SERVICE);
});
}
@Override
protected void doHealthCheck(Health.Builder builder) throws Exception {
ReadinessState state = this.applicationAvailability.getReadinessState();
builder.status(ReadinessState.ACCEPTING_TRAFFIC == state ? Status.UP : Status.OUT_OF_SERVICE);
protected AvailabilityState getState(ApplicationAvailability applicationAvailability) {
return applicationAvailability.getReadinessState();
}
}

View File

@@ -0,0 +1,118 @@
/*
* 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.availability;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.springframework.boot.actuate.health.Status;
import org.springframework.boot.availability.ApplicationAvailability;
import org.springframework.boot.availability.AvailabilityState;
import org.springframework.boot.availability.LivenessState;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;
import static org.mockito.BDDMockito.given;
/**
* Tests for {@link AvailabilityStateHealthIndicator}.
*
* @author Phillip Webb
*/
class AvailabilityStateHealthIndicatorTests {
@Mock
private ApplicationAvailability applicationAvailability;
@BeforeEach
void setup() {
MockitoAnnotations.initMocks(this);
}
@Test
void createWhenApplicationAvailabilityIsNullThrowsException() {
assertThatIllegalArgumentException()
.isThrownBy(() -> new AvailabilityStateHealthIndicator(null, LivenessState.class, (statusMappings) -> {
})).withMessage("ApplicationAvailability must not be null");
}
@Test
void createWhenStateTypeIsNullThrowsException() {
assertThatIllegalArgumentException().isThrownBy(
() -> new AvailabilityStateHealthIndicator(this.applicationAvailability, null, (statusMappings) -> {
})).withMessage("StateType must not be null");
}
@Test
void createWhenStatusMappingIsNullThrowsException() {
assertThatIllegalArgumentException().isThrownBy(
() -> new AvailabilityStateHealthIndicator(this.applicationAvailability, LivenessState.class, null))
.withMessage("StatusMappings must not be null");
}
@Test
void createWhenStatusMappingDoesNotCoverAllEnumsThrowsException() {
assertThatIllegalArgumentException()
.isThrownBy(() -> new AvailabilityStateHealthIndicator(this.applicationAvailability,
LivenessState.class, (statusMappings) -> statusMappings.add(LivenessState.CORRECT, Status.UP)))
.withMessage("StatusMappings does not include BROKEN");
}
@Test
void healthReturnsMappedStatus() {
AvailabilityStateHealthIndicator indicator = new AvailabilityStateHealthIndicator(this.applicationAvailability,
LivenessState.class, (statusMappings) -> {
statusMappings.add(LivenessState.CORRECT, Status.UP);
statusMappings.add(LivenessState.BROKEN, Status.DOWN);
});
given(this.applicationAvailability.getState(LivenessState.class)).willReturn(LivenessState.BROKEN);
assertThat(indicator.getHealth(false).getStatus()).isEqualTo(Status.DOWN);
}
@Test
void healthReturnsDefaultStatus() {
AvailabilityStateHealthIndicator indicator = new AvailabilityStateHealthIndicator(this.applicationAvailability,
LivenessState.class, (statusMappings) -> {
statusMappings.add(LivenessState.CORRECT, Status.UP);
statusMappings.addDefaultStatus(Status.UNKNOWN);
});
given(this.applicationAvailability.getState(LivenessState.class)).willReturn(LivenessState.BROKEN);
assertThat(indicator.getHealth(false).getStatus()).isEqualTo(Status.UNKNOWN);
}
@Test
void healthWhenNotEnumReturnsMappedStatus() {
AvailabilityStateHealthIndicator indicator = new AvailabilityStateHealthIndicator(this.applicationAvailability,
TestAvailabilityState.class, (statusMappings) -> {
statusMappings.add(TestAvailabilityState.ONE, Status.UP);
statusMappings.addDefaultStatus(Status.DOWN);
});
given(this.applicationAvailability.getState(TestAvailabilityState.class)).willReturn(TestAvailabilityState.TWO);
assertThat(indicator.getHealth(false).getStatus()).isEqualTo(Status.DOWN);
}
static class TestAvailabilityState implements AvailabilityState {
static final TestAvailabilityState ONE = new TestAvailabilityState();
static final TestAvailabilityState TWO = new TestAvailabilityState();
}
}

View File

@@ -28,20 +28,20 @@ import static org.mockito.BDDMockito.given;
import static org.mockito.Mockito.mock;
/**
* Tests for {@link LivenessProbeHealthIndicator}
* Tests for {@link LivenessStateHealthIndicator}
*
* @author Brian Clozel
*/
class LivenessProbeHealthIndicatorTests {
class LivenessStateHealthIndicatorTests {
private ApplicationAvailability availability;
private LivenessProbeHealthIndicator healthIndicator;
private LivenessStateHealthIndicator healthIndicator;
@BeforeEach
void setUp() {
this.availability = mock(ApplicationAvailability.class);
this.healthIndicator = new LivenessProbeHealthIndicator(this.availability);
this.healthIndicator = new LivenessStateHealthIndicator(this.availability);
}
@Test

View File

@@ -28,20 +28,20 @@ import static org.mockito.BDDMockito.given;
import static org.mockito.Mockito.mock;
/**
* Tests for {@link ReadinessProbeHealthIndicator}
* Tests for {@link ReadinessStateHealthIndicator}
*
* @author Brian Clozel
*/
class ReadinessProbeHealthIndicatorTests {
class ReadinessStateHealthIndicatorTests {
private ApplicationAvailability availability;
private ReadinessProbeHealthIndicator healthIndicator;
private ReadinessStateHealthIndicator healthIndicator;
@BeforeEach
void setUp() {
this.availability = mock(ApplicationAvailability.class);
this.healthIndicator = new ReadinessProbeHealthIndicator(this.availability);
this.healthIndicator = new ReadinessStateHealthIndicator(this.availability);
}
@Test