Browse Source

Consider reactive indicators in HealthEndpointGroupMembershipValidator

Update `HealthEndpointGroupMembershipValidator` to also consider
reactive health indicators.

Fixes gh-48387
pull/48396/head
Phillip Webb 2 weeks ago
parent
commit
739b92e525
  1. 36
      module/spring-boot-health/src/main/java/org/springframework/boot/health/autoconfigure/actuate/endpoint/HealthEndpointConfiguration.java
  2. 38
      module/spring-boot-health/src/test/java/org/springframework/boot/health/autoconfigure/actuate/endpoint/HealthEndpointAutoConfigurationTests.java

36
module/spring-boot-health/src/main/java/org/springframework/boot/health/autoconfigure/actuate/endpoint/HealthEndpointConfiguration.java

@ -17,6 +17,7 @@ @@ -17,6 +17,7 @@
package org.springframework.boot.health.autoconfigure.actuate.endpoint;
import java.util.Set;
import java.util.function.BiFunction;
import org.jspecify.annotations.Nullable;
@ -34,6 +35,7 @@ import org.springframework.boot.health.actuate.endpoint.SimpleHttpCodeStatusMapp @@ -34,6 +35,7 @@ import org.springframework.boot.health.actuate.endpoint.SimpleHttpCodeStatusMapp
import org.springframework.boot.health.actuate.endpoint.SimpleStatusAggregator;
import org.springframework.boot.health.actuate.endpoint.StatusAggregator;
import org.springframework.boot.health.contributor.HealthContributors;
import org.springframework.boot.health.contributor.ReactiveHealthContributors;
import org.springframework.boot.health.registry.HealthContributorRegistry;
import org.springframework.boot.health.registry.ReactiveHealthContributorRegistry;
import org.springframework.context.ApplicationContext;
@ -78,8 +80,10 @@ class HealthEndpointConfiguration { @@ -78,8 +80,10 @@ class HealthEndpointConfiguration {
@Bean
@ConditionalOnBooleanProperty(name = "management.endpoint.health.validate-group-membership", matchIfMissing = true)
HealthEndpointGroupMembershipValidator healthEndpointGroupMembershipValidator(HealthEndpointProperties properties,
HealthContributorRegistry healthContributorRegistry) {
return new HealthEndpointGroupMembershipValidator(properties, healthContributorRegistry);
HealthContributorRegistry healthContributorRegistry,
ObjectProvider<ReactiveHealthContributorRegistry> reactiveHealthContributorRegistry) {
return new HealthEndpointGroupMembershipValidator(properties, healthContributorRegistry,
reactiveHealthContributorRegistry.getIfAvailable());
}
@Bean
@ -138,10 +142,13 @@ class HealthEndpointConfiguration { @@ -138,10 +142,13 @@ class HealthEndpointConfiguration {
private final HealthContributorRegistry registry;
HealthEndpointGroupMembershipValidator(HealthEndpointProperties properties,
HealthContributorRegistry registry) {
@Nullable ReactiveHealthContributorRegistry fallbackRegistry;
HealthEndpointGroupMembershipValidator(HealthEndpointProperties properties, HealthContributorRegistry registry,
@Nullable ReactiveHealthContributorRegistry fallbackRegistry) {
this.properties = properties;
this.registry = registry;
this.fallbackRegistry = fallbackRegistry;
}
@Override
@ -172,13 +179,28 @@ class HealthEndpointConfiguration { @@ -172,13 +179,28 @@ class HealthEndpointConfiguration {
}
private boolean contributorExists(String[] path) {
return contributorExistsInMainRegistry(path) || contributorExistsInFallbackRegistry(path);
}
private boolean contributorExistsInMainRegistry(String[] path) {
return contributorExists(path, this.registry, HealthContributors.class, HealthContributors::getContributor);
}
private boolean contributorExistsInFallbackRegistry(String[] path) {
return contributorExists(path, this.fallbackRegistry, ReactiveHealthContributors.class,
ReactiveHealthContributors::getContributor);
}
@SuppressWarnings("unchecked")
private <C> boolean contributorExists(String[] path, @Nullable Object registry, Class<C> collectionType,
BiFunction<C, String, Object> getFromCollection) {
int pathOffset = 0;
Object contributor = this.registry;
Object contributor = registry;
while (pathOffset < path.length) {
if (!(contributor instanceof HealthContributors)) {
if (contributor == null || !collectionType.isInstance(contributor)) {
return false;
}
contributor = ((HealthContributors) contributor).getContributor(path[pathOffset]);
contributor = getFromCollection.apply((C) contributor, path[pathOffset]);
pathOffset++;
}
return (contributor != null);

38
module/spring-boot-health/src/test/java/org/springframework/boot/health/autoconfigure/actuate/endpoint/HealthEndpointAutoConfigurationTests.java

@ -44,6 +44,7 @@ import org.springframework.boot.health.autoconfigure.actuate.endpoint.HealthEndp @@ -44,6 +44,7 @@ import org.springframework.boot.health.autoconfigure.actuate.endpoint.HealthEndp
import org.springframework.boot.health.autoconfigure.contributor.HealthContributorAutoConfiguration;
import org.springframework.boot.health.autoconfigure.registry.HealthContributorRegistryAutoConfiguration;
import org.springframework.boot.health.contributor.CompositeHealthContributor;
import org.springframework.boot.health.contributor.CompositeReactiveHealthContributor;
import org.springframework.boot.health.contributor.Health;
import org.springframework.boot.health.contributor.HealthContributors;
import org.springframework.boot.health.contributor.HealthIndicator;
@ -145,6 +146,20 @@ class HealthEndpointAutoConfigurationTests { @@ -145,6 +146,20 @@ class HealthEndpointAutoConfigurationTests {
});
}
@Test
void runDoesNotFailWhenHealthEndpointGroupIncludesContributorThatExists() {
this.contextRunner.withUserConfiguration(CompositeHealthIndicatorConfiguration.class)
.withPropertyValues("management.endpoint.health.group.ready.include=composite/b/c")
.run((context) -> assertThat(context).hasNotFailed());
}
@Test // gh-48387
void runDoesNotFailWhenHealthEndpointGroupIncludesReactiveContributorThatExists() {
this.contextRunner.withUserConfiguration(CompositeReactiveHealthIndicatorConfiguration.class)
.withPropertyValues("management.endpoint.health.group.ready.include=composite/b/c")
.run((context) -> assertThat(context).hasNotFailed());
}
@Test
void runFailsWhenHealthEndpointGroupIncludesContributorThatDoesNotExist() {
this.contextRunner.withUserConfiguration(CompositeHealthIndicatorConfiguration.class)
@ -377,8 +392,27 @@ class HealthEndpointAutoConfigurationTests { @@ -377,8 +392,27 @@ class HealthEndpointAutoConfigurationTests {
@Bean
CompositeHealthContributor compositeHealthIndicator() {
return CompositeHealthContributor.fromMap(Map.of("a", (HealthIndicator) () -> Health.up().build(), "b",
CompositeHealthContributor.fromMap(Map.of("c", (HealthIndicator) () -> Health.up().build()))));
return CompositeHealthContributor.fromMap(Map.of("a", createHealthIndicator(), "b",
CompositeHealthContributor.fromMap(Map.of("c", createHealthIndicator()))));
}
private HealthIndicator createHealthIndicator() {
return () -> Health.up().build();
}
}
@Configuration(proxyBeanMethods = false)
static class CompositeReactiveHealthIndicatorConfiguration {
@Bean
CompositeReactiveHealthContributor compositeHealthIndicator() {
return CompositeReactiveHealthContributor.fromMap(Map.of("a", createHealthIndicator(), "b",
CompositeReactiveHealthContributor.fromMap(Map.of("c", createHealthIndicator()))));
}
private ReactiveHealthIndicator createHealthIndicator() {
return () -> Mono.just(Health.up().build());
}
}

Loading…
Cancel
Save