Load application.properties when the config server is not resolved - java

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.

Related

Can't Discovery-Locate Config Upon Transition to Spring 2.4

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.

How to change database dynamically in Spring Boot?

How can we change database credentials and sources in Spring Boot project on run time or you can say dynamically? Let suppose if we are deploying to test server, database connection in API project point to test database, and if deploy to production server, database automatically point to production server. Do some research what kind of config we need to set these settings in our spring boot project. Technically our project will switch to more than one database connections dynamically.
How all this process will happen in Spring boot?
Let suppose if we are deploying to test server, database connection in
API project point to test database, and if deploy to production
server, database automatically point to production server.
There're couple of ways. The first you can provide the properties values as part of env variables. Ex:
-Ddb_url=jdbc:mysql://<server>:<port>/<databasename> -Ddb_username=root -Ddb_password=mysql
spring.datasource.url=${db_url}
spring.datasource.username=${db_username}
spring.datasource.password=${db_password}
or
You can use different properties files in accordance with different envs.
application.properties
application-test.properties
application-prod.properties
In these properties file you can pre-configured the addresses as per the profile. To activate the profile for particular env, you've to provide the following prop to springboot application. Ex:
-Dspring.profiles.active=prod
In this case, application.properties file will be used first to configured the properties and then the properties from application-prod.properties will be configured. In case of matching properties in both the files, the later file's properties will override the properties in the default property file i.e application.properties.

Spring Config Server delivering config file but not activating profiles

I have a config server.
I'm migrating from 2.2 to 2.6 and I've seen that properties file are changed.
I have a situation that I don't think is covered (or maybe I misunderstood).
My situation is that I've a Config server that deliver:
application.properties --> containing properties shared by every microservices
application-dev.properties --> containing properties specific for dev profile
application-test.properties --> containing properties specific for test profile
application-production.properties --> containing properties specific for production profile
microservicesName.properties --> containing specific properties
microservicesName-mysql.properties --> if I use mysql profile
microservicesName-h2.properties --> if I use h2 profile
Before 2.4 I used to put in microservices application.properties path to fetch configuration from config server and in application.properties delivered by config server:
spring.profiles.include=dev,mysql
if for example I would like to activate for all microservices dev and mysql.
Now, I've converted include to groups. E.G.:
spring.profiles.groups.myConf=dev,mysql
(tried also with array notation --> spring.profiles.groups.myConf[0]=dev and [1]=mysql)
and adding to every microservices
spring.profiles.active=myConf
Watching for the config server logs, it deliver the configuration properly. My problem is that I have some Bean annotated with profile E.g.:
#Profile('h2')
or
#Profile('dev')
but these beans are not activated and application not start (also, in startup-logs profiles=[myConf])
The only solution that I found is to insert in every microservice application.properties
spring.profile.active=dev,mysql
but this don't look very comfortable because I've a lot of microservices.
Which is the proper way?
P.s.: I tried also to not touch anything and use simply
spring.config.use-legacy-processing=true
in all .properties but no luck.
After some month, with Spring Boot 2.6.6, and Spring Framework 5.3.18, this bug is solved.

what is the mechanism of configuring Spring Cloud Config in a bootstrap.properties file?

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.

Save Properties Downloaded from Spring Cloud Config Server

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.

Categories