From b200749af181232f5b842e5dc21f3db2c42ec2d4 Mon Sep 17 00:00:00 2001 From: Thomas Kosiewski Date: Fri, 13 Feb 2026 10:27:56 +0000 Subject: [PATCH 1/5] config/e2e: enable aggregated API server in e2e deployment --- config/e2e/deployment.yaml | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) 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 From 64cdc00a24dcbabd7d1f57e2aadebdf24853734f Mon Sep 17 00:00:00 2001 From: Thomas Kosiewski Date: Fri, 13 Feb 2026 10:27:50 +0000 Subject: [PATCH 2/5] config/e2e: add CoderTemplate manifest for e2e test --- config/e2e/codertemplate.yaml | 15 +++++++++++++++ 1 file changed, 15 insertions(+) create mode 100644 config/e2e/codertemplate.yaml 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" {} From 917309104d318098521310710021afcedecf7331 Mon Sep 17 00:00:00 2001 From: Thomas Kosiewski Date: Fri, 13 Feb 2026 10:29:10 +0000 Subject: [PATCH 3/5] ci: replace e2e smoke test with CNPG + Templates merge-queue E2E --- .github/workflows/ci.yaml | 100 +++++++++++++++++++++++++++++++++----- 1 file changed, 87 insertions(+), 13 deletions(-) diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index a8abd201..78a52fe7 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 ---- + - name: Skip full E2E outside merge queue + if: github.event_name != 'merge_group' + run: | + echo "::notice::Skipping full E2E on ${GITHUB_EVENT_NAME}; runs in merge queue only." + + # ---- Merge-queue: full CNPG + Templates E2E ---- - name: Checkout + if: github.event_name == 'merge_group' uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1 with: persist-credentials: false - name: Setup Go + if: github.event_name == 'merge_group' uses: actions/setup-go@40f1582b2485089dde7abd97c1529aa768e1baff # v5.6.0 with: go-version-file: go.mod cache: true - name: Create Kind cluster + if: github.event_name == 'merge_group' uses: helm/kind-action@a1b0e391336a6ee6713a0583f8c6240d70863de3 # v1.12.0 with: cluster_name: e2e - name: Build binary + if: github.event_name == 'merge_group' 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 == 'merge_group' 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 == 'merge_group' 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 == 'merge_group' + 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 == 'merge_group' 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 == 'merge_group' + 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 == 'merge_group' + run: | + kubectl apply --server-side -f https://raw.githubusercontent.com/cloudnative-pg/cloudnative-pg/release-1.25/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 == 'merge_group' + run: kubectl apply -f examples/cloudnativepg/ + + - name: Wait for PostgreSQL + if: github.event_name == 'merge_group' + run: kubectl -n coder wait --for=condition=Ready cluster/coder-db --timeout=600s - - name: Apply sample CR - run: kubectl apply -f config/samples/coder_v1alpha1_codercontrolplane.yaml + - name: Wait for Coder deployment + if: github.event_name == 'merge_group' + run: kubectl -n coder rollout status deployment/coder --timeout=600s + + - name: Wait for CoderControlPlane readiness + if: github.event_name == 'merge_group' + 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 == 'merge_group' + 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 == 'merge_group' + run: kubectl apply -f config/e2e/codertemplate.yaml + + - name: Verify CoderTemplate exists + if: github.event_name == 'merge_group' + 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 == 'merge_group' 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) From 597d7398e3b7b3c33fa9c53ce5bc9dafcf6ee1d2 Mon Sep 17 00:00:00 2001 From: Thomas Kosiewski Date: Fri, 13 Feb 2026 10:38:49 +0000 Subject: [PATCH 4/5] ci: narrow e2e skip gate to pull_request events only Addresses review feedback: the skip condition was too broad (github.event_name != 'merge_group'), which would also skip on direct pushes to main, allowing publish without E2E validation. Changed to github.event_name == 'pull_request' so the full E2E runs on both merge_group and push events. --- .github/workflows/ci.yaml | 46 +++++++++++++++++++-------------------- 1 file changed, 23 insertions(+), 23 deletions(-) diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 78a52fe7..14f53720 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -161,34 +161,34 @@ jobs: runs-on: depot-ubuntu-24.04-8 timeout-minutes: 45 steps: - # ---- PR fast-path: skip heavy E2E ---- - - name: Skip full E2E outside merge queue - if: github.event_name != 'merge_group' + # ---- 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 only." + echo "::notice::Skipping full E2E on ${GITHUB_EVENT_NAME}; runs in merge queue and on push to main." - # ---- Merge-queue: full CNPG + Templates E2E ---- + # ---- Full CNPG + Templates E2E (merge queue + push) ---- - name: Checkout - if: github.event_name == 'merge_group' + if: github.event_name != 'pull_request' uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1 with: persist-credentials: false - name: Setup Go - if: github.event_name == 'merge_group' + 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 == 'merge_group' + 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 == 'merge_group' + if: github.event_name != 'pull_request' env: GOFLAGS: -mod=vendor CGO_ENABLED: "0" @@ -197,78 +197,78 @@ jobs: run: go build -o coder-k8s ./ - name: Build and load image - if: github.event_name == 'merge_group' + 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 == 'merge_group' + 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: Enable aggregated API server - if: github.event_name == 'merge_group' + 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 == 'merge_group' + if: github.event_name != 'pull_request' run: kubectl apply -f config/e2e/deployment.yaml - name: Wait for operator and APIService - if: github.event_name == 'merge_group' + 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 == 'merge_group' + if: github.event_name != 'pull_request' run: | kubectl apply --server-side -f https://raw.githubusercontent.com/cloudnative-pg/cloudnative-pg/release-1.25/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 == 'merge_group' + if: github.event_name != 'pull_request' run: kubectl apply -f examples/cloudnativepg/ - name: Wait for PostgreSQL - if: github.event_name == 'merge_group' + 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 == 'merge_group' + 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 == 'merge_group' + 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 == 'merge_group' + 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 == 'merge_group' + if: github.event_name != 'pull_request' run: kubectl apply -f config/e2e/codertemplate.yaml - name: Verify CoderTemplate exists - if: github.event_name == 'merge_group' + if: github.event_name != 'pull_request' run: | kubectl -n coder get codertemplates kubectl -n coder get codertemplate default.e2e-template -o yaml # ---- Failure diagnostics ---- - name: Dump cluster state on failure - if: failure() && github.event_name == 'merge_group' + if: failure() && github.event_name != 'pull_request' run: | echo "=== Pods (all namespaces) ===" kubectl get pods -A || true From 38418c079c7222bbfaf315d78a2d75bd19436677 Mon Sep 17 00:00:00 2001 From: Thomas Kosiewski Date: Fri, 13 Feb 2026 10:46:09 +0000 Subject: [PATCH 5/5] ci: pin CNPG manifest URL to immutable v1.25.0 tag Use the v1.25.0 git tag instead of the release-1.25 branch ref for deterministic CI builds. --- .github/workflows/ci.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 14f53720..ed9ab361 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -228,7 +228,7 @@ jobs: - name: Install CloudNativePG operator if: github.event_name != 'pull_request' run: | - kubectl apply --server-side -f https://raw.githubusercontent.com/cloudnative-pg/cloudnative-pg/release-1.25/releases/cnpg-1.25.0.yaml + 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