Sample of Spring Cloud Config Server embedded application that uses database as backend for configuration properties.
Background
Spring Cloud Config Server provides several options to store configuration for an application. In general it is handled by Environment Repository.
Available options are git, file system, vault, svn, and database. This application demonstrates usage of JdbcEnvironmentRepository which allows an application to store its configuration in database.
Configuration
In order to enable this feature we will include spring-boot-starter-jdbc
as one of the dependencies for the application and
include jdbc
as one of its active profiles.
Include JDBC as dependency
This can be seen in build.gradle.
implementation 'org.springframework.boot:spring-boot-starter-data-jdbc'
Creating table and populating data
By default the JdbcEnvironmentRepository will look into a table called PROPERTIES
which contains the following columns:
-
KEY
-
VALUE
-
APPLICATION
-
PROFILE
-
LABEL
Create table schema
Schema to create the table can found in init-script.sql:
CREATE TABLE `PROPERTIES`
(
`KEY` VARCHAR(128),
`VALUE` VARCHAR(128),
`APPLICATION` VARCHAR(128),
`PROFILE` VARCHAR(128),
`LABEL` VARCHAR(128),
PRIMARY KEY (`KEY`, `APPLICATION`, `LABEL`)
);
In the script above KEY, APPLICATION, PROFILE, and LABEL are marked as composite key in order to avoid duplicated entry.
Populate table
For this demonstration will we have a configuration called app.greet.name
and this will be populated upon start-up.
Its script can be found in init-script.sql.
INSERT INTO PROPERTIES (`APPLICATION`, `PROFILE`, `LABEL`, `KEY`, `VALUE`)
VALUES ('demo', 'default', 'master', 'app.greet.name', 'Demo');
The script above explains that the configuration app.greet.name
belongs to:
-
an application called demo
-
with profile called default
-
and labelled master
Configure Application Properties
In order for JdbcEnvironmentRepository to retrieve properties for this application it will need to be informed on its name, profile, and label. This configurations can be found in bootstrap.yml
spring:
application:
name: demo
We are not configuring spring.cloud.profile
because its default value is default
.
Create Bootstrap Application Context
Finally, we will need to inform Spring Cloud on what are the classes needed in order to build the bootstrap application context. This can found in spring.factories:
org.springframework.cloud.bootstrap.BootstrapConfiguration=\
org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration,\
org.springframework.boot.autoconfigure.jdbc.JdbcTemplateAutoConfiguration
These two classes will help us to build JdbcTemplate which is needed to construct JdbcEnvironmentRepository.
Verify Configuration Properties
In order to ensure that the application will use configurations from database we will create same configuration in application.yml:
app:
greet:
name: Default
Configure Environment Properties Retrieval SQL
By default, there are two SQL statements that are used to retrieve properties from database. However, these queries need to be modified to follow MySQL requirement and implemented in bootstrap.yml:
spring:
cloud:
config:
server:
jdbc:
sql: SELECT `KEY`, `VALUE` from PROPERTIES where APPLICATION=? and PROFILE=? and LABEL=?
sql-without-profile: SELECT `KEY`, `VALUE` from PROPERTIES where APPLICATION=? and PROFILE='default' and LABEL=?
We will have GreetResource which will retrieve the value of app.greet.name
from GreetProperties.
@RestController
class GreetResource {
private final GreetProperties properties;
GreetResource(GreetProperties properties) {
this.properties = properties;
}
@GetMapping("/greet")
public String greet(@RequestParam String greeting) {
return String.format("%s, my name is %s", greeting, properties.name());
}
}
Next we will have CloudJdbcEnvRepoApplicationTests class that verifies that the value for app.greet.name
is Demo and not Default:
@Testcontainers
@SpringBootTest(properties = "spring.datasource.url=jdbc:tc:mysql:8:///test?TC_INITSCRIPT=init-script.sql", webEnvironment = RANDOM_PORT)
class CloudJdbcEnvRepoApplicationTests {
@Container
private static final MySQLContainer<?> MYSQL = new MySQLContainer<>("mysql:8");
@Autowired
private TestRestTemplate restClient;
@Test
@DisplayName("Given app.greet.name is configured to Demo in the database When I call greet Then I should get Hello, my name is Demo")
void greet() {
var response = restClient.getForEntity("/greet?greeting={0}", String.class, "Hello");
assertThat(response.getBody()).isEqualTo("Hello, my name is Demo");
}
}
By executing greet()
we verify that the returned response is Hello, my name is Demo and not Hello, my name is Default.