Skip to content

Commit

Permalink
Configure health on additional path only when health exposed
Browse files Browse the repository at this point in the history
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
  • Loading branch information
mbhave committed Jan 27, 2022
1 parent 8c5a5f8 commit 0597c68
Show file tree
Hide file tree
Showing 10 changed files with 56 additions and 13 deletions.
Expand Up @@ -54,7 +54,7 @@ public CachesEndpoint cachesEndpoint(Map<String, CacheManager> cacheManagers) {
@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);
}
Expand Down
Expand Up @@ -63,7 +63,7 @@ public ConfigurationPropertiesReportEndpoint configurationPropertiesReportEndpoi
@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);
Expand Down
Expand Up @@ -113,9 +113,6 @@ private ConditionOutcome getMatchOutcome(Environment environment,
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) {
Expand Down Expand Up @@ -168,6 +165,9 @@ private Set<ExposureFilter> getExposureFilters(Environment environment) {
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);
}
Expand All @@ -181,9 +181,16 @@ static final class ExposureFilter extends IncludeExcludeEndpointFilter<Exposable
@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() {
Expand Down
Expand Up @@ -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;

Expand Down
Expand Up @@ -61,7 +61,7 @@ public EnvironmentEndpoint environmentEndpoint(Environment environment, Environm
@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);
}
Expand Down
Expand Up @@ -45,7 +45,8 @@
*/
@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
Expand All @@ -57,6 +58,7 @@ ReactiveHealthEndpointWebExtension reactiveHealthEndpointWebExtension(
}

@Configuration(proxyBeanMethods = false)
@ConditionalOnAvailableEndpoint(endpoint = HealthEndpoint.class, exposure = EndpointExposure.WEB)
static class WebFluxAdditionalHealthEndpointPathsConfiguration {

@Bean
Expand Down
Expand Up @@ -65,7 +65,8 @@
@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
Expand All @@ -82,6 +83,7 @@ private static ExposableWebEndpoint getHealthEndpoint(WebEndpointsSupplier webEn
}

@ConditionalOnBean(DispatcherServlet.class)
@ConditionalOnAvailableEndpoint(endpoint = HealthEndpoint.class, exposure = EndpointExposure.WEB)
static class MvcAdditionalHealthEndpointPathsConfiguration {

@Bean
Expand All @@ -97,6 +99,7 @@ AdditionalHealthEndpointPathsWebMvcHandlerMapping healthEndpointWebMvcHandlerMap
@Configuration(proxyBeanMethods = false)
@ConditionalOnClass(ResourceConfig.class)
@ConditionalOnMissingClass("org.springframework.web.servlet.DispatcherServlet")
@ConditionalOnAvailableEndpoint(endpoint = HealthEndpoint.class, exposure = EndpointExposure.WEB)
static class JerseyAdditionalHealthEndpointPathsConfiguration {

@Bean
Expand Down
Expand Up @@ -54,7 +54,7 @@ public QuartzEndpoint quartzEndpoint(Scheduler scheduler) {
@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);
}
Expand Down
Expand Up @@ -327,7 +327,8 @@ DashedEndpoint dashedEndpoint() {
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";
}
Expand Down
Expand Up @@ -91,6 +91,30 @@ void groupsAreNotConfiguredWhenHealthEndpointIsNotExposed() {
.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();
Expand Down

0 comments on commit 0597c68

Please sign in to comment.