Skip to content

How to detect that a configmap is present #1682

@c4rth

Description

@c4rth

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 with CloudPlatform.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 without SPRING_CLOUD_BOOTSTRAP_ENABLED

Here is the code of my EnvironmentPostProcessor

public class TestEnvironmentPostProcessor implements EnvironmentPostProcessor {

    private final Log log;

    public TestEnvironmentPostProcessor(DeferredLogFactory logFactory) {
        this.log = logFactory.getLog(getClass());
    }

    @Override
    public void postProcessEnvironment(ConfigurableEnvironment environment, SpringApplication application) {
        if (isKubernetes(environment)) {
            log.info("Running in Kubernetes");
        } else {
            log.info("NOT running in Kubernetes");
            return;
        }
        environment.getPropertySources().forEach(ps -> {
            log.info(ps.getName() + " = " + ps.getClass().getName());
            if (ps.getName().equals("KUBERNETES_NAMESPACE_PROPERTY_SOURCE")) {
                log.info("--> KUBERNETES_NAMESPACE_PROPERTY_SOURCE");
                MapPropertySource mps = (MapPropertySource) ps;
                var str1 = Arrays.stream(mps.getPropertyNames()).map(key -> key + " = " + mps.getProperty(key))
                        .collect(Collectors.joining(", ", "{", "}"));
                log.info("properties = " + str1);
                var source = mps.getSource();
                var str2 = source.keySet().stream()
                        .map(key -> key + " = " + source.get(key))
                        .collect(Collectors.joining(", ", "{", "}"));
                log.info("source = " + str2);
                List.of("spring.application.name", "k8s.key1", "k8s.key2").forEach(key -> {
                var x= environment.getProperty(key);
                log.warn(key + " = " + x);
              });
            }
        });

    }

    private boolean isKubernetes(ConfigurableEnvironment environment) {
        return CloudPlatform.KUBERNETES.isActive(environment);
    }
}

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

[           main] g.p.b.v.TestEnvironmentPostProcessor     : *******************************
[           main] g.p.b.v.TestEnvironmentPostProcessor     : Running in Kubernetes
[           main] g.p.b.v.TestEnvironmentPostProcessor     : configurationProperties = org.springframework.boot.context.properties.source.ConfigurationPropertySourcesPropertySource
[           main] g.p.b.v.TestEnvironmentPostProcessor     : bootstrap = org.springframework.core.env.MapPropertySource
[           main] g.p.b.v.TestEnvironmentPostProcessor     : systemProperties = org.springframework.core.env.PropertiesPropertySource
[           main] g.p.b.v.TestEnvironmentPostProcessor     : systemEnvironment = org.springframework.boot.env.SystemEnvironmentPropertySourceEnvironmentPostProcessor$OriginAwareSystemEnvironmentPropertySource
[           main] g.p.b.v.TestEnvironmentPostProcessor     : random = org.springframework.boot.env.RandomValuePropertySource
[           main] g.p.b.v.TestEnvironmentPostProcessor     : cachedrandom = org.springframework.cloud.util.random.CachedRandomPropertySource
[           main] g.p.b.v.TestEnvironmentPostProcessor     : KUBERNETES_NAMESPACE_PROPERTY_SOURCE = org.springframework.core.env.MapPropertySource
[           main] g.p.b.v.TestEnvironmentPostProcessor     : --> KUBERNETES_NAMESPACE_PROPERTY_SOURCE
[           main] g.p.b.v.TestEnvironmentPostProcessor     : properties = {spring.cloud.kubernetes.client.namespace = default}
[           main] g.p.b.v.TestEnvironmentPostProcessor     : source = {spring.cloud.kubernetes.client.namespace = default}
[           main] g.p.b.v.TestEnvironmentPostProcessor     : springCloudClientHostInfo = org.springframework.core.env.MapPropertySource
[           main] g.p.b.v.TestEnvironmentPostProcessor     : spring.application.name = null
[           main] g.p.b.v.TestEnvironmentPostProcessor     : k8s.key1 = null
[           main] g.p.b.v.TestEnvironmentPostProcessor     : k8s.key2 = null

  .   ____          _            __ _ _
 /\\ / ___'_ __ _ _(_)_ __  __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
 \\/  ___)| |_)| | | | | || (_| |  ) ) ) )
  '  |____| .__|_| |_|_| |_\__, | / / / /
 =========|_|==============|___/=/_/_/_/

 :: Spring Boot ::                (v3.3.2)

[spring-app-virtual] [           main] o.s.c.k.commons.config.ConfigUtils       : ConfigMap name has not been set, taking it from property/env spring.application.name (default=application)
[spring-app-virtual] [           main] s.c.k.c.c.ConfigMapPropertySourceLocator : Config Map normalized sources : [{ config-map name : 'Optional[spring-app-virtual]', namespace : 'Optional.empty', prefix : '[ConfigUtils.Prefix@36417a54 name = 'DEFAULT']' }]
[spring-app-virtual] [           main] o.s.c.kubernetes.fabric8.Fabric8Utils    : configmap namespace from provider : default
[spring-app-virtual] [           main] o.s.c.k.f.config.Fabric8ConfigMapsCache  : Loaded all config maps in namespace 'default'
[spring-app-virtual] [           main] o.s.c.k.commons.config.ConfigUtils       : Found source with name : 'spring-app-virtual in namespace: 'default'
[spring-app-virtual] [           main] o.s.c.k.c.c.SourceDataEntriesProcessor   : The single property with name: [application.yaml] will be treated as a yaml file
[spring-app-virtual] [           main] s.c.k.c.c.ConfigMapPropertySourceLocator : paths property sources : []
[spring-app-virtual] [           main] b.c.PropertySourceBootstrapConfiguration : Located property source: [BootstrapPropertySource {name='bootstrapProperties-configmap.spring-app-virtual.default'}]
[spring-app-virtual] [           main] o.s.c.k.commons.config.ConfigUtils       : Secret name has not been set, taking it from property/env spring.application.name (default=application)
[spring-app-virtual] [           main] o.s.c.k.c.c.SecretsPropertySourceLocator : Secrets normalized sources : [{ secret name : 'Optional[spring-app-virtual]', namespace : 'Optional.empty']
[spring-app-virtual] [           main] ubernetesProfileEnvironmentPostProcessor : 'kubernetes' already in list of active profiles
[spring-app-virtual] [           main] gae.piaz.boot.virtual.Application        : The following 1 profile is active: "kubernetes"
[spring-app-virtual] [           main] g.p.b.v.TestEnvironmentPostProcessor     : *******************************
[spring-app-virtual] [           main] g.p.b.v.TestEnvironmentPostProcessor     : Running in Kubernetes
[spring-app-virtual] [           main] g.p.b.v.TestEnvironmentPostProcessor     : configurationProperties = org.springframework.boot.context.properties.source.ConfigurationPropertySourcesPropertySource
[spring-app-virtual] [           main] g.p.b.v.TestEnvironmentPostProcessor     : servletConfigInitParams = org.springframework.core.env.PropertySource$StubPropertySource
[spring-app-virtual] [           main] g.p.b.v.TestEnvironmentPostProcessor     : servletContextInitParams = org.springframework.core.env.PropertySource$StubPropertySource
[spring-app-virtual] [           main] g.p.b.v.TestEnvironmentPostProcessor     : systemProperties = org.springframework.core.env.PropertiesPropertySource
[spring-app-virtual] [           main] g.p.b.v.TestEnvironmentPostProcessor     : systemEnvironment = org.springframework.boot.env.SystemEnvironmentPropertySourceEnvironmentPostProcessor$OriginAwareSystemEnvironmentPropertySource
[spring-app-virtual] [           main] g.p.b.v.TestEnvironmentPostProcessor     : random = org.springframework.boot.env.RandomValuePropertySource
[spring-app-virtual] [           main] g.p.b.v.TestEnvironmentPostProcessor     : springCloudDefaultProperties = org.springframework.cloud.bootstrap.BootstrapApplicationListener$ExtendedDefaultPropertySource
[spring-app-virtual] [           main] g.p.b.v.TestEnvironmentPostProcessor     : cachedrandom = org.springframework.cloud.util.random.CachedRandomPropertySource
[spring-app-virtual] [           main] g.p.b.v.TestEnvironmentPostProcessor     : KUBERNETES_NAMESPACE_PROPERTY_SOURCE = org.springframework.core.env.MapPropertySource
[spring-app-virtual] [           main] g.p.b.v.TestEnvironmentPostProcessor     : --> KUBERNETES_NAMESPACE_PROPERTY_SOURCE
[spring-app-virtual] [           main] g.p.b.v.TestEnvironmentPostProcessor     : properties = {spring.cloud.kubernetes.client.namespace = default}
[spring-app-virtual] [           main] g.p.b.v.TestEnvironmentPostProcessor     : source = {spring.cloud.kubernetes.client.namespace = default}
[spring-app-virtual] [           main] g.p.b.v.TestEnvironmentPostProcessor     : Config resource 'class path resource [application.yaml]' via location 'optional:classpath:/' = org.springframework.boot.env.OriginTrackedMapPropertySource
[spring-app-virtual] [           main] g.p.b.v.TestEnvironmentPostProcessor     : springCloudClientHostInfo = org.springframework.core.env.MapPropertySource
[spring-app-virtual] [           main] g.p.b.v.TestEnvironmentPostProcessor     : applicationConfig: [classpath:/application.yaml] = org.springframework.boot.env.OriginTrackedMapPropertySource
[spring-app-virtual] [           main] g.p.b.v.TestEnvironmentPostProcessor     : spring.application.name = spring-app-virtual
[spring-app-virtual] [           main] g.p.b.v.TestEnvironmentPostProcessor     : k8s.key1 = null
[spring-app-virtual] [           main] g.p.b.v.TestEnvironmentPostProcessor     : k8s.key2 = null
[spring-app-virtual] [           main] .s.d.r.c.RepositoryConfigurationDelegate : Bootstrapping Spring Data JPA repositories in DEFAULT mode.
[spring-app-virtual] [           main] .s.d.r.c.RepositoryConfigurationDelegate : Finished Spring Data repository scanning in 100 ms. Found 3 JPA repository interfaces.
[spring-app-virtual] [           main] led$OnBlockingOrReactiveDiscoveryEnabled : Condition ConditionalOnBlockingOrReactiveDiscoveryEnabled.OnBlockingOrReactiveDiscoveryEnabled on org.springframework.cloud.kubernetes.fabric8.discovery.KubernetesCatalogWatchAutoConfiguration matched due to AnyNestedCondition 1 matched 1 did not; NestedCondition on ConditionalOnBlockingOrReactiveDiscoveryEnabled.OnBlockingOrReactiveDiscoveryEnabled.OnReactiveDiscoveryEnabled found non-matching nested conditions @ConditionalOnClass did not find required class 'org.springframework.web.reactive.function.client.WebClient'; NestedCondition on ConditionalOnBlockingOrReactiveDiscoveryEnabled.OnBlockingOrReactiveDiscoveryEnabled.OnBlockingDiscoveryEnabled @ConditionalOnProperty (spring.cloud.discovery.blocking.enabled) matched
[spring-app-virtual] [           main] o.s.cloud.context.scope.GenericScope     : BeanFactory id=aa14e53e-a512-3d44-9df4-9fdd14a1a107
[spring-app-virtual] [           main] trationDelegate$BeanPostProcessorChecker : Bean 'org.springframework.cloud.client.loadbalancer.LoadBalancerAutoConfiguration$DeferringLoadBalancerInterceptorConfig' of type [org.springframework.cloud.client.loadbalancer.LoadBalancerAutoConfiguration$DeferringLoadBalancerInterceptorConfig] is not eligible for getting processed by all BeanPostProcessors (for example: not eligible for auto-proxying). The currently created BeanPostProcessor [lbRestClientPostProcessor] is declared through a non-static factory method on that class; consider declaring it as static instead.
[spring-app-virtual] [           main] trationDelegate$BeanPostProcessorChecker : Bean 'deferringLoadBalancerInterceptor' of type [org.springframework.cloud.client.loadbalancer.DeferringLoadBalancerInterceptor] is not eligible for getting processed by all BeanPostProcessors (for example: not eligible for auto-proxying). Is this bean getting eagerly injected into a currently created BeanPostProcessor [lbRestClientPostProcessor]? Check the corresponding BeanPostProcessor declaration and its dependencies.
[spring-app-virtual] [           main] o.s.b.w.embedded.tomcat.TomcatWebServer  : Tomcat initialized with port 8080 (http)
[spring-app-virtual] [           main] o.apache.catalina.core.StandardService   : Starting service [Tomcat]
[spring-app-virtual] [           main] o.apache.catalina.core.StandardEngine    : Starting Servlet engine: [Apache Tomcat/10.1.26]
[spring-app-virtual] [           main] o.a.c.c.C.[Tomcat].[localhost].[/]       : Initializing Spring embedded WebApplicationContext
[spring-app-virtual] [           main] w.s.c.ServletWebServerApplicationContext : Root WebApplicationContext: initialization completed in 1393 ms
[spring-app-virtual] [           main] o.hibernate.jpa.internal.util.LogHelper  : HHH000204: Processing PersistenceUnitInfo [name: default]
[spring-app-virtual] [           main] org.hibernate.Version                    : HHH000412: Hibernate ORM core version 6.5.2.Final
[spring-app-virtual] [           main] o.h.c.internal.RegionFactoryInitiator    : HHH000026: Second-level cache disabled
[spring-app-virtual] [           main] o.s.o.j.p.SpringPersistenceUnitInfo      : No LoadTimeWeaver setup: ignoring JPA class transformer
[spring-app-virtual] [           main] com.zaxxer.hikari.HikariDataSource       : HikariPool-1 - Starting...
[spring-app-virtual] [           main] com.zaxxer.hikari.pool.HikariPool        : HikariPool-1 - Added connection conn0: url=jdbc:h2:mem:testdb user=SA
[spring-app-virtual] [           main] com.zaxxer.hikari.HikariDataSource       : HikariPool-1 - Start completed.
[spring-app-virtual] [           main] org.hibernate.orm.deprecation            : HHH90000025: H2Dialect does not need to be specified explicitly using 'hibernate.dialect' (remove the property setting and it will be selected by default)
[spring-app-virtual] [           main] o.h.e.t.j.p.i.JtaPlatformInitiator       : HHH000489: No JTA platform available (set 'hibernate.transaction.jta.platform' to enable JTA platform integration)
[spring-app-virtual] [           main] j.LocalContainerEntityManagerFactoryBean : Initialized JPA EntityManagerFactory for persistence unit 'default'
[spring-app-virtual] [           main] JpaBaseConfiguration$JpaWebConfiguration : spring.jpa.open-in-view is enabled by default. Therefore, database queries may be performed during view rendering. Explicitly configure spring.jpa.open-in-view to disable this warning
[spring-app-virtual] [           main] o.s.c.k.c.KubernetesNamespaceProvider    : Looking for service account namespace at: [/var/run/secrets/kubernetes.io/serviceaccount/namespace].
[spring-app-virtual] [           main] o.s.c.k.c.KubernetesNamespaceProvider    : Found service account namespace at: [/var/run/secrets/kubernetes.io/serviceaccount/namespace].
[spring-app-virtual] [           main] o.s.c.k.c.KubernetesNamespaceProvider    : Service account namespace value: /var/run/secrets/kubernetes.io/serviceaccount/namespace
[spring-app-virtual] [           main] bernetesDiscoveryClientAutoConfiguration : Will publish InstanceRegisteredEvent from blocking implementation
[spring-app-virtual] [           main] iscoveryClientHealthIndicatorInitializer : publishing InstanceRegisteredEvent
[spring-app-virtual] [           main] o.s.c.k.f.d.KubernetesCatalogWatch       : stateGenerator is of type: Fabric8EndpointsCatalogWatch
[spring-app-virtual] [           main] iguration$LoadBalancerCaffeineWarnLogger : Spring Cloud LoadBalancer is currently working with the default cache. While this cache implementation is useful for development and tests, it's recommended to use Caffeine cache in production.You can switch to using Caffeine cache, by adding it and org.springframework.cache.caffeine.CaffeineCacheManager to the classpath.
[spring-app-virtual] [           main] o.s.b.a.e.web.EndpointLinksResolver      : Exposing 17 endpoints beneath base path '/actuator'
[spring-app-virtual] [           main] o.s.b.w.embedded.tomcat.TomcatWebServer  : Tomcat started on port 8080 (http) with context path '/'
[spring-app-virtual] [           main] gae.piaz.boot.virtual.Application        : Started Application in 5.525 seconds (process running for 5.715)
[spring-app-virtual] [           main] gae.piaz.boot.virtual.Application        : spring.application.name = spring-app-virtual
[spring-app-virtual] [           main] gae.piaz.boot.virtual.Application        : k8s.key1 = value1
[spring-app-virtual] [           main] gae.piaz.boot.virtual.Application        : k8s.key2 = value2

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    Milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions