I am using Spring Cloud Config Server/Client and the documentation says that the server takes precedence by default over a local application.properties file. If the cloud config server happens to be unavailable the application will retry for some time and then fail to start up correctly. I would like to go around this issue by having the application save the properties fetched from the config server when it starts correctly and therefore when the config server isn't available default to the last set of properties it downloaded. Is this possible? If yes how do I access and save the properties?
I solved this issue by creating a class that implements EnvironmentAware and instantiated it as a spring bean so a property of type Environement get set automatically. I then used environmet.getPropertySources() to get the properties I needed. I then proceeded to write them to file.
I arrived to this solution by examining the code of Spring's actuator and the /env endpoint displays the applications properties.
Related
I'm working on a Spring Boot project (v2.3.x) connected to a MongoDB instance.
The connection is configured using the property spring.data.mongodb.uri.
Now, for local development I'd like to configure the connection using host/port, i.e. using these properties (I'm configuring these via ENV VARs):
spring.data.mongodb.host
spring.data.mongodb.port
Adding these properties, while leaving spring.data.mongodb.uri, obviously results in an error on application run:
java.lang.IllegalStateException: Invalid mongo configuration, either uri or host/port/credentials/replicaSet must be specified
So I'm wondering if there is a way to disable the spring.data.mongodb.uri configuration using properties override (externalized configuraion) provided by Spring Boot.
Is this possible? I tried setting spring.data.mongodb.uri=null but the startup error remains.
How can I achieve this without directly modifying the application.properties file?
NOTE: I also considered using profiles, but also using this feature I cannot find a way to override the "main" configuration.
You could use "application-default.properties" file and put spring.data.mongodb.uri in there. The "default" Spring profile will be active when no other profiles are selected. So you can start locally with any profile ("dev" or whatever) and "application-default.properties" file will not be loaded.
Of course, keep in mind that adding any profile in production would also disable spring.data.mongodb.uri in this case.
Versions
Spring Parent: 2.7.4, Spring Cloud Version: 2021.0.4, Java Version: 11
Issue
My Spring service has been using Eureka to connect to the config server for a long time, but I want to upgrade to Spring 2.7.4. I understand that as of Spring 2.4, the bootstrap context has been deprecated (source) and I need to make some adjustments to the old bootstrap properties and move them over to application.properties.
The documentation for Spring Cloud specifies that in order for me to continue to use discovery-first config lookup, I need to define a spring.config.import property with an optional configserver entry (source). Since I'm also using Vault, I define the property as follows:
spring.config.import = optional:configserver:placeholder,vault://<my-generic-backend>/dev
Next, I need to define the following properties (source). These properties were already defined in my old bootstrap.properties, so all I need to do is copy and paste.
spring.cloud.config.discovery.enabled = true
spring.cloud.config.discovery.serviceId = config-server
eureka.client.serviceUrl.defaultZone = <my-eureka-url>
Unless I'm missing something, these are all the steps I need to take in order to upgrade to 2.7.4. However, when I run the Spring service, it complains that it can't find the config server (via Eureka, or via URL), then it registers successfully with Eureka, and then continues trying and failing to find the config server.
Here is some of the output of the program:
> Running with Spring Boot v2.7.4, Spring v5.3.23
> Could not locate configserver via discovery: No instances found of configserver (config-server)
> Could not locate PropertySource ([ConfigServerConfigDataResource#2aa6311a uris = array<String>['placeholder'], optional = true, profiles = list['local']]): Invalid URL: placeholder
...
> DiscoveryClient_<my-project-name>/local - registration status: 204
I understand why it's failing to find a config server at URL: placeholder since that's not a valid URL, but I don't understand how the service can successfully register with Eureka yet not be able to find the config server. I know the service is registered because the output of the program says it registered correctly (and I can see it in the registry), and I know that the config server has the correct entity ID (config-server) because it was copied and pasted from the old bootstrap (and I can see config-server in the registry).
Workaround with Hardcoded URL
When I hardcode the config server URL like this (and set spring.cloud.config.discovery.enabled to false), the config is loaded properly from the server:
spring.config.import=configserver:https://<my-hardcoded-config-url>.com,vault://<my-generic-backend>/dev
Workaround with Bootstrap
It's possible to return to using the bootstrap context and still use Spring 2.7.4 with discovery-first config lookup by adding the "spring-cloud-starter-bootstrap" dependency. So I added the dependency to my POM and moved these properties back to bootstrap.properties from application.properties.
spring.cloud.config.discovery.enabled=true
spring.cloud.config.discovery.service-id=config-server
I moved the Vault and Eureka properties back into bootstrap.properties as well. The new application.properties now contains no values relating to Eureka, Vault, and Cloud Config.
When I run the service, it does indeed find the address for the config server through Eureka, as expected (although it fails to connect because it's the internal address and I'm running locally).
Conclusion
While these are valid workarounds, it's frustrating to not be able to have a dynamic URL for the config server (as is the entire point of using Eureka). Right now, it looks like my choices are either to use a hard-coded URL and risk having to change every property file, or use a deprecated behavior that Spring documentation specifically disfavors (source).
I would appreciate any guidance you have on the issue, and I thank you in advance.
My project has a bootstrap.yaml and a config server that deliver its appropriate profile. Everything is working great but I have to accomplish some others task like CI and CD.
My problem comes because the Jenkins machine is not allowed to resolved external domains and try to compile and run the Spring boot app without an a profile.
So my question is:
Is there a way to load application.properties when the config server
is not resolved?
Yes, there is a natural way based on the order in which Spring Boot loads PropertySources.
You can include properties you want to be applied in application.properties.
In case config server is not available - properties from application.properties will be used. If config server is available - you'll receive properties from there.
You might also want to disable config server connectivity for your CI using environment variable SPRING_CLOUD_CONFIG_ENABLED=false.
Can I use #RefreshScope (along with #Value on a property) only in Cloud config server or can I use without config server as well? I'm trying to use it without config server. I am trying to fetch #Value property by changing value in a .property file and trying to request again, will I get updated value? Is that possible?
No, you should use it along with Config server otherwise you won't be able to read the update properties on fly. Follow this article and have a look into this if you face any issue loading updated properties dynamically.
In theory, you could refresh the application context, but I wouldn't
recommend this. Spring Cloud has provided an annotation to mark a bean
as refreshable. By adding spring actuator, we can refresh those beans
on the fly.
According to the docs, to connect to the config server, spring.application.name and spring.cloud.config.uri should be set in a bootstrap.properties file for the config client, such that the parameters fetched from the config server can be prior to those set locally. I am just wondering how this works, since:
I searched the source code of Spring Cloud Config Client, but cannot find what it does with the bootstrap.properties
It still works if I set spring.application.name in application.properties
Is it a feature of SpringBoot?
This feature is not located in spring-cloud-config. Instead, it is in spring-cloud-context which is a dependency of spring-cloud-config.
Basically, it creates a parent context for the actual context of the application and initializes it with the parameters of the bootstrap.
You can find more information in https://cloud.spring.io/spring-cloud-commons/multi/multi__spring_cloud_context_application_context_services.html
It is a feature of spring cloud. The spring.cloud.config.uri needs to be set in bootstratp.properties. This comes from the spring-cloud-commons project. My guess is you might be running the config server in the default location of localhost:8888.