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

spring.config.import configuration clashes with spring.cloud.config.enabled #1877

Closed
nucatus opened this issue May 11, 2021 · 13 comments
Closed

Comments

@nucatus
Copy link

nucatus commented May 11, 2021

Observed behaviour

A spring.config.import config property is set to optional:configserver:http://myconfigserver:5656 in a main configuration file application.properties. In the application-dev.properties config file, the property spring.cloud.config.enabled is set to false, since we don't require any property to be loaded from the remote config server on the dev profile. However, the observed behavior is that ConfigServerConfigDataLoader implementation doesn't take into account that setting and is going to try to pull that configuration from the remote server. If the connection fails, it will check for the fail-fast and isOptional flags and make a decision based on that outcome.

Intended behavior

If the spring.cloud.config.enabled property is set to false, ConfigServerConfigDataLoader should not even try to connect to the remote server and return as soon as possible.

@nucatus nucatus changed the title spring.config.import configuration clash with spring.cloud.config.enabled spring.config.import configuration clashes with spring.cloud.config.enabled May 11, 2021
@ryanjbaxter
Copy link
Contributor

What version of Spring Cloud are you using? Can you provide the error?

@nucatus
Copy link
Author

nucatus commented May 12, 2021

Spring Cloud 2020.0.2

It's not an error that is thrown, only that the behavior is not the expected one.

When I disable the cloud config client, I expect the config server not to be called at all, which is not the case.

@nucatus
Copy link
Author

nucatus commented May 12, 2021

I created a small project to demonstrate the issue.

https://github.com/nucatus/spring-config-enabled-issue

@ryanjbaxter
Copy link
Contributor

the problem is that you have spring.cloud.config.enabled in a profile specific document. My guess is that spring.config.import is processed before any properties from profiles are applied and so boot includes the config data loader from the config client.

if you move the property to application.properties, or use an environment variable, it works as expected.

@spencergibb @philwebb is this behavior intended? Should we look at any profile specific configuration properties and apply those before fetching configuration?

@nucatus
Copy link
Author

nucatus commented May 12, 2021

Based on the observed behavior, your assessment is valid. However, even if I set spring.cloud.config.enabled=false in the application.properties file or set it through the SPRING_CLOUD_CONFIG_ENABLED=false environment variable, the problem gets even worse, because the application fails to start with this error:

java.lang.IllegalStateException: Unable to load config data from 'optional:configserver:'
	at org.springframework.boot.context.config.StandardConfigDataLocationResolver.getReferences(StandardConfigDataLocationResolver.java:128)
	at org.springframework.boot.context.config.StandardConfigDataLocationResolver.resolve(StandardConfigDataLocationResolver.java:115)
	at org.springframework.boot.context.config.ConfigDataLocationResolvers.lambda$resolve$1(ConfigDataLocationResolvers.java:115)
	at org.springframework.boot.context.config.ConfigDataLocationResolvers.resolve(ConfigDataLocationResolvers.java:126)
	at org.springframework.boot.context.config.ConfigDataLocationResolvers.resolve(ConfigDataLocationResolvers.java:115)
	at org.springframework.boot.context.config.ConfigDataLocationResolvers.resolve(ConfigDataLocationResolvers.java:107)
	at org.springframework.boot.context.config.ConfigDataImporter.resolve(ConfigDataImporter.java:101)
	at org.springframework.boot.context.config.ConfigDataImporter.resolve(ConfigDataImporter.java:93)
	at org.springframework.boot.context.config.ConfigDataImporter.resolveAndLoad(ConfigDataImporter.java:81)
	at org.springframework.boot.context.config.ConfigDataEnvironmentContributors.withProcessedImports(ConfigDataEnvironmentContributors.java:121)
	at org.springframework.boot.context.config.ConfigDataEnvironment.processInitial(ConfigDataEnvironment.java:242)
	at org.springframework.boot.context.config.ConfigDataEnvironment.processAndApply(ConfigDataEnvironment.java:230)
	at org.springframework.boot.context.config.ConfigDataEnvironmentPostProcessor.postProcessEnvironment(ConfigDataEnvironmentPostProcessor.java:97)
	at org.springframework.boot.context.config.ConfigDataEnvironmentPostProcessor.postProcessEnvironment(ConfigDataEnvironmentPostProcessor.java:89)
	at org.springframework.boot.env.EnvironmentPostProcessorApplicationListener.onApplicationEnvironmentPreparedEvent(EnvironmentPostProcessorApplicationListener.java:100)
	at org.springframework.boot.env.EnvironmentPostProcessorApplicationListener.onApplicationEvent(EnvironmentPostProcessorApplicationListener.java:86)
	at org.springframework.context.event.SimpleApplicationEventMulticaster.doInvokeListener(SimpleApplicationEventMulticaster.java:176)
	at org.springframework.context.event.SimpleApplicationEventMulticaster.invokeListener(SimpleApplicationEventMulticaster.java:169)
	at org.springframework.context.event.SimpleApplicationEventMulticaster.multicastEvent(SimpleApplicationEventMulticaster.java:143)
	at org.springframework.context.event.SimpleApplicationEventMulticaster.multicastEvent(SimpleApplicationEventMulticaster.java:131)
	at org.springframework.boot.context.event.EventPublishingRunListener.environmentPrepared(EventPublishingRunListener.java:82)
	at org.springframework.boot.SpringApplicationRunListeners.lambda$environmentPrepared$2(SpringApplicationRunListeners.java:63)
	at java.base/java.util.ArrayList.forEach(ArrayList.java:1541)
	at org.springframework.boot.SpringApplicationRunListeners.doWithListeners(SpringApplicationRunListeners.java:117)
	at org.springframework.boot.SpringApplicationRunListeners.doWithListeners(SpringApplicationRunListeners.java:111)
	at org.springframework.boot.SpringApplicationRunListeners.environmentPrepared(SpringApplicationRunListeners.java:62)
	at org.springframework.boot.SpringApplication.prepareEnvironment(SpringApplication.java:375)
	at org.springframework.boot.SpringApplication.run(SpringApplication.java:333)
	at org.springframework.boot.SpringApplication.run(SpringApplication.java:1340)
	at org.springframework.boot.SpringApplication.run(SpringApplication.java:1329)
	at com.example.demo.DemoApplication.main(DemoApplication.java:10)
Caused by: java.lang.IllegalStateException: File extension is not known to any PropertySourceLoader. If the location is meant to reference a directory, it must end in '/'
	at org.springframework.boot.context.config.StandardConfigDataLocationResolver.getReferencesForFile(StandardConfigDataLocationResolver.java:214)
	at org.springframework.boot.context.config.StandardConfigDataLocationResolver.getReferences(StandardConfigDataLocationResolver.java:125)

In that case, this is the final configuration file:

spring.config.import=optional:configserver:
spring.cloud.config.uri=http://configs:20101
spring.cloud.config.enabled=false

@ryanjbaxter
Copy link
Contributor

The problem doesn't get worse IMO, that is the expected behavior. Boot does not know what configserver is because you have removed disable spring cloud config and it has not config data loader to handle that protocol.

Lets take a step back...

Why disable spring cloud config in the first place? With the optional syntax it will fail silently when the config server is not present.

@nucatus
Copy link
Author

nucatus commented May 12, 2021

Because that optional prefix actually means that spring cloud config will try to connect to that server and will wait until the connection times out, if that server is not available. You will loose few good seconds there in the startup process, which in a development environment, it's quite a pain.

This is why we need a dev profile where we don't rely on that config loaded from the spring config remote server.

Moreover, what is then the purpose of having the ability to disable the client config if that is not working?! Basically, that flag becomes useless.

@spencergibb
Copy link
Member

all spring.config.import entries are evaluated. They do not behave like normal properties that can be overridden. Properties in a profile can not, by definition, influence an import in the default profile. If you don't want import in default, the only option is to not put an import there.

@nucatus
Copy link
Author

nucatus commented May 13, 2021

OK. This, indeed, is the path we should have thought of. The slightly change is that we have to enable the config client and provide the config server URL thgrough environment variables for the targeted profiles and have the client config disabled on the default configuration. This is the other way around compared to the old implementation where we had a spring config server set for the default profile and config client disabled for some particular profiles, like dev.

After reading the documentation again for the spring.config.import, it is clear that that import is evaluated before any profile specific configuration is applied. However, there is a bit of confusion that comes with the added complexity. Maybe, this is because the new mechanics about how configuration is loaded in Spring Boot 2.4.

@ryanjbaxter
Copy link
Contributor

@nucatus let us know if there is anything we can do to make things clearer. I will close this for now.

@mplain
Copy link

mplain commented Sep 7, 2021

Just spent two days trying to figure out why the dockerfile command line argument does not override spring.config.import specified in application.properties.

spencergibb
all spring.config.import entries are evaluated. They do not behave like normal properties that can be overridden. 

this migration has been very painful =_=

@roookeee
Copy link

roookeee commented Oct 28, 2021

Leaving my stackoverflow answer here in case someone stumbles into this issue via google: https://stackoverflow.com/questions/68702479/disable-config-service-client-activated-with-spring-config-import-in-springboott/69739888#69739888

There are workarounds but the situation of spring.config.import not being overridable, Spring Cloud Config requiring at least one spring.config.import to start with configserver: when it's enabled and Splring Cloud Config removing it's PropertySourceLoader for configserver: prefixed spring.config.import entries when disabled makes this very cumbersome.

I for one would wish that Spring Cloud Config would still register its PropertySourceLoader when disabled and just make it a noop. This way no fancy shenanigans (like linked above) are needed - especially when these fancy workarounds break apart because Spring Cloud Configs check for "at least one spring.cloud.config entry must start with configserver:" is not resolving variable placeholders when doing this check:

some.workaround = configserver:localhost:1337
spring.config.import = ${some.workaround}

This is not working because of the aformentioned missing variable resolution. This issue is creating a lot of headaches for our production environment at the moment.

@JassonLuiz
Copy link

o problema é que você tem spring.cloud.config.enabledem um documento específico do perfil. Meu palpite é que spring.config.importé processado antes que quaisquer propriedades dos perfis sejam aplicadas e, portanto, a inicialização inclui o carregador de dados de configuração do cliente de configuração.

se você mover a propriedade para application.properties ou usar uma variável de ambiente, ela funcionará conforme o esperado.

@spencergibb @philwebbesse comportamento é intencional? Devemos examinar as propriedades de configuração específicas do perfil e aplicá-las antes de buscar a configuração?

Hi Ryan, thank you very much. I was having problem with this, so I put the 'spring.config.import' first than any property and that solved it for me. I am using Spring 3.1.1

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

7 participants