diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index a8abd201..ed9ab361 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -155,29 +155,40 @@ jobs: run: go build ./... e2e-kind: - name: E2E Smoke (Kind) + name: E2E (Kind + CNPG + Templates) needs: [changes, test] if: needs.changes.outputs.go == 'true' && (github.event_name != 'push' || github.actor != 'github-merge-queue[bot]') runs-on: depot-ubuntu-24.04-8 timeout-minutes: 45 steps: + # ---- PR fast-path: skip heavy E2E on pull requests ---- + - name: Skip full E2E on pull requests + if: github.event_name == 'pull_request' + run: | + echo "::notice::Skipping full E2E on ${GITHUB_EVENT_NAME}; runs in merge queue and on push to main." + + # ---- Full CNPG + Templates E2E (merge queue + push) ---- - name: Checkout + if: github.event_name != 'pull_request' uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1 with: persist-credentials: false - name: Setup Go + if: github.event_name != 'pull_request' uses: actions/setup-go@40f1582b2485089dde7abd97c1529aa768e1baff # v5.6.0 with: go-version-file: go.mod cache: true - name: Create Kind cluster + if: github.event_name != 'pull_request' uses: helm/kind-action@a1b0e391336a6ee6713a0583f8c6240d70863de3 # v1.12.0 with: cluster_name: e2e - name: Build binary + if: github.event_name != 'pull_request' env: GOFLAGS: -mod=vendor CGO_ENABLED: "0" @@ -186,33 +197,96 @@ jobs: run: go build -o coder-k8s ./ - name: Build and load image + if: github.event_name != 'pull_request' run: | docker build -f Dockerfile.goreleaser -t ghcr.io/coder/coder-k8s:e2e . kind load docker-image ghcr.io/coder/coder-k8s:e2e --name e2e - name: Apply namespace, CRDs, and RBAC + if: github.event_name != 'pull_request' run: | kubectl apply -f config/e2e/namespace.yaml kubectl apply -f config/crd/bases/ kubectl apply -f config/rbac/ - - name: Deploy controller + - name: Enable aggregated API server + if: github.event_name != 'pull_request' + run: | + kubectl apply -f deploy/apiserver-service.yaml + kubectl apply -f deploy/apiserver-apiservice.yaml + + - name: Deploy coder-k8s (controller + aggregated API server) + if: github.event_name != 'pull_request' run: kubectl apply -f config/e2e/deployment.yaml - - name: Wait for controller - run: kubectl wait --for=condition=Available deploy/coder-k8s -n coder-system --timeout=120s + - name: Wait for operator and APIService + if: github.event_name != 'pull_request' + run: | + kubectl wait --for=condition=Available deploy/coder-k8s -n coder-system --timeout=120s + kubectl wait --for=condition=Available apiservice/v1alpha1.aggregation.coder.com --timeout=180s + + - name: Install CloudNativePG operator + if: github.event_name != 'pull_request' + run: | + kubectl apply --server-side -f https://raw.githubusercontent.com/cloudnative-pg/cloudnative-pg/v1.25.0/releases/cnpg-1.25.0.yaml + kubectl wait --for=condition=Available deploy/cnpg-controller-manager -n cnpg-system --timeout=180s + + - name: Provision PostgreSQL and CoderControlPlane + if: github.event_name != 'pull_request' + run: kubectl apply -f examples/cloudnativepg/ - - name: Apply sample CR - run: kubectl apply -f config/samples/coder_v1alpha1_codercontrolplane.yaml + - name: Wait for PostgreSQL + if: github.event_name != 'pull_request' + run: kubectl -n coder wait --for=condition=Ready cluster/coder-db --timeout=600s + + - name: Wait for Coder deployment + if: github.event_name != 'pull_request' + run: kubectl -n coder rollout status deployment/coder --timeout=600s + + - name: Wait for CoderControlPlane readiness + if: github.event_name != 'pull_request' + run: | + kubectl -n coder wait --for=jsonpath='{.status.phase}'=Ready codercontrolplane/coder --timeout=600s + kubectl -n coder wait --for=jsonpath='{.status.operatorAccessReady}'=true codercontrolplane/coder --timeout=600s + + - name: Verify operator token secret + if: github.event_name != 'pull_request' + run: | + TOKEN_SECRET=$(kubectl -n coder get codercontrolplane coder -o jsonpath='{.status.operatorTokenSecretRef.name}') + test -n "$TOKEN_SECRET" + kubectl -n coder get secret "$TOKEN_SECRET" + + - name: Create CoderTemplate via kubectl + if: github.event_name != 'pull_request' + run: kubectl apply -f config/e2e/codertemplate.yaml + + - name: Verify CoderTemplate exists + if: github.event_name != 'pull_request' + run: | + kubectl -n coder get codertemplates + kubectl -n coder get codertemplate default.e2e-template -o yaml - - name: Verify CR exists + # ---- Failure diagnostics ---- + - name: Dump cluster state on failure + if: failure() && github.event_name != 'pull_request' run: | - kubectl get codercontrolplanes -A - COUNT=$(kubectl get codercontrolplanes -A -o json | jq '.items | length') - if [ "$COUNT" -lt 1 ]; then - echo "assertion failed: expected at least 1 CoderControlPlane resource" >&2 - exit 1 - fi + echo "=== Pods (all namespaces) ===" + kubectl get pods -A || true + echo "" + echo "=== APIService ===" + kubectl describe apiservice v1alpha1.aggregation.coder.com || true + echo "" + echo "=== coder-k8s logs ===" + kubectl -n coder-system logs deploy/coder-k8s --tail=200 || true + echo "" + echo "=== CoderControlPlane ===" + kubectl -n coder describe codercontrolplane coder || true + echo "" + echo "=== Coder deployment logs ===" + kubectl -n coder logs deploy/coder --tail=200 || true + echo "" + echo "=== CNPG controller logs ===" + kubectl -n cnpg-system logs deploy/cnpg-controller-manager --tail=200 || true terraform: name: Terraform (fmt/validate/tflint/trivy) diff --git a/config/e2e/codertemplate.yaml b/config/e2e/codertemplate.yaml new file mode 100644 index 00000000..4e825d50 --- /dev/null +++ b/config/e2e/codertemplate.yaml @@ -0,0 +1,15 @@ +apiVersion: aggregation.coder.com/v1alpha1 +kind: CoderTemplate +metadata: + name: default.e2e-template + namespace: coder +spec: + organization: default + displayName: "E2E Template" + description: "Created by coder-k8s merge-queue e2e" + files: + main.tf: | + terraform { + required_version = ">= 1.0" + } + resource "null_resource" "example" {} diff --git a/config/e2e/deployment.yaml b/config/e2e/deployment.yaml index ae2eb4f7..f3fc579d 100644 --- a/config/e2e/deployment.yaml +++ b/config/e2e/deployment.yaml @@ -19,13 +19,15 @@ spec: containers: - name: manager image: ghcr.io/coder/coder-k8s:e2e - # E2E tests validate controller mode only; all mode requires - # additional infrastructure (APIService, TLS) not provisioned here. - args: ["--app=controller"] + # E2E tests validate both controller and aggregated API server + # behavior in one pod for APIService-backed integration coverage. + args: ["--app=all"] imagePullPolicy: Never ports: - containerPort: 8081 name: health + - containerPort: 6443 + name: https livenessProbe: httpGet: path: /healthz