Closed c4rth closed 2 weeks ago
I would like to verify that a configmap linked to the application is available and has been loaded.
Can I ask why and what you are trying to accomplish by knowing this?
Sure. I just would like to know when I start an application if a configmap is loaded, to validate that the deployments in k8s are ok. Sometimes, the configmap definition is not right: name that doesn't match the spring app name, wrong namespace or not deployed. It is simply to add a validation step.
One approach I can think of is to have a Bean which takes in all SourceDataEntriesProcessor
or Fabric8ConfigMapPropertySource
if you really want to by hyper-specific. You can look at the name of those PropertySources to determine whether one of them is the config map you are concerned about and if not throw an Exception. You could then use AbstractFailureAnalyze
from Spring Boot to produce a FailureAnalysis
object and provide a meaningful reason for the failure.
Thanks for this possible solution.
With spring.config.import: 'kubernetes:'
, I can find a CompositePropertySource
named composite-configmap containing a Fabric8ConfigMapPropertySource
containing all the properties of the configmap and if no configmap, the properties are empty.
With 'spring.cloud.bootstrap.enabled: true', no Fabric8ConfigMapPropertySource
but a BootstrapPropertySource
containing all the properties of the configmap and if no configmap, the properties are empty.
I prefer the 1st possibility, but the issue is if I add org.springframework.cloud:spring-cloud-starter-bootstrap
or spring.cloud.bootstrap.enabled:
, the app doesn't start.
You are going to have to provide more information than "the app doesn't start" for us to help
Indeed, sorry.
2024-08-06T16:16:50.748+01:00 WARN 1 --- [ main] [ ] s.c.a.AnnotationConfigApplicationContext : Exception encountered during context initialization - cancelling refresh attempt: org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'configMapPropertySourceLocator' defined in class path resource [org/springframework/cloud/kubernetes/fabric8/config/Fabric8BootstrapConfiguration.class]: Unsatisfied dependency expressed through method 'configMapPropertySourceLocator' parameter 0: No qualifying bean of type 'org.springframework.cloud.kubernetes.commons.config.ConfigMapConfigProperties' available: expected single matching bean but found 2: spring.cloud.kubernetes.config-org.springframework.cloud.kubernetes.commons.config.ConfigMapConfigProperties,configDataConfigMapConfigProperties
2024-08-06T16:16:50.749+01:00 INFO 1 --- [ main] [ ] .s.b.a.l.ConditionEvaluationReportLogger :
Error starting ApplicationContext. To display the condition evaluation report re-run your application with 'debug' enabled.
2024-08-06T16:16:50.760+01:00 ERROR 1 --- [ main] [ ] o.s.b.d.LoggingFailureAnalysisReporter :
***************************
APPLICATION FAILED TO START
***************************
Description:
Parameter 0 of method configMapPropertySourceLocator in org.springframework.cloud.kubernetes.fabric8.config.Fabric8BootstrapConfiguration required a single bean, but 2 were found:
- spring.cloud.kubernetes.config-org.springframework.cloud.kubernetes.commons.config.ConfigMapConfigProperties: defined in unknown location
- configDataConfigMapConfigProperties: a programmatically registered singleton
This may be due to missing parameter name information
Action:
Consider marking one of the beans as @Primary, updating the consumer to accept multiple beans, or using @Qualifier to identify the bean that should be consumed
Ensure that your compiler is configured to use the '-parameters' flag.
You may need to update both your build tool settings as well as your IDE.
(See https://github.com/spring-projects/spring-framework/wiki/Upgrading-to-Spring-Framework-6.x#parameter-name-retention)
2024-08-06T16:16:50.761+01:00 ERROR 1 --- [ main] [ ] o.s.boot.SpringApplication : Application run failed
org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'configMapPropertySourceLocator' defined in class path resource [org/springframework/cloud/kubernetes/fabric8/config/Fabric8BootstrapConfiguration.class]: Unsatisfied dependency expressed through method 'configMapPropertySourceLocator' parameter 0: No qualifying bean of type 'org.springframework.cloud.kubernetes.commons.config.ConfigMapConfigProperties' available: expected single matching bean but found 2: spring.cloud.kubernetes.config-org.springframework.cloud.kubernetes.commons.config.ConfigMapConfigProperties,configDataConfigMapConfigProperties
at org.springframework.beans.factory.support.ConstructorResolver.createArgumentArray(ConstructorResolver.java:795) ~[spring-beans-6.1.11.jar:6.1.11]
at org.springframework.beans.factory.support.ConstructorResolver.instantiateUsingFactoryMethod(ConstructorResolver.java:542) ~[spring-beans-6.1.11.jar:6.1.11]
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.instantiateUsingFactoryMethod(AbstractAutowireCapableBeanFactory.java:1355) ~[spring-beans-6.1.11.jar:6.1.11]
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1185) ~[spring-beans-6.1.11.jar:6.1.11]
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:562) ~[spring-beans-6.1.11.jar:6.1.11]
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:522) ~[spring-beans-6.1.11.jar:6.1.11]
at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:337) ~[spring-beans-6.1.11.jar:6.1.11]
at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:234) ~[spring-beans-6.1.11.jar:6.1.11]
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:335) ~[spring-beans-6.1.11.jar:6.1.11]
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:200) ~[spring-beans-6.1.11.jar:6.1.11]
at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:975) ~[spring-beans-6.1.11.jar:6.1.11]
at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:971) ~[spring-context-6.1.11.jar:6.1.11]
at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:625) ~[spring-context-6.1.11.jar:6.1.11]
at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:754) ~[spring-boot-3.3.2.jar:3.3.2]
at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:456) ~[spring-boot-3.3.2.jar:3.3.2]
at org.springframework.boot.SpringApplication.run(SpringApplication.java:335) ~[spring-boot-3.3.2.jar:3.3.2]
at org.springframework.boot.builder.SpringApplicationBuilder.run(SpringApplicationBuilder.java:149) ~[spring-boot-3.3.2.jar:3.3.2]
at org.springframework.cloud.bootstrap.BootstrapApplicationListener.bootstrapServiceContext(BootstrapApplicationListener.java:195) ~[spring-cloud-context-4.1.4.jar:4.1.4]
at org.springframework.cloud.bootstrap.BootstrapApplicationListener.onApplicationEvent(BootstrapApplicationListener.java:114) ~[spring-cloud-context-4.1.4.jar:4.1.4]
at org.springframework.cloud.bootstrap.BootstrapApplicationListener.onApplicationEvent(BootstrapApplicationListener.java:77) ~[spring-cloud-context-4.1.4.jar:4.1.4]
at org.springframework.context.event.SimpleApplicationEventMulticaster.doInvokeListener(SimpleApplicationEventMulticaster.java:185) ~[spring-context-6.1.11.jar:6.1.11]
at org.springframework.context.event.SimpleApplicationEventMulticaster.invokeListener(SimpleApplicationEventMulticaster.java:178) ~[spring-context-6.1.11.jar:6.1.11]
at org.springframework.context.event.SimpleApplicationEventMulticaster.multicastEvent(SimpleApplicationEventMulticaster.java:156) ~[spring-context-6.1.11.jar:6.1.11]
at org.springframework.context.event.SimpleApplicationEventMulticaster.multicastEvent(SimpleApplicationEventMulticaster.java:138) ~[spring-context-6.1.11.jar:6.1.11]
at org.springframework.boot.context.event.EventPublishingRunListener.multicastInitialEvent(EventPublishingRunListener.java:136) ~[spring-boot-3.3.2.jar:3.3.2]
at org.springframework.boot.context.event.EventPublishingRunListener.environmentPrepared(EventPublishingRunListener.java:81) ~[spring-boot-3.3.2.jar:3.3.2]
at org.springframework.boot.SpringApplicationRunListeners.lambda$environmentPrepared$2(SpringApplicationRunListeners.java:64) ~[spring-boot-3.3.2.jar:3.3.2]
at java.base/java.lang.Iterable.forEach(Iterable.java:75) ~[na:na]
at org.springframework.boot.SpringApplicationRunListeners.doWithListeners(SpringApplicationRunListeners.java:118) ~[spring-boot-3.3.2.jar:3.3.2]
at org.springframework.boot.SpringApplicationRunListeners.doWithListeners(SpringApplicationRunListeners.java:112) ~[spring-boot-3.3.2.jar:3.3.2]
at org.springframework.boot.SpringApplicationRunListeners.environmentPrepared(SpringApplicationRunListeners.java:63) ~[spring-boot-3.3.2.jar:3.3.2]
at org.springframework.boot.SpringApplication.prepareEnvironment(SpringApplication.java:370) ~[spring-boot-3.3.2.jar:3.3.2]
at org.springframework.boot.SpringApplication.run(SpringApplication.java:330) ~[spring-boot-3.3.2.jar:3.3.2]
at org.springframework.boot.SpringApplication.run(SpringApplication.java:1363) ~[spring-boot-3.3.2.jar:3.3.2]
at org.springframework.boot.SpringApplication.run(SpringApplication.java:1352) ~[spring-boot-3.3.2.jar:3.3.2]
at org.c4rth.virtual.Application.main(Application.java:21) ~[classes/:0.0.1-SNAPSHOT]
at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:103) ~[na:na]
at java.base/java.lang.reflect.Method.invoke(Method.java:580) ~[na:na]
at org.springframework.boot.loader.launch.Launcher.launch(Launcher.java:91) ~[application/:na]
at org.springframework.boot.loader.launch.Launcher.launch(Launcher.java:53) ~[application/:na]
at org.springframework.boot.loader.launch.JarLauncher.main(JarLauncher.java:58) ~[application/:na]
Caused by: org.springframework.beans.factory.NoUniqueBeanDefinitionException: No qualifying bean of type 'org.springframework.cloud.kubernetes.commons.config.ConfigMapConfigProperties' available: expected single matching bean but found 2: spring.cloud.kubernetes.config-org.springframework.cloud.kubernetes.commons.config.ConfigMapConfigProperties,configDataConfigMapConfigProperties
at org.springframework.beans.factory.config.DependencyDescriptor.resolveNotUnique(DependencyDescriptor.java:218) ~[spring-beans-6.1.11.jar:6.1.11]
at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1420) ~[spring-beans-6.1.11.jar:6.1.11]
at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:1353) ~[spring-beans-6.1.11.jar:6.1.11]
at org.springframework.beans.factory.support.ConstructorResolver.resolveAutowiredArgument(ConstructorResolver.java:904) ~[spring-beans-6.1.11.jar:6.1.11]
at org.springframework.beans.factory.support.ConstructorResolver.createArgumentArray(ConstructorResolver.java:782) ~[spring-beans-6.1.11.jar:6.1.11]
... 40 common frames omitted
Do you have any idea where this is coming from configDataConfigMapConfigProperties
? Can you provide a sample?
Here is a sample repository https://github.com/c4rth/spring-k8s
The 'configDataConfigMapConfigProperties' is defined in method registerProperties of KubernetesConfigDataLocationResolver For the other, I'm still looking.
OK looking at your sample gave me more clues.
Why are you specifying spring.config.import=kubernetes:
and enabling Bootstrap? You are running into issues because we are trying to load configuration using two different approaches and this is not supported.
So spring.config.import=kubernetes:
and Cloud Bootstrap are mutually exclusive.
OK, it's was not clear for me.
Maybe mentioning it in the documentation would be useful for others.
Thanks for your support and your patience.
I have a spring boot 3 application deployed in Kubernetes and also a configmap The spring app uses :
implementation("org.springframework.cloud:spring-cloud-starter")
implementation("org.springframework.cloud:spring-cloud-starter-kubernetes-fabric8-all")
In Kubernetes, the app has an environment variable
SPRING_CLOUD_BOOTSTRAP_ENABLED
to true.I would like to verify that a configmap linked to the application is available and has been loaded.
rem: I don't known which values will be defined in it, I cannot check one value, I just want check its existence.
What I tried
I tried to detect this in an
org.springframework.boot.env.EnvironmentPostProcessor
(maybe not the best place) loaded with spring.factories Detect that the application is running in Kubernetes is easily done withCloudPlatform.KUBERNETES.isActive(environment)
But I don't find a way to detect the configmap. I tried in the EnvironmentPostProcessor to check the different property sources of the environment, I found one named 'KUBERNETES_NAMESPACE_PROPERTY_SOURCE' but with or without a configmap, it exists. And my EnvironmentPostProcessor is executed before my app starts and
I tried combinations: with and without
org.springframework.cloud:spring-cloud-starter-bootstrap
with and withoutSPRING_CLOUD_BOOTSTRAP_ENABLED
Here is the code of my EnvironmentPostProcessor
An other strange thing: the TestEnvironmentPostProcessor runs before the app starts and after. In both case the properties defined in configmap are unknow. When my app has started, the values are known, here is the log