From 181c11f8728d169df3664fa4758baeaa68647535 Mon Sep 17 00:00:00 2001 From: Daniel Roux Date: Sat, 13 Apr 2024 20:59:23 +0200 Subject: [PATCH] Perform smoke tests on GHA/CI --- .changelog/28.txt | 3 + .github/workflows/docker.yml | 140 +++++++++++++++++++++++++++---- .github/workflows/k8s-test.yml | 71 ++++++++++++++++ .github/workflows/k8s.yml | 36 ++++++++ Dockerfile | 4 + Dockerfile.test | 12 +++ Makefile | 32 +++++++ ci/ui_smoke_tests.sh | 17 ++++ cypress.config.ts | 24 ++++++ cypress/integration/sk8l.spec.js | 23 +++++ production.dockerfile | 4 + testdata/cypress-job.yml | 71 ++++++++++++++++ testdata/sk8l-kind.yml | 43 ++++++++++ testdata/sk8l-values.yml | 43 ++++++++++ 14 files changed, 507 insertions(+), 16 deletions(-) create mode 100644 .changelog/28.txt create mode 100644 .github/workflows/k8s-test.yml create mode 100644 .github/workflows/k8s.yml create mode 100644 Dockerfile.test create mode 100755 ci/ui_smoke_tests.sh create mode 100644 cypress.config.ts create mode 100644 cypress/integration/sk8l.spec.js create mode 100644 testdata/cypress-job.yml create mode 100644 testdata/sk8l-kind.yml create mode 100644 testdata/sk8l-values.yml diff --git a/.changelog/28.txt b/.changelog/28.txt new file mode 100644 index 0000000..6497590 --- /dev/null +++ b/.changelog/28.txt @@ -0,0 +1,3 @@ +```release-note:enhancement +gha/ci: Execute smoke tests on GHA/CI +``` diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml index 91a03aa..ba2ed35 100644 --- a/.github/workflows/docker.yml +++ b/.github/workflows/docker.yml @@ -1,35 +1,143 @@ name: Build & Push images -on: workflow_dispatch +on: + pull_request: + workflow_dispatch: + workflow_call: + inputs: + image_tag: + required: true + type: string # push: # branches: # - 'main' + +permissions: + packages: write + jobs: - production-image: + sk8l-ui-production-img: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@9bb56186c3b09b4f86b1c65136769dd318469633 # v4.1.2 + with: + fetch-depth: 0 - name: Checkout - set env run: echo "IMAGE_TAG=$(make version)" >> $GITHUB_ENV - - - name: Set up QEMU + - name: Set up QEMU uses: docker/setup-qemu-action@v3 - - - name: Set up Docker Buildx - uses: docker/setup-buildx-action@v3 - - - name: Login to Docker Hub - uses: docker/login-action@v3 + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@d70bba72b1f3fd22344832f00baa16ece964efeb # v3.3.0 + - name: Login to Docker Hub + uses: docker/login-action@e92390c5fb421da1463c202d546fed0ec5c39f20 # v3.1.0 with: username: ${{ secrets.DOCKERHUB_USERNAME }} password: ${{ secrets.DOCKERHUB_TOKEN }} - - - name: Build and push - uses: docker/build-push-action@v5 + - name: Build and push release version + uses: docker/build-push-action@2cdde995de11925a030ce8070c3d77a52ffcf1c0 # v5.3.0 + if: ${{ github.event_name != 'pull_request' && github.event_name != 'workflow_call' && github.event_name != 'workflow_dispatch' }} with: context: . file: production.dockerfile push: true tags: | - ${{ vars.DOCKERHUB_SK8L_UI_IMAGE_NAME }}:latest - ${{ vars.DOCKERHUB_SK8L_UI_IMAGE_NAME }}:${{ env.IMAGE_TAG }} + ${{ vars.DOCKERHUB_SK8L_UI_IMAGE_NAME }}:latestx + ${{ vars.DOCKERHUB_SK8L_UI_IMAGE_NAME }}:x-${{ env.IMAGE_TAG }} + sk8l-ui-dev-img: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@9bb56186c3b09b4f86b1c65136769dd318469633 # v4.1.2 + with: + fetch-depth: 0 + submodules: recursive + - name: Checkout - set env + run: echo "IMAGE_TAG=$(make version)" >> $GITHUB_ENV + - name: Set up QEMU + uses: docker/setup-qemu-action@v3 + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@d70bba72b1f3fd22344832f00baa16ece964efeb # v3.3.0 + - name: Login to Docker Hub + uses: docker/login-action@e92390c5fb421da1463c202d546fed0ec5c39f20 # v3.1.0 + with: + registry: ghcr.io + username: ${{ github.repository_owner }} + password: ${{ secrets.GITHUB_TOKEN }} + - name: Build and push dev version + uses: docker/build-push-action@2cdde995de11925a030ce8070c3d77a52ffcf1c0 # v5.3.0 + if: ${{ github.event_name == 'workflow_call' && inputs.image_tag || github.event_name == 'workflow_dispatch' || github.event_name == 'pull_request' }} + with: + context: . + file: Dockerfile + cache-from: type=gha + cache-to: type=gha,mode=max + push: true + tags: | + ${{ vars.GHCR_SK8L_UI_IMAGE_NAME }}:dev + ${{ vars.GHCR_SK8L_UI_IMAGE_NAME }}:dev-${{ github.event.pull_request.head.ref }} + ${{ vars.GHCR_SK8L_UI_IMAGE_NAME }}:dev-${{ github.event.pull_request.number }} + sk8l-ui-pre-img: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@9bb56186c3b09b4f86b1c65136769dd318469633 # v4.1.2 + with: + fetch-depth: 0 + submodules: recursive + - name: Checkout - set env + run: echo "IMAGE_TAG=$(make version)" >> $GITHUB_ENV + - name: Set up QEMU + uses: docker/setup-qemu-action@v3 + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@d70bba72b1f3fd22344832f00baa16ece964efeb # v3.3.0 + - name: Login to Docker Hub + uses: docker/login-action@e92390c5fb421da1463c202d546fed0ec5c39f20 # v3.1.0 + with: + registry: ghcr.io + username: ${{ github.repository_owner }} + password: ${{ secrets.GITHUB_TOKEN }} + - name: Build and push dev version + uses: docker/build-push-action@2cdde995de11925a030ce8070c3d77a52ffcf1c0 # v5.3.0 + if: ${{ github.event_name == 'workflow_call' && inputs.image_tag || github.event_name == 'workflow_dispatch' || github.event_name == 'pull_request' }} + with: + context: . + file: production.dockerfile + cache-from: type=gha + cache-to: type=gha,mode=max + push: true + tags: | + ${{ vars.GHCR_SK8L_UI_IMAGE_NAME }}:pre + ${{ vars.GHCR_SK8L_UI_IMAGE_NAME }}:pre-${{ github.event.pull_request.head.ref }} + ${{ vars.GHCR_SK8L_UI_IMAGE_NAME }}:pre-${{ github.event.pull_request.number }} + sk8l-ui-test-img: + runs-on: ubuntu-latest + name: sk8l-ui:ui-test-${{ github.event.pull_request.number }} + needs: [sk8l-ui-pre-img] + steps: + - uses: actions/checkout@9bb56186c3b09b4f86b1c65136769dd318469633 # v4.1.2 + with: + fetch-depth: 0 + submodules: recursive + - name: Checkout - set env + run: echo "IMAGE_TAG=$(make version)" >> $GITHUB_ENV + - name: Set up QEMU + uses: docker/setup-qemu-action@v3 + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@d70bba72b1f3fd22344832f00baa16ece964efeb # v3.3.0 + - name: Login to Docker Hub + uses: docker/login-action@e92390c5fb421da1463c202d546fed0ec5c39f20 # v3.1.0 + with: + registry: ghcr.io + username: ${{ github.repository_owner }} + password: ${{ secrets.GITHUB_TOKEN }} + - name: Build and push test version + uses: docker/build-push-action@2cdde995de11925a030ce8070c3d77a52ffcf1c0 # v5.3.0 + if: ${{ github.event_name == 'workflow_call' && inputs.image_tag || github.event_name == 'workflow_dispatch' || github.event_name == 'pull_request' }} + with: + context: . + file: Dockerfile.test + cache-from: type=gha + cache-to: type=gha,mode=max + push: true + tags: | + ${{ vars.GHCR_SK8L_UI_IMAGE_NAME }}:ui-test + ${{ vars.GHCR_SK8L_UI_IMAGE_NAME }}:ui-test-${{ github.event.pull_request.head.ref }} + ${{ vars.GHCR_SK8L_UI_IMAGE_NAME }}:ui-test-${{ github.event.pull_request.number }} diff --git a/.github/workflows/k8s-test.yml b/.github/workflows/k8s-test.yml new file mode 100644 index 0000000..f530d0f --- /dev/null +++ b/.github/workflows/k8s-test.yml @@ -0,0 +1,71 @@ +name: k8s-rwkld + +on: + workflow_call: + inputs: + pull_request_number: + required: true + type: number + image_tag: + required: true + type: string + kind_version: + required: true + type: string + k8s_version: + required: true + type: string + k8s_image: + required: true + type: string +jobs: + k8s-tests: + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@9bb56186c3b09b4f86b1c65136769dd318469633 # v4.1.2 + with: + fetch-depth: 0 + submodules: recursive + - name: Setup Kubernetes cluster ${{ inputs.kind_version }}/${{ inputs.k8s_version }} + uses: engineerd/setup-kind@v0.5.0 + with: + name: sk8l + version: ${{ inputs.kind_version }} + image: ${{ inputs.k8s_image }} + config: testdata/sk8l-kind.yml + - name: Test connection + run: | + kubectl cluster-info > /dev/null + kubectl describe node > /dev/null + - name: Install Helm + uses: azure/setup-helm@v4.1.0 + with: + version: 'v3.13.3' + - name: Setup certs + run: | + make setup-certs > /dev/null + - name: Install Chart + run: | + make install-chart-ci > /dev/null + - name: ui smoke tests + id: ui_smoke_tests + run: | + ./ci/ui_smoke_tests.sh + - name: ui smoke tests error output + if: ${{ failure() && steps.ui_smoke_tests.conclusion == 'failure' }} + run: | + echo "----------------------------" + kubectl get pods -n sk8l + kubectl get cronjobs -n sk8l + echo "----------------------------" + kubectl get jobs cypress-job -n sk8l + kubectl logs job.batch/cypress-job -n sk8l + echo "----------------------------" + curl -vvv http://0.0.0.0:9901/clusters + exit 1 + - uses: actions/upload-artifact@v4 + if: failure() + with: + name: e2e-job-ss + path: cypress/sk8l.js/screenshots/ diff --git a/.github/workflows/k8s.yml b/.github/workflows/k8s.yml new file mode 100644 index 0000000..5698f64 --- /dev/null +++ b/.github/workflows/k8s.yml @@ -0,0 +1,36 @@ +name: k8s +on: + pull_request: + workflow_dispatch: + +env: + GO_VERSION: "1.22.2" + +permissions: + packages: write + +jobs: + docker-img: + name: Build Docker image dev-${{ github.event.pull_request.number }} + uses: ./.github/workflows/docker.yml + if: ${{ github.event_name == 'pull_request' || github.event_name == 'workflow_dispatch' }} + with: + image_tag: dev-${{ github.event.pull_request.number }} + secrets: inherit + k8s-tests: + name: k8s ${{ matrix.k8s.version }}/pr#${{ github.event.pull_request.number }} tests + needs: [docker-img] + strategy: + matrix: + k8s: + - image: "kindest/node:v1.29.2@sha256:51a1434a5397193442f0be2a297b488b6c919ce8a3931be0ce822606ea5ca245" + version: v1.29.2 + kind: + - version: "v0.22.0" + uses: ./.github/workflows/k8s-test.yml + with: + image_tag: dev-${{ github.event.pull_request.number }} + pull_request_number: ${{ github.event.pull_request.number }} + kind_version: ${{ matrix.kind.version }} + k8s_version: ${{ matrix.k8s.version }} + k8s_image: ${{ matrix.k8s.image }} diff --git a/Dockerfile b/Dockerfile index cb2d60f..a606aec 100644 --- a/Dockerfile +++ b/Dockerfile @@ -41,6 +41,10 @@ RUN mkdir -p $(pwd)/node_modules/.cache \ # # RUN yarn install --production FROM alpine:3.19 AS release +LABEL org.opencontainers.image.source=https://github.com/danroux/sk8l-ui +LABEL org.opencontainers.image.description="sk8l-ui dev image" +LABEL org.opencontainers.image.licenses=MIT + ENV npm_config_cache=/usr/app/node_modules/.cache ENV V 20.5.1 ENV FILE node-v$V-linux-x64-musl.tar.xz diff --git a/Dockerfile.test b/Dockerfile.test new file mode 100644 index 0000000..56213c8 --- /dev/null +++ b/Dockerfile.test @@ -0,0 +1,12 @@ +FROM ghcr.io/danroux/sk8l-ui:dev AS release + +FROM cypress/included:13.7.3 +LABEL org.opencontainers.image.source=https://github.com/danroux/sk8l-ui +LABEL org.opencontainers.image.description="sk8l-ui ui-test image" +LABEL org.opencontainers.image.licenses=MIT + +# USER 1001 + +# RUN chown -R 1001:1001 /var/cache +# COPY --chown=1001:1001 --from=release /usr/app ./e2e +COPY --from=release /usr/app ./e2e diff --git a/Makefile b/Makefile index 58f8253..b80e62e 100644 --- a/Makefile +++ b/Makefile @@ -38,3 +38,35 @@ changelog-entry: ## changelog-entry @changelog-entry -dir .changelog/ .PHONY: version changelog changelog-entry +setup-certs: # setup-certs + helm repo add jetstack https://charts.jetstack.io --force-update + kubectl apply -f https://github.com/cert-manager/cert-manager/releases/download/v1.14.4/cert-manager.crds.yaml + helm upgrade --install \ + cert-manager jetstack/cert-manager \ + --namespace cert-manager \ + --create-namespace=true \ + --version v1.14.4 \ + --set prometheus.enabled=false \ + --set extraArgs={--feature-gates=AdditionalCertificateOutputFormats=true} \ + --set webhook.extraArgs={--feature-gates=AdditionalCertificateOutputFormats=true} \ + --set webhook.timeoutSeconds=4 + kubectl apply -f https://raw.githubusercontent.com/danroux/sk8l-api/main/testdata/sk8l-cert-manager.yml + helm upgrade --install trust-manager jetstack/trust-manager \ + --install \ + --namespace cert-manager \ + --set app.trust.namespace=sk8l \ + --wait + kubectl apply -f https://raw.githubusercontent.com/danroux/sk8l-api/main/testdata/sk8l-trust.yml + +install-chart-ci: # install-chart-ci + helm repo add sk8l https://sk8l.io/charts + set +e; \ + helm uninstall sk8l -n sk8l; \ + kubectl get ns sk8l --ignore-not-found -o name | xargs -r kubectl wait --for=delete namespace/sk8l --timeout=120s; \ + set -e + helm upgrade --install sk8l sk8l/sk8l \ + -f testdata/sk8l-values.yml \ + --namespace sk8l \ + --create-namespace=false \ + --set namespace.create=false \ + --set namespace.name=sk8l diff --git a/ci/ui_smoke_tests.sh b/ci/ui_smoke_tests.sh new file mode 100755 index 0000000..58b12a8 --- /dev/null +++ b/ci/ui_smoke_tests.sh @@ -0,0 +1,17 @@ +#!/bin/bash + +set -e + +kubectl apply -f https://raw.githubusercontent.com/danroux/sk8l-api/main/testdata/sk8l-cronjobs.yml -n sk8l > /dev/null +kubectl wait -n sk8l --for=condition=ready pod -l app.kubernetes.io/pod=sk8l-ui --timeout=300s +sleep 30 +kubectl apply -f testdata/cypress-job.yml -n sk8l +kubectl wait --for=condition=complete job.batch/cypress-job -n sk8l --timeout=600s + +failed=$(kubectl get jobs cypress-job -n sk8l -o jsonpath='{.status.failed}') +kubectl logs job.batch/cypress-job -n sk8l +if [[ "$failed" -gt 0 ]]; then + echo "Failed jobs found. Failing the step." + find cypress/ -type f + exit 1 +fi diff --git a/cypress.config.ts b/cypress.config.ts new file mode 100644 index 0000000..f74f20a --- /dev/null +++ b/cypress.config.ts @@ -0,0 +1,24 @@ +import { defineConfig } from 'cypress' + +export default defineConfig({ + clientCertificates: [ + { + url: 'https://sk8l-ui', + ca: ['testdata/sk8l-certs/ca-cert.pem'], + certs: [ + { + cert: 'testdata/sk8l-certs/server-cert.pem', + key: 'testdata/sk8l-certs/server-key.pem', + }, + ], + } + ], + e2e: { + baseUrl: 'https://sk8l-ui:8001', + supportFile: false, + video: false, + specPattern: [ + "cypress/**/*.spec.js" + ] + } +}) \ No newline at end of file diff --git a/cypress/integration/sk8l.spec.js b/cypress/integration/sk8l.spec.js new file mode 100644 index 0000000..c240f74 --- /dev/null +++ b/cypress/integration/sk8l.spec.js @@ -0,0 +1,23 @@ +describe('Home Test', () => { + it('visits the homepage', () => { + cy.visit('https://sk8l-ui:8001/') + // cy.reload(true) + // cy.contains('It looks like no cronjobs exist for this namespace') + // cy.screenshot('my-screenshot') + // cy.screenshot('home-test', { + // log: true, + // onAfterScreenshot($el, props) { + // console.log('here', props); + // } + // }); + cy.contains('Cronjob Overview') + cy.contains('Overview') + cy.contains('Dashboard') + cy.contains('sk8l / download-report-files') + cy.contains('sk8l / process-csv-files') + cy.contains('sk8l / process-videos') + cy.contains('Cronjob activity') + cy.contains('33%') + cy.contains('Latest completions') + }) +}) diff --git a/production.dockerfile b/production.dockerfile index 8c160a1..2dd6b95 100644 --- a/production.dockerfile +++ b/production.dockerfile @@ -41,6 +41,10 @@ RUN yarn build # RUN yarn add serve FROM nginx:1.25.3-alpine3.18-slim as production-stage +LABEL org.opencontainers.image.source=https://github.com/danroux/sk8l-ui +LABEL org.opencontainers.image.description="sk8l-ui image" +LABEL org.opencontainers.image.licenses=MIT + WORKDIR /app ENV APP_PORT 8001 diff --git a/testdata/cypress-job.yml b/testdata/cypress-job.yml new file mode 100644 index 0000000..4f3a2df --- /dev/null +++ b/testdata/cypress-job.yml @@ -0,0 +1,71 @@ +apiVersion: batch/v1 +kind: Job +metadata: + name: cypress-job +spec: + template: + spec: + containers: + - name: cypress + image: ghcr.io/danroux/sk8l-ui:ui-test + imagePullPolicy: Always + command: [ + "cypress" + ] + args: [ + "run", + "--browser", + "firefox:stable", + "--headless", + "--config-file=cypress.config.ts" + ] + # command: ["/bin/sh"] + # args: ["-c", "while true; do echo hello; sleep 120;done"] + workingDir: /e2e + volumeMounts: + - name: tls-certs + mountPath: /e2e/testdata/sk8l-certs + readOnly: true + - name: xapp + mountPath: /e2e/cypress/screenshots + - name: ca-certificate-only + mountPath: /usr/lib/mozilla/certificates + readOnly: true + - name: ca-certificate-only + mountPath: /usr/lib64/mozilla/certificates + readOnly: true + - name: ca-certificate-only + mountPath: ~/.mozilla/certificates + readOnly: true + restartPolicy: Never + volumes: + - name: xapp + hostPath: + path: /xapp + type: Directory + - name: tls-certs + projected: + sources: + - secret: + name: sk8l-tls-server-cert + items: + - key: tls.crt + path: server-cert.pem + - key: tls.key + path: server-key.pem + - key: ca.crt + path: ca-cert.pem + - configMap: + name: sk8l + items: + - key: trust-bundle.crt + path: ca-certificates.crt + - name: ca-certificate-only + projected: + sources: + - configMap: + name: sk8l + items: + - key: trust-bundle.crt + path: ca-certificates.crt + backoffLimit: 1 diff --git a/testdata/sk8l-kind.yml b/testdata/sk8l-kind.yml new file mode 100644 index 0000000..8386264 --- /dev/null +++ b/testdata/sk8l-kind.yml @@ -0,0 +1,43 @@ +kind: Cluster +apiVersion: kind.x-k8s.io/v1alpha4 +nodes: +- role: control-plane + extraMounts: + - hostPath: /home/runner/work/sk8l-ui/sk8l-ui/cypress/sk8l.js/screenshots + containerPath: /xapp + extraPortMappings: + - containerPort: 30950 + # app-port: root + hostPort: 8001 + listenAddress: "0.0.0.0" + protocol: TCP + - containerPort: 30951 + # app-port: api + hostPort: 8585 + listenAddress: "0.0.0.0" + protocol: TCP + - containerPort: 30955 + # app-port: api-health + hostPort: 8588 + listenAddress: "0.0.0.0" + protocol: TCP + - containerPort: 30958 + # app-port: api-health + hostPort: 8590 + listenAddress: "0.0.0.0" + protocol: TCP + - containerPort: 31337 + # app-port: sk8l-web-proxy + hostPort: 1337 + listenAddress: "0.0.0.0" + protocol: TCP + - containerPort: 30960 + # app-port: envoy + hostPort: 9080 + listenAddress: "0.0.0.0" + protocol: TCP + - containerPort: 30965 + # app-port: envoy-admin + hostPort: 9901 + listenAddress: "0.0.0.0" + protocol: TCP diff --git a/testdata/sk8l-values.yml b/testdata/sk8l-values.yml new file mode 100644 index 0000000..d950b24 --- /dev/null +++ b/testdata/sk8l-values.yml @@ -0,0 +1,43 @@ +service: + type: NodePort + labels: + app: "sk8l" + ports: + - name: sk8l-api + port: 8585 + protocol: TCP + nodePort: 30951 + - name: sk8l-api-health + port: 8588 + protocol: TCP + nodePort: 30955 + - name: sk8l-api-metrics + port: 8590 + protocol: TCP + nodePort: 30958 +uiService: + type: NodePort + labels: + app: "sk8l" + ports: + - name: sk8l-ui + port: 8001 + protocol: TCP + nodePort: 30950 + - name: envoy + port: 9080 + protocol: TCP + nodePort: 30960 + - name: envoy-admin + protocol: TCP + port: 9901 + nodePort: 30965 + +sk8lUi: + image: "ghcr.io/danroux/sk8l-ui" + imageTag: "pre" + imagePullPolicy: "Always" + +configMaps: + ui: + vite_sk8l_api_url: "https://sk8l-ui:9080"