Spring cloud config environment variable interpolation - spring-boot

I'm using spring-cloud-config in my Spring Boot project and I don't understand how to interpolate environment variables.
For example I have MYSQL_PASSWORD variable set in the config server (as environment variable) and I want to get it from other clients, without redefining the variable inside all of them. This is my config:
service.yml (in config-server)
spring:
datasource:
url: jdbc:mysql://mysql:3306/${MYSQL_DATABASE}?autoReconnect=true&useSSL=false
username: ${MYSQL_USER}
password: ${MYSQL_PASSWORD}
bootstrap.yml (into the client)
spring:
application:
name: event-service
cloud:
config:
uri: http://config-service:8888
fail-fast: true
If I start my services with this configuration, placeholder ${MYSQL_***} is not interpolated and I cannot connect to the database obviously.
What I have to do to make it work is to define environment variables like MYSQL_*** also in the client.
This is weird for me as I want config-server to be the central repository for everything. Do you have any advice?

I suggest you try to replace ${MYSQL_USER} and ${MYSQL_PASSWORD} with ${mysql.user} and ${mysql.password} - that way you'll be relying on Spring Boot's default property replacement.
Although I'm not sure that configuration server supports what you're trying to use, I've never tried a similar use case, please write back with a solution if the approach I suggested didn't work :)

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.

Is there anyway to use configure-server with servlet context path when register on service discovery?

I have a service-discovery which register all the services. I have configure-server which maintain all the configuration. configure-server already register in service-discovery. I know by default configure-server will register with id: configserver. I know how to change the id. But when I tried to use servlet.context.path= /config all the configure-client can not pull from configure-sever through service-discovery look like can not use /config in configure-server.
configure-server:
server:
port: 0
servlet:
context-path: /config
spring:
application:
name: configserver
cloud:
config:
server:
git:
uri: https://github.com/PheaSoy/spring-completed-microservice
search-paths: config/{application}
discovery:
enabled: true
configure-client
spring:
application:
name: song-service
cloud:
config:
uri: http://configserver/config
discovery:
enabled: true
Even I added context path /path configure-client always fetched without context path.
ConfigServicePropertySourceLocator : Fetching config from server at : http://192.168.1.34:57945/
Is there any way to configure configure-client with available configure-server context path through service-discovery?
The discovery client implementations all support some kind of metadata map (for example, we have eureka.instance.metadataMap for Eureka). Some additional properties of the Config Server may need to be configured in its service registration metadata so that clients can connect correctly. If the Config Server is secured with HTTP Basic, you can configure the credentials as user and password. Also, if the Config Server has a context path, you can set configPath. For example, the following YAML file is for a Config Server that is a Eureka client:
bootstrap.yml.
eureka:
instance:
...
metadataMap:
configPath: /config
Reference:
Spring Cloud Config with Eureka - contextPath
Discovery with bootstrap
Yes, you can define your context path for your configuration server as you have done.
But doing so, you also need to take into account the alignments you need to do.
Eureka. By default will call your management API. For example, http://BASE_URI/actuator/health. But since you are adding a context path "config", it means it should be now: http://BASE_URI/config/actuator/health. You can correct following the suggestion above on eureka.instance...metadataMap.configPath: /config
Configuration Clients. In your application (client to the config server), you can add the context path in spring.cloud.config.uri. For example, if it was "http://BASE_URI", then it should be updated as "http://BASE_URI/config" now since you added a context path.
Please try and see if it helps.

SpringBoot project, Environment Variable in application.yml always empty

I'm trying to run a SpringBoot project that runs with docker-compose. i started its dependencies (Redis, MongoDB ,and RabbitMQ) with docker-compose up
and i'm building the project and running it with these commands
mvn clean package -DskipTests && mvn spring-boot:run
I keep having these errors :
Error processing condition on org.springframework.boot.autoconfigure.security.oauth2.client.OAuth2RestOp erationsConfiguration$RequestScopedConfiguration
Caused by: java.lang.IllegalArgumentException: Could not resolve placeholder 'OAUTH_CLIENTID' in value "${OAUTH_CLIENTID}"
In the docker-compose file, the values of the environment variables are defined in the environment section.
environment:
- RABBIT_HOST=rabbitstomp
- RABBIT_USER=guest
- RABBIT_PASS=user
- MONGO_HOST=mongodb://localhost:27017
- OAUTH_CLIENTID=nz-kek
- OAUTH_CLIENT_SECRET=DzXZxeOZOJHFZIUhObSpsne
- SSO_HOST=https://webweb.com
- CORS_HOSTS=HOST1,HOST2
- SES_HOST=ses
- SES_PORT=6000
- REDIS_HOST=localhost
- REDIS_PORT=6379
This is how application.yml looks like :
spring.data.mongodb:
database: ${DB_NAME} #notificationdb
uri: ${MONGO_HOST}
security:
oauth2:
resource:
jwk:
key-set-uri: ${auth-server:${SSO_HOST}}/keys
token-info-uri: ${auth-server:${SSO_HOST}}/userinfo
client:
client-id: ${OAUTH_CLIENTID}
client-secret: ${OAUTH_CLIENT_SECRET}`
So when running the project without docker-compose, am i supposed to put the values in the application.yml ?
i also tried mvn spring-boot:run -Dspring-boot.run.arguments=--path.to.value=value1, but i'm not sure how the path should be with variables like key-set-uri: ${auth-server:${SSO_HOST}}/keys
Spring has support for providing default values in the configuration via the PlaceholderConfigurerSupport. The default value is what comes after the :. In your case, you should write:
client-id: ${OAUTH_CLIENTID:yourDevelopmentDefaultClientID}
If you use the #Value annotation to inject the values, you have support to SpEL for using expressions of the type: #{someExpression} for more complex cases.
UPDATE:
In your case, I believe you are reversing the position of the variables. The correct should be:
key-set-uri: ${SSO_HOST:auth-server}/keys
Here is what it means: first, it will try to use the SSO_HOST environment variable, which is provided to the container through docker-compose. In case this variable is not provided to the process, Spring will use auth-server as the address of the server. It seems to me that this address is visible only inside the docker-compose network, so if you are running your app outside this network, the auth-server address will not be visible. Do you know where is the auth server? Is it another docker container? Is it running on localhost?
Some interesting reference: https://www.baeldung.com/spring-value-defaults
Pass env variables in docker compose as object not a list:
environment:
RABBIT_HOST: rabbitstomp
RABBIT_USER: guest
RABBIT_PASS: user
MONGO_HOST: mongodb://localhost:27017
OAUTH_CLIENTID: nz-kek
OAUTH_CLIENT_SECRET: DzXZxeOZOJHFZIUhObSpsne
SSO_HOST: https://webweb.com
CORS_HOSTS: HOST1,HOST2
SES_HOST: ses
SES_PORT: 6000
REDIS_HOST: localhost
REDIS_PORT: 6379`

Disabling config server for tests

I am using microservice architecture where the services are done with spring boot, to control them, I am using zuul as a proxy, eureka as register and config server as the configuration provider.
In the microservices I configure my bootstrap.yml file as follows:
spring:
application:
name: portal-gca-server-${MYENV:local}
cloud:
config:
uri: http://localhost:9090
As seen above, the config file name that I look for in the config server is according to my environment variable, otherwise I get the local profile.
This works fine, but when I run the tests on the development machine it will never work, because instead of searching for the test profile it searches for the location, because the dev machine has no environment variable.
I know I could register the environment variable to run the tests but that's not the intention or else I would have to do this all the time to run tests and to run the local application.
Is there any way to solve this? I already tried to use the annotations:
webEnvironment = SpringBootTest.WebEnvironment.MOCK
#ActiveProfiles("test")
#TestPropertySource(locations="classpath:application-test.properties")
None of them any good, because the first thing the application does when executed is to fetch the information in git according to bootstrap.yml.
Has anyone ever experienced this?
Go to your bootstrap.yml and then add :
spring:
cloud:
config:
enabled: false

How to create Spring Cloud Config Client with env specific configuration?

I have facing an issue with Spring Cloud Config Server and Eureka Server Profiling.
Let's say I have 3 services with their name ("spring.application.name") as :
myapp-svc
myapp-spring-cloud-config-svc
myapp-spring-eureka-svc
I want to deploy each service in 2 regions ( dev and prod ). In Dev region, each service will run on localhost and in prod it will have some different url. 'myapp-spring-cloud-config-svc' in dev region will point to local git repo, while in prod region it will point to remote git repo.I can have 2 configurations:
1) When I start 'myapp-svc' service in local, it should connect to 'myapp-spring-cloud-config-svc' in dev.
I can do this by setting spring.cloud.config.uri = .
But the issue with this set up is that the property needs to be defined in bootstrap.properties.
So, If deploy 'myapp-svc' to prod, I will have to change config uri there to point it to prod config service which in turn would need another build creation.
This doesn't seem like a good solution, what if I have 50 app related services, I can't change this property in each one of them before prod deployment.
I tried setting spring.cloud.config.uri in application-dev.properties of 'myapp-svc' but it doesn't work. As per docs, it must be changed in bootstrap.
So, how do I implement this without having to create new build for prod ?
2) I can first call eureka and then using eureka I can call config service here.
The problem here is also same.
If I use eureka to look up config then "eureka.client.serviceUrl.defaultZone" must be defined in "bootstrap.yml".
See this:https://cloud.spring.io/spring-cloud-config/multi/multi__spring_cloud_config_client.html
So, in this case too, I need to change eureka url before deploying this service to prod.
Please help me on this...!!
Here is how, the properties, yml looks like for each of the above mentioned services:
1) myapp-svc:
1.1)bootstrap.yml
spring:
application:
name: myapp-svc
cloud:
config:
discovery:
enabled: true
serviceId: myapp-spring-cloud-config-svc
eureka:
client:
serviceUrl:
defaultZone: http://localhost:8762/eureka/
server:
port: 8082
2) myapp-spring-cloud-config-svc:
2.1)application-dev.properties:
spring.cloud.config.server.git.uri=file:///C:/config-repo
eureka.client.serviceUrl.defaultZone=http://localhost:8762/eureka
2.2)application-prod.properties:
spring.cloud.config.server.git.uri=https://github.com/<mygit Repo>
2.3)bootstrap.proerties:
spring.application.name=myapp-spring-cloud-config-svc
server.port=8888
3) myapp-spring-eureka-svc
3.1)bootstrap.proerties
spring.application.name=myapp-spring-eureka-svc
server.port=8762
1) You can have profile specific bootstrap-<profile>.properties (like for application-<profile>.properties) for each supported profile to avoid rebuilding your application for each env. Then just pass application profile using to your application during start-up. Spring will load correct bootstrap-<profile>.properties and will connect to proper configuration server (or eureka, etc). Example:
java -jar your-app.jar --spring.profiles.active=dev
2) You can pass your URLs externally as custom properties (same as with profile above) and have smth like this in bootstrap.properties. Example:
spring.cloud.config.uri=${config.server.url}
then pass --config.server.url= ... during start-up.
3) You can pass Spring properties in the same way during start-up. Example:
java -jar your-app.jar --spring.cloud.config.uri= ...
4) You can use system env variables. Example:
spring.cloud.config.uri=${SYSTEM_ENV_CLOUD_CONFIG_URI}

Resources