From 7471f133823eb342eff3fde8cb62ac15728b66ea Mon Sep 17 00:00:00 2001 From: Richard Watts Date: Fri, 4 Oct 2024 08:40:39 +0100 Subject: [PATCH] (feat) Start of CICD - pushing for test --- .github/workflows/cicd-stg.yaml | 137 ++++++++++++++++++++++ Dockerfile.zilliqa | 85 ++++++++++++++ Makefile | 24 ++++ cd/base/backend-config.yaml | 11 ++ cd/base/deployment.yaml | 33 ++++++ cd/base/frontend-config.yaml | 11 ++ cd/base/ingress.yaml | 21 ++++ cd/base/kustomization.yaml | 9 ++ cd/base/namespace.yaml | 4 + cd/base/svc.yaml | 18 +++ cd/overlays/dev/certificate.yaml | 7 ++ cd/overlays/dev/kustomization.yaml | 33 ++++++ cd/overlays/production/certificate.yaml | 7 ++ cd/overlays/production/kustomization.yaml | 26 ++++ cd/overlays/staging/certificate.yaml | 7 ++ cd/overlays/staging/kustomization.yaml | 26 ++++ package.json | 2 + 17 files changed, 461 insertions(+) create mode 100644 .github/workflows/cicd-stg.yaml create mode 100644 Dockerfile.zilliqa create mode 100644 Makefile create mode 100644 cd/base/backend-config.yaml create mode 100644 cd/base/deployment.yaml create mode 100644 cd/base/frontend-config.yaml create mode 100644 cd/base/ingress.yaml create mode 100644 cd/base/kustomization.yaml create mode 100644 cd/base/namespace.yaml create mode 100644 cd/base/svc.yaml create mode 100644 cd/overlays/dev/certificate.yaml create mode 100644 cd/overlays/dev/kustomization.yaml create mode 100644 cd/overlays/production/certificate.yaml create mode 100644 cd/overlays/production/kustomization.yaml create mode 100644 cd/overlays/staging/certificate.yaml create mode 100644 cd/overlays/staging/kustomization.yaml diff --git a/.github/workflows/cicd-stg.yaml b/.github/workflows/cicd-stg.yaml new file mode 100644 index 00000000..f8e1a2ba --- /dev/null +++ b/.github/workflows/cicd-stg.yaml @@ -0,0 +1,137 @@ +name: "CICD staging" + +on: + # Test run before merging + pull_request: + branches: + - main + # On merged + push: + branches: + - main + - users/richard/61-cicd + +jobs: + build-makefile: + permissions: + id-token: write + contents: write + runs-on: ubuntu-22.04 + # To test deployments, remove the github.ref_name clause: see devops/docs/z2-testing-apps.md - rrw 2024-04-12 + # && github.ref_name == 'main' + if: github.actor != 'dependabot[bot]' + name: "Build image with Makefile" + strategy: + fail-fast: false + matrix: + application: [otterscan] + include: + - application: otterscan + image_name: otterscan + path: . + tag_length: 8 + tag_latest: false + env: + DOCKER_DOMAIN: asia-docker.pkg.dev + REGISTRY: asia-docker.pkg.dev/prj-d-devops-services-4dgwlsse/zilliqa-public + steps: + - name: Checkout code + uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 + with: + submodules: recursive + ref: ${{ github.event.pull_request.head.ref }} + repository: ${{ github.event.pull_request.head.repo.full_name }} + fetch-depth: 0 + + - name: "Authenticate to Google Cloud - staging" + id: google-auth + uses: "google-github-actions/auth@71fee32a0bb7e97b4d33d548e7d957010649d8fa" + with: + token_format: "access_token" + workload_identity_provider: "${{ secrets.GCP_PRD_GITHUB_WIF }}" + service_account: "${{ secrets.GCP_STG_GITHUB_SA_DOCKER_REGISTRY }}" + create_credentials_file: true + + - name: Login to the registry - staging + uses: docker/login-action@e92390c5fb421da1463c202d546fed0ec5c39f20 + with: + registry: ${{ env.DOCKER_DOMAIN }} + username: "oauth2accesstoken" + password: "${{ steps.google-auth.outputs.access_token }}" + + - name: Get tag version - staging + id: set-tag + uses: Zilliqa/gh-actions-workflows/actions/generate-tag@v1 + with: + tag: ${{ env.REGISTRY }}/${{ matrix.image_name }} + length: ${{ matrix.tag_length }} + + - name: "Build and push ${{ matrix.application }} - staging" + env: + ENVIRONMENT: stg + IMAGE_TAG: ${{ steps.set-tag.outputs.tags }} + run: | + cd ${{ matrix.path }} + make image/build-and-push + + - name: "Build and push ${{ matrix.application }} tag latest - staging" + if: ${{ matrix.tag_latest == true }} + env: + ENVIRONMENT: stg + IMAGE_TAG: "${{ env.REGISTRY }}/${{ matrix.image_name }}:latest" + run: | + cd ${{ matrix.path }} + make image/build-and-push + + deploy-to-staging: + needs: [build-makefile] + permissions: + id-token: write + contents: write + runs-on: ubuntu-22.04 + if: github.actor != 'dependabot[bot]' && github.ref_name == 'main' + strategy: + fail-fast: false + matrix: + application: + - developer-portal + env: + APP_NAME: ${{ matrix.application }} + Z_ENV: infra/live/gcp/non-production/prj-d-staging/z_ase1.yaml + Z_SERVICE_ACCOUNT: ${{ secrets.GCP_STG_GITHUB_SA_K8S_DEPLOY }} + OP_SERVICE_ACCOUNT_TOKEN: ${{ secrets.OP_SERVICE_ACCOUNT_TOKEN_STG }} + GITHUB_PAT: ${{ secrets.GH_PAT }} + Z_IMAGE: asia-docker.pkg.dev/prj-d-devops-services-4dgwlsse/zilliqa-private/z:latest + REGISTRY: asia-docker.pkg.dev + steps: + - name: Checkout + uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 + with: + repository: Zilliqa/devops + token: ${{ env.GITHUB_PAT }} + ref: main + sparse-checkout: | + ${{ env.Z_ENV }} + + - name: Authenticate to Google Cloud + id: google-auth + uses: google-github-actions/auth@71fee32a0bb7e97b4d33d548e7d957010649d8fa + with: + token_format: "access_token" + workload_identity_provider: "${{ secrets.GCP_PRD_GITHUB_WIF }}" + service_account: ${{ env.Z_SERVICE_ACCOUNT }} + create_credentials_file: true + + - name: Deploy application + run: | + gcloud auth print-access-token | docker login -u oauth2accesstoken --password-stdin https://${{ env.REGISTRY }} + docker run --rm \ + -e ZQ_USER='${{ env.Z_SERVICE_ACCOUNT }}' \ + -e Z_ENV='/devops/${{ env.Z_ENV }}' \ + -e OP_SERVICE_ACCOUNT_TOKEN='${{ env.OP_SERVICE_ACCOUNT_TOKEN }}' \ + -e GITHUB_PAT='${{ env.GITHUB_PAT }}' \ + -e CLOUDSDK_AUTH_CREDENTIAL_FILE_OVERRIDE='/google/application_default_credentials.json' \ + -v `pwd`:/devops \ + -v ${{ steps.google-auth.outputs.credentials_file_path }}:/google/application_default_credentials.json \ + --name z_container ${{ env.Z_IMAGE }} \ + bash -c "gcloud config set account ${{ env.Z_SERVICE_ACCOUNT }} && z /app /devops app sync --cache-dir .cache ${{ env.APP_NAME }}" diff --git a/Dockerfile.zilliqa b/Dockerfile.zilliqa new file mode 100644 index 00000000..92fac9db --- /dev/null +++ b/Dockerfile.zilliqa @@ -0,0 +1,85 @@ +FROM node:22.9.0-alpine3.19 AS builder +WORKDIR /otterscan-build +COPY --link ["package.json", "package-lock.json", "/otterscan-build/"] +RUN npm ci --fetch-timeout 6000000 --verbose +COPY --link ["run-nginx.sh", "tsconfig.json", "tsconfig.node.json", "postcss.config.js", "tailwind.config.js", "vite.config.ts", "index.html", "/otterscan-build/"] +COPY --link ["public", "/otterscan-build/public/"] +COPY --link ["src", "/otterscan-build/src/"] +COPY --link ["autogen", "/otterscan-build/autogen/"] +RUN npm run build-docker + +# Add brotli module to official nginx image +# Based on: https://github.com/nginxinc/docker-nginx/tree/master/modules +FROM nginx:1.27.1-alpine3.20 as nginxbuilder + +RUN set -ex \ + && apk update \ + && apk add linux-headers openssl-dev pcre-dev zlib-dev openssl abuild \ + musl-dev libxslt libxml2-utils make mercurial gcc unzip git \ + xz g++ coreutils \ + # allow abuild as a root user \ + && printf "#!/bin/sh\\nSETFATTR=true /usr/bin/abuild -F \"\$@\"\\n" > /usr/local/bin/abuild \ + && chmod +x /usr/local/bin/abuild \ + && hg clone -r ${NGINX_VERSION}-${PKG_RELEASE} https://hg.nginx.org/pkg-oss/ \ + && cd pkg-oss \ + && mkdir /tmp/packages \ + && for module in "brotli"; do \ + echo "Building $module for nginx-$NGINX_VERSION"; \ + if [ -d /modules/$module ]; then \ + echo "Building $module from user-supplied sources"; \ + # check if module sources file is there and not empty + if [ ! -s /modules/$module/source ]; then \ + echo "No source file for $module in modules/$module/source, exiting"; \ + exit 1; \ + fi; \ + # some modules require build dependencies + if [ -f /modules/$module/build-deps ]; then \ + echo "Installing $module build dependencies"; \ + apk update && apk add $(cat /modules/$module/build-deps | xargs); \ + fi; \ + # if a module has a build dependency that is not in a distro, provide a + # shell script to fetch/build/install those + # note that shared libraries produced as a result of this script will + # not be copied from the builder image to the main one so build static + if [ -x /modules/$module/prebuild ]; then \ + echo "Running prebuild script for $module"; \ + /modules/$module/prebuild; \ + fi; \ + /pkg-oss/build_module.sh -v $NGINX_VERSION -f -y -o /tmp/packages -n $module $(cat /modules/$module/source); \ + BUILT_MODULES="$BUILT_MODULES $(echo $module | tr '[A-Z]' '[a-z]' | tr -d '[/_\-\.\t ]')"; \ + elif make -C /pkg-oss/alpine list | grep -E "^$module\s+\d+" > /dev/null; then \ + echo "Building $module from pkg-oss sources"; \ + cd /pkg-oss/alpine; \ + make abuild-module-$module BASE_VERSION=$NGINX_VERSION NGINX_VERSION=$NGINX_VERSION; \ + apk add $(. ./abuild-module-$module/APKBUILD; echo $makedepends;); \ + make module-$module BASE_VERSION=$NGINX_VERSION NGINX_VERSION=$NGINX_VERSION; \ + find ~/packages -type f -name "*.apk" -exec mv -v {} /tmp/packages/ \;; \ + BUILT_MODULES="$BUILT_MODULES $module"; \ + else \ + echo "Don't know how to build $module module, exiting"; \ + exit 1; \ + fi; \ + done \ + && echo "BUILT_MODULES=\"$BUILT_MODULES\"" > /tmp/packages/modules.env + +FROM nginx:1.27.1-alpine3.20 +COPY --from=nginxbuilder /tmp/packages /tmp/packages +RUN set -ex \ + && . /tmp/packages/modules.env \ + && for module in $BUILT_MODULES; do \ + apk add --no-cache --allow-untrusted /tmp/packages/nginx-module-${module}-${NGINX_VERSION}*.apk; \ + done \ + && rm -rf /tmp/packages +RUN apk update && apk add jq +WORKDIR /usr/share/nginx/html/ +COPY --link --from=otterscan/otterscan-assets:v1.1.1 /usr/share/nginx/html/chains chains/ +COPY --link --from=otterscan/otterscan-assets:v1.1.1 /usr/share/nginx/html/topic0 topic0/ +COPY --link --from=otterscan/otterscan-assets:v1.1.1 /usr/share/nginx/html/assets assets/ +COPY --link --from=otterscan/otterscan-assets:v1.1.1 /usr/share/nginx/html/signatures signatures/ +COPY --link nginx/conf.d/default.conf /etc/nginx/conf.d/default.conf +COPY --link nginx/nginx.conf /etc/nginx/nginx.conf +COPY --link --from=builder /otterscan-build/dist /usr/share/nginx/html/ +COPY --link --from=builder /otterscan-build/run-nginx.sh / +WORKDIR / + +CMD ["/run-nginx.sh"] diff --git a/Makefile b/Makefile new file mode 100644 index 00000000..28e52ee7 --- /dev/null +++ b/Makefile @@ -0,0 +1,24 @@ +.PHONY: all +all: image/build-and-push + +.ONESHELL: +SHELL := /bin/bash +.SHELLFLAGS = -ec + +ENVIRONMENT ?= dev +VALID_ENVIRONMENTS := dev stg prd +# Check if the ENVIRONMENT variable is in the list of valid environments +ifeq ($(filter $(ENVIRONMENT),$(VALID_ENVIRONMENTS)),) +$(error Invalid value for ENVIRONMENT. Valid values are dev, stg, or prd.) +endif + +HERE=$(shell pwd) + +IMAGE_TAG ?= otterscan:latest + + +.PHONY: image/build-and-push +image/build-and-push: + ./scripts/gen-version.sh autogen/version.ts + docker buildx build -f Dockerfile.zilliqa . -t $(IMAGE_TAG) + docker push "$(IMAGE_TAG)" diff --git a/cd/base/backend-config.yaml b/cd/base/backend-config.yaml new file mode 100644 index 00000000..e15cd7b6 --- /dev/null +++ b/cd/base/backend-config.yaml @@ -0,0 +1,11 @@ +apiVersion: cloud.google.com/v1 +kind: BackendConfig +metadata: + name: otterscan + namespace: otterscan + labels: + app.kubernetes.io/name: "otterscan" +spec: + timeoutSec: 120 + healthCheck: + requestPath: /health diff --git a/cd/base/deployment.yaml b/cd/base/deployment.yaml new file mode 100644 index 00000000..7d16db78 --- /dev/null +++ b/cd/base/deployment.yaml @@ -0,0 +1,33 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: otterscan + namespace: otterscan + labels: + app.kubernetes.io/name: "otterscan" +spec: + replicas: 1 + selector: + matchLabels: + app.kubernetes.io/name: "otterscan" + strategy: + type: RollingUpdate + template: + metadata: + labels: + app.kubernetes.io/name: "otterscan" + spec: + containers: + - image: otterscan + name: otterscan + ports: + - containerPort: 80 + resources: + limits: + memory: 200Mi + requests: + memory: 100Mi + readinessProbe: + httpGet: + path: /health + port: 80 diff --git a/cd/base/frontend-config.yaml b/cd/base/frontend-config.yaml new file mode 100644 index 00000000..1c05f4d6 --- /dev/null +++ b/cd/base/frontend-config.yaml @@ -0,0 +1,11 @@ +apiVersion: networking.gke.io/v1beta1 +kind: FrontendConfig +metadata: + name: developer-portal + namespace: developer-portal + labels: + app.kubernetes.io/name: "otterscan" +spec: + redirectToHttps: + enabled: true + responseCodeName: RESPONSE_CODE diff --git a/cd/base/ingress.yaml b/cd/base/ingress.yaml new file mode 100644 index 00000000..8b54950f --- /dev/null +++ b/cd/base/ingress.yaml @@ -0,0 +1,21 @@ +apiVersion: networking.k8s.io/v1 +kind: Ingress +metadata: + name: otterscan + namespace: otterscan + labels: + app.kubernetes.io/name: "otterscan" + annotations: + nginx.ingress.kubernetes.io/rewrite-target: /$2 +spec: + rules: + - host: localhost + http: + paths: + - path: / + pathType: Prefix + backend: + service: + name: otterscan + port: + number: 80 diff --git a/cd/base/kustomization.yaml b/cd/base/kustomization.yaml new file mode 100644 index 00000000..adbf3a07 --- /dev/null +++ b/cd/base/kustomization.yaml @@ -0,0 +1,9 @@ +apiVersion: kustomize.config.k8s.io/v1beta1 +kind: Kustomization +resources: + - namespace.yaml + - deployment.yaml + - svc.yaml + - ingress.yaml + - backend-config.yaml + - frontend-config.yaml diff --git a/cd/base/namespace.yaml b/cd/base/namespace.yaml new file mode 100644 index 00000000..3c128e88 --- /dev/null +++ b/cd/base/namespace.yaml @@ -0,0 +1,4 @@ +apiVersion: v1 +kind: Namespace +metadata: + name: developer-portal diff --git a/cd/base/svc.yaml b/cd/base/svc.yaml new file mode 100644 index 00000000..b0f561fc --- /dev/null +++ b/cd/base/svc.yaml @@ -0,0 +1,18 @@ +apiVersion: v1 +kind: Service +metadata: + name: otterscan + namespace: otterscan + labels: + app.kubernetes.io/name: "otterscan" + annotations: + beta.cloud.google.com/backend-config: '{"default": "otterscan"}' +spec: + type: ClusterIP + ports: + - port: 80 + targetPort: 80 + protocol: TCP + name: http + selector: + app.kubernetes.io/name: "otterscan" diff --git a/cd/overlays/dev/certificate.yaml b/cd/overlays/dev/certificate.yaml new file mode 100644 index 00000000..f2c5cae9 --- /dev/null +++ b/cd/overlays/dev/certificate.yaml @@ -0,0 +1,7 @@ +apiVersion: networking.gke.io/v1 +kind: ManagedCertificate +metadata: + name: developer-portal +spec: + domains: + - developer-portal.zildev.dev diff --git a/cd/overlays/dev/kustomization.yaml b/cd/overlays/dev/kustomization.yaml new file mode 100644 index 00000000..c6cd3e19 --- /dev/null +++ b/cd/overlays/dev/kustomization.yaml @@ -0,0 +1,33 @@ +apiVersion: kustomize.config.k8s.io/v1beta1 +kind: Kustomization + +resources: + - ../../base + - certificate.yaml + +patches: + - target: + kind: Deployment + name: developer-portal + patch: |- + - op: replace + path: "/spec/template/spec/containers/0/image" + value: asia-docker.pkg.dev/prj-d-dev-apps-n3p4o97j/zilliqa/developer-portal:latest + - target: + kind: Ingress + name: developer-portal + patch: |- + - op: replace + path: "/spec/rules/0/host" + value: developer-portal.zildev.dev + - op: replace + path: "/spec/rules/1/host" + value: dev-dev.zilliqa.com + - op: replace + path: /metadata/annotations + value: + kubernetes.io/ingress.class: gce + kubernetes.io/ingress.global-static-ip-name: developer-portal-zildev-dev + networking.gke.io/managed-certificates: developer-portal + +namespace: developer-portal-dev diff --git a/cd/overlays/production/certificate.yaml b/cd/overlays/production/certificate.yaml new file mode 100644 index 00000000..09c99882 --- /dev/null +++ b/cd/overlays/production/certificate.yaml @@ -0,0 +1,7 @@ +apiVersion: networking.gke.io/v1 +kind: ManagedCertificate +metadata: + name: developer-portal +spec: + domains: + - dev.zilliqa.com diff --git a/cd/overlays/production/kustomization.yaml b/cd/overlays/production/kustomization.yaml new file mode 100644 index 00000000..997d4970 --- /dev/null +++ b/cd/overlays/production/kustomization.yaml @@ -0,0 +1,26 @@ +apiVersion: kustomize.config.k8s.io/v1beta1 +kind: Kustomization + +resources: + - ../../base + - certificate.yaml + +patches: + - target: + kind: Ingress + name: developer-portal + patch: |- + - op: replace + path: "/spec/rules/0/host" + value: dev.zilliqa.com + - op: remove + path: "/spec/rules/1" + - op: replace + path: /metadata/annotations + value: + kubernetes.io/ingress.class: gce + kubernetes.io/ingress.global-static-ip-name: dev-zilliqa-com + networking.gke.io/managed-certificates: developer-portal + networking.gke.io/v1beta1.FrontendConfig: developer-portal + +namespace: developer-portal-prd diff --git a/cd/overlays/staging/certificate.yaml b/cd/overlays/staging/certificate.yaml new file mode 100644 index 00000000..15d9e962 --- /dev/null +++ b/cd/overlays/staging/certificate.yaml @@ -0,0 +1,7 @@ +apiVersion: networking.gke.io/v1 +kind: ManagedCertificate +metadata: + name: otterscan +spec: + domains: + - otterscan.zilstg.dev diff --git a/cd/overlays/staging/kustomization.yaml b/cd/overlays/staging/kustomization.yaml new file mode 100644 index 00000000..a5bbf0c2 --- /dev/null +++ b/cd/overlays/staging/kustomization.yaml @@ -0,0 +1,26 @@ +apiVersion: kustomize.config.k8s.io/v1beta1 +kind: Kustomization + +resources: + - ../../base + - certificate.yaml + +patches: + - target: + kind: Ingress + name: otterscan + patch: |- + - op: replace + path: "/spec/rules/0/host" + value: otterscan.zilstg.dev + - op: replace + path: "/spec/rules/1/host" + value: otterscan.zilliqa.com + - op: replace + path: /metadata/annotations + value: + kubernetes.io/ingress.class: gce + kubernetes.io/ingress.global-static-ip-name: otterscan-zilstg-dev + networking.gke.io/managed-certificates: otterscan + +namespace: otterscan-stg diff --git a/package.json b/package.json index a73b48e8..03b60605 100644 --- a/package.json +++ b/package.json @@ -51,6 +51,8 @@ "start": "./scripts/gen-version.sh autogen/version.ts && vite", "build": "./scripts/gen-version.sh autogen/version.ts && tsc && vite build", "preview": "./scripts/gen-version.sh autogen/version.ts && vite preview", + "autogen": "./scripts/gen-version.sh autogen/version.ts", + "build-docker": "tsc && vite build", "test": "jest", "source-map-explorer": "source-map-explorer build/static/js/*.js", "assets-start": "docker run --rm -p 5175:80 --name otterscan-assets -d otterscan/otterscan-assets:v1.0.1",