Spring Cloud remote config tutorial

Introduction

In the big applications where there are multiple moving parts ( microservices ) there is a problem of configuration management. Microservices must be disposable applications that can be started and killed frequently, so would it just be awesome to hold configuration at one place and allow microservices to obtain the configuration from this single location.

We can easily manage all configurations on one place and all that is required for the microservices is to know what is the profile it is running in, for example PRODUCTION, STAGE …

In this post I will cover the Spring Cloud Remote Config using three config sources

  1. Native, where configuration is stored on the hard drive
  2. Git, where configuration is stored in git repository
  3. Vault, this is my favorite all secrets are stored in vault
Setting the project

To get things started first we need to create two applications:

  • Config Server
  • Client

Config server serves the configuration to the clients and has REST API that is used by the client apps that want to obtain configurations.

To get things started here is the pom.xml I used to create config server for brevity I removed all unimportant parts of it.

Because this is Spring Boot application we need Application class that is annotated with @EnableConfigServer and @SpringBootApplication.

@EnableConfigServer is very powerful annotation as it is all that it takes to have config server running.

If you run ConfigServerApplication you will not get far because you will get error stating:

You need to configure a uri for the git repository

This is because config server default profile is git and we did not specify the config server profiles and configuration yet. So lets create one application.properties file and add next lines of code that will specify port, config name and profiles. For now we will have only native profile and latter we will add git and vault here as well.

Spring Cloud Config native profile

What is native profile?

Native profile allows that configuration files are located on the hard drive of the server. Not very good for production but is awesome as a starting point.

We are going to have one application that feeds the configuration from config server and it is going to have most awesome name ever configclient. It is important to memorize app name as it will be important in the next paragraph.

We can split configuration into two separate parts for each application:

  • Default configuration
  • Configuration for specific profile

Default configuration will go in the file configclient.properties  or {APPLICATION_NAME}.yml and settings for specific profile ( in our case production) will to go into configclient-production.yml or configclient-{PROFILE}.yml. All the files can be located in location specified by configuration  ( you may investigate the class NativeEnvironmentRepository to see all the default locations):

configclient.yml:

configclient-production.yml

As you can see here configuration for production and for default overlap in rs.pscode.value .

Now if you start the config server application and hit the URL  localhost:8888/configclient/default response is

and if you hit the  localhost:8888/configclient/production

response will be :

Awesome, this was easy. Lets move on to git profile.

Spring Cloud Config git profile

In previous section we enabled native profile and now we will enable git profile, to do this we need to add git to the profile list :

Remember you do not need to leave native if you do not want to.

Next we need to specify the git repository url with username and password if required like so.

You will notice that search-paths is used , this is because configuration is not located in the root of the project it is located in config folder.

There are numerous options for git, like multiple repositories and so on …  you can check this out in the reference and looking at the implementation of EnvironmentRepository for git  MultipleJGitEnvironmentRepository .

You may wonder because there are two profiles native and git who will win ? Well last specified profile will override previously specified.

Spring Cloud Config Vault

What about those configurations that are supposed to be secrets, you know things that only few know? We can not put those in the git repository or store them on the hard drive of the server, well no.

For this problem spring vault comes to rescue, make sure to check the vaults website https://www.vaultproject.io/ and their get started pages.

This part of the post is going to require some docker knowledge, but nothing big. First we are going to create two containers vault and vault-ui , then we will add two secrets in vault and configure our config application to point to vault server.

Step 1: Create vault container.

I did specify the root token here, do not do that in production.

Step 2: Enter the vault container

Step 3: Authenticate to vault and configure the vault client properly

If you see the logs of the started container you will see something like:

So as it says we need to enter the container using

then execute ( observe myroot is used as token specified when container is started)

Step 3: Save some secrets

You should have two response Success! messages like this

so the secret names are composed of secret/{ApplicationName}-{profile}.

Step 4: Configure the config server to talk to Vault

In the application.properties add and append( or just set) profiles with vault

Step 5: Get URL  localhost:8888/configclient

And it does not work  it shows the error :

What the hell is this you may ask , well the client must provide with especial header

X-Config-Token with value myroot .

 

Vault-ui is a really nice application that allows you to manage vault using great UI. Find the page of vault-ui on docker hub and start it ( https://hub.docker.com/r/djenriquez/vault-ui/)

Then open the page and enter the url of the vault server. Here you may have multiple docker configurations, so if you have docker for mac you can :

1: Obtain the ip of the host computer by entering the vault

and executing

Then in the vault-ui enter the IP address printed by the upper command like so:

and enter the token myroot to see this

As you see in the image are the secrets we entered using console.

Spring Cloud Config Client Part

Now when we know how to setup config server we need to use the values using the client application. Here is the ConfigClientApplication main class. It is really simple except it has @RefreshScope annotation.

Configuration is loaded on application startup and if we update any of the configurations in our config server client application will now know. This is where @RefreshScope comes handy, it marks the beans that are refreshed once /refresh REST API is called. Awesome right?

Here is the important part of the pom.xml for the client app

If you read the spring cloud reference you will notice that there are two contexts application and bootstrap context as a parent of the previous one. This means that configuration related to config server has to be put in bootstrap.properties for the bootstrap phase …

  • spring.application.name is the name of the application that maps to config file names
  • spring.cloud.config.uri is the url to the config server
  • spring.cloud.config.token is the vault token, remember the Missing required header: X-Config-Token
  • management.security.enabled , I skipped security
  • spring.profiles.include, current profile configuration

Now for the closing words lets call the localhost:8080/test . And here it is one value from vault, one from git.

 

Please do find the code on my git hub repository here:

https://github.com/savicprvoslav/Spring-Cloud-Config

 

 

 

 

JPA – Associations @OneToOne , @OneToMany, @ManyToOne, @ManyToMany part 2

In previous post I wrote about @OneOnOne association, now I will go trough oneToMany and manyToOne associations with examples.

This associations are used to present that list of entities are part of other “bigger” entity, for example company can have several departments. Department is the part of the company and company is consisted of several departments.

Import is to note that if company is deleted then department is destroyed as well. Why is this important? Because if you setup cascade in the wrong way you might delete important information from your database.

Entities

 

There is detailed explanation in previous post about annotations @Entity, @Table, @Id, @Column, @GeneratedValue so I will focus only on @OneToMany and @ManyToOne.

There are two sides of the coin same as there are two sides of association, there is owning side and inverse side, one side has joinColumn and other has mappedBy property filled. Owning side related table has foreign key to the inverse related table.

FetchType

When ever one has an association there is a question of FetchType value. Two options there are to be picked from EAGER or LAZY. Difference is only that if EAGER option is selected data is loaded right away and if LAZY option is selected data is loaded after the request. This difference can be used into your advantage depending of how you use the data. It is important to note that eager option takes more time to execute, since it is bigger sql query right?

Lets see traverse problem:

If we have LAZY option selected every time getDepartments method is called database will be pinged for new data, this can be very slow in case of many companies.

If we have EAGER option, what if we have company with 10000 departments, do we really need to load all of them every time? Or we can get company first and then filter through the departments …

EAGER vs LAZY depends of the problem that is supposed to be solved, so think first and code after is the mantra here.

 

ForeignKey

@JoingColumn has many properties, few that are important are nullable and foreignKey.

Nullable property set to false tells JPA that Department must have the Company attached. This will translate into notNull column in the related sql table.
ForeignKey property specifies SQL foreign key details in this case its name, if this property is not filled JPA will find suitable name for your foreign key.

 

Spring Repository

There are two entities and two Spring repositories with CRUD methods. Additionally I have added findAll that returns the java.util.List.

Unit Tests

To demonstrate that all works as expected 3 tests are created

  1. Test Create Company with Departments in one save action.
  2. Test create Company and connect it with new Department, here i have two save actions one to create company and other to create department.
  3. Test Cascade deletion of Department, company is deleted and with it departmetns are deleted as well automatically.

Two lines here are most important :

Here both sides of the association are maintained and without it of course is will not work.

 

In above test I have created company with out any departments then new department is created and added to the company. Rest of the code just validates.

 

Cascade delete works as it is expected, when company is deleted all departments go with it.

In the next post I will talk about association ManyToMany using example Teacher Student. Here teacher can tech more then one student and student can be thought by more then one teacher.