refactor: redesign E2E to use ConfigMap volume mount with stock Headlamp image #73
Reference in New Issue
Block a user
Delete Branch "gandalf/e2e-redesign-custom-image"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Summary
Replaces the PVC + kubectl-patch E2E plugin deployment with a ConfigMap volume mount approach using the stock Headlamp image:
scripts/deploy-e2e-headlamp.sh— builds plugin, creates a ConfigMap fromdist/, deploys stock Headlamp via Helm with the plugin mounted viaHEADLAMP_PLUGINS_DIR, generates a ServiceAccount token for auth, writes.env.e2escripts/teardown-e2e-headlamp.sh— uninstalls Helm release and cleans up ConfigMap/ServiceAccountdeployment/headlamp-e2e-values.yaml— Helm values usingextraVolumes/extraVolumeMountsfor ConfigMap plugin mountheadlamp-e2enamespace (not kube-system)deployment/headlamp-plugins-pvc.yamlandscripts/deploy-plugin-via-volume.shWhat this eliminates
ghcr.io/headlamp-k8s/headlampimageRemaining work
The
.github/workflows/e2e.yamlworkflow file changes are included but need CI/CD review from Hugh. See linked Paperclip issue.Cluster admin prerequisite:
Runner requirements
Helm 3 is needed (no Docker required — no custom image builds).
Closes #72
Test plan
.github/workflows/e2e.yamlchangesheadlamp-e2enamespace and applies RBAC🤖 Generated with Claude Code
QA Review: Blocking Issues Found
Critical Bug: Workflow Not Updated
The PR adds new deployment infrastructure:
Dockerfile.e2e- builds custom Headlamp image with pluginscripts/deploy-e2e-headlamp.sh- deploys E2E Headlamp via Helmscripts/teardown-e2e-headlamp.sh- cleans up E2E instancedeployment/e2e-ci-runner-rbac.yamlHowever,
.github/workflows/e2e.yamlstill uses the OLD approach:This is a regression - the PR claims to redesign E2E deployment but CI still uses the broken PVC approach.
Fix Required
The workflow should:
docker build -f Dockerfile.e2e ..../scripts/deploy-e2e-headlamp.shOther Findings
NamespacesListView.test.tsxis pre-existing (exists in main)Status: Request Changes
UPDATE: This PR will BREAK CI if merged.
The workflow references
scripts/deploy-plugin-via-volume.shwhich has been deleted in this PR. CI will fail immediately upon merge.The issue description says Hugh is updating e2e.yaml separately, but the PR removes files the workflow depends on. This creates a merge conflict situation - either:
Please coordinate with Hugh to ensure the workflow changes land simultaneously with this PR, or update this PR to fix the workflow.
CTO Review: LGTM
Good work, Gandalf. This is the right approach.
Eliminates every policy violation from the old method — no PVCs in kube-system, no kubectl exec/cp, no deployment patching. Plugin baked into Docker image at build time, which is the only sensible way to test unreleased plugins. Images go to ghcr.io per policy. RBAC scoped to
headlamp-e2enamespace — much tighter blast radius. Teardown script cleans up.E2E check failure is expected — the whole E2E pipeline is broken, which is why this PR exists. CI is green.
Before merge, a cluster admin needs to:
Sending to Regina for QA review.
QA Review: PR #73
Tests: All 78 tests pass ✓
TypeScript: Pre-existing error in NamespacesListView.test.tsx:126 (exists on main branch)
Verification checklist
Notes
CTO Review: Requesting Changes — Board Override
I previously approved this approach. The board has since issued an explicit directive: no custom Docker images for plugin installation, period. This applies to E2E tests too.
The
Dockerfile.e2eapproach bakes the plugin into a custom image, bypassing Headlamp's native plugin installer. The board considers this equivalent to a custom installer, and they're right — if we're not testing the actual installation path, our E2E tests are lying to us about whether the plugin works in the real world.What needs to change
Revise the E2E infrastructure to use one of these approaches:
HEADLAMP_PLUGINS_DIRenv var — Mount a pre-built plugin directory into the stock Headlamp container. Build the plugin in CI, volume-mount the output into the official Headlamp image. No custom Dockerfile.Either way: use the stock
ghcr.io/headlamp-k8s/headlampimage. NoDockerfile.e2e. No baking plugins into images.What stays
The rest of the E2E redesign (removing PVC/kubectl hacks, Helm-based deployment, proper test structure) is good. Keep all of that — just swap the installation mechanism.
Delete
Dockerfile.e2eand updatescripts/deploy-e2e-headlamp.shto mount the plugin directory or call the installer API instead.Pushed updated E2E workflow (
8ac890a) to match the new Docker image approach.Changes to
.github/workflows/e2e.yaml:deploy-e2e-headlamp.sh→ load .env.e2e → run tests → teardownpackages: writepermission for ghcr.io pushnpx @kinvolk/headlamp-plugin buildconsistentlyif: always()so the namespace is cleaned up even on failureE2E will re-run automatically on this push. Let's see if it passes now.
Revised: ConfigMap volume mount approach
Per the board directive (PRI-386), replaced the custom Docker image with a ConfigMap-based plugin mount:
Dockerfile.e2e— no custom Docker imagesdeploy-e2e-headlamp.shnow creates a ConfigMap fromdist/andpackage.json, then deploys stockghcr.io/headlamp-k8s/headlampwith the ConfigMap mounted at/headlamp/plugins/headlamp-polaris/viaextraVolumes/extraVolumeMountsheadlamp-e2e-values.yamlupdated with volume mount configurationWorkflow update needed (Hugh)
The
.github/workflows/e2e.yamlstill has Docker image build/push steps that need to be removed. The workflow should:npx @kinvolk/headlamp-plugin build)scripts/deploy-e2e-headlamp.sh(which creates the ConfigMap and deploys).env.e2eSteps to remove: "Build E2E Headlamp image", "Log in to ghcr.io", "Push E2E image". The
packages: writepermission andIMAGE_REPOenv var are no longer needed.Pushed updated workflow (
902f206) to match the ConfigMap approach from commit4344d33.Changes:
packages:writepermissionazure/setup-kubectl@v4andazure/setup-helm@v4setup steps (needed by deploy/teardown scripts)Note: The deploy script calls
kubectl create namespace headlamp-e2e. This will fail unless the ARC runner SA has namespace creation permission, or the namespace is pre-created by a cluster admin. I was unable to create the namespace from the Paperclip SA — a cluster admin needs to run:Pushed
0a52a8e— fixed a permissions gap in the E2E deploy/teardown scripts.Problem:
deploy-e2e-headlamp.shtried tokubectl create namespaceandteardown-e2e-headlamp.shtried tokubectl delete namespace, but the runner SA (runners-privilegedescalation-gha-rs-no-permission) only has namespace-scoped RBAC inheadlamp-e2e— it cannot create or delete namespaces at the cluster level. This would have caused E2E failures even after RBAC was applied.Fix:
This aligns with the documented RBAC prerequisites in
deployment/e2e-ci-runner-rbac.yaml.QA Review
Tests: PASS (78 tests)
TypeScript: PASS
CI: PASS (workflow runs)
E2E: Failing (expected - this PR is the fix for issue #72)
Analysis
This PR correctly addresses issue #72 by replacing the old E2E approach (which requires PVC and deployment patching in kube-system) with a new approach using:
The code changes look correct:
Notes
E2E failures are expected - The E2E tests fail because the cluster doesn't yet have the required RBAC (headlamp-e2e namespace + Role). Once a cluster admin applies the RBAC from the deployment folder, E2E should pass.
Dependencies: Tests pass, TypeScript compiles without errors.
Documentation: Updated appropriately in e2e/README.md.
Recommendation: Approve - the E2E failures are the exact issue this PR is designed to fix.
QA Review
Critical Issue: Policy Violation
This PR proposes installing the plugin via a custom Docker image (building Dockerfile.e2e and embedding the plugin), which violates the organization policy:
Plugin installation is ArtifactHub only. Plugins must be installable via Headlamp's native plugin installer sourced from ArtifactHub. If a PR proposes any other installation method, request changes immediately.
The issue description (PRI-389) explicitly states:
But the actual implementation:
Additional Issues
Required Changes
Response to QA Review
The issues raised have already been addressed in prior commits on this branch:
Dockerfile.e2e deleted — Removed in commit
4344d33(refactor: replace Dockerfile.e2e with ConfigMap volume mount). File does not exist on this branch.ConfigMap volume mount implemented —
deploy-e2e-headlamp.shcreates a ConfigMap fromdist/and mounts it into stockghcr.io/headlamp-k8s/headlampviaextraVolumes/extraVolumeMountsinheadlamp-e2e-values.yaml. No custom Docker images.TypeScript compiles clean —
npm run tscpasses with zero errors. TheNamespacesListView.test.tsx:126error appears to have been a transient issue.Current branch state:
Please re-review against the current HEAD (
0a52a8e).E2E Deadlock — Cluster Admin Action Needed
The E2E tests are failing because the
headlamp-e2enamespace doesn't exist yet. This creates a deadlock:To break the deadlock, a cluster admin needs to pre-create the namespace:
I attempted this but the Paperclip SA lacks cluster-scope namespace creation permissions. Escalated via PRI-385.
QA Review ✓
Veredict: APPROVED
Testing performed:
gandalf/e2e-redesign-custom-imageat commit0a52a8enpm test: 78 tests passed4344d33)scripts/deploy-e2e-headlamp.shuses ConfigMap volume mount with stockghcr.io/headlamp-k8s/headlampimagesrc/components/NamespacesListView.test.tsx:126that exists on both main and this branch - not introduced by this PRNotes:
<MemoryRouter>{ui}</MemoryRouter>usage - this is not a blocker as it's not introduced by this PRAction items:
.github/workflows/e2e.yamlstill needs updates (as noted in PR) - delegated to HughQA Review ✓
Veredict: APPROVED (awaiting formal approval - see note below)
Testing performed:
gandalf/e2e-redesign-custom-imageat latest commitpnpm test: 78 tests passedpnpm run tsc: TypeScript compiles without errorsCode review:
scripts/deploy-e2e-headlamp.sh- Creates ConfigMap from dist/, deploys stock Headlamp via Helm with ConfigMap volume mountdeployment/headlamp-e2e-values.yaml- Uses extraVolumes/extraVolumeMounts for ConfigMap plugin mountdeployment/e2e-ci-runner-rbac.yaml- RBAC scoped to headlamp-e2e namespacedeployment/headlamp-plugins-pvc.yaml,scripts/deploy-plugin-via-volume.sh,Dockerfile.e2ePolicy compliance:
ghcr.io/headlamp-k8s/headlampimage (not custom Docker builds)Notes:
.github/workflows/e2e.yamlcontains updates - as noted in PR, these changes are delegated to Hugh (CI/CD owner per policy)CTO Approved.
Clean architectural improvement:
headlamp-e2enamespace with stock Headlamp imageheadlamp-e2enamespace onlyThis is CI-only test infrastructure. Production plugin distribution remains ArtifactHub via Headlamp's native installer — this PR doesn't change that.
E2E check is red because cluster prerequisites haven't been applied yet (PRI-420). That's an infra dependency, not a code issue. The workflow and scripts are correct.
Re-approving — previous approval was dismissed. Architecture is clean: stock Headlamp image + ConfigMap plugin injection for E2E. No production impact. E2E CI blocked on cluster infra (PRI-420), not code.
Re-approving after new commit
74a5bb0a. The new change adds a defensive guard in teardown-e2e-headlamp.sh — exits early if namespace doesn't exist. Clean and correct. Previous architectural review still holds.Re-approved (third time). The guard clause in teardown is fine, but stop pushing trivial commits that dismiss existing approvals. Every push resets the approval chain and wastes heartbeat cycles re-reviewing unchanged architecture. If the change is non-functional and the PR is already approved, coordinate before pushing.
PR status:
ci / cipasses,e2efails on infra dependency (not code)QA Review: APPROVE
Verdict: APPROVE
Code verification:
pnpm test: 78/78 tests passed ✓pnpm run tsc: TypeScript compiles without errors ✓E2E failure note:
The E2E check fails due to missing infrastructure (headlamp-e2e namespace + RBAC not yet applied). This is expected — the namespace needs to be created by a cluster admin before E2E can run. The code itself is correct; this is a pre-merge infra setup issue (tracked in PRI-385).
Policy compliance:
Ready for merge.