Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 21 additions & 0 deletions .github/actions/helm-render/action.yml
Original file line number Diff line number Diff line change
@@ -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
13 changes: 13 additions & 0 deletions .github/actions/setup-helm/action.yml
Original file line number Diff line number Diff line change
@@ -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
9 changes: 9 additions & 0 deletions .github/actions/setup-kind/action.yml
Original file line number Diff line number Diff line change
@@ -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
11 changes: 11 additions & 0 deletions .github/actions/setup-kubeconform/action.yml
Original file line number Diff line number Diff line change
@@ -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/
7 changes: 7 additions & 0 deletions .github/actions/setup-kubectl/action.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
name: "Setup Kubectl"
description: "Installs kubectl"
runs:
using: "composite"
steps:
- name: Install kubectl
uses: azure/setup-kubectl@v4
59 changes: 0 additions & 59 deletions .github/workflows/ci.yml

This file was deleted.

51 changes: 51 additions & 0 deletions .github/workflows/deploy-preview.yml
Original file line number Diff line number Diff line change
@@ -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
37 changes: 37 additions & 0 deletions .github/workflows/validate.yml
Original file line number Diff line number Diff line change
@@ -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
28 changes: 28 additions & 0 deletions docs/adr/0001-ci-validation-strategy.md
Original file line number Diff line number Diff line change
@@ -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.
27 changes: 27 additions & 0 deletions docs/adr/0002-ephemeral-environments-and-smoke-testing.md
Original file line number Diff line number Diff line change
@@ -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.
40 changes: 40 additions & 0 deletions tests/smoke/smoke-test.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
#!/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-proxy to be ready.
echo "Waiting for frontend-proxy deployment to be ready..."
kubectl wait --for=condition=available --timeout=300s deployment/astronomy-shop-frontend-proxy -n "$NAMESPACE" || kubectl wait --for=condition=available --timeout=300s deployment/frontend-proxy -n "$NAMESPACE" || kubectl wait --for=condition=available --timeout=300s deployment/astronomy-shop-frontendproxy -n "$NAMESPACE"

# Port forward in the background
echo "Port-forwarding frontend-proxy service..."
(kubectl port-forward svc/astronomy-shop-frontend-proxy 8080:8080 -n "$NAMESPACE" || kubectl port-forward svc/frontend-proxy 8080:8080 -n "$NAMESPACE" || kubectl port-forward svc/astronomy-shop-frontendproxy 8080:8080 -n "$NAMESPACE") &
PORT_FORWARD_PID=$!

# Give it a second to establish the connection
sleep 5

echo "Testing frontend HTTP response (with retries for startup)..."
MAX_RETRIES=15
RETRY_COUNT=0
HTTP_STATUS=0

while [ $RETRY_COUNT -lt $MAX_RETRIES ]; do
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
fi
echo "Frontend returned HTTP $HTTP_STATUS (attempt $((RETRY_COUNT+1))/$MAX_RETRIES). Waiting..."
sleep 5
RETRY_COUNT=$((RETRY_COUNT+1))
done

echo "❌ Smoke test failed! Frontend returned HTTP $HTTP_STATUS after $MAX_RETRIES attempts."
kill $PORT_FORWARD_PID
exit 1
Loading