Important
This chapter covers the following topics:
- Pinpoint Configuration management issues due to API Versioning
- Exposing the two versions on the API Gateway
You can configure your services either during deployment using CI tooling, such as Gitlab Environments, or any other Infra As Code tool (Istio, Ansible,...) or using a configuration server. For this workshop, all the configuration items will be provided by Spring Cloud Config.
We will illustrate in this chapter the impacts of versioning in the configuration management.
Here are the issues to fix in this chapter:
- Specify a different port number for the new rest-book version
- Specify a new version number on all the layers
- Apply different parameters for the number of results of the
/books
API, timeout,...
Warning
Now you MUST stop all your Spring apps.
For this workshop, we will only carry out a simple version management based on Spring profiles.
Copy / paste the rest-book.yml to rest-book-v1.yml and rest-book-v2.yml.
In the latter, UPDATE the properties server.port
and server.servet.context-path
:
server:
port: 8083
servlet:
context-path: /v2
You can also remove the book.find.limit
property in the first version and modify the booknumbers.api.timeout_sec
property in the second one.
Now, start your config server, RUN:
./gradlew clean bootRun -p config-server
Test it using these HTTP requests:
http :8888/rest-book/v1 --print b | jq ' .propertySources[0].source' | jq '."server.servlet.context-path"'
You MUST get this output:
"/v1"
and
http :8888/rest-book/v2 --print b | jq ' .propertySources[0].source."server.servlet.context-path"'
You MUST get this output:
"/v2"
First, UPDATE the application.properties
files to specify the current profile:
In the V1:
spring.profiles.active=v1
And in the V2:
spring.profiles.active=v2
CHECK the rest-book v1 OpenAPI description file to specify the new version:
openapi: 3.0.0
info:
title: OpenAPI definition
version: "v1"
servers:
- url: http://localhost:8082/v1
UPDATE the rest-book v2 OpenAPI description file to specify the new version:
openapi: 3.0.0
info:
title: OpenAPI definition
version: "v2"
servers:
- url: http://localhost:8083/v2
First, stop the config server, and build the whole application:
./gradlew clean build
The build MUST be successful.
Start your backends (we assume your Docker infrastructure is still up).
In the first shell, RUN:
./gradlew bootRun -p config-server
In the second shell, RUN:
./gradlew bootRun -p rest-book-2
In the third shell, RUN:
./gradlew bootRun -p rest-book
In the fourth shell, RUN:
./gradlew bootRun -p rest-number
Now, reach your APIs (without using the gateway):
RUN for the V1:
http :8082/v1/books
and the V2, RUN:
http :8083/v2/books
This service doesn't really need to be versioned now. To put in place the whole infrastructure and the same behaviour of rest-book module, we will apply the same configuration:
- Stop the config server.
- UPDATE the rest-number.yml configuration file renaming it to rest-number-v1.yml
- Restart the config server
./gradlew bootRun -p config-server
- UPDATE the rest-number application.properties file defining the current profile:
spring.profiles.active=v1
- RUN then the rest-number module:
./gradlew bootRun -p rest-number
Now, we will expose both versions in the gateway.
UPDATE the gateway configuration file, adding the following content:
# HTTP HEADER VERSIONING
- id: rewrite_v2
uri: http://127.0.0.1:8083
predicates:
- Path=/books/{segment}
- Header=X-API-VERSION, v2
filters:
- RewritePath=/books/(?<segment>.*),/v2/books/$\{segment}
- id: rewrite_v2
uri: http://127.0.0.1:8083
predicates:
- Path=/books
- Header=X-API-VERSION, v2
filters:
- RewritePath=/books,/v2/books
- id: rewrite_v2
uri: http://127.0.0.1:8081
predicates:
- Path=/isbns
- Header=X-API-VERSION, v2
filters:
- RewritePath=/isbns,/v1/isbns
# HTTP ACCEPT MEDIA TYPE HEADER VERSIONING
- id: rewrite_accept_v2
uri: http://127.0.0.1:8083
predicates:
- Path=/books
- Header=accept, application/vnd.api\.v2\+json
filters:
- RewritePath=/books,/v2/books
- id: rewrite_accept_v2
uri: http://127.0.0.1:8083
predicates:
- Path=/books/{segment}
- Header=accept, application/vnd.api\.v2\+json
filters:
- RewritePath=/books/(?<segment>.*),/v2/books/$\{segment}
- id: rewrite_accept_v2
uri: http://127.0.0.1:8081
predicates:
- Path=/isbns
- Header=accept, application/vnd.api\.v2\+json
filters:
- RewritePath=/isbns,/v1/isbns
# URI PATH VERSIONING
- id: path_route
uri: http://127.0.0.1:8083
predicates:
- Path=/v2/books
- id: path_route
uri: http://127.0.0.1:8083
predicates:
- Path=/v2/books/{segment}
- id: path_route
uri: http://127.0.0.1:8081
predicates:
- Path=/v2/isbns
filters:
- RewritePath=/v2/isbns,/v1/isbns
To propose a cohesive and coherent API to our customer, we chose to publish all our API endpoints with a v1 and v2 prefix.
Although rest-number only provides ONE version (i.e., the v1
), we will expose both on the gateway.
UPDATE the configuration file adding this configuration:
- id: path_route
uri: http://127.0.0.1:8081
predicates:
- Path=/v2/isbns
filters:
- RewritePath=/v2/isbns,/v1/isbns
Restart your gateway and test it:
./gradlew clean bootRun -p gateway
You can now use the scripts files.
For every script (e.g., countBooks.sh
), you have one which reach the V2 endpoints (e.g., countBooks-v2.sh
)
Here is an example for the countBooks.sh
script file copied to countBooks-v2.sh
.
http :8080/v2/books/count
This action is voluntary simple. Feel free to add an argument to the existing script files if you want ;-).
Note
In this chapter, we have seen one part of the impacts of API versioning in configuration management. The most important part is done before, both in the GIT configuration and the release management.