From 0597c6831e7bfb262856af5017669c9f255257af Mon Sep 17 00:00:00 2001 From: Madhura Bhave Date: Wed, 26 Jan 2022 20:47:59 -0800 Subject: [PATCH] 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 --- .../CachesEndpointAutoConfiguration.java | 2 +- ...ertiesReportEndpointAutoConfiguration.java | 2 +- .../OnAvailableEndpointCondition.java | 17 +++++++++---- .../endpoint/expose/EndpointExposure.java | 8 ++++++- .../EnvironmentEndpointAutoConfiguration.java | 2 +- ...ointReactiveWebExtensionConfiguration.java | 4 +++- ...althEndpointWebExtensionConfiguration.java | 5 +++- .../QuartzEndpointAutoConfiguration.java | 2 +- .../ConditionalOnAvailableEndpointTests.java | 3 ++- ...ndpointAdditionalPathIntegrationTests.java | 24 +++++++++++++++++++ 10 files changed, 56 insertions(+), 13 deletions(-) diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/cache/CachesEndpointAutoConfiguration.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/cache/CachesEndpointAutoConfiguration.java index e78a66e14fe..c216ee44dbd 100644 --- a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/cache/CachesEndpointAutoConfiguration.java +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/cache/CachesEndpointAutoConfiguration.java @@ -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); } diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/context/properties/ConfigurationPropertiesReportEndpointAutoConfiguration.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/context/properties/ConfigurationPropertiesReportEndpointAutoConfiguration.java index 44536ab5abc..7fb384d6a81 100644 --- a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/context/properties/ConfigurationPropertiesReportEndpointAutoConfiguration.java +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/context/properties/ConfigurationPropertiesReportEndpointAutoConfiguration.java @@ -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); diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/endpoint/condition/OnAvailableEndpointCondition.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/endpoint/condition/OnAvailableEndpointCondition.java index 1b185fdb4d7..ac5ad1b2e60 100644 --- a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/endpoint/condition/OnAvailableEndpointCondition.java +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/endpoint/condition/OnAvailableEndpointCondition.java @@ -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 exposuresToCheck = getExposuresToCheck(conditionAnnotation); Set 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() { diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/endpoint/expose/EndpointExposure.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/endpoint/expose/EndpointExposure.java index a678eb7456c..a8abe8fdf3a 100644 --- a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/endpoint/expose/EndpointExposure.java +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/endpoint/expose/EndpointExposure.java @@ -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; diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/env/EnvironmentEndpointAutoConfiguration.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/env/EnvironmentEndpointAutoConfiguration.java index 1dd998dd32f..2a2d28d13d8 100644 --- a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/env/EnvironmentEndpointAutoConfiguration.java +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/env/EnvironmentEndpointAutoConfiguration.java @@ -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); } diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/health/HealthEndpointReactiveWebExtensionConfiguration.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/health/HealthEndpointReactiveWebExtensionConfiguration.java index 6fd895cade1..ec16083331c 100644 --- a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/health/HealthEndpointReactiveWebExtensionConfiguration.java +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/health/HealthEndpointReactiveWebExtensionConfiguration.java @@ -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 diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/health/HealthEndpointWebExtensionConfiguration.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/health/HealthEndpointWebExtensionConfiguration.java index 7fdc2bd0912..57bde64f6dc 100644 --- a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/health/HealthEndpointWebExtensionConfiguration.java +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/health/HealthEndpointWebExtensionConfiguration.java @@ -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 diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/quartz/QuartzEndpointAutoConfiguration.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/quartz/QuartzEndpointAutoConfiguration.java index d4a1e9e501d..0221fd03745 100644 --- a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/quartz/QuartzEndpointAutoConfiguration.java +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/quartz/QuartzEndpointAutoConfiguration.java @@ -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); } diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/endpoint/condition/ConditionalOnAvailableEndpointTests.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/endpoint/condition/ConditionalOnAvailableEndpointTests.java index 530e27f0616..31b1bd49d4f 100644 --- a/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/endpoint/condition/ConditionalOnAvailableEndpointTests.java +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/endpoint/condition/ConditionalOnAvailableEndpointTests.java @@ -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"; } diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/integrationtest/AbstractHealthEndpointAdditionalPathIntegrationTests.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/integrationtest/AbstractHealthEndpointAdditionalPathIntegrationTests.java index dddc5eaf07c..11e1d3b4638 100644 --- a/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/integrationtest/AbstractHealthEndpointAdditionalPathIntegrationTests.java +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/integrationtest/AbstractHealthEndpointAdditionalPathIntegrationTests.java @@ -91,6 +91,30 @@ abstract class AbstractHealthEndpointAdditionalPathIntegrationTests 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();