Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Actuator endpoints with no operations that use selectors are not accessible when mapped to / #35426

Closed
pavelorehov opened this issue May 15, 2023 · 6 comments
Assignees
Labels
type: regression A regression from a previous release
Milestone

Comments

@pavelorehov
Copy link

pavelorehov commented May 15, 2023

Below worked fine with Spring Boot 2.7.11 and earlier, stopped working with Spring Boot 3.

http://host:8989/metrics

{"timestamp":"2023-05-15T08:17:22.903+00:00","status":404,"error":"Not Found","path":"/metrics/"}

Tested other health endpoint with root - works as expected.

management.server.port=8989
management.server.base-path=/metrics
management.endpoints.web.base-path=/
management.endpoints.web.path-mapping.prometheus=/

Works for health:

management.server.port=8989
management.server.base-path=/metrics
management.endpoints.web.base-path=/
management.endpoints.web.path-mapping.health=/

Was asked to open in spring boot by micrometer owners:
micrometer-metrics/micrometer#3839

@spring-projects-issues spring-projects-issues added the status: waiting-for-triage An issue we've not yet triaged label May 15, 2023
@mhalbritter mhalbritter added the theme: observability Issues related to observability label May 15, 2023
@wilkinsona
Copy link
Member

Thanks for the report but I cannot reproduce the behavior that you have described.

With 2.7.11, the server does not start when using the configuration properties that you have shared:


  .   ____          _            __ _ _
 /\\ / ___'_ __ _ _(_)_ __  __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
 \\/  ___)| |_)| | | | | || (_| |  ) ) ) )
  '  |____| .__|_| |_|_| |_\__, | / / / /
 =========|_|==============|___/=/_/_/_/
 :: Spring Boot ::               (v2.7.11)

2023-05-15 20:41:31.108  INFO 65152 --- [           main] com.example.demo.Gh35426Application      : Starting Gh35426Application using Java 17.0.1 on wilkinsonaGMD6R.vmware.com with PID 65152 (/Users/awilkinson/dev/workspaces/spring-projects/spring-boot/3.0.x/gh-35426/bin/main started by awilkinson in /Users/awilkinson/dev/workspaces/spring-projects/spring-boot/3.0.x/gh-35426)
2023-05-15 20:41:31.110  INFO 65152 --- [           main] com.example.demo.Gh35426Application      : No active profile set, falling back to 1 default profile: "default"
2023-05-15 20:41:31.858  INFO 65152 --- [           main] o.s.b.w.embedded.tomcat.TomcatWebServer  : Tomcat initialized with port(s): 8080 (http)
2023-05-15 20:41:31.866  INFO 65152 --- [           main] o.apache.catalina.core.StandardService   : Starting service [Tomcat]
2023-05-15 20:41:31.867  INFO 65152 --- [           main] org.apache.catalina.core.StandardEngine  : Starting Servlet engine: [Apache Tomcat/9.0.74]
2023-05-15 20:41:31.943  INFO 65152 --- [           main] o.a.c.c.C.[Tomcat].[localhost].[/]       : Initializing Spring embedded WebApplicationContext
2023-05-15 20:41:31.943  INFO 65152 --- [           main] w.s.c.ServletWebServerApplicationContext : Root WebApplicationContext: initialization completed in 795 ms
2023-05-15 20:41:32.405  INFO 65152 --- [           main] o.s.b.w.embedded.tomcat.TomcatWebServer  : Tomcat started on port(s): 8080 (http) with context path ''
2023-05-15 20:41:32.456  INFO 65152 --- [           main] o.s.b.w.embedded.tomcat.TomcatWebServer  : Tomcat initialized with port(s): 8989 (http)
2023-05-15 20:41:32.457  INFO 65152 --- [           main] o.apache.catalina.core.StandardService   : Starting service [Tomcat]
2023-05-15 20:41:32.457  INFO 65152 --- [           main] org.apache.catalina.core.StandardEngine  : Starting Servlet engine: [Apache Tomcat/9.0.74]
2023-05-15 20:41:32.470  INFO 65152 --- [           main] o.a.c.c.C.[.[localhost].[/metrics]       : Initializing Spring embedded WebApplicationContext
2023-05-15 20:41:32.470  INFO 65152 --- [           main] w.s.c.ServletWebServerApplicationContext : Root WebApplicationContext: initialization completed in 62 ms
2023-05-15 20:41:32.481  INFO 65152 --- [           main] o.s.b.a.e.web.EndpointLinksResolver      : Exposing 1 endpoint(s) beneath base path ''
2023-05-15 20:41:32.484  WARN 65152 --- [           main] ConfigServletWebServerApplicationContext : Exception encountered during context initialization - cancelling refresh attempt: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'webEndpointServletHandlerMapping' defined in class path resource [org/springframework/boot/actuate/autoconfigure/endpoint/web/servlet/WebMvcEndpointManagementContextConfiguration.class]: Invocation of init method failed; nested exception is java.lang.IllegalStateException: Ambiguous mapping. Cannot map 'Actuator root web endpoint' method 
Actuator root web endpoint
to {GET [], produces [application/vnd.spring-boot.actuator.v3+json || application/vnd.spring-boot.actuator.v2+json || application/json]}: There is already 'Actuator web endpoint 'health'' bean method
Actuator web endpoint 'health' mapped.
2023-05-15 20:41:32.487  INFO 65152 --- [           main] o.apache.catalina.core.StandardService   : Stopping service [Tomcat]
2023-05-15 20:41:32.491  WARN 65152 --- [           main] ConfigServletWebServerApplicationContext : Exception encountered during context initialization - cancelling refresh attempt: org.springframework.context.ApplicationContextException: Failed to start bean 'webServerStartStop'; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'webEndpointServletHandlerMapping' defined in class path resource [org/springframework/boot/actuate/autoconfigure/endpoint/web/servlet/WebMvcEndpointManagementContextConfiguration.class]: Invocation of init method failed; nested exception is java.lang.IllegalStateException: Ambiguous mapping. Cannot map 'Actuator root web endpoint' method 
Actuator root web endpoint
to {GET [], produces [application/vnd.spring-boot.actuator.v3+json || application/vnd.spring-boot.actuator.v2+json || application/json]}: There is already 'Actuator web endpoint 'health'' bean method
Actuator web endpoint 'health' mapped.
2023-05-15 20:41:32.502  INFO 65152 --- [           main] o.apache.catalina.core.StandardService   : Stopping service [Tomcat]
2023-05-15 20:41:32.514  INFO 65152 --- [           main] ConditionEvaluationReportLoggingListener : 

Error starting ApplicationContext. To display the conditions report re-run your application with 'debug' enabled.
2023-05-15 20:41:32.531 ERROR 65152 --- [           main] o.s.boot.SpringApplication               : Application run failed

org.springframework.context.ApplicationContextException: Failed to start bean 'webServerStartStop'; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'webEndpointServletHandlerMapping' defined in class path resource [org/springframework/boot/actuate/autoconfigure/endpoint/web/servlet/WebMvcEndpointManagementContextConfiguration.class]: Invocation of init method failed; nested exception is java.lang.IllegalStateException: Ambiguous mapping. Cannot map 'Actuator root web endpoint' method 
Actuator root web endpoint
to {GET [], produces [application/vnd.spring-boot.actuator.v3+json || application/vnd.spring-boot.actuator.v2+json || application/json]}: There is already 'Actuator web endpoint 'health'' bean method
Actuator web endpoint 'health' mapped.
	at org.springframework.context.support.DefaultLifecycleProcessor.doStart(DefaultLifecycleProcessor.java:181) ~[spring-context-5.3.27.jar:5.3.27]
	at org.springframework.context.support.DefaultLifecycleProcessor.access$200(DefaultLifecycleProcessor.java:54) ~[spring-context-5.3.27.jar:5.3.27]
	at org.springframework.context.support.DefaultLifecycleProcessor$LifecycleGroup.start(DefaultLifecycleProcessor.java:356) ~[spring-context-5.3.27.jar:5.3.27]
	at java.base/java.lang.Iterable.forEach(Iterable.java:75) ~[na:na]
	at org.springframework.context.support.DefaultLifecycleProcessor.startBeans(DefaultLifecycleProcessor.java:155) ~[spring-context-5.3.27.jar:5.3.27]
	at org.springframework.context.support.DefaultLifecycleProcessor.onRefresh(DefaultLifecycleProcessor.java:123) ~[spring-context-5.3.27.jar:5.3.27]
	at org.springframework.context.support.AbstractApplicationContext.finishRefresh(AbstractApplicationContext.java:937) ~[spring-context-5.3.27.jar:5.3.27]
	at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:586) ~[spring-context-5.3.27.jar:5.3.27]
	at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.refresh(ServletWebServerApplicationContext.java:147) ~[spring-boot-2.7.11.jar:2.7.11]
	at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:731) ~[spring-boot-2.7.11.jar:2.7.11]
	at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:408) ~[spring-boot-2.7.11.jar:2.7.11]
	at org.springframework.boot.SpringApplication.run(SpringApplication.java:307) ~[spring-boot-2.7.11.jar:2.7.11]
	at org.springframework.boot.SpringApplication.run(SpringApplication.java:1303) ~[spring-boot-2.7.11.jar:2.7.11]
	at org.springframework.boot.SpringApplication.run(SpringApplication.java:1292) ~[spring-boot-2.7.11.jar:2.7.11]
	at com.example.demo.Gh35426Application.main(Gh35426Application.java:10) ~[main/:na]
Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'webEndpointServletHandlerMapping' defined in class path resource [org/springframework/boot/actuate/autoconfigure/endpoint/web/servlet/WebMvcEndpointManagementContextConfiguration.class]: Invocation of init method failed; nested exception is java.lang.IllegalStateException: Ambiguous mapping. Cannot map 'Actuator root web endpoint' method 
Actuator root web endpoint
to {GET [], produces [application/vnd.spring-boot.actuator.v3+json || application/vnd.spring-boot.actuator.v2+json || application/json]}: There is already 'Actuator web endpoint 'health'' bean method
Actuator web endpoint 'health' mapped.
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1804) ~[spring-beans-5.3.27.jar:5.3.27]
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:620) ~[spring-beans-5.3.27.jar:5.3.27]
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:542) ~[spring-beans-5.3.27.jar:5.3.27]
	at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:335) ~[spring-beans-5.3.27.jar:5.3.27]
	at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:234) ~[spring-beans-5.3.27.jar:5.3.27]
	at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:333) ~[spring-beans-5.3.27.jar:5.3.27]
	at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:208) ~[spring-beans-5.3.27.jar:5.3.27]
	at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:955) ~[spring-beans-5.3.27.jar:5.3.27]
	at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:920) ~[spring-context-5.3.27.jar:5.3.27]
	at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:583) ~[spring-context-5.3.27.jar:5.3.27]
	at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.refresh(ServletWebServerApplicationContext.java:147) ~[spring-boot-2.7.11.jar:2.7.11]
	at org.springframework.boot.actuate.autoconfigure.web.server.ManagementContextAutoConfiguration$DifferentManagementContextConfiguration.onApplicationEvent(ManagementContextAutoConfiguration.java:149) ~[spring-boot-actuator-autoconfigure-2.7.11.jar:2.7.11]
	at org.springframework.boot.actuate.autoconfigure.web.server.ManagementContextAutoConfiguration$DifferentManagementContextConfiguration.onApplicationEvent(ManagementContextAutoConfiguration.java:122) ~[spring-boot-actuator-autoconfigure-2.7.11.jar:2.7.11]
	at org.springframework.context.event.SimpleApplicationEventMulticaster.doInvokeListener(SimpleApplicationEventMulticaster.java:176) ~[spring-context-5.3.27.jar:5.3.27]
	at org.springframework.context.event.SimpleApplicationEventMulticaster.invokeListener(SimpleApplicationEventMulticaster.java:169) ~[spring-context-5.3.27.jar:5.3.27]
	at org.springframework.context.event.SimpleApplicationEventMulticaster.multicastEvent(SimpleApplicationEventMulticaster.java:143) ~[spring-context-5.3.27.jar:5.3.27]
	at org.springframework.context.support.AbstractApplicationContext.publishEvent(AbstractApplicationContext.java:421) ~[spring-context-5.3.27.jar:5.3.27]
	at org.springframework.context.support.AbstractApplicationContext.publishEvent(AbstractApplicationContext.java:378) ~[spring-context-5.3.27.jar:5.3.27]
	at org.springframework.boot.web.servlet.context.WebServerStartStopLifecycle.start(WebServerStartStopLifecycle.java:46) ~[spring-boot-2.7.11.jar:2.7.11]
	at org.springframework.context.support.DefaultLifecycleProcessor.doStart(DefaultLifecycleProcessor.java:178) ~[spring-context-5.3.27.jar:5.3.27]
	... 14 common frames omitted
Caused by: java.lang.IllegalStateException: Ambiguous mapping. Cannot map 'Actuator root web endpoint' method 
Actuator root web endpoint
to {GET [], produces [application/vnd.spring-boot.actuator.v3+json || application/vnd.spring-boot.actuator.v2+json || application/json]}: There is already 'Actuator web endpoint 'health'' bean method
Actuator web endpoint 'health' mapped.
	at org.springframework.web.servlet.handler.AbstractHandlerMethodMapping$MappingRegistry.validateMethodMapping(AbstractHandlerMethodMapping.java:669) ~[spring-webmvc-5.3.27.jar:5.3.27]
	at org.springframework.web.servlet.handler.AbstractHandlerMethodMapping$MappingRegistry.register(AbstractHandlerMethodMapping.java:635) ~[spring-webmvc-5.3.27.jar:5.3.27]
	at org.springframework.web.servlet.handler.AbstractHandlerMethodMapping.registerMapping(AbstractHandlerMethodMapping.java:189) ~[spring-webmvc-5.3.27.jar:5.3.27]
	at org.springframework.boot.actuate.endpoint.web.servlet.AbstractWebMvcEndpointHandlerMapping.registerLinksMapping(AbstractWebMvcEndpointHandlerMapping.java:252) ~[spring-boot-actuator-2.7.11.jar:2.7.11]
	at org.springframework.boot.actuate.endpoint.web.servlet.AbstractWebMvcEndpointHandlerMapping.initHandlerMethods(AbstractWebMvcEndpointHandlerMapping.java:182) ~[spring-boot-actuator-2.7.11.jar:2.7.11]
	at org.springframework.web.servlet.handler.AbstractHandlerMethodMapping.afterPropertiesSet(AbstractHandlerMethodMapping.java:213) ~[spring-webmvc-5.3.27.jar:5.3.27]
	at org.springframework.boot.actuate.endpoint.web.servlet.AbstractWebMvcEndpointHandlerMapping.afterPropertiesSet(AbstractWebMvcEndpointHandlerMapping.java:171) ~[spring-boot-actuator-2.7.11.jar:2.7.11]
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.invokeInitMethods(AbstractAutowireCapableBeanFactory.java:1863) ~[spring-beans-5.3.27.jar:5.3.27]
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1800) ~[spring-beans-5.3.27.jar:5.3.27]
	... 33 common frames omitted

With 3.0.6 the server starts but a request to localhost:8989/metrics/ returns the hypermedia links to all of the actuator's endpoints.

If you would like us to spend some more time investigating, please spend some time providing a complete yet minimal sample that reproduces the behavior that you have described. You can share it with us by pushing it to a separate repository on GitHub or by zipping it up and attaching it to this issue.

@wilkinsona wilkinsona added status: waiting-for-feedback We need additional information before we can continue and removed theme: observability Issues related to observability labels May 15, 2023
@pavelorehov
Copy link
Author

Created small demo app where it is reproduces with spring boot 3.
Added comments in pom.xml how to downgrade to 2.7.11 where it works perfect.
https://github.com/pavelorehov/sb3prometheus/tree/main/demo

@spring-projects-issues spring-projects-issues added status: feedback-provided Feedback has been provided and removed status: waiting-for-feedback We need additional information before we can continue labels May 16, 2023
@wilkinsona
Copy link
Member

Thank you. I've now reproduced the behaviour that you have described. The following properties were missing from those shared above:

management.endpoints.web.discovery.enabled=false
management.endpoints.web.exposure.include=*

I believe the change in behaviour is due to spring-projects/spring-framework#28552. The health endpoint isn't affected as its operations mean that it has a mapping for /**. Most other endpoints have no such mapping and they also cannot be mapped to the root any more.

When the base path and an endpoint's path mapping are set to /, the resulting pattern for the endpoint is an empty string. This then clashes with Tomcat redirecting requests to the context root by appending a /. In the context of this issue, this means that a request to http://localhost:8989/metrics is redirected to http://localhost:8989/metrics/ which doesn't match. As a workaround, the problem does not occur with 3.0.x if server.tomcat.redirect-context-root is set to false.

#31563 tracked Boot adapting to the changes in Framework but this looks like a scenario that we didn't consider. We'll have to review the changes that we made and see if we can accommodate this scenario without breaking anything else.

@wilkinsona wilkinsona changed the title micrometer-registry-prometheus root path stopped working with spring boot 3 Actuator endpoints with no operations that use selectors are not accessible when mapped to / May 16, 2023
@wilkinsona wilkinsona added type: regression A regression from a previous release and removed status: waiting-for-triage An issue we've not yet triaged status: feedback-provided Feedback has been provided labels May 16, 2023
@wilkinsona wilkinsona added this to the 3.0.x milestone May 16, 2023
@pavelorehov
Copy link
Author

Thank you, can server.tomcat.redirect-context-root=false be temporary WA until you find permanent solution ?

@wilkinsona
Copy link
Member

Yes, it's certainly worth trying to see if it meets your needs.

@wilkinsona

This comment was marked as outdated.

@wilkinsona wilkinsona self-assigned this Nov 15, 2023
@wilkinsona wilkinsona added the for: team-meeting An issue we'd like to discuss as a team to make progress label Nov 15, 2023
@wilkinsona wilkinsona modified the milestones: 3.0.x, 3.1.x Nov 15, 2023
@wilkinsona wilkinsona removed the for: team-meeting An issue we'd like to discuss as a team to make progress label Nov 15, 2023
@wilkinsona wilkinsona modified the milestones: 3.1.x, 3.1.8 Jan 12, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
type: regression A regression from a previous release
Projects
None yet
Development

No branches or pull requests

4 participants