/gradle-example

Java project starter with CI/CD, Kubernetes, ArgoCD, Helm, ArtifactHub, Docker, ChatOps, Sonarqube

Primary LanguageJava

Gradle Example

gradle-version: 7.0.2
java-version: 16

Quality gate

Build Docker Repository Artifact Hub

  • com.example.myproduct.domain:avro-events
    Quality Gate Status Bugs Code Smells
  • com.example.myproduct.domain:events
    Quality Gate Status Bugs Code Smells
  • com.example.myproduct.domain:entities
    Quality Gate Status Bugs Code Smells
  • com.example.myproduct.features:cat
    Quality Gate Status Bugs Code Smells
  • com.example.myproduct.features:cat-api
    Quality Gate Status Bugs Code Smells
  • com.example.myproduct.features:entity
    Quality Gate Status Bugs Code Smells
  • com.example.myproduct.features:entity-api
    Quality Gate Status Bugs Code Smells
  • com.example.myproduct.services:app
    Quality Gate Status Bugs Code Smells

You can open this sample inside an IDE using the IntelliJ's Gradle import or Eclipse Buildship.
You can add Google CheckStyle config for your IDE.

This sample shows how to structure a software product that consists of multiple components as a set of connected Gradle builds. As such, it shows how Gradle is used to model a project's architecture and reflect that in the physical structure of the files that make up the software. This example is described as part of the documentation on this topic.

The product that is built in this sample is an application that displays link: Gradle Build Tool releases.

There are different ways to work with the sample:

  • You may build or import the umbrella build in the root. There you can, for example, run the Spring Boot web application via ./gradlew :services:app:bootRun or build events domain using ./gradlew :domain:events:build.
  • You may only build or import one of the application builds directly. For example, cd services and run the app using ../gradlew :app:bootRun.
  • You may only build or import a selected component (and its dependencies). For example, only import the features/cat in the IDE.
  1. Create an Organization
    • Organization must match GitHub username
    • Add SONAR_TOKEN to GitHub security
  2. Setup a Quality Profile:
    • Go to Administration > New Code > Number of days
    • Set 30 days
  3. Create Projects:
    • Go to Administration > Projects Management
    • Create Project[name=app,key=com.example.myproduct.services:app]

Setup Quay.io Docker registry

  1. Create repository for app: gradle-example-app
  2. Create User Robot Account with repository write access
  3. Add Robot Account DOCKER_USER, DOCKER_TOKEN to GitHub Security

Create Docker

gradlew :services:app:build
docker build -f services/app/src/docker/Dockerfile -t gradle-example-app services/app

Build in Docker

docker build -f services/app/src/docker/Dockerfile.build -t gradle-example-app .
docker run -p80:8080 -t -i gradle-example-app # Use CTRL+C to close
curl -w "\n" http://localhost/cat
curl -w "\n" http://localhost/entity
hey -c 12 -n 200 -z 30s http://localhost/cat # Optional load testing

Build in Quay.io Docker registry

For Java 16 build specify:

  1. Dockerfile location: /services/app/src/docker/Dockerfile.build
  2. Context location: /
  3. Branches/tags: ALL
  4. Pull robot: (use your robot account here)
  5. Tagging options:
    • branch/tag name
    • latest if default branch
    • java-16-${parsed_ref.branch}-${commit_info.short_sha}
    • java-16-${parsed_ref.branch}
    • java-16-${commit_info.short_sha}

Setup CI notifications with Telegram Bot and CloudFlare Workers

Telegram Bot for CI notifications.

  1. Telegram Bot
    • Create Telegram Bot from @BotFather bot
    • Generate TELEGRAM_BOT_TOKEN for your bot
    • Get your TELEGRAM_USER_ID from @UserIdInfoBot
  2. CloudFlare Workers
    • Create Worker with code provided in index.js
    • Add secrets: TELEGRAM_USER_ID, TELEGRAM_BOT_TOKEN

For additional information refer to telegram-bot README.md

Build and Run Project in Docker Compose

docker-compose -f envs/local-env/docker-compose.yml up --build

Run Project in microk8s

  1. Install microk8s
  2. Deploy app
microk8s status --wait-ready 1️⃣
microk8s config > ~/.kube/config
kubectl get all --all-namespaces

microk8s enable dashboard 2️⃣
microk8s dashboard-proxy

kubectl create namespace dev 3️⃣
kubectl apply -f envs/kubernetes-env -n dev

kubectl get deployments app 4️⃣
kubectl describe deployments app
kubectl get services app
kubectl describe services app

kubectl get replicasets 5️⃣
kubectl describe replicasets <app-XXXXXXXX>
kubectl describe pod <app-XXXXXXXX-XXXXX>
kubectl logs <app-XXXXXXXX-XXXXX>

1️⃣ Setup microk8s and configure kubectl
2️⃣ Enable dashboard proxy
3️⃣ Create all services in dev. To create a specific service use:

kubectl apply -f services/app/src/kubernetes -n dev

4️⃣ Describe deployment and service
5️⃣ Describe pod

  1. Get app response:
export NODE_PORT=$(kubectl get --namespace dev -o jsonpath="{.spec.ports[0].nodePort}" services app)
export NODE_IP=$(kubectl get nodes --namespace dev -o jsonpath="{.items[0].status.addresses[0].address}")
echo http://$NODE_IP:$NODE_PORT/cat
echo http://$NODE_IP:$NODE_PORT/entity
  1. Get app response inside kubernetes:
kubectl run curl-app --image=radial/busyboxplus:curl -i --tty --rm -n dev

Run project with HELM

  1. Install HELM
  2. Deploy app
git checkout --orphan gh-pages 1️⃣
cat << EOF > index.html
<html>
<head>
  <title>gradle-example charts repository</title>
</head>
<body>
  <h1>gradle-example charts repository</h1>
  <p>Add this repository</p>
  <pre>helm repo add gradle-example https://srcmaxim.github.io/gradle-example</pre>
</body>
</html>
EOF
cat << EOF > README.md
# gradle-example charts repository
Add this repository
`helm repo add gradle-example https://srcmaxim.github.io/gradle-example`
EOF
helm package envs/helm-env/gradle-example
helm repo index . --url https://srcmaxim.github.io/gradle-example/
git add gradle-example-*.tgz index.yaml index.html README.md
git commit -m "Add HELM artifact gradle-example:1.0.0"

helm repo add gradle-example https://srcmaxim.github.io/gradle-example/ 2️⃣
repo list
helm repo update

kubectl create namespace dev 3️⃣
helm install gradle-example gradle-example/gradle-example -n dev

1️⃣ Create HELM artifact gradle-example in GitHub pages from gh-pages branch
2️⃣ Add HELM repository
3️⃣ Apply HELM to kubernetes

  1. Add HELM repository to ArtifactHUB

  2. Get app response:

export NODE_PORT=$(kubectl get --namespace dev -o jsonpath="{.spec.ports[0].nodePort}" services gradle-example)
export NODE_IP=$(kubectl get nodes --namespace dev -o jsonpath="{.items[0].status.addresses[0].address}")
curl -w "\n" http://$NODE_IP:$NODE_PORT/cat
curl -w "\n" http://$NODE_IP:$NODE_PORT/entity

Deploy project with Argo CD and plain YAML

  1. Deploy application
kubectl create namespace argocd 1️⃣ 
kubectl apply -n argocd -f https://raw.githubusercontent.com/argoproj/argo-cd/stable/manifests/install.yaml
kubectl port-forward svc/argocd-server -n argocd 8080:443

VERSION=$(curl --silent "https://api.github.com/repos/argoproj/argo-cd/releases/latest" \ 2️⃣ 
  | grep '"tag_name"' | sed -E 's/.*"([^"]+)".*/\1/')
curl -sSL -o /usr/local/bin/argocd https://github.com/argoproj/argo-cd/releases/download/$VERSION/argocd-linux-amd64
chmod +x /usr/local/bin/argocd

kubectl describe secrets/argocd-initial-admin-secret -n argocd 3️⃣ 
password = $(kubectl get secret argocd-initial-admin-secret -n argocd -o jsonpath='{.data.password}' | base64 -d)
argocd login localhost:8080 --username admin --password $password --insecure
argocd account update-password

argocd app create gradle-example --repo https://github.com/srcmaxim/gradle-example.git \ 4️⃣ 
  --path envs/kubernetes-env --dest-server https://kubernetes.default.svc --dest-namespace dev
argocd app get gradle-example 5️⃣ 
argocd app sync gradle-example 6️⃣

1️⃣ Install Argo CD
2️⃣ Download Argo CD CLI
3️⃣ Access The Argo CD API Server
4️⃣ Create An Application From A Git Repository
5️⃣ See app info
6️⃣ Sync (Deploy) app

  1. Get app response:
export NODE_PORT=$(kubectl get --namespace dev -o jsonpath="{.spec.ports[0].nodePort}" services app)
export NODE_IP=$(kubectl get nodes --namespace dev -o jsonpath="{.items[0].status.addresses[0].address}")
curl -w "\n" http://$NODE_IP:$NODE_PORT/cat
curl -w "\n" http://$NODE_IP:$NODE_PORT/entity

Recommended Project Structure

- aggragegation
  - test-aggregate
  - build-aggregate
- build-logic
  - avro-liblary
  - commons(jacoco, checkstyle, sonarqube)
  - java-liblary
  - report-aggreagation
  - spring-boot-app
  - lambda-app
- domain
  - entity
  - dto
  - avro-event(generated)
  - event
  - mapper(depends on entity, dto, event)
- platforms
  - plugin-platform
  - product-platform
  - test-platform
- features
  - [name]-feature-api(optional)
  - [name]-feature
- services
  - [name]-service
- lambdas
  - [name]-lambda
- commons(common modules)
  - [name]-common
- tools
  - [name]-tool
- envs
  - local-env
  - aws-env

Commands

Command
Run app ./gradlew :services:app:run
Build app ./gradlew :services:app:build
Test app ./gradlew :features:cat:test --continuous
Run linter report ./gradlew :aggregation:test-aggregate:linterReport
Run coverage report ./gradlew :aggregation:test-aggregate:codeCoverageReport
Run sonar report ./gradlew sonar
Run all reports ./gradlew :aggregation:test-aggregate:check sonar
Clean all ./gradlew clean