diff --git a/.github/actions/helm-render/action.yml b/.github/actions/helm-render/action.yml new file mode 100644 index 0000000..7b200b0 --- /dev/null +++ b/.github/actions/helm-render/action.yml @@ -0,0 +1,21 @@ +name: "Helm Render" +description: "Pulls, lints, and templates the Helm chart" +runs: + using: "composite" + steps: + - name: Render Helm Chart + shell: bash + run: | + source platform/helm/versions.env + helm pull open-telemetry/opentelemetry-demo \ + --version "$OTEL_DEMO_CHART_VERSION" \ + --untar + + helm lint opentelemetry-demo \ + --values platform/helm/values.yaml + + helm template astronomy-shop \ + opentelemetry-demo \ + --namespace astronomy-shop \ + --values platform/helm/values.yaml \ + > rendered.yaml diff --git a/.github/actions/setup-helm/action.yml b/.github/actions/setup-helm/action.yml new file mode 100644 index 0000000..b2541f4 --- /dev/null +++ b/.github/actions/setup-helm/action.yml @@ -0,0 +1,13 @@ +name: "Setup Helm" +description: "Installs Helm and adds necessary repositories" +runs: + using: "composite" + steps: + - name: Install Helm + uses: azure/setup-helm@v4 + + - name: Add OpenTelemetry Helm Repository + shell: bash + run: | + helm repo add open-telemetry https://open-telemetry.github.io/opentelemetry-helm-charts + helm repo update diff --git a/.github/actions/setup-kind/action.yml b/.github/actions/setup-kind/action.yml new file mode 100644 index 0000000..917e2e3 --- /dev/null +++ b/.github/actions/setup-kind/action.yml @@ -0,0 +1,9 @@ +name: "Setup Kind" +description: "Installs Kind" +runs: + using: "composite" + steps: + - name: Install Kind + uses: helm/kind-action@v1.10.0 + with: + install_only: true diff --git a/.github/actions/setup-kubeconform/action.yml b/.github/actions/setup-kubeconform/action.yml new file mode 100644 index 0000000..a492a7e --- /dev/null +++ b/.github/actions/setup-kubeconform/action.yml @@ -0,0 +1,11 @@ +name: "Setup Kubeconform" +description: "Installs kubeconform for Kubernetes manifest validation" +runs: + using: "composite" + steps: + - name: Install kubeconform + shell: bash + run: | + curl -sSL https://github.com/yannh/kubeconform/releases/latest/download/kubeconform-linux-amd64.tar.gz \ + | tar -xz + sudo mv kubeconform /usr/local/bin/ diff --git a/.github/actions/setup-kubectl/action.yml b/.github/actions/setup-kubectl/action.yml new file mode 100644 index 0000000..40e71ba --- /dev/null +++ b/.github/actions/setup-kubectl/action.yml @@ -0,0 +1,7 @@ +name: "Setup Kubectl" +description: "Installs kubectl" +runs: + using: "composite" + steps: + - name: Install kubectl + uses: azure/setup-kubectl@v4 diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml deleted file mode 100644 index b3b7a3a..0000000 --- a/.github/workflows/ci.yml +++ /dev/null @@ -1,59 +0,0 @@ ---- -name: Platform CI - -on: - pull_request: - branches: [main] - - push: - branches: [main] - -permissions: - contents: read - -jobs: - validate: - name: Validate Platform - runs-on: ubuntu-latest - - steps: - - name: Checkout Repository - uses: actions/checkout@v4 - - - name: Install Helm - uses: azure/setup-helm@v4 - - - name: Add OpenTelemetry Helm Repository - run: | - helm repo add open-telemetry https://open-telemetry.github.io/opentelemetry-helm-charts - helm repo update - - - name: Render Helm Chart - run: | - source platform/helm/versions.env - helm pull open-telemetry/opentelemetry-demo \ - --version "$OTEL_DEMO_CHART_VERSION" \ - --untar - - helm lint opentelemetry-demo \ - --values platform/helm/values.yaml - - helm template astronomy-shop \ - opentelemetry-demo \ - --namespace astronomy-shop \ - --values platform/helm/values.yaml \ - > rendered.yaml - - - name: Install kubeconform - run: | - curl -sSL https://github.com/yannh/kubeconform/releases/latest/download/kubeconform-linux-amd64.tar.gz \ - | tar -xz - sudo mv kubeconform /usr/local/bin/ - - - name: Validate Kubernetes Manifests - run: | - kubeconform \ - -strict \ - -summary \ - -ignore-missing-schemas \ - rendered.yaml diff --git a/.github/workflows/deploy-preview.yml b/.github/workflows/deploy-preview.yml new file mode 100644 index 0000000..525f169 --- /dev/null +++ b/.github/workflows/deploy-preview.yml @@ -0,0 +1,51 @@ +--- +name: Deploy Preview & Smoke Test + +on: + pull_request: + branches: [main] + +permissions: + contents: read + +jobs: + ephemeral-deploy: + name: Ephemeral Deploy & Test + runs-on: ubuntu-latest + + steps: + - name: Checkout Repository + uses: actions/checkout@v4 + + - name: Setup Kind + uses: ./.github/actions/setup-kind + + - name: Setup Kubectl + uses: ./.github/actions/setup-kubectl + + - name: Setup Helm + uses: ./.github/actions/setup-helm + + - name: Create Kind Cluster + run: | + kind create cluster --config platform/kind/cluster.yaml --wait 120s + kubectl cluster-info + + - name: Deploy Astronomy Shop + run: | + source platform/helm/versions.env + kubectl create namespace astronomy-shop + + helm upgrade --install astronomy-shop open-telemetry/opentelemetry-demo \ + --version "$OTEL_DEMO_CHART_VERSION" \ + --namespace astronomy-shop \ + --values platform/helm/values.yaml + + - name: Run Smoke Tests + run: | + chmod +x tests/smoke/smoke-test.sh + ./tests/smoke/smoke-test.sh + + - name: Tear Down Cluster + if: always() + run: kind delete cluster --name astronomy-shop diff --git a/.github/workflows/validate.yml b/.github/workflows/validate.yml new file mode 100644 index 0000000..34a3b96 --- /dev/null +++ b/.github/workflows/validate.yml @@ -0,0 +1,37 @@ +--- +name: Platform Validation + +on: + pull_request: + branches: [main] + push: + branches: [main] + +permissions: + contents: read + +jobs: + validate: + name: Validate Platform + runs-on: ubuntu-latest + + steps: + - name: Checkout Repository + uses: actions/checkout@v4 + + - name: Setup Helm + uses: ./.github/actions/setup-helm + + - name: Setup Kubeconform + uses: ./.github/actions/setup-kubeconform + + - name: Render Helm Chart + uses: ./.github/actions/helm-render + + - name: Validate Kubernetes Manifests + run: | + kubeconform \ + -strict \ + -summary \ + -ignore-missing-schemas \ + rendered.yaml diff --git a/docs/adr/0001-ci-validation-strategy.md b/docs/adr/0001-ci-validation-strategy.md new file mode 100644 index 0000000..79fcfb0 --- /dev/null +++ b/docs/adr/0001-ci-validation-strategy.md @@ -0,0 +1,28 @@ +# 1. CI Validation Strategy + +Date: 2026-07-16 + +## Status + +Accepted + +## Context + +We need a way to ensure that any changes to Kubernetes manifests or Helm charts are syntactically valid and conform to the Kubernetes API schema before they are merged into the main branch. This validation must run on every pull request to catch errors early in the development lifecycle. The validation process should be fast, reliable, and easily maintainable. + +## Decision + +We will implement a validation pipeline using GitHub Actions (`validate.yml`). The pipeline will perform the following steps: + +1. **Helm Linting:** Use `helm lint` to verify that the Helm chart is well-formed. +2. **Helm Templating:** Use `helm template` to render the Helm chart into raw Kubernetes manifests. This step uses the `values.yaml` specific to our deployment. +3. **Schema Validation:** Use `kubeconform` to validate the rendered manifests against the Kubernetes API schema. We chose `kubeconform` over alternatives like `kubeval` because it is actively maintained and supports custom resource definitions (CRDs) if needed in the future. + +To keep the workflow file clean and promote reusability, we will abstract the setup steps (Helm, kubeconform) and the rendering logic into GitHub Composite Actions. + +## Consequences + +* **Positive:** Early detection of misconfigurations, preventing broken deployments. +* **Positive:** Clean and readable main workflow file due to the use of composite actions. +* **Positive:** `kubeconform` is fast and runs completely locally without requiring a running Kubernetes cluster. +* **Negative:** Requires maintaining custom composite actions in the `.github/actions` directory. diff --git a/docs/adr/0002-ephemeral-environments-and-smoke-testing.md b/docs/adr/0002-ephemeral-environments-and-smoke-testing.md new file mode 100644 index 0000000..465a702 --- /dev/null +++ b/docs/adr/0002-ephemeral-environments-and-smoke-testing.md @@ -0,0 +1,27 @@ +# 2. Ephemeral Environments and Smoke Testing + +Date: 2026-07-16 + +## Status + +Accepted + +## Context + +We need to verify that changes to our platform configurations (Helm charts, Kubernetes manifests) result in a working, deployable application. Static validation (Phase 1) catches syntax errors, but it cannot guarantee that the application will actually start up, connect to its dependencies, and serve traffic. + +## Decision + +We will implement an Ephemeral Deployment and Smoke Testing pipeline in our CI process (`deploy-preview.yml`). + +1. **Ephemeral Cluster:** For every Pull Request, we will spin up a fresh, short-lived Kubernetes cluster using **Kind (Kubernetes in Docker)**. +2. **Deployment:** We will deploy the application into this cluster using Helm. +3. **Smoke Testing:** Rather than running exhaustive end-to-end (E2E) tests which are brittle and time-consuming, we will run a targeted "smoke test". This test will port-forward the frontend service and issue a simple HTTP GET request to verify a `200 OK` response. + +## Consequences + +* **Positive:** High confidence that merged PRs will not break the actual deployment process or the core application startup. +* **Positive:** Isolated testing environments prevent conflicts between different PRs. +* **Positive:** Using Kind in GitHub Actions is fast and incurs no external cloud infrastructure costs. +* **Negative:** Adds a few minutes to the PR build time compared to just static linting. +* **Negative:** Ephemeral CI clusters don't catch issues related to long-lived state (e.g., database schema migrations over time), which must be handled separately. diff --git a/tests/smoke/smoke-test.sh b/tests/smoke/smoke-test.sh new file mode 100644 index 0000000..ce6cc90 --- /dev/null +++ b/tests/smoke/smoke-test.sh @@ -0,0 +1,32 @@ +#!/usr/bin/env bash +set -eo pipefail + +echo "Running smoke tests..." + +# Ensure we're in the right namespace +NAMESPACE="astronomy-shop" + +# We will wait for the frontend to be ready. +echo "Waiting for frontend deployment to be ready..." +kubectl wait --for=condition=available --timeout=300s deployment/frontend -n "$NAMESPACE" + +# Port forward in the background +echo "Port-forwarding frontend service..." +kubectl port-forward svc/frontend 8080:8080 -n "$NAMESPACE" & +PORT_FORWARD_PID=$! + +# Give it a second to establish the connection +sleep 3 + +echo "Testing frontend HTTP response..." +HTTP_STATUS=$(curl -s -o /dev/null -w "%{http_code}" http://localhost:8080/) + +if [ "$HTTP_STATUS" -eq 200 ]; then + echo "✅ Smoke test passed! Frontend returned 200 OK." + kill $PORT_FORWARD_PID + exit 0 +else + echo "❌ Smoke test failed! Frontend returned HTTP $HTTP_STATUS." + kill $PORT_FORWARD_PID + exit 1 +fi