Integration Test with Spring Cloud Config Server - spring

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.

Related

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

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.

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

How to use a Spring profile expression?

Spring Boot: 2.0.4.RELEASE
I have specified a Spring Boot multi-profile YAML configuration file:
server:
address: 192.168.1.100
---
spring:
profiles: development
server:
address: 127.0.0.1
---
spring:
profiles: production | eu-central
server:
address: 192.168.1.120
According to the reference guide, if the production or eu-central profile is active, the server.address property is 192.168.1.120. But when I run this test
#ActiveProfiles({"production"})
#RunWith(SpringRunner.class)
#SpringBootTest
public class ProfileTest {
#Autowired
private Environment environment;
#Value("${server.address}")
private String serverAddress;
#Test
public void testProfile() {
assertThat(environment.getActiveProfiles(), is(new String[] {"production"} ));
assertThat(serverAddress, is("192.168.1.120"));
}
}
it fails:
java.lang.AssertionError:
Expected: is "192.168.1.120"
but: was "192.168.1.100"
at com.example.demo.ProfileTest.testProfile(ProfileTest.java:27)
Why does the test fail and how do I use a Spring profile expression correctly?
By the way, if I remove | eu-central from the spring.profiles key the test passes!
Boris. I've studied your problem. And I think that I've found a bug in Spring-boot. I've reported an issue so you can check it. The problem is SpringBoot always use default value for ${server.address} because framework couldn't recognise profiles: production | eu-central as a List of profiles. If I use
#ActiveProfiles(value = "production | eu-central")
All is ok.
The link on Spring-boot issue here https://github.com/spring-projects/spring-boot/issues/14314
So, you could see the discussion.
Thank you!
UPDATED
I've got an answer from Spring Team on my Issue.
This format is a Spring Framework 5.1 (and therefore a Spring Boot 2.1
feature).
So the answer on your question is
This feature will be available in Spring Boot 2.1.

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

#ConfigurationProperties not pulling the external properties file

I've created a personal repository on Git where I have kept my application.properties file.
I've created a cloud config server ('my-config-server') and used the git repository url.
I have bound my spring-boot application that is supposed to access the external properties file with Git repository.
#javax.jws.WebService(
serviceName = "myService",
portName = "my_service",
targetNamespace = "urn://vdc.com/xmlmessaging/SD",
wsdlLocation = "classpath:myService.wsdl",
endpointInterface = "com.my.service.SDType")
#PropertySource("application.properties")
#ConfigurationProperties
public class SDTypeImpl implements SDType {
/*It has various services implementation that use following method**/
private SDObj getObj (BigDecimal value) {
AnnotationConfigApplicationContext context =
new AnnotationConfigApplicationContext(
SDTypeImpl.class);
SDObj obj = context.getBean(SDPropertiesUtil.class).getObj(value);
context.close();
return obj;
}
}
Another Class:
public class SDPropertiesUtil {
#Autowired
public Environment env;
public SDObj getObj(BigDecimal value) {
String valueStr = env.getProperty(value.toString());
/*do logic*/
}
My application starts but fails to load properties file from my git repository.
I believe I should have an application.properties at src/main/resources in my application but since I'm using
#PropertySource("application.properties")
#ConfigurationProperties
I'm telling my application to use the application.properties from an external location and do not use internal properties file. But this is not happening. My application is still using the internal properties file.
The source you included doesn't show your app configuration settings to connect to the Config server. Do you mind sharing it?
This is how the config server could be queried from a client app:
/{application}/{profile}[/{label}]
/{application}-{profile}.yml
/{label}/{application}-{profile}.yml
/{application}-{profile}.properties
/{label}/{application}-{profile}.properties
Let's say a Config server points to a Git repo which includes this file: demo-config-client-development.properties
You should be able to query the Config Server as:
curl http://localhost:8101/demo-config-client-development.properties
Assuming Config Server is running in locally and listening on 8181.
Let's also say you have a client app named: demo-config-client that connects to the Config server and runs using the development Spring profile, this app would now be able to read remote properties hosted in a Git repo through a Config server.
A detailed tutorial could be found at my blog at: http://tech.asimio.net/2016/12/09/Centralized-and-Versioned-Configuration-using-Spring-Cloud-Config-Server-and-Git.html

Resources