Fix class cast during additional path matching with health probes
Previously, when health probes were enabled, the post-processor of AutoConfiguredHealthEndpointGroups resulted in the bean no longer implementing AdditionalPathMapper. This then caused a ClassCastException when working with AdditionalPathMapper beans in EndpointRequest's additional path mapping support. This commit updates the type returned by the post-processor to implement both HealthEndpointGroups and AdditionalPathMapper, as AutoConfiguredHealthEndpointGroups does. Its implementation of getAdditionalPaths produces a result that combines both the additional paths of the original HealthEndpointGroups bean and its own additional paths for the probes. Fixes gh-44052
This commit is contained in:
parent
1c0253b380
commit
c3c7ed4c2e
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright 2012-2022 the original author or authors.
|
||||
* Copyright 2012-2025 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
|
@ -16,14 +16,20 @@
|
|||
|
||||
package org.springframework.boot.actuate.autoconfigure.availability;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
|
||||
import org.springframework.boot.actuate.endpoint.EndpointId;
|
||||
import org.springframework.boot.actuate.endpoint.web.AdditionalPathsMapper;
|
||||
import org.springframework.boot.actuate.endpoint.web.WebServerNamespace;
|
||||
import org.springframework.boot.actuate.health.AdditionalHealthEndpointPath;
|
||||
import org.springframework.boot.actuate.health.HealthEndpoint;
|
||||
import org.springframework.boot.actuate.health.HealthEndpointGroup;
|
||||
import org.springframework.boot.actuate.health.HealthEndpointGroups;
|
||||
import org.springframework.util.Assert;
|
||||
|
@ -35,7 +41,7 @@ import org.springframework.util.Assert;
|
|||
* @author Brian Clozel
|
||||
* @author Madhura Bhave
|
||||
*/
|
||||
class AvailabilityProbesHealthEndpointGroups implements HealthEndpointGroups {
|
||||
class AvailabilityProbesHealthEndpointGroups implements HealthEndpointGroups, AdditionalPathsMapper {
|
||||
|
||||
private final HealthEndpointGroups groups;
|
||||
|
||||
|
@ -107,4 +113,23 @@ class AvailabilityProbesHealthEndpointGroups implements HealthEndpointGroups {
|
|||
return name.equals(LIVENESS) || name.equals(READINESS);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> getAdditionalPaths(EndpointId endpointId, WebServerNamespace webServerNamespace) {
|
||||
if (!HealthEndpoint.ID.equals(endpointId)) {
|
||||
return null;
|
||||
}
|
||||
List<String> additionalPaths = new ArrayList<>();
|
||||
if (this.groups instanceof AdditionalPathsMapper additionalPathsMapper) {
|
||||
additionalPaths.addAll(additionalPathsMapper.getAdditionalPaths(endpointId, webServerNamespace));
|
||||
}
|
||||
additionalPaths.addAll(this.probeGroups.values()
|
||||
.stream()
|
||||
.map(HealthEndpointGroup::getAdditionalPath)
|
||||
.filter(Objects::nonNull)
|
||||
.filter((additionalPath) -> additionalPath.hasNamespace(webServerNamespace))
|
||||
.map(AdditionalHealthEndpointPath::getValue)
|
||||
.toList());
|
||||
return additionalPaths;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright 2012-2023 the original author or authors.
|
||||
* Copyright 2012-2025 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.
|
||||
|
@ -17,10 +17,15 @@
|
|||
package org.springframework.boot.actuate.autoconfigure.availability;
|
||||
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.mockito.Mockito;
|
||||
|
||||
import org.springframework.boot.actuate.endpoint.EndpointId;
|
||||
import org.springframework.boot.actuate.endpoint.web.AdditionalPathsMapper;
|
||||
import org.springframework.boot.actuate.endpoint.web.WebServerNamespace;
|
||||
import org.springframework.boot.actuate.health.HealthEndpointGroup;
|
||||
import org.springframework.boot.actuate.health.HealthEndpointGroups;
|
||||
import org.springframework.mock.env.MockEnvironment;
|
||||
|
@ -103,6 +108,39 @@ class AvailabilityProbesHealthEndpointGroupsPostProcessorTests {
|
|||
assertThat(readiness.getAdditionalPath()).hasToString("server:/readyz");
|
||||
}
|
||||
|
||||
@Test
|
||||
void delegatesAdditionalPathMappingToOriginalBean() {
|
||||
HealthEndpointGroups groups = mock(HealthEndpointGroups.class,
|
||||
Mockito.withSettings().extraInterfaces(AdditionalPathsMapper.class));
|
||||
given(((AdditionalPathsMapper) groups).getAdditionalPaths(EndpointId.of("health"), WebServerNamespace.SERVER))
|
||||
.willReturn(List.of("/one", "/two", "/three"));
|
||||
MockEnvironment environment = new MockEnvironment();
|
||||
AvailabilityProbesHealthEndpointGroupsPostProcessor postProcessor = new AvailabilityProbesHealthEndpointGroupsPostProcessor(
|
||||
environment);
|
||||
HealthEndpointGroups postProcessed = postProcessor.postProcessHealthEndpointGroups(groups);
|
||||
assertThat(postProcessed).isInstanceOf(AdditionalPathsMapper.class);
|
||||
AdditionalPathsMapper additionalPathsMapper = (AdditionalPathsMapper) postProcessed;
|
||||
assertThat(additionalPathsMapper.getAdditionalPaths(EndpointId.of("health"), WebServerNamespace.SERVER))
|
||||
.containsExactly("/one", "/two", "/three");
|
||||
}
|
||||
|
||||
@Test
|
||||
void whenAddAdditionalPathsIsTrueThenIncludesOwnAdditionalPathsInGetAdditionalPathsResult() {
|
||||
HealthEndpointGroups groups = mock(HealthEndpointGroups.class,
|
||||
Mockito.withSettings().extraInterfaces(AdditionalPathsMapper.class));
|
||||
given(((AdditionalPathsMapper) groups).getAdditionalPaths(EndpointId.of("health"), WebServerNamespace.SERVER))
|
||||
.willReturn(List.of("/one", "/two", "/three"));
|
||||
MockEnvironment environment = new MockEnvironment();
|
||||
environment.setProperty("management.endpoint.health.probes.add-additional-paths", "true");
|
||||
AvailabilityProbesHealthEndpointGroupsPostProcessor postProcessor = new AvailabilityProbesHealthEndpointGroupsPostProcessor(
|
||||
environment);
|
||||
HealthEndpointGroups postProcessed = postProcessor.postProcessHealthEndpointGroups(groups);
|
||||
assertThat(postProcessed).isInstanceOf(AdditionalPathsMapper.class);
|
||||
AdditionalPathsMapper additionalPathsMapper = (AdditionalPathsMapper) postProcessed;
|
||||
assertThat(additionalPathsMapper.getAdditionalPaths(EndpointId.of("health"), WebServerNamespace.SERVER))
|
||||
.containsExactly("/one", "/two", "/three", "/livez", "/readyz");
|
||||
}
|
||||
|
||||
private HealthEndpointGroups getPostProcessed(String value) {
|
||||
MockEnvironment environment = new MockEnvironment();
|
||||
environment.setProperty("management.endpoint.health.probes.add-additional-paths", value);
|
||||
|
|
Loading…
Reference in New Issue