Describe the bug I am new to Spring config server and was playing around with the setup , and stumbled upon this usecase where the priority of properties is like

  • if the config-client has local application.properties has the property polar.greeting=A, when I say local it would be bundled in src/main/resources.
  • If the remote config backed by aws s3 has the property application.properties where polar.greeting=B then when I do /actuator/env on my config client , where local property takes precedence over the remote config without me setting any explicit overrides either at the system property level or either at the application.yml in the config-client.
  • if I add remote config which application-profile.properties where polar.greeting=C, then for sure that take priority over the local one.

My question , is this behaviour expected?

config client https://github.com/kunalsumbly/catalog-service/tree/spring_config_server

config server https://github.com/kunalsumbly/config-service/tree/spring-cloud-config-server

Comment From: ryanjbaxter

Yes this is the correct behavior. Property sources coming from spring.config.import will be prioritized over anything in application.properties unless, as you observed, the "local" properties are profile specific.

However I system properties should always take higher precedence.

Comment From: kunalsumbly

Thanks for your reply @ryanjbaxter , I guess my understanding based on the spring config documentation was that

in terms of Priority order should be this.

  1. Remote profile-specific (highest)

  2. Remote default (should override local)

  3. Local properties (lowest)

Clearly this is not that case , when we are using spring.config.import , in this case , I see that order is

  1. Remote profile-specific (highest)

  2. Local properties (would override remote default )

  3. Remote default (lowest)

Comment From: ryanjbaxter

This is not what I see when using your config server, here is what I see when I hit /actuator/env on a client using your config server

{
   "name":"Config resource 'class path resource [application-dev.yaml]' via location 'optional:classpath:/'",
   "properties":{
      "info.local":{
         "value":"******",
         "origin":"class path resource [application-dev.yaml] - 2:10"
      }
   }
},
{
   "name":"configserver:s3:foo/application",
   "properties":{
      "info.foo":{
         "value":"******"
      }
   }
},
{
   "name":"configClient",
   "properties":{
      "config.client.version":{
         "value":"******"
      }
   }
},
{
   "name":"Config resource 'class path resource [application.yaml]' via location 'optional:classpath:/'",
   "properties":{
      "spring.profiles.active":{
         "value":"******",
         "origin":"class path resource [application.yaml] - 3:13"
      },
      "spring.config.import":{
         "value":"******",
         "origin":"class path resource [application.yaml] - 5:13"
      },
      "spring.application.name":{
         "value":"******",
         "origin":"class path resource [application.yaml] - 7:11"
      },
      "management.endpoint.configprops.show-values":{
         "value":"******",
         "origin":"class path resource [application.yaml] - 11:20"
      },
      "management.endpoints.web.exposure.include":{
         "value":"******",
         "origin":"class path resource [application.yaml] - 15:18"
      },
      "info.foo":{
         "value":"******",
         "origin":"class path resource [application.yaml] - 17:8"
      }
   }
},
{
   "name":"springCloudClientHostInfo",
   "properties":{
      "spring.cloud.client.hostname":{
         "value":"******"
      },
      "spring.cloud.client.ip-address":{
         "value":"******"
      }
   }
},
{
   "name":"applicationInfo",
   "properties":{
      "spring.application.pid":{
         "value":"******"
      }
   }
}

As you can see the local profile specific file is highest, then the remote property source from s3, then the local non-profile specific property source

Comment From: kunalsumbly

@ryanjbaxter thanks for your reply. That should be fine, my observation was with regards to the priority for default profile properties and what takes precedence between local vs remote default properties. We both agree that once profile is set on remote that is the one, taking the highest priority.

I forgot to mention , that I run config-client using profile=local

in my config-client , I dont have application-local.yml rather my config is in application.yml (src/main/resources)

-- application.yml--- 
spring:
  application:
    name: catalog-service
  config:
    import: "optional:configserver:"
  cloud:
    config:
      uri: http://localhost:8888
      request-connect-timeout: 5000
      request-read-timeout: 5000
      retry:
        max-attempts: 6
        initial-interval: 1000
        max-interval: 2000
        multiplier: 1.1
management:
  endpoints:
    web:
      exposure:
        include: refresh,health,info,env,loggers
server:
  tomcat:
    connection-timeout: 2s
    keep-alive-timeout: 15s
    threads:
      max: 50
      min-spare: 5





and when I do the /actuator/env on config client, this is what I see 

 },
    {
      "name": "configserver:s3:catalog-service/application-local",
      "properties": {
        "logging.level.org.springframework": {
          "value": "******"
        },
        "logging.level.com.polarbookshop.catalogservice.config.client": {
          "value": "******"
        },
        "logging.level.root": {
          "value": "******"
        }
      }
    },
    {
      "name": "Config resource 'class path resource [application.properties]' via location 'optional:classpath:/'",
      "properties": {
        "spring.application.name": {
          "value": "******",
          "origin": "class path resource [application.properties] - 1:25"
        },
        "polar.greeting": {
          "value": "******",
          "origin": "class path resource [application.properties] - 2:16"
        }
      }
    },
    {
      "name": "configserver:s3:catalog-service/application",
      "properties": {
        "demo.demoProperty": {
          "value": "******"
        },
        "polar.greeting": {
          "value": "******"
        }
      }
    },
    {
      "name": "configClient",
      "properties": {

      }
    },
    {

Priority:- // 1 Highest which means s3 remote config with profile takes the highest priority,
// 2 then comes the application.properties from the classpath // 3 Lowest and then comes the appllication.properties from s3 remote

I was expecting this order to be 1 , 3 and 2 rather, which means "polar.greeting" from s3 default profile should have taken the precedence over class path resource [application.properties]

from the config-server when I hit localhost:8888/catalog-service/local this is what I see

{
  "name": "catalog-service",
  "profiles": [
    "local"
  ],
  "label": null,
  "version": null,
  "state": null,
  "propertySources": [
    {
      "name": "s3:catalog-service/application-local",
      "source": {
        "logging.level.org.springframework": "INFO",
        "logging.level.com.polarbookshop.catalogservice.config.client": "INFO",
        "logging.level.root": "INFO"
      }
    },
    {
      "name": "s3:catalog-service/application",
      "source": {
        "demo.demoProperty": "Generic demo value",
        "polar.greeting": "Welcome from application properties default"
      }
    }
  ]
}

When I was debugging this setting the Priority precedence in spring config client org.springframework.core.env.MutablePropertySources, see the screenshot default application properties from s3 have the lowest priority, which is not what I expected.

Image

Comment From: ryanjbaxter

So I ran this by the Spring Boot team and this is the expected behavior.

The reason why application.properties on the classpath comes before the property source from the s3 bucket is because when Spring Boot is loading configuration sources it checks application.properties first. Then it will move on to loading application.yaml if it exists. Since in this case it does it loads that. Then it finds the spring.config.import statement in which case any property sources from the import will be loaded and be placed before application.yaml in the list, but not ahead of application.properties. If you moved the spring.config.import to application.properties then you would see the property source from s3 come before application.properties and application.yaml from the classpath.

Comment From: spring-cloud-issues

If you would like us to look at this issue, please provide the requested information. If the information is not provided within the next 7 days this issue will be closed.

Comment From: kunalsumbly

Apologies for not replying sooner. Please give me another day or so to confirm the fix. Thanks

Comment From: kunalsumbly

thanks @ryanjbaxter , I have tried your solution and it works - I have moved the spring.config.import to application.properties that are bundled with my config-client and now I can see that the property source from s3 comes before application.properties and application.yaml from the classpath, which is good. - Now my question is that in my s3 , these are the files that I have - bucket name (my-config) - foo
- application.properties has polar.greeting=A - application-local.properties has polar.greeting=B - foo.properties has polar.greeting=C - foo-local.properties has polar.greeting=D - bar - application.properties - application-local.properties - bar.properties - bar-local.properties

  • foo app has the following in its src/main/resources
  • application.properties with spring.config.import
  • application.yml with config server setting
  • foo and bar are config client that run with profile as local , and they both use config server to fetch these config from s3
  • in my config server ,
    application.yml

cloud: config: server: awss3: bucket: my-config region: ap-southeast-2 use-directory-layout: true

  • since s3 folder for foo has foo.properties and foo-local.properties , when I do /actuator/env on foo , I dont see values from foo.properties or foo-local.properties that I have set in s3 taking effect, I only see the value from application-local.properties and application.properties from s3 . Is this intentional?

{ "name": "configserver:s3:catalog-service/application-local", "properties": { "polar.greeting": { "value": "**" }, "logging.level.com.polarbookshop.catalogservice.config.client": { "value": "*" }, "logging.level.org.springframework": { "value": "" }, "logging.level.root": { "value": "" } } }, { "name": "configserver:s3:catalog-service/application", "properties": { "polar.greeting": { "value": "" }, "demo.demoProperty": { "value": "" } } }, { "name": "configClient", "properties": {} }, { "name": "Config resource 'class path resource [application.properties]' via location 'optional:classpath:/'", "properties": { "polar.greeting": { "origin": "class path resource [application.properties] - 2:14", "value": "" }, "spring.application.name": { "origin": "class path resource [application.properties] - 1:25", "value": "" }, "spring.config.import": { "origin": "class path resource [application.properties] - 3:22", "value": "***" } }