Quarkus Consul Config: how to inject application.yaml file from Consul Agent? - java

I am using Quarkus inside a microservice Java application.
I recently started to migrate from Spring Boot to Quarkus itself.
I am having some trouble while migrating "Spring Cloud Consul" to "Quarkus Consul Config". In order to be more specific, I am getting the following error:
java.lang.RuntimeException: Key 'my/consul/path/application.yaml' not found in Consul
at io.quarkus.consul.config.runtime.ConsulConfigSourceProvider$1.accept(ConsulConfigSourceProvider.java:66)
at io.quarkus.consul.config.runtime.ConsulConfigSourceProvider$1.accept(ConsulConfigSourceProvider.java:56)
at io.smallrye.context.impl.wrappers.SlowContextualConsumer.accept(SlowContextualConsumer.java:21)
at io.smallrye.mutiny.operators.uni.UniOnItemConsume$UniOnItemComsumeProcessor.invokeEventHandler(UniOnItemConsume.java:77)
at io.smallrye.mutiny.operators.uni.UniOnItemConsume$UniOnItemComsumeProcessor.onItem(UniOnItemConsume.java:42)
at io.smallrye.mutiny.operators.uni.UniOnItemTransform$UniOnItemTransformProcessor.onItem(UniOnItemTransform.java:43)
at io.smallrye.mutiny.vertx.AsyncResultUni.lambda$subscribe$1(AsyncResultUni.java:35)
(...)
Inside my Consul instance, the key my/consul/path/application.yaml corresponds to an application.yaml external file that I would like to import from there during the startup phase.
Below you can find my consul config (application.yaml):
quarkus:
application:
name: myapplication
consul-config:
enabled: true
properties-value-keys: my/consul/path/application.yaml
agent:
host-port: http://localhost:9500
prefix: myappprefix
If I try to switch from properties-value-keys to properties-raw-value-keys, I see that my property is not being injected inside my application context:
#ConfigProperty(name = "consultest")
String test;
java.util.NoSuchElementException: SRCFG00014: The config property consultest is required but it could not be found in any config source
Below you can find the application.yaml content (located on Consul):
consultest: testtest
The intent, here, is to delegate application.yaml properties to Consul, divided by environment (dev, test, prod).
I would like to threat my local application.yaml file (located in src/main/resources) as a bootstrap.yaml file, similarly to Spring Boot approach.
How could this be done with Quarkus? Thank you a lot for your support.

Related

How can a library module of a Spring application add additional configuration to the application's `application.yml`?

I have a Spring Boot application built with Maven which uses JDBC. The application.yml file has
spring:
application:
(stuff)
datasource:
url: jdbc:informix-sqli://......
driver-class-name: com.informix.jdbc.IfxDriver
I want to move the JDBC specific parts into a library so now the app/src/main/resources/application.yml only contains
spring:
application:
(stuff)
and the datasource configuration parameters need to live in the library repository. I tried creating lib/src/main/resources/application.yml with
spring:
datasource:
url: jdbc:informix-sqli://......
driver-class-name: com.informix.jdbc.IfxDriver
hoping that both the yml files would be picked up and merged when Spring loads up. Apparently not.
The library and application build fine, but when I run it
***************************
APPLICATION FAILED TO START
***************************
Description:
Failed to configure a DataSource: 'url' attribute is not specified and no embedded datasource could be configured.
How can I get the configuration in the library to be merged into the application.yml configuration in the application?
I have always found "merging" of external configuration from sub-modules with spring-boot to be problematic at worst, confusing at best. I now organize all external configuration using one of two approaches. But first, there are multiple ways to specify external configuration that you should be aware of:
Spring Boot and multiple external configuration files
Starting Spring Application by merging yml files
https://docs.spring.io/spring-boot/docs/current/reference/html/spring-boot-features.html#boot-features-external-config
I previously would specify multiple locations and that sort-of worked. But, I now typically use one of two approaches to avoid confusion:
I use profiles and specify multiple profiles at runtime when launching the spring boot app. i.e. Multiple profiles like "shared-common, shared-jdbc, deploy-prod" which will load "application-shared-jdbc.yml" out the sub-module.
or
I create a module that contains nothing but configuration files that get used by all related modules, often with multiple profiles for different configuration scenarios. All other modules (executable and libraries) depend on this shared configuration module.
AFAICT, spring-boot's external configuration handling not setup ideally for having standalone submodule configuration. It's more oriented around the notion that configuration belongs to runtime/executable modules, not libraries.
you can use #PropertySource. you have to implement your own PropertySourceFactory
if your props is in yaml format. then define it in the PropertySource
#PropertySource(value = ResourceUtils.CLASSPATH_URL_PREFIX
+ "application.yml", factory = Factory.class)

SpringBoot 2 with Flyway: spring.flyway.locations is ignored

I am using Spring Boot 2.2.2 with Flyway 5.2.4 and I tried to configure flyway to use a differente location for the scripts, but spring.flyway.locations=filesystem:db_other/migration/{vendor} neither flyway.locations=filesystem:db_other/migration/{vendor} configurations on application.properties worked.
When running the program, the following exception appear in the log:
FlywayMigrationScriptMissingException: Cannot find migration scripts in: [classpath:db/migration]
I already tried using Spring Boot 2.2.1, 2.2.0, 2.1.11 and Flyway 6.1.0 and 6.1.3, but the result is the same.
The default value for that property is classpath:db/migration as shown here (search for flyway).
Since you're using a different folder in the resources directory you should only need to change "filesystem" to "classpath" in your application.properties value.
Actually if was my fault: as I used to work with just spring (not spring boot) I configured my test class with the annotations #ExtendWith(SpringExtension.class) AND #ContextConfiguration(classes = { MyConfiguration.class }) instead of just use #SpringBootTest. When making this change the test worked.

Set active Spring Boot profile for Tomcat Webapp internally (via application.properties)

I'd like to use two different spring profiles proda and prodb using Spring Boot 2.0.0 (setting the profile in the application.properties).
In Spring Boot, you can also set the active profile in application.properties, as shown in the following example:
spring.profiles.active=production
Source: 74.6
For now I'm only trying to get proda to work. I've got three different property files:
application-proda.yml
database:
conn:
blablabla: blablaqwer
and
application-prodb.yml
database:
conn:
blablabla: albalbrewq
and also
application.properties
spring.profiles.active=proda
When running the application in the IDE, or packaging it as jar with maven, everything works as expected (active profiles [proda] are set, application-proda.yml is loaded). Calling this in (for example a #PostConstruct of) some class:
System.out.println(Arrays.toString(env.getActiveProfiles()));
will result in
[proda]
but when buildung as war with maven, and deploying it to a Tomcat server, the same code will result in
[]
and application-proda.yml is not loaded. That means the application didn't read the application.properties and therefore didn't load the active profile proda for some reason.
But the finished war has all the needed files under WEB-INF\classes\.
I've seen some solutions where you can set -Dspring.profiles.active=proda as a command line parameter, or set the active profiles in the web.xml, but this is not what I need, as I don't have a web.xml and I'd like to use the Spring Boot feature and declare the profiles in the application.properties. It should work just like in the IDE or packaged as a jar with maven.

Managing dependencies with spring boot profiles and Gradle

I created a java project that will use spring boot and Gradle. I would like to configure profiles, for the different environment (development on my local machine, systemtest for integration test on server farm machine etc). I would use h2 in memory database for development environment and SqlServer for systemtest environment. In build.gradle I defined the following dependencies
dependencies {
compile("org.springframework.boot:spring-boot-starter-data-jpa")
compile("org.springframework.boot:spring-boot-starter-web-services")
compile('org.springframework.boot:spring-boot-starter-actuator')
runtime('com.h2database:h2:1.4.195')
runtime('com.microsoft.sqlserver:mssql-jdbc')
}
I created a application.yml file, application-development.yml and application-systemtest.yml where I would put common properties and environment specific properties. The file application-systemtest.yml defines the connecction parameters for sql server
spring:
datasource:
url: jdbc:sqlserver://<host>,1433;databaseName=MYDB
username: myuser
password: mypass
driverClassName: com.microsoft.sqlserver.jdbc.SQLServerDriver
jpa:
show-sql: true
hibernate:
dialect: org.hibernate.dialect.SQLServer2012Dialect
I would also create an uber-jar and select the profile as a launch parameter, ie
java -Dspring.profiles.active=systemtest -jar <my uber jar>
The development profiles starts fine and I am running on h2 in memory database. When trying systemtest profile, spring boot fails to load contexts and fails. This is caused by spring boot finding h2 dependency and trying to configure datasource defined in application-systemtest.yml
So I modified the build.gradle dependencies closure
def profile = project.findProperty('spring.profiles.active')
dependencies {
compile("org.springframework.boot:spring-boot-starter-data-jpa")
compile("org.springframework.boot:spring-boot-starter-web-services")
compile('org.springframework.boot:spring-boot-starter-actuator')
if (profile == 'development') {
runtime('com.h2database:h2:1.4.195')
} else {
runtime('com.microsoft.sqlserver:mssql-jdbc')
}
}
This time spring boot start correctly. Don't like very much this solution as I have to handle the profile configuration partly with Gradle. I would like to know if there is a way to configure spring boot so that profile is completely managed within itself, resolving h2 in development environment and sqlserver in systemtest environment, leaving Gradle unaware of spring profiles.
How to solve this problem ?
It wouldn't be advisable to have a different artifact / binary depending on the DB product. Try to configure the datasources / Spring profiles as suggested by #M. Deinum to prevent the datasource to be configured as H2 when using a different DB.

Spring Datasources based on Environment

I am trying to configure my Spring Boot application to use specific datasources when certain environmental variables exist. For example, if the MY_PROD_DATASOURCE environmental variable exists, I would like to use my production datasource; otherwise, I would like to use my local datasource (of the same type).
I have found something in the Spring reference that explains how a single datasource could be declared in my application.properties. Specifically, a MySQL datasource could look like:
spring.datasource.url=jdbc:mysql://localhost/test
spring.datasource.username=dbuser
spring.datasource.password=dbpass
spring.datasource.driverClassName=com.mysql.jdbc.Driver
However, I do not see how I could change the datasource properties conditionally in this file. Is there another way to do it?
In Spring Boot you can:
Externalize application.properties from your jar and provide file per environment by adding path as a startup parameter:
java -jar your-app.jar --spring.config.location=/path/to/app.properties
Use Spring profiles. Create application-${profile}.properties for each profile, in each one different datasource properties
Use Spring profiles and instead of application.properties, put your properties to application.yaml where you can put properties for all environments using convention as below:
spring:
profiles: development
server:
port: 9001
---
spring:
profiles: production
server:
port: 0
Use environment variables and set SPRING_DATASOURCE_URL, SPRING_DATASOURCE_USERNAME, SPRING_DATASOURCE_PASSWORD, and (optionally) SPRING_DATASOURCE_DRIVER_CLASS_NAME.
Learn more in the Spring Boot reference section on How to change configuration depending on the environment and External Configuration.

Categories

Resources