Configure health on additional path only when health exposed

Prior to this commit, limiting the exposure to a specific
technology in `ConditionalOnAvailableEndpoint` would not have
any effect because all endpoints would be considered to be available
if the app was running on Cloud Foundry. This caused issues in cases
where beans were meant to be exposed only if the endpoint was actually
exposed.

This commit adds CLOUD_FOUNDRY to the `EndpointExposure`
enum. This allows `ConditionalOnAvailableEndpoint` to limit
by exposure even when the Cloud Foundry platform is active.

Fixes gh-29532
This commit is contained in:
Madhura Bhave
2022-01-26 20:47:59 -08:00
parent 8c5a5f81ff
commit 0597c6831e
10 changed files with 56 additions and 13 deletions
@@ -54,7 +54,7 @@ public class CachesEndpointAutoConfiguration {
@Bean
@ConditionalOnMissingBean
@ConditionalOnBean(CachesEndpoint.class)
@ConditionalOnAvailableEndpoint(exposure = EndpointExposure.WEB)
@ConditionalOnAvailableEndpoint(exposure = { EndpointExposure.WEB, EndpointExposure.CLOUD_FOUNDRY })
public CachesEndpointWebExtension cachesEndpointWebExtension(CachesEndpoint cachesEndpoint) {
return new CachesEndpointWebExtension(cachesEndpoint);
}
@@ -63,7 +63,7 @@ public class ConfigurationPropertiesReportEndpointAutoConfiguration {
@Bean
@ConditionalOnMissingBean
@ConditionalOnBean(ConfigurationPropertiesReportEndpoint.class)
@ConditionalOnAvailableEndpoint(exposure = EndpointExposure.WEB)
@ConditionalOnAvailableEndpoint(exposure = { EndpointExposure.WEB, EndpointExposure.CLOUD_FOUNDRY })
public ConfigurationPropertiesReportEndpointWebExtension configurationPropertiesReportEndpointWebExtension(
ConfigurationPropertiesReportEndpoint configurationPropertiesReportEndpoint) {
return new ConfigurationPropertiesReportEndpointWebExtension(configurationPropertiesReportEndpoint);
@@ -113,9 +113,6 @@ class OnAvailableEndpointCondition extends SpringBootCondition {
if (!enablementOutcome.isMatch()) {
return enablementOutcome;
}
if (CloudPlatform.CLOUD_FOUNDRY.isActive(environment)) {
return ConditionOutcome.match(message.because("application is running on Cloud Foundry"));
}
Set<EndpointExposure> exposuresToCheck = getExposuresToCheck(conditionAnnotation);
Set<ExposureFilter> exposureFilters = getExposureFilters(environment);
for (ExposureFilter exposureFilter : exposureFilters) {
@@ -168,6 +165,9 @@ class OnAvailableEndpointCondition extends SpringBootCondition {
if (environment.getProperty(JMX_ENABLED_KEY, Boolean.class, false)) {
exposureFilters.add(new ExposureFilter(environment, EndpointExposure.JMX));
}
if (CloudPlatform.CLOUD_FOUNDRY.isActive(environment)) {
exposureFilters.add(new ExposureFilter(environment, EndpointExposure.CLOUD_FOUNDRY));
}
exposureFilters.add(new ExposureFilter(environment, EndpointExposure.WEB));
exposureFiltersCache.put(environment, exposureFilters);
}
@@ -181,9 +181,16 @@ class OnAvailableEndpointCondition extends SpringBootCondition {
@SuppressWarnings({ "unchecked", "rawtypes" })
private ExposureFilter(Environment environment, EndpointExposure exposure) {
super((Class) ExposableEndpoint.class, environment,
"management.endpoints." + exposure.name().toLowerCase() + ".exposure",
exposure.getDefaultIncludes());
"management.endpoints." + getCanonicalName(exposure) + ".exposure", exposure.getDefaultIncludes());
this.exposure = exposure;
}
private static String getCanonicalName(EndpointExposure exposure) {
if (EndpointExposure.CLOUD_FOUNDRY.equals(exposure)) {
return "cloud-foundry";
}
return exposure.name().toLowerCase();
}
EndpointExposure getExposure() {
@@ -32,7 +32,13 @@ public enum EndpointExposure {
/**
* Exposed via a web endpoint.
*/
WEB("health");
WEB("health"),
/**
* Exposed on Cloud Foundry via `/cloudfoundryapplication`.
* @since 2.6.4
*/
CLOUD_FOUNDRY("*");
private final String[] defaultIncludes;
@@ -61,7 +61,7 @@ public class EnvironmentEndpointAutoConfiguration {
@Bean
@ConditionalOnMissingBean
@ConditionalOnBean(EnvironmentEndpoint.class)
@ConditionalOnAvailableEndpoint(exposure = EndpointExposure.WEB)
@ConditionalOnAvailableEndpoint(exposure = { EndpointExposure.WEB, EndpointExposure.CLOUD_FOUNDRY })
public EnvironmentEndpointWebExtension environmentEndpointWebExtension(EnvironmentEndpoint environmentEndpoint) {
return new EnvironmentEndpointWebExtension(environmentEndpoint);
}
@@ -45,7 +45,8 @@ import org.springframework.context.annotation.Configuration;
*/
@Configuration(proxyBeanMethods = false)
@ConditionalOnWebApplication(type = Type.REACTIVE)
@ConditionalOnAvailableEndpoint(endpoint = HealthEndpoint.class, exposure = EndpointExposure.WEB)
@ConditionalOnAvailableEndpoint(endpoint = HealthEndpoint.class,
exposure = { EndpointExposure.WEB, EndpointExposure.CLOUD_FOUNDRY })
class HealthEndpointReactiveWebExtensionConfiguration {
@Bean
@@ -57,6 +58,7 @@ class HealthEndpointReactiveWebExtensionConfiguration {
}
@Configuration(proxyBeanMethods = false)
@ConditionalOnAvailableEndpoint(endpoint = HealthEndpoint.class, exposure = EndpointExposure.WEB)
static class WebFluxAdditionalHealthEndpointPathsConfiguration {
@Bean
@@ -65,7 +65,8 @@ import org.springframework.web.servlet.DispatcherServlet;
@Configuration(proxyBeanMethods = false)
@ConditionalOnWebApplication(type = Type.SERVLET)
@ConditionalOnBean(HealthEndpoint.class)
@ConditionalOnAvailableEndpoint(endpoint = HealthEndpoint.class, exposure = EndpointExposure.WEB)
@ConditionalOnAvailableEndpoint(endpoint = HealthEndpoint.class,
exposure = { EndpointExposure.WEB, EndpointExposure.CLOUD_FOUNDRY })
class HealthEndpointWebExtensionConfiguration {
@Bean
@@ -82,6 +83,7 @@ class HealthEndpointWebExtensionConfiguration {
}
@ConditionalOnBean(DispatcherServlet.class)
@ConditionalOnAvailableEndpoint(endpoint = HealthEndpoint.class, exposure = EndpointExposure.WEB)
static class MvcAdditionalHealthEndpointPathsConfiguration {
@Bean
@@ -97,6 +99,7 @@ class HealthEndpointWebExtensionConfiguration {
@Configuration(proxyBeanMethods = false)
@ConditionalOnClass(ResourceConfig.class)
@ConditionalOnMissingClass("org.springframework.web.servlet.DispatcherServlet")
@ConditionalOnAvailableEndpoint(endpoint = HealthEndpoint.class, exposure = EndpointExposure.WEB)
static class JerseyAdditionalHealthEndpointPathsConfiguration {
@Bean
@@ -54,7 +54,7 @@ public class QuartzEndpointAutoConfiguration {
@Bean
@ConditionalOnBean(QuartzEndpoint.class)
@ConditionalOnMissingBean
@ConditionalOnAvailableEndpoint(exposure = EndpointExposure.WEB)
@ConditionalOnAvailableEndpoint(exposure = { EndpointExposure.WEB, EndpointExposure.CLOUD_FOUNDRY })
public QuartzEndpointWebExtension quartzEndpointWebExtension(QuartzEndpoint endpoint) {
return new QuartzEndpointWebExtension(endpoint);
}
@@ -327,7 +327,8 @@ class ConditionalOnAvailableEndpointTests {
static class ExposureEndpointConfiguration {
@Bean
@ConditionalOnAvailableEndpoint(endpoint = TestEndpoint.class, exposure = EndpointExposure.WEB)
@ConditionalOnAvailableEndpoint(endpoint = TestEndpoint.class,
exposure = { EndpointExposure.WEB, EndpointExposure.CLOUD_FOUNDRY })
String unexposed() {
return "unexposed";
}
@@ -91,6 +91,30 @@ abstract class AbstractHealthEndpointAdditionalPathIntegrationTests<T extends Ab
.exchange().expectStatus().isNotFound(), "local.server.port"));
}
@Test
void groupsAreNotConfiguredWhenHealthEndpointIsNotExposedAndCloudFoundryPlatform() {
this.runner.withPropertyValues("spring.jmx.enabled=true", "management.endpoints.web.exposure.exclude=health",
"spring.main.cloud-platform=cloud_foundry", "management.endpoint.health.group.live.include=diskSpace",
"management.endpoint.health.group.live.additional-path=server:healthz",
"management.endpoint.health.group.live.show-components=always")
.withInitializer(new ConditionEvaluationReportLoggingListener())
.run(withWebTestClient((client) -> client.get().uri("/healthz").accept(MediaType.APPLICATION_JSON)
.exchange().expectStatus().isNotFound(), "local.server.port"));
}
@Test
void groupsAreNotConfiguredWhenHealthEndpointIsNotExposedWithDifferentManagementPortAndCloudFoundryPlatform() {
this.runner
.withPropertyValues("spring.jmx.enabled=true", "management.endpoints.web.exposure.exclude=health",
"spring.main.cloud-platform=cloud_foundry", "management.server.port=0",
"management.endpoint.health.group.live.include=diskSpace",
"management.endpoint.health.group.live.additional-path=server:healthz",
"management.endpoint.health.group.live.show-components=always")
.withInitializer(new ConditionEvaluationReportLoggingListener())
.run(withWebTestClient((client) -> client.get().uri("/healthz").accept(MediaType.APPLICATION_JSON)
.exchange().expectStatus().isNotFound(), "local.server.port"));
}
private void testResponse(WebTestClient client) {
client.get().uri("/healthz").accept(MediaType.APPLICATION_JSON).exchange().expectStatus().isOk().expectBody()
.jsonPath("status").isEqualTo("UP").jsonPath("components.diskSpace").exists();