Kalau terkena Connection refused saat menjalankan Apps Spring boot, ini karena si Docker itu seolah-olah jalan di computer lain atau Virtual Machine jadi portnya itu tidak kelihatan dari si laptop. Maka dari itu kita harus mengluarkan port tersebut atau mengexposenya. https://software.endy.muhardin.com/linux/intro-docker
atau dengan command line ini, tapi hapus dulu dependency spring-boot-composer yang ada di pom.xml
docker run --rm --name restful-api-contact-management -e POSTGRES_DB=contact_management_db -e POSTGRES_USER=dani -e POSTGRES_PASSWORD=dani -e PGDATA=/var/lib/postgresql/data/pgdata -v "$PWD/restful-api-contact-management-data:/var/lib/postgresql/data" -p 5432:5432 postgres:15
# Masuk ke container vault
docker exec -it contact-management-vault-1 sh
export VAULT_ADDR='http://127.0.0.1:8288'
export VAULT_TOKEN='root-token-for-dev-purpose-only'
Buka terminal baru
cd tf-provisioner
terraform init
terraform apply
Balik lagi ke terminal container vault
vault kv get secret/aplikasi/contact-management
# jalankan ini ambil datanya untuk nanti dimasukan ke application.properties
vault read auth/approle/role/jawasundapadangbetawi/role-id
vault write -force auth/approle/role/jawasundapadangbetawi/secret-id
# masuk ke application.properties lalu isi role-id dan secret-id
# spring.cloud.vault.app-role.role-id=b830f1e1-63fc-0ce5-46e1-21f79c048513
# spring.cloud.vault.app-role.secret-id=48e63ee2-f596-6e2f-cebe-466251dae922
Nanti ini diambil dari Vault yang ada di secret saat aplikasi kita jalan
- spring.datasource.url=jdbc:postgresql://localhost:5432/contact_management_db
- spring.datasource.username=dani
- spring.datasource.password=dani
SPRING_CLOUD_VAULT_APPROLE_ROLEID=e26d0199-34d2-ce03-fc4d-a72f0cf147c3 \
SPRING_CLOUD_VAULT_APPROLE_SECRETID=2c11b21b-eaaf-c154-72a2-27e3a5cfc591 \
mvn clean install spring-boot:run
generate tokennya dulu di vault container
vault write -wrap-ttl=1m -force auth/approle/role/jawasundapadangbetawi/secret-id
SPRING_CLOUD_VAULT_APPROLE_ROLEID=e26d0199-34d2-ce03-fc4d-a72f0cf147c3 \
SPRING_CLOUD_VAULT_TOKEN=hvs.CAESIDzsjEIkZCneHuDF8I1x0_1d9em1v2OOWJMbPIUgODNCGh4KHGh2cy5uRGk2OUVyRDR6TnBrSm5RaFhsVjhPUlU \
mvn clean install spring-boot:run
masuk ke container DB nya
docker exec -it contact-management-db-contact-management-1 sh
psql -U dani -d contact_management_db
select * from flyway_schema_history;
Atau kalau sudah install client PSQL bisa pakai ini:
psql -h 127.0.0.1 -U dani contact_management_db
\x ## Expanded display is on. like \G on MySQL
select * from flyway_schema_history;
start kubernet dengan minikube setelah itu tunggu sampe servicenya ready, lalu masuk ke folder k8s setelah itu jalankan ini
# start minikube
minikube start
# export ENV
export VAULT_ADDR='http://[::]:8200'
export VAULT_TOKEN='root'
# init terra dan jalankan teraform ke vult
cd tf-provisioner
terraform init
terraform apply
# create service account
kubectl create serviceaccount sa-contact-management
kubectl apply -f k8s/00-configmap.yml
kubectl apply -f k8s/00-secret-vault-store.yml
kubectl apply -f k8s/01-database.yml
# check datanya diikuti dengan nama file setelah strip - jadi "configmap" dan disesuaikan dengan nama filenya
kubectl get configmap
# check database deployment service
kubectl get deployments
mvn spring-boot:build-image
# start minikube
minikube start
# export ENV
export VAULT_ADDR='http://[::]:8200'
export VAULT_TOKEN='root'
# create service account
kubectl create serviceaccount sa-contact-management
helm install vault hashicorp/vault \
--set "server.dev.enabled=true" \
--set "injector.enabled=false" \
--set "csi.enabled=true" \
--set='ui.enabled=true'
kubectl port-forward service/vault-ui 8200:8200
# http://localhost:8200
kubectl exec -it vault-0 -- /bin/sh
export VAULT_ADDR='http://[::]:8200'
export VAULT_TOKEN='root'
vault auth enable kubernetes
vault write auth/kubernetes/config \
kubernetes_host="https://$KUBERNETES_PORT_443_TCP_ADDR:443"
vault write auth/kubernetes/role/database \
bound_service_account_names=sa-contact-management \
bound_service_account_namespaces=default \
policies=applikasi-contact-management-readonly \
ttl=20m
exit
helm repo add secrets-store-csi-driver https://kubernetes-sigs.github.io/secrets-store-csi-driver/charts
helm install csi secrets-store-csi-driver/secrets-store-csi-driver \
--set syncSecret.enabled=true
## Verify
kubectl get deployment,pod,svc
## pastikan statusnya running dan ready 1/1
kubectl describe SecretProviderClass vault-database
## Geser ke ENV minikube image registry
eval $(minikube docker-env)
## Lanjut build spring boot jadi docker image, kalau udah ada jangan lupa yang lama dihapus dulu
mvn spring-boot:build-image -Dmaven.test.skip
# init terra dan jalankan teraform ke vult
cd tf-provisioner
terraform init
terraform apply
# create service account
kubectl create serviceaccount sa-contact-management
## Deploy
kubectl apply -f k8s/00-configmap.yml
kubectl apply -f k8s/00-secret-vault-store.yml
kubectl apply -f k8s/01-database.yml
kubectl apply -f k8s/02-aplikasi.yml
docker run --rm -it -p 8080:8080 -v $PWD:/data/project/ jetbrains/qodana-jvm --show-report
Endpoint : POST /api/v1/users
Request Body :
{
"username": "yeahbutstill",
"password": "rahasia",
"name": "Dani"
}
Response Body (Success) :
{
"data": "OK"
}
Response Body (Failed) :
{
"errors": "Username must not blank, ???"
}
Endpoint : POST /api/v1/auth/login
Request Body :
{
"username": "yeahbutstill",
"password": "rahasia"
}
Response Body (Success) :
{
"data": {
"token": "TOKEN",
"expiredAt": 2342342423423
// milliseconds
}
}
Endpoint : DELETE /api/v1/auth/logout
Request Header :
- X-API-TOKEN : Token (Mandatory)
Response Body (Success) :
{
"data": "OK"
}
Endpoint : GET /api/v1/users/current
Request Header :
- X-API-TOKEN : Token (Mandatory)
Response Body (Success) :
{
"data": {
"username": "yeahbutstill",
"name": "Dani"
}
}
Response Body (Failed, 401) :
{
"errors": "Unauthorized"
}
Endpoint : PATCH /api/v1/users/current
Request Header :
- X-API-TOKEN : Token (Mandatory)
Request Body :
{
"name": "Dani",
// put if only want to update name
"password": "newpassword"
// put if only want to update password
}
Response Body (Success) :
{
"data": {
"username": "yeahbutstill",
"name": "Dani"
}
}
Response Body (Failed, 401) :
{
"errors": "Unauthorized"
}
Endpoint : DELETE /api/v1/auth/logout
Request Header :
- X-API-TOKEN : Token (Mandatory)
Response Body (Success) :
{
"data": "OK"
}
Endpoint : POST /api/v1/contacts
Request Header :
- X-API-TOKEN : Token (Mandatory)
Request Body :
{
"firstName": "Dani",
"lastName": "yeahbutstill",
"email": "[email protected]",
"phone": "0899889998"
}
Response Body (Success) :
{
"data": {
"id": "random-string",
"firstName": "Dani",
"lastName": "Setiawan",
"email": "[email protected]",
"phone": "0899889998"
}
}
Response Body (Failed) :
{
"errors": "Email format invalid, phone formar invalid, ..."
}
Endpoint : PUT /api/v1/contacts/{idContact}
Request Header :
- X-API-TOKEN : Token (Mandatory)
Request Body :
{
"firstName": "Dani",
"lastName": "Setiawan",
"email": "[email protected]",
"phone": "0899889998"
}
Response Body (Success) :
{
"data": {
"id": "random-string",
"firstName": "Dani",
"lastName": "Setiawan",
"email": "[email protected]",
"phone": "0899889998"
}
}
Response Body (Failed) :
{
"errors": "Email format invalid, phone formar invalid, ..."
}
Endpoint : GET /api/v1/contacts/{idContact}
Request Header :
- X-API-TOKEN : Token (Mandatory)
Response Body (Success) :
{
"data": {
"id": "random-string",
"firstName": "Dani",
"lastName": "Setiawan",
"email": "[email protected]",
"phone": "0899889998"
}
}
Response Body (Failed, 404) :
{
"errors": "Contact is not found"
}
Endpoint : GET /api/v1/contacts
Query Param :
- name : String, contact first name or last name, using like query, optional
- phone : String, contact phone, using like query, optional
- email : String, contact email, using like query, optional
- page : Integer, start from 0, default 0
- size : Integer, default 10
Request Header :
- X-API-TOKEN : Token (Mandatory)
Response Body (Success) :
{
"data": [
{
"id": "random-string",
"firstName": "Dani",
"lastName": "Setiawan",
"email": "[email protected]",
"phone": "0899889998"
}
],
"paging": {
"currentPage": 0,
"totalPage": 10,
"size": 10
}
}
Response Body (Failed) :
{
"errors": "Unauthorized"
}
Endpoint : DELETE /api/v1/contacts/{idContact}
Request Header :
- X-API-TOKEN : Token (Mandatory)
Response Body (Success) :
{
"data": "OK"
}
Response Body (Failed) :
{
"errors": "Contact is not found"
}
Endpoint : POST /api/v1/contacts/{idContact}/addresses
Request Header :
- X-API-TOKEN : Token (Mandatory)
Request Body :
{
"street": "Jalan apa",
"city": "Kota",
"province": "provinsi",
"country": "Negara",
"postalCode": "12313"
}
Response Body (Success) :
{
"data": {
"id": "randomstring",
"street": "Jalan apa",
"city": "Kota",
"province": "provinsi",
"country": "Negara",
"postalCode": "12313"
}
}
Response Body (Failed) :
{
"errors": "Contact is not found"
}
Endpoint : PUT /api/v1/contacts/{idContact}/addresses/{idAddress}
Request Header :
- X-API-TOKEN : Token (Mandatory)
Request Body :
{
"street": "Jalan apa",
"city": "Kota",
"province": "provinsi",
"country": "Negara",
"postalCode": "12313"
}
Response Body (Success) :
{
"data": {
"id": "randomstring",
"street": "Jalan apa",
"city": "Kota",
"province": "provinsi",
"country": "Negara",
"postalCode": "12313"
}
}
Response Body (Failed) :
{
"errors": "Address is not found"
}
Endpoint : GET /api/v1/contacts/{idContact}/addresses/{idAddress}
Request Header :
- X-API-TOKEN : Token (Mandatory)
Response Body (Success) :
{
"data": {
"id": "randomstring",
"street": "Jalan apa",
"city": "Kota",
"province": "provinsi",
"country": "Negara",
"postalCode": "12313"
}
}
Response Body (Failed) :
{
"errors": "Address is not found"
}
Endpoint : DELETE /api/v1/contacts/{idContact}/addresses/{idAddress}
Request Header :
- X-API-TOKEN : Token (Mandatory)
Response Body (Success) :
{
"data": "OK"
}
Response Body (Failed) :
{
"errors": "Address is not found"
}
Endpoint : GET /api/v1/contacts/{idContact}/addresses
Request Header :
- X-API-TOKEN : Token (Mandatory)
Response Body (Success) :
{
"data": [
{
"id": "randomstring",
"street": "Jalan apa",
"city": "Kota",
"province": "provinsi",
"country": "Negara",
"postalCode": "12313"
}
]
}
Response Body (Failed) :
{
"errors": "Contact is not found"
}