This project is part of the 'IBM Cloud Native Reference Architecture' suite, available at https://github.com/ibm-garage-ref-storefront/refarch-cloudnative-storefront
This project will demonstrate how to deploy a Spring Boot Application with a MariaDB database onto a Kubernetes Cluster.
Here is an overview of the project's features:
- Leverages
Spring Boot
framework to build a Microservices application. - Uses
MariaDB
as the orders database. - Uses
Spring Data JPA
to persist data to MariaDB database. - OAuth 2.0 protected APIs using Spring Security framework.
- Uses
Docker
to package application binary and its dependencies. - When retrieving orders using the OAuth 2.0 protected APIs, return only orders belonging to the user identity encoded in the user_name claim in the JWT payload. For more details on how identity is propagated, refer Auth Microservice.
The Orders Microservice REST API is OAuth 2.0 protected. These APIs identifies and validates the caller using signed JWT tokens.
-
GET /micro/orders
Returns all orders. The caller of this API must pass a valid OAuth token. The OAuth token is a JWT with the orders ID of the caller encoded in the
user_name
claim. A JSON object array is returned consisting of only orders created by the orders ID. -
GET /micro/orders/{id}
Return order by ID. The caller of this API must pass a valid OAuth token. The OAuth token is a JWT with the orders ID of the caller encoded in the
user_name
claim. If the id of the order is owned by the orders passed in the header, it is returned as a JSON object in the response; otherwiseHTTP 401
is returned. -
POST /micro/orders
Create an order. The caller of this API must pass a valid OAuth token. The OAuth token is a JWT with the orders ID of the caller encoded in the
user_name
claim. The Order object must be passed as JSON object in the request body with the following format.{ "itemId": "item_id", "count": "number_of_items_in_order", }
On success,
HTTP 201
is returned with the ID of the created order in theLocation
response header.
-
- Installing on MacOS
- Installing on Windows
- Installing on RHEL
- Installing on Ubuntu For more details on installation, check this out.
-
Docker Desktop
We created a new spring boot project using appsody as follows.
appsody repo add kabanero https://github.com/kabanero-io/kabanero-stack-hub/releases/download/0.6.5/kabanero-stack-hub-index.yaml
appsody init kabanero/java-spring-boot2
And then we defined the necessary code for the application on top on this template.
- Clone orders repository:
git clone https://github.com/ibm-garage-ref-storefront/orders-ms-spring.git
cd orders-ms-spring
Run the below command to get MariaDB running via a Docker container.
# Start a MariaDB Container with a database user, a password, and create a new database
docker run --name ordersmysql \
-e MYSQL_ROOT_PASSWORD=admin123 \
-e MYSQL_USER=dbuser \
-e MYSQL_PASSWORD=password \
-e MYSQL_DATABASE=ordersdb \
-p 3306:3306 \
-d mariadb
If it is successfully deployed, you will see something like below.
$ docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
ae5ed47cb0be mariadb "docker-entrypoint.s…" 45 minutes ago Up 45 minutes 0.0.0.0:3306->3306/tcp ordersmysql
- Before running the application, make sure you grab the
HS256
shared secret.
To make things easier for you, we pasted below the 2048-bit secret here.
E6526VJkKYhyTFRFMC0pTECpHcZ7TGcq8pKsVVgz9KtESVpheEO284qKzfzg8HpWNBPeHOxNGlyudUHi6i8tFQJXC8PiI48RUpMh23vPDLGD35pCM0417gf58z5xlmRNii56fwRCmIhhV7hDsm3KO2jRv4EBVz7HrYbzFeqI45CaStkMYNipzSm2duuer7zRdMjEKIdqsby0JfpQpykHmC5L6hxkX0BT7XWqztTr6xHCwqst26O0g8r7bXSYjp4a
As the APIs in this microservice are OAuth protected, the HS256 shared secret used to sign the JWT generated by the Authorization Server is needed to validate the access token provided by the caller.
However, if you must create your own 2048-bit secret, one can be generated using the following command:
cat /dev/urandom | env LC_CTYPE=C tr -dc 'a-zA-Z0-9' | fold -w 256 | head -n 1 | xargs echo -n
Note that if the Authorization Server is also deployed, it must use the same HS256 shared secret.
- To run the test cases for the orders application, run the below command.
appsody test --docker-options "-e MYSQL_HOST=host.docker.internal -e MYSQL_PORT=3306 -e MYSQL_DATABASE=ordersdb -e MYSQL_USER=dbuser -e MYSQL_PASSWORD=password -e HS256_KEY=<Paste HS256 key here>"
- To run the orders application, run the below command.
appsody run --docker-options "-e MYSQL_HOST=host.docker.internal -e MYSQL_PORT=3306 -e MYSQL_DATABASE=ordersdb -e MYSQL_USER=dbuser -e MYSQL_PASSWORD=password -e HS256_KEY="
- If it is successfully running, you will see something like below.
[Container] 2020-05-07 08:57:32.703 INFO 176 --- [ restartedMain] o.s.s.concurrent.ThreadPoolTaskExecutor : Initializing ExecutorService 'applicationTaskExecutor' [Container] 2020-05-07 08:57:32.787 WARN 176 --- [ restartedMain] aWebConfiguration$JpaWebMvcConfiguration : spring.jpa.open-in-view is enabled by default. Therefore, database queries may be performed during view rendering. Explicitly configure spring.jpa.open-in-view to disable this warning [Container] 2020-05-07 08:57:32.835 INFO 176 --- [ restartedMain] o.s.b.a.w.s.WelcomePageHandlerMapping : Adding welcome page: class path resource [public/index.html] [Container] 2020-05-07 08:57:33.385 INFO 176 --- [ restartedMain] o.s.b.a.e.web.EndpointLinksResolver : Exposing 4 endpoint(s) beneath base path '/actuator' [Container] 2020-05-07 08:57:33.504 INFO 176 --- [ restartedMain] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat started on port(s): 8080 (http) with context path '/micro' [Container] 2020-05-07 08:57:33.507 INFO 176 --- [ restartedMain] application.Main : Started Main in 11.91 seconds (JVM running for 14.039)
- You can also verify it as follows.
$ docker ps CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 98af4c22269f kabanero/java-spring-boot2:0.3 "/.appsody/appsody-c…" About a minute ago Up About a minute 0.0.0.0:5005->5005/tcp, 0.0.0.0:8080->8080/tcp, 0.0.0.0:8443->8443/tcp, 0.0.0.0:35729->35729/tcp orders-ms-spring ae5ed47cb0be mariadb "docker-entrypoint.s…" 3 hours ago Up 3 hours 0.0.0.0:3306->3306/tcp ordersmysql
### Validating the application
#### Set Up
- To validate the application, we need `HS256` shared secret to generate JWT token.
To make things easier for you, we pasted below the 2048-bit secret that's included in the app, which you can export to your environment as follows:
export HS256_KEY="E6526VJkKYhyTFRFMC0pTECpHcZ7TGcq8pKsVVgz9KtESVpheEO284qKzfzg8HpWNBPeHOxNGlyudUHi6i8tFQJXC8PiI48RUpMh23vPDLGD35pCM0417gf58z5xlmRNii56fwRCmIhhV7hDsm3KO2jRv4EBVz7HrYbzFeqI45CaStkMYNipzSm2duuer7zRdMjEKIdqsby0JfpQpykHmC5L6hxkX0BT7XWqztTr6xHCwqst26O0g8r7bXSYjp4a"
As the APIs in this microservice are OAuth protected, the HS256 shared secret used to sign the JWT generated by the [Authorization Server](https://github.com/ibm-garage-ref-storefront/auth-ms-spring) is needed to validate the access token provided by the caller.
However, if you must create your own 2048-bit secret, one can be generated using the following command:
cat /dev/urandom | env LC_CTYPE=C tr -dc 'a-zA-Z0-9' | fold -w 256 | head -n 1 | xargs echo -n
Note that if the [Authorization Server](https://github.com/ibm-garage-ref-storefront/auth-ms-spring) is also deployed, it must use the same HS256 shared secret.
- Now generate a JWT Token with a `blue` scope, which will let you create/get/delete orders.
To do so, run the commands below:
jwt1=$(echo -n '{"alg":"HS256","typ":"JWT"}' | openssl enc -base64);
jwt2=$(echo -n "{"scope":["blue"],"user_name":"admin"}" | openssl enc -base64);
jwt3=$(echo -n "${jwt1}.${jwt2}" | tr '+/' '-_' | tr -d '=' | tr -d '\r\n');
jwt4=$(echo -n "${jwt3}" | openssl dgst -binary -sha256 -hmac "${HS256_KEY}" | openssl enc -base64 | tr '+/' '-_' | tr -d '=' | tr -d '\r\n');
jwt=$(echo -n "${jwt3}.${jwt4}");
Where:
- `blue` is the scope needed to create the order.
- `${HS256_KEY}` is the 2048-bit secret from the previous step.
#### Validation
Now, you can validate the application as follows.
* Create an Order
Run the following to create an order for the `admin` user. Be sure to use the JWT retrieved from the previous step in place of `${jwt}`.
curl -i -H "Content-Type: application/json" -H "Authorization: Bearer ${jwt}" -X POST -d '{"itemId":13401, "count":1}' "http://<orders_host>:<orders_port>/micro/orders"
If successfully created, you will see something like below.
$ curl -i -H "Content-Type: application/json" -H "Authorization: Bearer ${jwt}" -X POST -d '{"itemId":13401, "count":1}' "http://localhost:8080/micro/orders" HTTP/1.1 201 Location: http://localhost:8080/micro/orders/2c91808371ee5aa50171ee653f440000 X-Content-Type-Options: nosniff X-XSS-Protection: 1; mode=block Cache-Control: no-cache, no-store, max-age=0, must-revalidate Pragma: no-cache Expires: 0 X-Frame-Options: DENY Content-Length: 0 Date: Thu, 07 May 2020 09:09:05 GMT
* Get all Orders
Run the following to retrieve all orders for the `admin` customerId. Be sure to use the JWT retrieved from the previous step in place of `${jwt}`.
curl -H "Authorization: Bearer ${jwt}" "http://<orders_host>:<orders_port>/micro/orders"
If it is running successfully, you will see something like below.
$ curl -H "Authorization: Bearer ${jwt}" "http://localhost:8080/micro/orders" [{"id":"2c91808371ee5aa50171ee653f440000","date":"2020-05-07T09:09:04.000+0000","itemId":13401,"customerId":"admin","count":1}]
- Also you can access the swagger ui at http://localhost:8080/micro/swagger-ui.html
![Orders Swagger UI](static/swagger_orders.png?raw=true)
### Exiting the application
To exit the application, just press `Ctrl+C`.
It shows you something like below.
[Container] [INFO] ------------------------------------------------------------------------ [Container] [INFO] BUILD SUCCESS [Container] [INFO] ------------------------------------------------------------------------ [Container] [INFO] Total time: 20:35 min [Container] [INFO] Finished at: 2020-05-07T09:17:39Z [Container] [INFO] ------------------------------------------------------------------------ Closing down development environment.
## Conclusion
You have successfully deployed and tested the Orders Microservice and a MariaDB database in local Docker Containers using Appsody.
To see the Orders application working in a more complex microservices use case, checkout our Microservice Reference Architecture Application [here](https://github.com/ibm-garage-ref-storefront/refarch-cloudnative-storefront).