How do I set the default Spring Boot profile for running tests (without using environment variables)? - spring

I have a Spring Boot project, using gradle, with three Spring profiles:
"development" (for when I'm developing)
"test" (for when I'm running tests)
"production" (for when it is deployed in production [to heroku])
I therefore have four application*.yml files:
application.yml (contains all the shared defaults)
application-development.yml
application-test.yml
application-production.yml
These environments all work fine. The problem is that when I deploy the code to heroku, heroku runs 'gradle build' (which in turn runs 'gradle test'), and heroku does not have an option of setting an environment variable. Therefore I cannot set an active profile. So when it runs the tests it is using application.yml without the overrides in application-test.yml. So therefore the tests obviously fail.
My only solution so far is to put all the application-test.yml defaults into application.yml, and then override them again in the other profiles, but this is obviously far from ideal.
Is there a way to:
set the active Spring profile from within gradle (note that I don't have edit access to the 'test' or 'build' tasks as they come from the 'spring boot gradle plugin')?
or is there a way in Spring Boot to set the default active Spring profile when tests are run?

Think joshiste's answer is the correct one.
Anyway I'd guess you also could set the SPRING_PROFILES_ACTIVE environment variable like so:
$ heroku config:set SPRING_PROFILES_ACTIVE=test
Adding config vars and restarting myapp... done, v12
SPRING_PROFILES_ACTIVE: test
$ heroku config
SPRING_PROFILES_ACTIVE: test
$ heroku config:get SPRING_PROFILES_ACTIVE
test
$ heroku config:unset SPRING_PROFILES_ACTIVE
Unsetting SPRING_PROFILES_ACTIVE and restarting myapp... done, v13

In case you are using the #WebIntegrationTest or #IntegrationTest you can set the profiles as property value with the annotation.
Looks like this:
#RunWith(SpringJUnit4ClassRunner.class)
#WebIntegrationTest({"spring.profiles.active=test"})
public class MyWebIntegrationTests {
// ...
}

Related

How to mock Consul properties in local environment of SpringBoot app?

There is a SpringBoot applicattion with the following configuration:
application.yml
custom:
api-base-url: http://${application.environment}-internal-api:8080
secret-key: ${my.consul.property.secretKey}
Normally on staging env, the placeholders are being resolved by a configuration in Consul (all needed spring cloud consul dependencies were added).
I would like to run application locally and override both of these properties (api-base-url, secret-key)
with the following properties file:
/my/local/directory/app-local.yml
application.environment: local
custom:
api-base-url: http://extenal-api.org
secret-key: 36vch34c53j54v563ch3
Trying to run spring-boot:run command I get a property resolving error.
mvn spring-boot:run -D spring-boot.run.arguments=--spring.config.additional-location=/my/local/directory/app-local.yml
java.lang.IllegalArgumentException:
Could not resolve placeholder 'my.consul.property.secretKey'
in value "${my.consul.property.secretKey}"
at o.s.u.PropertyPlaceholderHelper.parseStringValue(PropertyPlaceholderHelper.java:180)
at o.s.util.PropertyPlaceholderHelper.replacePlaceholders(PropertyPlaceholderHelper.java:126)
at o.s.c.env.AbstractPropertyResolver.doResolvePlaceholders(AbstractPropertyResolver.java:239)
And even if we exclude custom.secret-key, it appears that custom.api-base-url is resolved to http://local-internal-api:8080, that means the app-local.yml is processed, application.environment is resolved to "local", but the other two properties are still resolved from the original values in application.yml
What is the proper way to override properties and placeholders for running application locally?

Support of different test profiles for quarkus

Is there any support for different test profiles? During my local tests I would like to use "mvn package" which uses the "test" profile. This points to my localhost db. For my devops toolchain I want to use a different test profile because we are using containers and cannot use localhost. Goal is to distinguish between local machine test and cloud env. test.
you mean you run 'mvn packge' which leads to the tests being run - like with 'mvn test' . In this case #QuarkusTest tests will run with the 'test' profile. The same goes for running tests in the ide.
There is a system property (used with '-D') 'quarkus.test.profile'. It leads to this profile being activated:
mvn test -Dquarkus.test.profile=foo
.....
2020-04-10 14:06:20,451 INFO [io.quarkus] (main) Quarkus 1.3.0.Final started in 17.408s. Listening on: http://0.0.0.0:8081
2020-04-10 14:06:20,451 INFO [io.quarkus] (main) Profile foo activated.
You may set this property on the surefire or failsafe plugin in your pom.xml (see 1).
You may also set this property within your IDE on a run/launch configuration to start the test (IntelliJ: use the vm options field and add '-Dquarkus.test.profile=integrate')
https://quarkus.io/guides/maven-tooling
Quarkus supports custom profiles. You have two ways to set a custom profile: via the quarkus-profile system property or the QUARKUS_PROFILE environment variable.
For your needs, you can for example, use a 'staging' profile with a different db address in application.properties in this way:
%staging.db.address=value
And set the QUARKUS_PROFILE environment variable to staging to activate the profile.
you can use quarkus.profile property so at runtime it will be:mvn package -Dquarkus.profile=your_custom_profile

How to detect dev and production environment in Gradle based Spring Boot 2.0.0 app?

This question is in the context running app as war deployed to tomcat. It is not quite clear to me how can I specify parameters in application.properties or application.yml that have different values depending if the app is running in production or in dev? Like below
if env == dev then myparam1 = devvalue
if env == prod then myparam1 = prodvalue
Note that I am using Gradle and not Maven.
As refered to in the comments you could use Spring profiles for the different environments and provide separate configuration files for these profiles.
On production:
Add a file application-prod.yml with the production settings.
Start the application with option --spring.profiles.active=prod or environment variable SPRING_PROFILES_ACTIVE=prod.
On dev environment:
Add a file application-dev.yml with the development settings.
Start the application with option --spring.profiles.active=dev or environment variable SPRING_PROFILES_ACTIVE=dev.
Note that you can keep some common settings suitable for all environments in your application.yml and only override some specific keys in the environment config files.
EDIT:
In order to activate a certain Spring profile for your application in a Tomcat servlet context you can add a context parameter with the name spring.profiles.active to the <Context> element. See: https://tomcat.apache.org/tomcat-9.0-doc/config/context.html#Context_Parameters

In Spring Boot, how do you build a JAR file for a different profile even if proper environment variables have not been set?

I'm new to Spring Boot, so bear with me. Currently, I'm working on a small app just for the purposes of learning Spring Boot. My goal is to deploy it using AWS (elastic beanstalk).
So far, I've created three application properties files:
application.properties: Properties which apply to all profiles.
application-dev.properties: Properties only for development. This includes localhost connection to DB, path to self signed key store, etc.
application-prod.properties: Properties used only for prod. This includes the prod DB details, etc.
Everything works fine when running the app locally using the dev profile since everything has been hard coded in the application-dev.properties.
However, the application-prod.properties file contains references which will be resolved through OS environment variables, such as:
spring.datasource.username=${DB_USERNAME}
spring.datasource.password=${DB_PASSWORD}
I currently do not have the variables DB_USERNAME and DB_PASSWORD set up in my local OS, and I do not wish to do so. But when I run the following command, it obviously fails:
mvn package spring-boot:repackage -Dspring.profiles.active=prod
It fails because it's unable to find the above environment variables.
Is there any way to delay this check until I actually execute the JAR? My plan is to build the JAR locally and then copy it over to my prod server, and run it there. The prod server will definitely have these environment variables.
The workaround I found is this:
mvn package spring-boot:repackage -Dspring.profiles.active=dev
java -jar -Dspring.profiles.active=prod [jar-file-name].jar
However, this feels like a hack. And it may cause issues in the future that I can't think of right now.
You can use any value you want in those properties for prod profile. If the env varieble exists, Spring will take the value from there instead of the properties.
As explained here:
Spring Boot uses a very particular PropertySource order that is
designed to allow sensible overriding of values. Properties are
considered in the following order:
[...]
OS environment variables.
[...]
Profile-specific application properties packaged inside your jar (application-{profile}.properties and YAML variants).
So your application-prod.properties can look like this:
spring.datasource.username=willBeOverridenByEnvValue
spring.datasource.password=willBeOverridenByEnvValue
You have to make sure though that you set both SPRING_DATASOURCE_USERNAME and SPRING_DATASOURCE_PASSWORD env values in your prod server

How do I activate a Spring Boot profile when running from IntelliJ?

I have 5 environments:
- local (my development machine)
- dev
- qc
- uat
- live
- staging
I want different application properties to be used for each environment, so I have the following properties files each which have a different URL for the datasource:
- application.properties (containing common properties)
- application-local.properties
- application-dev.properties
- application-qc.properties
- application-uat.properties
- application-live.properties
I am using IntelliJ and running my app using bootRun in the Gradle plugin on my local machine. I will be using deploying the same application war file on all other environments which run Tomcat.
I have tried adding:
--spring.profiles.active=local
to the run configuration under script parameters.
I have tried adding
-Dspring.profiles.active=local
to the run configuration under VM options.
Neither work. I keep seeing the INFO message on startup say: No active profile set, falling back to default profiles: default
If I run my app from the windows command line using
gradle bootRun
but I first set the environment variable
set SPRING_PROFILES_ACTIVE=local
Then everything works.
So my question is, how do I activate my local spring boot profile when running bootRun from IntelliJ ?
I added -Dspring.profiles.active=test to VM Options and then re-ran that configuration. It worked perfectly.
This can be set by
Choosing Run | Edit Configurations...
Go to the Configuration tab
Expand the Environment section to reveal VM options
If you actually make use of spring boot run configurations (currently only supported in the Ultimate Edition) it's easy to pre-configure the profiles in "Active Profiles" setting.
Spring Boot seems had changed the way of reading the VM options as it evolves. Here's some way to try when you launch an application in Intellij and want to active some profile:
1. Change VM options
Open "Edit configuration" in "Run", and in "VM options", add: -Dspring.profiles.active=local
It actually works with one project of mine with Spring Boot v2.0.3.RELEASE and Spring v5.0.7.RELEASE, but not with another project with Spring Boot v2.1.1.RELEASE and Spring v5.1.3.RELEASE.
Also, when running with Maven or JAR, people mentioned this:
mvn spring-boot:run -Drun.profiles=dev
or
java -jar -Dspring.profiles.active=dev XXX.jar
(See here: how to use Spring Boot profiles)
2. Passing JVM args
It is mentioned somewhere, that Spring changes the way of launching the process of applications if you specify some JVM options; it forks another process and will not pass the arg it received so this does not work. The only way to pass args to it, is:
mvn spring-boot:run -Dspring-boot.run.jvmArguments="..."
Again, this is for Maven.
https://docs.spring.io/spring-boot/docs/current/maven-plugin/examples/run-debug.html
3. Setting (application) env var
What works for me for the second project, was setting the environment variable, as mentioned in some answer above: "Edit configuration" - "Environment variable", and:
SPRING_PROFILES_ACTIVE=local
Tested with IntelliJ Community edition 2021.x
You can create Multiple configurations, one each for a specific profile, In my case below, I have created a dev config with dev profile environment variable.
Goto Run > Edit Configuration
Choose the configuration you want to edit, in the left under Application.
On the right side > Under Environment Variable, update spring.profiles.active=<your profile name> example
spring.profiles.active=dev
(observer:- the variable should be without -D flag)
Save the changes and Run the Spring boot app with the same configuration.
Note:- You can also create a new configuration or copy existing in step 2 above, using the option available in the same panel.
Try add this command in your build.gradle
So for running configure that shape:
For Spring Boot 2.1.0 and later you can use
mvn spring-boot:run -Dspring-boot.run.profiles=foo,bar
I ended up adding the following to my build.gradle:
bootRun {
environment SPRING_PROFILES_ACTIVE: environment.SPRING_PROFILES_ACTIVE ?: "local"
}
test {
environment SPRING_PROFILES_ACTIVE: environment.SPRING_PROFILES_ACTIVE ?: "test"
}
So now when running bootRun from IntelliJ, it defaults to the "local" profile.
On our other environments, we will simply set the 'SPRING_PROFILES_ACTIVE' environment variable in Tomcat.
I got this from a comment found here: https://github.com/spring-projects/spring-boot/pull/592
A probable cause could be that you do not pass the command line parameters into the applications main method. I made the same mistake some weeks ago.
public static final void main(String... args) {
SpringApplication.run(Application.class, args);
}
I use the Intellij Community Edition.
Go to the "Run/Debug Configurations" > Runner tab > Environment variables > click button "...". Add:
SPRING_PROFILES_ACTIVE = local
spring.profiles.active
In my case I used below configuration at VM options in IntelliJ , it was not picking the local configurations but after a restart of IntelliJ it picked configuration details from IntelliJ and service started running.
-Dspring.profiles.active=local
So for resuming...
If you have the IntelliJ Ultimate the correct answer is the one provided by Daniel Bubenheim
But if you don't, create in Run->Edit Configurations and in Configuration tab add the next Environment variable:
SPRING_PROFILES_ACTIVE=profilename
And to execute the jar do:
java -jar -Dspring.profiles.active=profilename XXX.jar
Try this. Edit your build.gradle file as followed.
ext { profile = project.hasProperty('profile') ? project['profile'] : 'local' }
You can try the above way to activate a profile
Here are 2 ways
Using gradle project property
In build.gradle, add
bootRun{
//https://github.com/spring-projects/spring-boot/pull/592#issuecomment-880263914
if (project.hasProperty('profiles')) {
environment SPRING_PROFILES_ACTIVE: profiles
} else {
def profiles = 'dev'
environment SPRING_PROFILES_ACTIVE: profiles
}
}
In intellij gradle configuration, change the value "test" in "-Pprofiles" as appropriate to environment you want to run
Using environment property
Follow answer by #Hubert https://stackoverflow.com/a/39749545/3333878
And configure the run configuration as
Create files properties like these
application.properties
application-dev.properties
application-prod.properties
then run
VM option is hidden by default.
Here is the right way to do it
Run->Edit Configurations->Select the application on the left menu->Add VM Options
and then add
-Dspring.profiles.active=<profile_name>
Replace the <profile_name> with the profile, say local
Click Apply & OK.
Set -Dspring.profiles.active=local under program arguments.

Resources