diff --git a/.github/workflows/pipeline-dev.yaml b/.github/workflows/pipeline-dev.yaml new file mode 100644 index 00000000..906555a5 --- /dev/null +++ b/.github/workflows/pipeline-dev.yaml @@ -0,0 +1,136 @@ +name: CI-CD-Dev + +on: + workflow_dispatch: + push: + branches: [ "main" ] + pull_request: + branches: + - main + +env: + OCI_CLI_USER: ${{ secrets.OCI_CLI_USER }} + OCI_CLI_TENANCY: ${{ secrets.OCI_CLI_TENANCY }} + OCI_CLI_FINGERPRINT: ${{ secrets.OCI_CLI_FINGERPRINT }} + OCI_CLI_KEY_CONTENT: ${{ secrets.OCI_CLI_KEY_CONTENT }} + OCI_CLI_REGION: ${{ secrets.OCI_CLI_REGION }} + +jobs: + unit-testing: + name: Unit Testing + runs-on: ubuntu-latest + steps: + - name: Run unit testing + run: echo " Run Unit testing" + + build: + name: Build and analyze + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v4 + with: + fetch-depth: 0 # Shallow clones should be disabled for a better relevancy of analysis + - name: Set up JDK 17 + uses: actions/setup-java@v1 + with: + java-version: 17 + - name: Cache SonarQube packages + uses: actions/cache@v1 + with: + path: ~/.sonar/cache + key: ${{ runner.os }}-sonar + restore-keys: ${{ runner.os }}-sonar + - name: Cache Maven packages + uses: actions/cache@v1 + with: + path: ~/.m2 + key: ${{ runner.os }}-m2-${{ hashFiles('**/pom.xml') }} + restore-keys: ${{ runner.os }}-m2 + - name: Build and analyze + env: + SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }} + SONAR_HOST_URL: ${{ secrets.SONAR_HOST_URL }} + run: mvn -f ./api/pom.xml -B verify org.sonarsource.scanner.maven:sonar-maven-plugin:sonar -Dsonar.projectKey=demo-project -Dsonar.projectName='demo-project' + - name: Check SonarQube Quality Gate + run: | + STATUS=$(curl -s "http://129.150.35.183:9000/api/project_badges/measure?project=demo-project&metric=alert_status&token=sqb_ce32b49127641027d6b263bf6d5c1e155bad8d60" | grep -oP '(?<=)[^<]+') + if [ "$STATUS" != "passed" ]; then + echo "Quality gate failed!" + exit 1 + else + echo "Quality gate passed!" + fi + + push_to_registry: + if: ${{ github.event_name != 'pull_request' }} + name: Push Docker image to Docker Hub + runs-on: ubuntu-latest + needs: build + permissions: + packages: write + contents: read + attestations: write + id-token: write + steps: + - name: Check out the repo + uses: actions/checkout@v4 + + - name: Log in to Docker Hub + uses: docker/login-action@f4ef78c080cd8ba55a85445d5b36e214a81df20a + with: + username: ${{ secrets.DOCKER_USERNAME }} + password: ${{ secrets.DOCKER_PASSWORD }} + + - name: Extract metadata (tags, labels) for API Docker + id: meta + uses: docker/metadata-action@9ec57ed1fcdbf14dcef7dfbe97b2010124a938b7 + with: + images: shivin8080/demo-api + + - name: Build and push Docker image API + id: push + uses: docker/build-push-action@3b5e8027fcad23fda98b2e3ac259d8d67585f671 + with: + context: ./api + file: ./api/Dockerfile + push: true + tags: ${{ steps.meta.outputs.tags }} + labels: ${{ steps.meta.outputs.labels }} + + - name: Extract metadata (tags, labels) for Web Docker + id: web + uses: docker/metadata-action@9ec57ed1fcdbf14dcef7dfbe97b2010124a938b7 + with: + images: shivin8080/demo-web + + - name: Build and push Docker image Web + id: push-web + uses: docker/build-push-action@3b5e8027fcad23fda98b2e3ac259d8d67585f671 + with: + context: ./web + file: ./web/Dockerfile + push: true + tags: ${{ steps.web.outputs.tags }} + labels: ${{ steps.web.outputs.labels }} + + install-kubectl: + if: ${{ github.event_name != 'pull_request' }} + name: Install Kubectl for OKE + runs-on: ubuntu-latest + needs: push_to_registry + steps: + - name: Configure Kubectl + uses: oracle-actions/configure-kubectl-oke@v1.5.0 + id: test-configure-kubectl-oke-action + with: + cluster: ${{ secrets.OKE_CLUSTER_OCID }} + + - name: Run Kubectl + run: kubectl get nodes -A + + - name: Check out the repo + uses: actions/checkout@v4 + + - name: Helm deploy + run: helm upgrade wordsmith-dev ./Helm/wordsmith --namespace ws-dev --install --create-namespace diff --git a/.github/workflows/pipeline-prod.yaml b/.github/workflows/pipeline-prod.yaml new file mode 100644 index 00000000..1a1761c6 --- /dev/null +++ b/.github/workflows/pipeline-prod.yaml @@ -0,0 +1,95 @@ +name: CI-CD-Production + +on: + workflow_dispatch: + push: + tags: # e.g., v1.2.3 + - 'prod[0-9]+.[0-9]+.[0-9]+' + +env: + OCI_CLI_USER: ${{ secrets.OCI_CLI_USER }} + OCI_CLI_TENANCY: ${{ secrets.OCI_CLI_TENANCY }} + OCI_CLI_FINGERPRINT: ${{ secrets.OCI_CLI_FINGERPRINT }} + OCI_CLI_KEY_CONTENT: ${{ secrets.OCI_CLI_KEY_CONTENT }} + OCI_CLI_REGION: ${{ secrets.OCI_CLI_REGION }} + +jobs: + + push_to_registry: + if: ${{ github.event_name != 'pull_request' }} + name: Push Docker image to Docker Hub + runs-on: ubuntu-latest + outputs: + api_tag: ${{ steps.meta.outputs.version }} + web_tag: ${{ steps.web.outputs.version }} + permissions: + packages: write + contents: read + attestations: write + id-token: write + steps: + - name: Check out the repo + uses: actions/checkout@v4 + + - name: Log in to Docker Hub + uses: docker/login-action@f4ef78c080cd8ba55a85445d5b36e214a81df20a + with: + username: ${{ secrets.DOCKER_USERNAME }} + password: ${{ secrets.DOCKER_PASSWORD }} + + - name: Extract metadata (tags, labels) for API Docker + id: meta + uses: docker/metadata-action@9ec57ed1fcdbf14dcef7dfbe97b2010124a938b7 + with: + images: shivin8080/demo-api + tags: | + type=match,pattern=prod(.*),group=1 + + - name: Build and push Docker image API + id: push + uses: docker/build-push-action@3b5e8027fcad23fda98b2e3ac259d8d67585f671 + with: + context: ./api + file: ./api/Dockerfile + push: true + tags: ${{ steps.meta.outputs.tags }} + labels: ${{ steps.meta.outputs.labels }} + + - name: Extract metadata (tags, labels) for Web Docker + id: web + uses: docker/metadata-action@9ec57ed1fcdbf14dcef7dfbe97b2010124a938b7 + with: + images: shivin8080/demo-web + tags: | + type=match,pattern=prod(.*),group=1 + + - name: Build and push Docker image Web + id: push-web + uses: docker/build-push-action@3b5e8027fcad23fda98b2e3ac259d8d67585f671 + with: + context: ./web + file: ./web/Dockerfile + push: true + tags: ${{ steps.web.outputs.tags }} + labels: ${{ steps.web.outputs.labels }} + + install-kubectl: + if: ${{ github.event_name != 'pull_request' }} + name: Install Kubectl for OKE + runs-on: ubuntu-latest + needs: push_to_registry + steps: + - name: Configure Kubectl + uses: oracle-actions/configure-kubectl-oke@v1.5.0 + id: test-configure-kubectl-oke-action + with: + cluster: ${{ secrets.OKE_CLUSTER_OCID }} + + - name: Run Kubectl + run: kubectl get nodes -A + + - name: Check out the repo + uses: actions/checkout@v4 + + - name: Helm deploy + run: helm upgrade wordsmith-prod ./Helm/wordsmith --set-string web.tag=${{ needs.push_to_registry.outputs.web_tag }} --set-string api.tag=${{ needs.push_to_registry.outputs.api_tag }} --namespace ws-prod --install --create-namespace diff --git a/.github/workflows/pipeline-stage.yaml b/.github/workflows/pipeline-stage.yaml new file mode 100644 index 00000000..33a123d1 --- /dev/null +++ b/.github/workflows/pipeline-stage.yaml @@ -0,0 +1,95 @@ +name: CI-CD-Stage + +on: + workflow_dispatch: + push: + tags: # e.g., v1.2.3 + - 'stage[0-9]+.[0-9]+.[0-9]+' + +env: + OCI_CLI_USER: ${{ secrets.OCI_CLI_USER }} + OCI_CLI_TENANCY: ${{ secrets.OCI_CLI_TENANCY }} + OCI_CLI_FINGERPRINT: ${{ secrets.OCI_CLI_FINGERPRINT }} + OCI_CLI_KEY_CONTENT: ${{ secrets.OCI_CLI_KEY_CONTENT }} + OCI_CLI_REGION: ${{ secrets.OCI_CLI_REGION }} + +jobs: + + push_to_registry: + if: ${{ github.event_name != 'pull_request' }} + name: Push Docker image to Docker Hub + runs-on: ubuntu-latest + outputs: + api_tag: ${{ steps.meta.outputs.version }} + web_tag: ${{ steps.web.outputs.version }} + permissions: + packages: write + contents: read + attestations: write + id-token: write + steps: + - name: Check out the repo + uses: actions/checkout@v4 + + - name: Log in to Docker Hub + uses: docker/login-action@f4ef78c080cd8ba55a85445d5b36e214a81df20a + with: + username: ${{ secrets.DOCKER_USERNAME }} + password: ${{ secrets.DOCKER_PASSWORD }} + + - name: Extract metadata (tags, labels) for API Docker + id: meta + uses: docker/metadata-action@9ec57ed1fcdbf14dcef7dfbe97b2010124a938b7 + with: + images: shivin8080/demo-api + tags: | + type=match,pattern=stage(.*),group=1 + + - name: Build and push Docker image API + id: push + uses: docker/build-push-action@3b5e8027fcad23fda98b2e3ac259d8d67585f671 + with: + context: ./api + file: ./api/Dockerfile + push: true + tags: ${{ steps.meta.outputs.tags }} + labels: ${{ steps.meta.outputs.labels }} + + - name: Extract metadata (tags, labels) for Web Docker + id: web + uses: docker/metadata-action@9ec57ed1fcdbf14dcef7dfbe97b2010124a938b7 + with: + images: shivin8080/demo-web + tags: | + type=match,pattern=stage(.*),group=1 + + - name: Build and push Docker image Web + id: push-web + uses: docker/build-push-action@3b5e8027fcad23fda98b2e3ac259d8d67585f671 + with: + context: ./web + file: ./web/Dockerfile + push: true + tags: ${{ steps.web.outputs.tags }} + labels: ${{ steps.web.outputs.labels }} + + install-kubectl: + if: ${{ github.event_name != 'pull_request' }} + name: Install Kubectl for OKE + runs-on: ubuntu-latest + needs: push_to_registry + steps: + - name: Configure Kubectl + uses: oracle-actions/configure-kubectl-oke@v1.5.0 + id: test-configure-kubectl-oke-action + with: + cluster: ${{ secrets.OKE_CLUSTER_OCID }} + + - name: Run Kubectl + run: kubectl get nodes -A + + - name: Check out the repo + uses: actions/checkout@v4 + + - name: Helm deploy + run: helm upgrade wordsmith-stage ./Helm/wordsmith --set-string web.tag=${{ needs.push_to_registry.outputs.web_tag }} --set-string api.tag=${{ needs.push_to_registry.outputs.api_tag }} --namespace ws-stage --install --create-namespace diff --git a/Helm/wordsmith/Chart.yaml b/Helm/wordsmith/Chart.yaml new file mode 100644 index 00000000..501fa113 --- /dev/null +++ b/Helm/wordsmith/Chart.yaml @@ -0,0 +1,4 @@ +name: wordsmith +version: 1.0.0 +description: A Helm chart for the Wordsmith demo application +AppVersion: 1.0.0 diff --git a/Helm/wordsmith/templates/_helpers.tpl b/Helm/wordsmith/templates/_helpers.tpl new file mode 100644 index 00000000..fef7c869 --- /dev/null +++ b/Helm/wordsmith/templates/_helpers.tpl @@ -0,0 +1,32 @@ +{{/* +Create a default fully qualified app name. +We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec). +If release name contains chart name it will be used as a full name. +*/}} +{{- define "wordsmith.fullname" -}} +{{- $name := .Chart.Name }} +{{- if contains $name .Release.Name }} +{{- .Release.Name | trunc 63 | trimSuffix "-" }} +{{- else }} +{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" }} +{{- end }} +{{- end }} +{{- define "wordsmith.chart" -}} +{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" }} +{{- end }} +{{/* +Common labels +*/}} +{{- define "wordsmith.labels" -}} +helm.sh/chart: {{ include "wordsmith.chart" . }} +{{ include "wordsmith.selectorLabels" . }} +{{- if .Chart.AppVersion }} +app.kubernetes.io/AppVersion: {{ .Chart.AppVersion | quote }} +{{- end }} +app.kubernetes.io/managed-by: {{ .Release.Service | quote }} +{{- end }} + +{{- define "wordsmith.selectorLabels" -}} +app.kubernetes.io/name: "wordsmith" +app.kubernetes.io/instance: {{ .Release.Name | quote }} +{{- end }} diff --git a/Helm/wordsmith/templates/api.yaml b/Helm/wordsmith/templates/api.yaml new file mode 100644 index 00000000..4176ffc7 --- /dev/null +++ b/Helm/wordsmith/templates/api.yaml @@ -0,0 +1,42 @@ +apiVersion: v1 +kind: Service +metadata: + name: "{{ include "wordsmith.fullname" . }}-api" + labels: + app: api + {{- include "wordsmith.labels" . | nindent 4 }} +spec: + ports: + - port: {{ .Values.api.port }} + targetPort: {{ .Values.api.targetPort }} + name: api + selector: + app: api + {{- include "wordsmith.selectorLabels" . | nindent 4 }} + clusterIP: None +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: "{{ include "wordsmith.fullname" . }}-api" + labels: + app: api + {{- include "wordsmith.labels" . | nindent 4 }} +spec: + replicas: {{ .Values.api.replicas }} + selector: + matchLabels: + app: api + {{- include "wordsmith.selectorLabels" . | nindent 6 }} + template: + metadata: + labels: + app: api + {{- include "wordsmith.selectorLabels" . | nindent 8 }} + spec: + containers: + - name: api + image: "{{ .Values.api.image }}:{{ .Values.api.tag }}" + ports: + - containerPort: {{ .Values.api.targetPort }} + name: api diff --git a/Helm/wordsmith/templates/db.yaml b/Helm/wordsmith/templates/db.yaml new file mode 100644 index 00000000..b537d784 --- /dev/null +++ b/Helm/wordsmith/templates/db.yaml @@ -0,0 +1,110 @@ +apiVersion: v1 +kind: Service +metadata: + name: db + labels: + app: db + {{- include "wordsmith.labels" . | nindent 4 }} +spec: + ports: + - port: {{ .Values.db.port }} + targetPort: {{ .Values.db.targetPort }} + name: db + selector: + app: db + {{- include "wordsmith.selectorLabels" . | nindent 4 }} + clusterIP: None +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: "{{ include "wordsmith.fullname" . }}-db" + labels: + app: db + {{- include "wordsmith.labels" . | nindent 4 }} +spec: + selector: + matchLabels: + app: db + {{- include "wordsmith.selectorLabels" . | nindent 6 }} + template: + metadata: + labels: + app: db + {{- include "wordsmith.selectorLabels" . | nindent 8 }} + spec: + containers: + - name: db + image: "{{ .Values.db.image }}:{{ .Values.db.tag }}" + ports: + - containerPort: {{ .Values.db.port }} + name: db + volumeMounts: + - name: db-schema + mountPath: /docker-entrypoint-initdb.d + volumes: + - name: db-schema + configMap: + name: db-schema +--- +apiVersion: v1 +data: + words.sql: | + CREATE TABLE nouns (word TEXT NOT NULL); + CREATE TABLE verbs (word TEXT NOT NULL); + CREATE TABLE adjectives (word TEXT NOT NULL); + + INSERT INTO nouns(word) VALUES + ('cloud'), + ('elephant'), + ('gø language'), + ('laptøp'), + ('cøntainer'), + ('micrø-service'), + ('turtle'), + ('whale'), + ('gøpher'), + ('møby døck'), + ('server'), + ('bicycle'), + ('viking'), + ('mermaid'), + ('fjørd'), + ('legø'), + ('flødebolle'), + ('smørrebrød'); + + INSERT INTO verbs(word) VALUES + ('will drink'), + ('smashes'), + ('smøkes'), + ('eats'), + ('walks tøwards'), + ('løves'), + ('helps'), + ('pushes'), + ('debugs'), + ('invites'), + ('hides'), + ('will ship'); + + INSERT INTO adjectives(word) VALUES + ('the exquisite'), + ('a pink'), + ('the røtten'), + ('a red'), + ('the serverless'), + ('a brøken'), + ('a shiny'), + ('the pretty'), + ('the impressive'), + ('an awesøme'), + ('the famøus'), + ('a gigantic'), + ('the gløriøus'), + ('the nørdic'), + ('the welcøming'), + ('the deliciøus'); +kind: ConfigMap +metadata: + name: db-schema diff --git a/Helm/wordsmith/templates/web.yaml b/Helm/wordsmith/templates/web.yaml new file mode 100644 index 00000000..e75912da --- /dev/null +++ b/Helm/wordsmith/templates/web.yaml @@ -0,0 +1,41 @@ +apiVersion: v1 +kind: Service +metadata: + name: "{{ include "wordsmith.fullname" . }}-web" + labels: + app: web + {{- include "wordsmith.labels" . | nindent 4 }} +spec: + ports: + - port: {{ .Values.web.port }} + targetPort: {{ .Values.web.targetPort }} + name: web + selector: + app: web + {{- include "wordsmith.selectorLabels" . | nindent 4 }} + type: LoadBalancer +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: "{{ include "wordsmith.fullname" . }}-web" + labels: + app: web + {{- include "wordsmith.labels" . | nindent 4 }} +spec: + selector: + matchLabels: + app: web + {{- include "wordsmith.selectorLabels" . | nindent 6 }} + template: + metadata: + labels: + app: web + {{- include "wordsmith.selectorLabels" . | nindent 8 }} + spec: + containers: + - name: web + image: "{{ .Values.web.image }}:{{ .Values.web.tag }}" + ports: + - containerPort: {{ .Values.web.targetPort }} + name: web diff --git a/Helm/wordsmith/values.yaml b/Helm/wordsmith/values.yaml new file mode 100644 index 00000000..5e8f0363 --- /dev/null +++ b/Helm/wordsmith/values.yaml @@ -0,0 +1,18 @@ +db: + image: postgres + tag: 10.0-alpine + port: 5432 + targetPort: 5432 + +web: + image: shivin8080/demo-web + tag: main + port: 8080 + targetPort: 80 + +api: + image: shivin8080/demo-api + tag: main + port: 8080 + targetPort: 8080 + replicas: 5 diff --git a/README.md b/README.md index 36797a87..5f245b5d 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# Wordsmith App +# Wordsmith-App Wordsmith is the demo project originally shown at DockerCon EU 2017 and 2018.