To use a Spring Boot app as a client for a Spring Cloud Services Config Server instance, you must add the dependencies listed in Client Dependencies to your app's build file. Be sure to include the dependencies for Config Server as well.
Important: Because of a dependency on Spring Security, the Spring Cloud Config Client starter will, by default, cause all app endpoints to be protected by HTTP Basic authentication. If you wish to deactivate this, please see Deactivate HTTP Basic Authentication below.
Refer to the "Cook" sample app to follow along with the code in this topic.
Spring Cloud Services uses HTTPS for all client-to-service communication. If your VMware Tanzu Application Service for VMs (TAS for VMs) foundation is using a self-signed SSL certificate, the certificate will need to be added to the JVM truststore before your client app can consume properties from a Config Server service instance.
Spring Cloud Services can add the certificate for you automatically. For this to work, you must set the TRUST_CERTS
environment variable on your client app to the API endpoint of your TAS for VMs or Elastic Runtime instance:
$ cf set-env cook TRUST_CERTS api.cf.example.com
Setting env variable 'TRUST_CERTS' to 'api.cf.example.com' for app cook in org myorg / space development as user...
OK
TIP: Use 'cf restage' to ensure your env variable changes take effect
$ cf restage cook
Note: The CF_TARGET
environment variable was formerly recommended for configuring Spring Cloud Services to add a certificate to the truststore. CF_TARGET
is still supported for this purpose, but TRUST_CERTS
is more flexible and is now recommended instead.
As the output from the cf set-env
command suggests, restage the app after setting the environment variable.
When the app requests a configuration from the Config Server, it will use a path containing the application name. You can declare the application name in the application.yml
file.
In application.yml
:
spring:
application:
name: cook
This app will use a path with the application name cook
, so the Config Server will look in its configuration source for files whose names begin with cook
, and return configuration properties from those files.
Now you can (for example) inject a configuration property value using the @Value
annotation. The Menu class reads the value of special
from the cook.special
configuration property.
@RefreshScope
@Component
public class Menu {
@Value("${cook.special:none}")
String special;
//...
public String getSpecial() {
return special;
}
//...
}
The CookController
class is a @RestController
. It has a private menu
and returns the special
(the value of which will be supplied by the Config Server) in its restaurant()
method, which it maps to /restaurant
.
@RestController
public class CookController {
private final Menu menu;
public CookController(Menu menu) {
this.menu = menu;
}
@RequestMapping("/restaurant")
public String restaurant() {
return String.format("Today's special is: %s", menu.getSpecial());
}
//...
You can provide configurations for multiple profiles by including appropriately-named .yml
or .properties
files in the Config Server instance's configuration source (the Git repository). Filenames follow the format {application}-{profile}.{extension}
, as in cook-production.yml
. (See Profile-Specific Configuration.)
The app will request configurations for any active profiles. To set profiles as active, you can use the SPRING_PROFILES_ACTIVE
environment variable, set for example in manifest.yml
.
---
applications:
- name: cook
host: cookie
instances: 1
memory: 1G
services:
- cook-config-server
env:
SPRING_PROFILES_ACTIVE: development
The sample configuration source cook-config contains the files cook.properties
and cook-production.properties
. If you add production
to the list of active profiles, the app will make a request of the Config Server using the path /cook/production
, and the Config Server will return properties from both cook-production.properties
(the profile-specific configuration) and cook.properties
(the default configuration); for example:
{
"name":"cook",
"profiles":[
"production"
],
"label":"main",
"propertySources":[
{
"name":"https://github.com/spring-cloud-services-samples/cook-config/cook-production.properties",
"source":
{
"cook.special":"Cake a la mode"
}
},
{
"name":"https://github.com/spring-cloud-services-samples/cook-config/cook.properties",
"source":
{
"cook.special":"Pickled Cactus"
}
}
]
}
When the Config Server returns multiple values for a configuration property, the app must decide which value to use for the property. A Spring app will take the first value for each property. In the example response above, the configuration for the production
profile is first in the list, so the Spring Boot sample app will use values from that configuration.
Spring Boot Actuator adds an env
endpoint to the app and maps it to /actuator/env
. This endpoint displays the app's profiles and property sources from the Spring ConfigurableEnvironment
. (See Endpoints" in the "Spring Boot Actuator" section of the Spring Boot Reference Guide.) In the case of an app which is bound to a Config Server service instance, env
will display properties provided by the instance.
To use Actuator, you must add the spring-boot-starter-actuator
dependency to your project. If using Maven, add to pom.xml
:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
If using Gradle, add to build.gradle
:
compile("org.springframework.boot:spring-boot-starter-actuator")
You can now visit /actuator/env
to see the application environment's properties (the following shows an excerpt of an example response):
$ curl https://cookie.apps.example.com/actuator/env
{
"activeProfiles": [
"development",
"cloud"
],
"propertySources": [
...
{
"name": "configService:configClient",
"properties": {
"config.client.version": {
"value": "85c636fc400632e45985bdc86651b5f6e3efae73"
}
}
},
{
"name": "configService:https://github.com/spring-cloud-services-samples/cook-config/cook.properties",
"properties": {
"cook.special": {
"value": "Pickled Cactus"
}
}
},
...
]
}
Spring Boot Actuator also adds a refresh
endpoint to the app. This endpoint is mapped to /actuator/refresh
, and a POST request to the refresh
endpoint refreshes any beans which are annotated with @RefreshScope
. You can thus use @RefreshScope
to refresh properties which were initialized with values provided by the Config Server.
The Menu.java
class is marked as a @Component
and also annotated with @RefreshScope
.
import org.springframework.cloud.context.config.annotation.RefreshScope;
import org.springframework.stereotype.Component;
@RefreshScope
@Component
public class Menu {
@Value("${cook.special}")
String special;
//...
This means that after you change values in the configuration source Git repository, you can update the special
on the CookController
class's menu
by creating a refresh event for the app:
$ curl https://cookie.apps.example.com/restaurant
Today's special is: Pickled Cactus
$ git commit -am "new special"
[main 3c9ff23] new special
1 file changed, 1 insertion(+), 1 deletion(-)
$ git push
$ curl -X POST -d {} -H "Content-Type: application/json" https://cookie.apps.example.com/actuator/refresh
["cook.special"]
$ curl https://cookie.apps.example.com/restaurant
Today's special is: Birdfeather Tea
You can use the Config Server to serve the contents of a plain text file from a Git repository. The Spring Cloud Services Config Client library adds a PlainTextConfigClient
that can read in a text file and make its contents available to a client app.
The PlainTextConfigClient
provides a getPlainTextResource()
method which accepts an application profile, a label, and a filename. You can use this method to retrieve a Spring Resource
. The Cook app has a DessertMenu
class whose fetchMenu()
method takes the InputStream
from the retrieved Resource
and uses the copyToString()
method of Spring's StreamUtils
to return a String
.
public String fetchMenu() throws IOException {
InputStream input = configClient
.getPlainTextResource("cloud", "main", "dessert.json")
.getInputStream();
return StreamUtils.copyToString(input, Charset.defaultCharset());
}
The CookController
has a dessertMenu()
method, which is mapped to /restaurant/dessert-menu
and returns the String
from DessertMenu.fetchMenu()
.
@RequestMapping("/restaurant/dessert-menu")
public String dessertMenu() throws IOException {
return dessertMenu.fetchMenu();
}
You can configure the Config Server to use a HashiCorp Vault server as a configuration source, as described in Configuring with Vault. To consume configuration from the Vault server via the service instance, your client app must enable Spring's scheduled task execution support and be given a Vault token. The Spring Cloud Services Connectors for Config Server will automatically renew the app's token for as long as the app is running.
Important: If the app is entirely stopped (i.e., no instances continue to run) and its Vault token expires, you will need to create a new token and provide it to the app.
To enable Spring scheduled task support, add the Spring @EnableScheduling
annotation to a configuration class in your app:
package cook;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.scheduling.annotation.EnableScheduling;
@EnableScheduling
@SpringBootApplication
public class CookApplication {
public static void main(String[] args) {
//...
To generate a token for use in the app, you can run the vault token-create
command.
Note: The app consumes the token using the spring.cloud.config.token
configuration property, which can be set in any Spring Boot property source. For ease of maintenance, VMware recommends using the SPRING_CLOUD_CONFIG_TOKEN
environment variable to provide the token to the app.
If you are using the SPRING_CLOUD_CONFIG_TOKEN
environment variable to provide the token to the app, generate a token with a Time To Live (TTL) that is long enough for the app to be restaged after you have set the environment variable. The following command creates a token with a TTL of one hour:
$ vault token-create -ttl="1h"
After generating the token, set the environment variable on your client app and then restage the app for the environment variable setting to take effect:
$ cf set-env cook SPRING_CLOUD_CONFIG_TOKEN c3432ef5-6a78-8673-ea23-5528c26849e4
$ cf restage cook
When the app makes a request of the Config Server service instance, it will include the Vault token. The Config Server, in turn, will include the token as the value of an X-Vault-Token
header in its subsequent requests to the Vault API. For more information, see the Vault documentation.
The Spring Cloud Services Connectors for Config Server renew the app token for as long as the app continues to run. For more information about the token renewal performed by the connectors, see the HashiCorp Vault Token Renewal section of Spring Cloud Connectors.
After creating a Vault token for an app, you can renew the token manually using the Config Server service instance bound to the app.
Note: The following procedure uses the jq command-line JSON processing tool.
Run cf env
, giving the name of an app that is bound to the service instance:
$ cf services
Getting services in org myorg / space development as user...
OK
name service plan bound apps last operation
config-server p-config-server standard vault-app create succeeded
$ cf env vault-app
Getting env variables for app vault-app in org myorg / space development as user...
OK
System-Provided:
{
"VCAP_SERVICES": {
"p-config-server": [
{
"credentials": {
"access_token_uri": "https://p-spring-cloud-services.uaa.cf.example.com/oauth/token",
"client_id": "p-config-server-876cd13b-1564-4a9a-9d44-c7c8a6257b73",
"client_secret": "rU7dMUw6bQjR",
"uri": "https://config-86b38ce0-eed8-4c01-adb4-1a651a6178e2.apps.example.com"
},
[...]
Then create a Bash script that accesses the Vault token renewal endpoint on the service instance backing app:
TOKEN=$(curl -k [ACCESS_TOKEN_URI] -u [CLIENT_ID]:[CLIENT_SECRET] \
-d grant_type=client_credentials | jq -r .access_token); \
curl -H "Authorization: bearer $TOKEN" -H "X-VAULT-Token: [VAULT_TOKEN]" \
-H "Content-Type: application/json" -X POST \
[URI]/vault/v1/auth/token/renew-self -d '{"increment": [INTERVAL]}'
This script retrieves an access token using an OAuth 2.0 credential exchange with Cloud Foundry's UAA service. After being given an authorization token, it uses this token to make a call to the service instance's API endpoints.
Replace the following placeholders in the script with values from the cf env
command above:
[ACCESS_TOKEN_URI]
with the value of p-config-server.credentials.access_token_uri
[CLIENT_ID]
with the value of p-config-server.credentials.client_id
[CLIENT_SECRET]
with the value of p-config-server.credentials.client_secret
[URI]
with the value of p-config-server.credentials.uri
Replace the following placeholders with the relevant values:
[VAULT_TOKEN]
with the Vault token string[INTERVAL]
with the number of seconds to set as the Vault token's Time To Live (TTL)After renewing the token, you can view its TTL by looking it up using the Vault command line. Run vault token-lookup [TOKEN]
, replacing [TOKEN]
with the Vault token string:
$ vault token-lookup 72ec7ca0-de41-b2dc-8fe4-d74c4c9a4e75
Key Value
--- -----
accessor 436db91b-6bfb-9eec-7bfb-913260488ce8
creation_time 1493360487
creation_ttl 3600
display_name token
explicit_max_ttl 0
id 72ec7ca0-de41-b2dc-8fe4-d74c4c9a4e75
last_renewal_time 1493360718
meta <nil>
num_uses 0
orphan false
path auth/token/create
policies [root]
renewable true
ttl 997
The Spring Cloud Config Client starter has a dependency on Spring Security. Unless your app has other security configuration, this will cause all app endpoints to be protected by HTTP Basic authentication.
If you do not yet want to address application security, you may turn off Basic authentication using a class that is annotated with the Spring @Configuration
annotation. The sample app deactivates all default security for the development
profile only, using the @Profile
annotation:
@Configuration
@Profile("development")
public class SecurityConfiguration {
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
return http
.authorizeHttpRequests().anyRequest().permitAll()
.and()
.httpBasic().disable()
.csrf().disable()
.build();
}
}
For more information, see Security in the Spring Boot Reference Guide.
If you have exiting application using SCS Client Starters and planning to upgrade to recent versions, check this compatibility matrix to make sure upgrading to the compatible versions of Spring Boot, Spring Cloud and SCS Client Starters.
Starting from version 3.2.1.RELEASE the support for legacy bootstrap processing has been dropped in favour of spring.config.import
. So if you are upgrading from SCS Client Starters 3.2.0.RELEASE or earlier versions:
bootstrap.yaml
or bootstrap.properties
files in to application.yaml
or application.properties
and remove those boostrap files.spring.cloud.bootstrap.enabled=true
flag from your application.yaml
, application.properties
files or environment variables, if there is any.spring-cloud-starter-bootstrap
, if there is any.spring.config.use-legacy-processing=true
flag from your application.yaml
, application.properties
files or environment variables, if there is any.