spring boot: how to verify env variables? - spring-boot

Is it possible to check env vars on startup in Spring Boot?
For example config looks like this:
spring:
data:
mongodb:
uri: ${MONGODB_URI}
rabbitmq:
addresses: ${RMQ_CLUSTER_ADDRESSES}
I want the app to not start if one of var is not exists.

Related

Default Spring Data MongoDB properties values

I have a new MongoDB, that means there's no user, password and/or an authentication database.
Translating this to a .properties file, this should be:
spring.data.mongodb.host=localhost
spring.data.mongodb.port=27017
...and for .yaml/.yml:
spring:
data:
mongodb:
host: localhost
port: 27017
Now let's suppose I want to use environment variables instead, if they're set, like this:
spring:
data:
mongodb:
host: ${MONGODB_HOST:localhost}
port: ${MONGODB_PORT:27017}
Everything until now works as expected.
What I want to achieve is the same for the spring.data.mongodb.user, spring.data.mongodb.password and spring.data.mongodb.authentication-database properties. I've tried doing the same technique for these properties, but in case they're not found in the environment, an exception is thrown, like this:
spring:
data:
mongodb:
host: ${MONGODB_HOST:localhost}
port: ${MONGODB_PORT:27017}
username: ${MONGODB_USERNAME}
password: ${MONGODB_PASSWORD}
authentication-database: ${MONGODB_AUTHENTICATION_DATABASE}
I have even tried setting empty/blank valuesm like ${MONGODB_USERNAME:}, ${MONGODB_USERNAME:''} and ${MONGODB_USERNAME:""}.
How do I get to achieve this? Is it even possible?
I've not tried this, but Spring boot should be able to pickup environment variables and use them as properties without having to put them in the yaml file. For example if you name a OS environment variable SPRING_DATA_MONGODB_USERNAME the value should show up in the spring property spring.data.mongodb.username. This would allow you to specify the username property only when you require it.
The other way to do this is to use externalised config. If you are booting your application from a fat jar, an application.yml outside the jar in the same directory can add in your additional properties:
spring:
data:
mongodb:
username: fred
password: password
without affecting the properties specified in the application.yml provided inside the jar.
There's a long ordered list of where spring-boot looks for properties documented here.

Passing list of YAML properties from Docker compose to Spring Boot application

My spring Boot application contains the following list of properties in application.yml:
hs:
targets:
- name: system_a
url: https://system-a
username: username-a
password: password-a
- name: system_b
url: https://system-b
username: username-b
password: password-b
Instead of defining them there, I'd like to pass them from Docker Compose via docker-compose.yml.
my-app:
image: my-image
ports:
- 9002:9002
environment:
SPRING_PROFILES_ACTIVE: dev
HS_TARGETS_NAME: system-a
HS_TARGETS_URL: https://system-a
HS_TARGETS_USERNAME: username_a
HS_TARGETS_PASSWORD: password_a
HS_TARGETS_NAME: system-b
HS_TARGETS_URL: https://system-b
HS_TARGETS_USERNAME: username_b
HS_TARGETS_PASSWORD: password_b
But I doubt that this will work because the variables all have the same name. Is there any way to do that or do I have to restructore my Spring Boot properties?
Pass the properties as
HS_TARGETS_0_NAME: system-a
HS_TARGETS_0_URL: https://system-a
HS_TARGETS_0_USERNAME: username_a
HS_TARGETS_0_PASSWORD: password_a
HS_TARGETS_1_NAME: system-b
HS_TARGETS_1_URL: https://system-b
HS_TARGETS_1_USERNAME: username_b
HS_TARGETS_1_PASSWORD: password_b
Tested with Spring Boot 2.1.17

Read docker env variable inside Spring application.yaml

I'm setting an environment variable inside my docker-compose.yaml file and want to use that variable's value inside my Spring Boot's application.yaml. I was told that doing something like
app:
auth:
tokenSecret: tokensecretvaluehere
tokenExpirationMsec: 864000000
oauth2:
sso:
url: ${SSO_URL}
(where SSO_URL is defined in my docker-compose.yaml) in my Spring application.yaml. However, this causes an error when I run docker-compose up --build because it can't find that variable (error is like: Could not resolve placeholder SSO_URL in value "${SSO_URL}"). This is an example of what my docker-compose.yaml:
api:
restart: always
ports:
- "8080:8080"
links:
- redis
- db
environment:
- SERVER_SERVLET_SESSION_COOKIE_DOMAIN=localhost
- SSO_URL=myvaluehere
I was asked to not uses the System.getenv functions in Java and instead set the variable like above. From there I would just use the #Value annotation to get it in my Java code as like below:
#Value("${app.oauth2.sso.url}")
private String ssoUrl;
This is more of the application.yaml:
heb:
togglr:
jwt:
secret:
id: 101
session:
seconds: 600
tokenheader: X-TOGGLR-TOKEN
logging:
level:
com:
heb: debug
default: debug
path: logs
server:
error:
whitelabel:
enabled: false
port: 8080
servlet:
context-path: /togglr-api
use-forward-headers: true
spring:
application:
name: togglr_api
freemarker:
enabled: false
groovy:
template:
enabled: false
jmx:
enabled: false
main:
banner-mode: 'off'
thymeleaf:
cache: false
security:
oauth2:
client:
registration:
github:
clientId:
clientSecret:
redirectUri:
scope:
- user:email
- read:user
app:
auth:
tokenSecret:
tokenExpirationMsec: 864000000
oauth2:
sso:
url: ${SSO_URL}
In general spring boot applications are able to read the Environment variables accessible in the docker container. Its stated in the documentation (see item 10 in the list at the very beginning of the document).
So the problem might be elsewhere:
It might be a typo in the question, but if you're using application.yaml as opposed to application properties, then you should have something like:
sso:
url: ${SSO_URL}
Make sure, that the env variable SSO_URL is indeed accessible in the container even before you start the spring boot application. In java (for debugging purposes only) you can do something like:
#SpringBootApplication
public class MyApp {
public static void main(String [] args) {
System.out.println(System.getenv("SSO_URL"));
SpringApplication.run(MyApp.class);
}
}
Solution was to not use an underscore character in the variable name.
I feel what you are missing is the build context within the docker-compose file. Or, you have multiple profile based application.yml and not the correct profile is being set.
Below is the working code / config. So we are creating a springboot application from docker-compose, where docker-compose builds the image of springboot application and passes the required environment variables.
Snippet from Spring application.yml where we are using the env variable named API_BASE and TEST_API_PATH
third-party-api:
base-url: ${API_BASE}
test-api-path: ${TEST_API_PATH}
Below is the snippet from docker-compose.yml
my-app:
image: my-app-image-name
build:
dockerfile: Dockerfile
context: .
ports:
- '9080:8080'
environment:
API_BASE: http://mock-api:8080
TEST_API_PATH: /someTestApiPath
SPRING_PROFILES_ACTIVE: dev
Docker file of my application is pretty simple.
FROM openjdk:17-alpine
ARG JAR_FILE=build/libs/*.jar
COPY ${JAR_FILE} app.jar
ENTRYPOINT ["java","-jar","/app.jar"]
Dockerfile and docker-compose.yml are on the same hierarcy, if there is any difference in your structure then it should reflect in below config of docker-compose.yml
build:
dockerfile: Dockerfile

Read values from consul while bootstrap spring boot

I have question is there any way to retrieve certain values and inject them to bootstrap.yml while application is coming up.
I have configuration file like this:
spring:
application:
name: myApp
cloud:
consul:
enabled: true
host: localhost
port: 8500
config:
enabled: true
datasource:
url: jdbc:oracle:thin:#localhost:1111:XXXX
username: ${nameOfVariable1}
password: ${nameOfVariable1}
driver-class-name: oracle.jdbc.OracleDriver
For example, I need to configure embedded tomcat port, or DB credentials, I don't want to put it hardcoded in .yml properties file, instead I want to put some variable name in .yml so Spring will go and bring value from Consul. Is it possible?
You can use Spring Cloud Consul Config project that helps to load configuration into the Spring Environment during the special "bootstrap" phase.
3 steps:
add pom dependency: spring-cloud-starter-consul-config
enable consul config: spring.cloud.consul.config.enabled=true
add some config in consul kv in specific folder, such as key: config/testConsulApp/server.port, value:8081
and then start the sample web app, it will listen 8081.
more detail at spring cloud consul doc.
and demo code here

how to make spring boot client's application.yml get value from config server

Is there anyway to make spring cloud config client's application.yml read values from spring config server?
For example,
on my spring cloud config client, the application.yml is like this
spring:
application:
name: clienttest
mvc:
view:
prefix: /jsp/
suffix: .jsp
server:
port: 8080
context-path: /clienttest
tomcat:
uri-encoding: UTF-8
eureka:
client:
service-url: {"defaultZone":"http://dev.euraka01.app.com:8769/eureka/,http://dev.euraka02.app.com:8770/eureka/"}
instance:
prefer-ip-address: true
and my bootstrap.yml file is as below
spring:
application:
name: clienttest
cloud:
config:
name: clienttest
uri: http://192.168.2.101:9000
enabled: true
profile: out_test
label: master
now for the service-url value, for different environment, I have to config different eureka url values, my question is that, is there anyway that I can config the service-url value in the config server? like I set the value as ${service-url} in the application.yml, and when I start the config client server, it get the value from the config server according the profile and label which I set in the bootstrap.yml.
You can look up properties on the config server by both profile and label, where label is either either a branch, tag.
/{application}/{profile}[/{label}]
/{application}-{profile}.yml
/{label}/{application}-{profile}.yml
/{application}-{profile}.properties
/{label}/{application}-{profile}.properties
In your example above your config server will try and find a file named
clienttest-out_test.properties
In the git repo on the master branch.
spring:
application:
name: clienttest
cloud:
config:
profile: out_test
label: master
See the example and also a good doc here
Essex Boy,
Thank you very much for your help, and I was able to read the value from different config profile before.
My question is how to make application.yml get the value from config server, and now I've solve it by myself, the answer is quite easy, in the application, set value like ${service-url}, the full answer is as below:
in my application.yml, the content is as below:
spring:
application:
name: clienttest
server:
port: 8080
context-path: /clienttest
tomcat:
uri-encoding: UTF-8
eureka:
client:
service-url: {"defaultZone":"${service-url}"}
instance:
prefer-ip-address: true
Please note the service-url value, now the value is set as {"defaultZone":"${service-url}"}, and in my application.properties file which on the config server, the properties file content is as below:
service-url=http://192.168.2.101:8769/eureka/,http://192.168.2.101:8770/eureka/
then when I start the mocroservice, it could resist it self on the http://192.168.2.101:8769/eureka/ and http://192.168.2.101:8770/eureka/
which is what result I want.

Resources