Spring spring.profiles.include: doesn't read profile-related properties - spring-boot

There are several ways present to define "active profiles" for a Spring Boot application.
The default one is to pass it through a command line, like this:
java -Dspring.profiles.active=dev,local -jar myapp.jar
it works just fine (as expected): All three sets of profile-related properties will be loaded in proper order:
application.yaml
application-dev.yaml will override the previous one
application-local.yaml will override the previous one as well (these properties will have the most priority)
Based on the idea, that my "local" profile should always "use and overrides" properties from the "dev", let's "hardcode" such behavior.
Let's use the 'spring.profiles.include' feature for this. So, the following lines are added to the 'application-local.yaml':
spring.profiles:
include:
- dev
I expect, now I can pass the "local" profile only in the command line, and the "dev" profile will be applied automatically (with his properties, of course):
java -Dspring.profiles.active=local -jar myapp.jar
But ooop!*: properties from the 'application-dev.yaml' are ignored.
Why? Is it a bug? Is it a feature that forces me to list all profiles in a command line directly?
I'm sure that the behavior around profiles activation should be the same without any difference in how the active-profiles list was passed to Spring Boot framework.
The application:
#SpringBootApplication #EnableConfigurationProperties( MyProps::class )
class SpringApp4
#ConfigurationProperties("my.db") #ConstructorBinding
data class MyProps(val name: String, val url: String, val user: String)
#Component
class MyRunner(val myProps: MyProps, val env: Environment) : CommandLineRunner {
override fun run(vararg args: String) {
println("myProps = $myProps")
println("activeProfiles = ${env.activeProfiles.joinToString()}")
exitProcess(0)
}
}
fun main() { runApplication<SpringApp4>() }
application.yaml:
my.db:
name: "default-name"
url: "default-url"
user: "default-user"
application-dev.yaml:
my.db:
url: "dev-url"
user: "dev-user"
application-local.yaml:
spring.profiles.include:
- dev
my.db:
user: "local-user"
Run1: java -Dspring.profiles.active=dev,local -jar myapp.jar
Correct output:
myProps = MyProps(name=default-name, url=dev-url, user=local-user)
activeProfiles = dev, local
it's correct because the url=dev-url
Run2: java -Dspring.profiles.active=local -jar myapp.jar
Incorrect output:
myProps = MyProps(name=default-name, url=default-url, user=local-user)
activeProfiles = local
It's not correct because the url=default-url and the activeProfiles doesn't contain the "dev" at all.
Help me please to figure out how to use the spring.profiles.include feature in yaml to build a kind of top level profiles that will activate other automatically.

In Run2 You are giving profile as local
i.e
-Dspring.profiles.active=local
So spring will first load application.yml and then application-local.yml
I can see the output is expected.
Since some properties like name and url are not present in application-local.yml, so the values of these fields will be same as present in application.yml
FYI : application.yml is always called irrespective of profile, and then it gets overridden by the profile mentioned in -Dspring.profiles.active property

spring.profiles.include deprecated in Spring Boot 2.4 and no longer works: https://spring.io/blog/2020/08/14/config-file-processing-in-spring-boot-2-4
It caused recursive resource loading; that broke Kubernates ConfigMap so they removed recursion.
Use spring.profiles.active or spring.profiles.group.

Related

Spring: Order of profile names with SpringBootTest

SpringBoot 2.7.1 with Spring Cloud Config
I have a test
#ActiveProfiles({"local","h2dbmem"})
#SpringBootTest(properties = {"server.ssl.enabled=false"})
#AutoConfigureTestDatabase(replace = AutoConfigureTestDatabase.Replace.NONE)
#TestPropertySource(locations = "classpath:application-integrationtest.properties")
public class SystemTest {
...
}
and I then run Maven to run the test :
mvn -Dspring.profiles.include=cloudconfig clean verify
When the test runs, the console says :
The following 3 profiles are active: "cloudconfig", "local", "h2dbmem"
Although this is exactly what I wanted, that it ends up using the JDBC URL and credentials from the h2dbmem profile and not the JDBC URL and credentials from the cloud-config profile, I could not find documentation on why cloudconfig ( supplied via command-line ) was added as the first entry in the list of active profiles after the profiles supplied via the #ActiveProfiles annotation.
To be honest, I was actually expecting the following order of profiles :
The following 3 profiles are active: "local", "h2dbmem", "cloudconfig"
... where the JDBC URL and credentials from cloudconfig are used, but I am glad it was not.
So question is:
Is there specific documentation on the sequence / order of profiles when they are supplied in multiple ways ( annotation, command-line, etc ) ?
Note I am aware of the order and sequence / preference of properties, which is not what I am asking.

override command line Spring properties with bootstrap.yml

i have a tomcat box , which has spring active profile set already, as a command line argument every time a spring app is deployed through catalina.
i am using spring cloud config server , so in config client i specify active profile in bootstrap.yml , but as i mention earlier it is overriden by tomcat command line argument .
how to override the command line argument passed through tomcat , with my boostrap.yml at the time of bootstrap context loading so that i can pass active profile from my bootstrap.yml to config server.
Tomcat set environment command (which i cannot change as i dont have access)
JAVA_OPTS="$JAVA_OPTS -Djava.library.path=/path -Dspring.profiles.active=e2"
bootstrap.yml
spring:
profiles:
active: e2,cron
cloud:
config:
uri: http://localhost:8888
application:
name: heartbeat_monitor.
Command line argument(-Dspring.profiles.active=e2) will always override your properties file, no matter how many hardcoded profiles you specify in your yaml file. I would suggest you to add additional profile to be set programatically at the runtime and keep two property files with -profilename before the .yml extension.
This could be done as follows:
ApplicationMain.java
public static void main(String[] args) {
SpringApplication app = new SpringApplication(DemoApplication.class);
app.setAdditionalProfiles("cron");
app.run(args);
}
bootstrap-e2.yml
// Keep all the properties which is specific to e2 profile.
bootstrap-cron.yml
// Keep all the properties which is specific to cron profile.
In this way, you can keep both the profiles in use .But, if a property is common in both the bootstrap files, then program will pick the property from that bootstrap file whose profile matches with the runtime args : -Dspring.profiles.active

Integration Test with Spring Cloud Config Server

It's my first time implementing a Spring application using Spring Cloud Config framework. The application is working fine, getting configuration properties from repository through the server application. So, at the moment I must write the integration tests to test connection between client application and server and repository. To do it I mean get a value from repository over properties and check the value, also make a request to the server, or if exists, other method inside spring cloud config library to make it.
The problem now, when executing the integrationTest the application can't read the properties from remote. I've created a bootstrap.yml on the integrationTest context but not works. So, I got the errors like: Could not resolve placeholder.
MyIntegrationTest
#RunWith(SpringJUnit4ClassRunner.class)
#SpringBootTest()
#ActiveProfiles("integrationTest")
public class MyIntegrationTest {
#Value("${throttling.request.rate.minute}")
private int MAX_REQUEST_PER_MINUTE;
#Test
public void validateGetRequestRateLimitFromRepository() {
Assert.assertNotEquals(MAX_REQUEST_PER_MINUTE,1L);
}
}
bootstrap.yml
spring:
application:
name: sample-name
---
spring:
profiles: local #also tried integrationTest
cloud:
config:
enabled: true
uri: https://endpoint-uri:443
skipSslValidation: true
name: ${spring.application.name},${spring.application.name}-${spring.profiles}
Also tried:
Set application-integrationTest.yml
Set application.yml
Set bootstrap-integrationTest.yml
Create a *-integrationTest.yml at the repo.
Set #ActiveProfiles("local") on integrationTest class.
Nothing works.

Environment VM arg passed to bootRun not being picked up by Spring Boot application

I am trying to set the environment of my Spring Boot (1.5.4.RELEASE) application at runtime, but it appears I have something out of alignment.
My application.yml is defined like this:
spring:
profiles.active: ${env:local}
---
spring:
profiles: local
foo: bar
---
spring:
profiles: dev
foo: bar
In a class that I have annotated as #Configuration, I have a method that does the following just so I can show the environment that is being used:
#Value('${spring.profiles.active}')
String activeProfile
#PostConstruct
def bootComplete() {
println "App started with profile: $activeProfile"
}
Under this configuration, when my application starts, I see this in the console:
App started with profile: local
If I modify ${env:local} to be ${env:dev} in my application.yml and I start the application, I see this in the console:
App started with profile: dev
My goal is to start the application with VM arguments to set the active profile at runtime. I am adding the argument: -Denv=dev but it appears that it has no effect on the starting of the application. Can anyone suggest what I might be overlooking here?
I found the solution to my problem. The issue was that I was starting the application using Gradle bootRun. My assumption was that the VM args set there would be used. I am now running the application by calling the class directly, and the VM args are working (both -Denv and -Dspring.profiles.active)
Doing -Denv=env will not make any effect since env is not a property key.
This is how you can do it:
-Dspring.profiles.active=dev

How to externalize configuration in Spring Boot using profiles?

I have an application where I would like to change a datasource password that is stored in a application.yml file. The password in the YML file is stored such as this:
----
spring:
profiles: production
datasource:
password: prodpassword
Note: I also have profiles for development and stage.
The password prop is set on a class using ConfigurationProperties such as follows:
#Component
#ConfigurationProperties(prefix="datasource")
public class DataSourceConnector {
private password;
public void setPassword(String password) {
this.password = password;
}
Now, I try to override the prodpassword with prodpa$$word via a command line arg but it doesn't work:
java -Dspring.profiles.active=production -jar /usr/share/myapp/myapp-1.0.jar --datasource.password='prodpa$$word'
I also tried creating an identical (except the new password) application.yml file outside of the jar. That doesn't work either.
java -Dspring.profiles.active=production -jar /usr/share/myapp/myapp-1.0.jar --spring.config.location=/usr/share/myapp/
Note: I left out the file name in the location param due to this note from http://docs.spring.io/spring-boot/docs/current/reference/htmlsingle/#boot-features-external-config-profile-specific-properties:
If you have specified any files in spring.config.location, profile-specific variants of those files will not be considered. Use directories inspring.config.location if you also want to also use profile-specific properties.
How can I override datasource.password within the application.yml of the jar?
Edit:
The application is being started / stopped using supervisorctl.
After changing the config file that contains the java command, supervisorctl must reread the change:
supervisorctl reread
Next, activate the changes with:
supervisorctl update

Resources