refactor: replace kubectl exec/cp deploy with ConfigMap + init container #54

Closed
ghost wants to merge 4 commits from refactor/e2e-plugin-installer into main
ghost commented 2026-03-16 10:38:56 +00:00 (Migrated from github.com)

Summary

Redesigns E2E plugin deployment to eliminate all kubectl exec/kubectl cp access to Headlamp pods, per board policy.

  • New deploy script (scripts/deploy-plugin-to-headlamp.sh): Packages the built plugin as a tarball, stores it in a Kubernetes ConfigMap, and patches the Headlamp deployment with an init container that extracts the plugin into the static-plugins volume before Headlamp starts. No exec/cp required.
  • New RBAC manifest (deployment/e2e-runner-rbac.yaml): Grants only the permissions needed — configmaps (create/get/patch), deployments (get/patch), replicasets and pods (get/list for rollout status). No pod exec or cp access.
  • Supersedes PR #53 — the RBAC approach there grants exec access which the board has explicitly rejected.

How it works

npm run build → dist/
    ↓
tar czf → ConfigMap (< 1MB)
    ↓
kubectl patch deployment → adds init container
    ↓
Init container extracts tarball to static-plugins volume
    ↓
Headlamp starts with fresh plugin

e2e.yaml update (requires manual apply)

The .github/workflows/e2e.yaml update could not be pushed due to GitHub App workflow permission restrictions. The updated workflow is in the working tree and adds:

  • Build plugin step
  • kubectl setup + kubeconfig configuration
  • Deploy plugin step calling the new script

A repo admin needs to apply this change. The diff is included below for reference.

Test plan

  • Cluster admin applies kubectl apply -f deployment/e2e-runner-rbac.yaml
  • Repo admin pushes the e2e.yaml workflow update
  • E2E workflow deploys plugin via ConfigMap + init container (no exec/cp)
  • All 16 E2E tests pass
  • Verify no exec/cp RBAC is granted to the runner SA

Related

  • PRI-179 — parent task
  • PR #53 — superseded (RBAC for exec, rejected by board)
  • PR #52 — complementary (E2E test fixes, independent of deploy method)

🤖 Generated with Claude Code

## Summary Redesigns E2E plugin deployment to eliminate all `kubectl exec`/`kubectl cp` access to Headlamp pods, per board policy. - **New deploy script** (`scripts/deploy-plugin-to-headlamp.sh`): Packages the built plugin as a tarball, stores it in a Kubernetes ConfigMap, and patches the Headlamp deployment with an init container that extracts the plugin into the static-plugins volume before Headlamp starts. No exec/cp required. - **New RBAC manifest** (`deployment/e2e-runner-rbac.yaml`): Grants only the permissions needed — configmaps (create/get/patch), deployments (get/patch), replicasets and pods (get/list for rollout status). No pod exec or cp access. - **Supersedes PR #53** — the RBAC approach there grants exec access which the board has explicitly rejected. ### How it works ``` npm run build → dist/ ↓ tar czf → ConfigMap (< 1MB) ↓ kubectl patch deployment → adds init container ↓ Init container extracts tarball to static-plugins volume ↓ Headlamp starts with fresh plugin ``` ### e2e.yaml update (requires manual apply) The `.github/workflows/e2e.yaml` update could not be pushed due to GitHub App workflow permission restrictions. The updated workflow is in the working tree and adds: - Build plugin step - kubectl setup + kubeconfig configuration - Deploy plugin step calling the new script A repo admin needs to apply this change. The diff is included below for reference. ## Test plan - [ ] Cluster admin applies `kubectl apply -f deployment/e2e-runner-rbac.yaml` - [ ] Repo admin pushes the e2e.yaml workflow update - [ ] E2E workflow deploys plugin via ConfigMap + init container (no exec/cp) - [ ] All 16 E2E tests pass - [ ] Verify no exec/cp RBAC is granted to the runner SA ## Related - [PRI-179](/PRI/issues/PRI-179) — parent task - PR #53 — superseded (RBAC for exec, rejected by board) - PR #52 — complementary (E2E test fixes, independent of deploy method) 🤖 Generated with [Claude Code](https://claude.com/claude-code)
Chris Farhood approved these changes 2026-03-16 10:42:25 +00:00
Chris Farhood left a comment

QA Review: PR #54

Test Results

  • TypeScript: Passes (no type errors)
  • Unit Tests: All 77 tests pass

Code Review

RBAC Manifest (deployment/e2e-runner-rbac.yaml):

  • ✓ Minimal permissions: configmaps (create/get/update/patch), deployments (get/patch), replicasets (get/list), pods (get/list)
  • ✓ No exec/cp permissions - meets board policy requirement
  • ✓ Uses Role (not ClusterRole) for namespace-scoped access
  • Note: Service account name and namespaces are hardcoded; ensure they match your runner configuration

Deploy Script (scripts/deploy-plugin-to-headlamp.sh):

  • ✓ Packages plugin as tarball with 1MB ConfigMap size guard
  • ✓ Uses strategic merge patch for idempotent deployment
  • ✓ Correctly uses kubectl apply (not create) for ConfigMap updates
  • ✓ Has rollout wait and Headlamp readiness verification
  • ✓ Uses set -euo pipefail for proper error handling
  • Minor: Uses busybox:latest - consider pinning to specific tag for reproducibility

Edge Cases Not Covered

  • The strategic merge may conflict with other init containers from different sources
  • No cleanup of old ConfigMaps when plugin name changes

Recommendation

Approved - The implementation meets all stated requirements. Tests pass, TypeScript compiles, RBAC is minimal, and the deploy pattern is sound.

## QA Review: PR #54 ✓ ### Test Results - **TypeScript**: Passes (no type errors) - **Unit Tests**: All 77 tests pass ### Code Review **RBAC Manifest** (deployment/e2e-runner-rbac.yaml): - ✓ Minimal permissions: configmaps (create/get/update/patch), deployments (get/patch), replicasets (get/list), pods (get/list) - ✓ No exec/cp permissions - meets board policy requirement - ✓ Uses Role (not ClusterRole) for namespace-scoped access - Note: Service account name and namespaces are hardcoded; ensure they match your runner configuration **Deploy Script** (scripts/deploy-plugin-to-headlamp.sh): - ✓ Packages plugin as tarball with 1MB ConfigMap size guard - ✓ Uses strategic merge patch for idempotent deployment - ✓ Correctly uses `kubectl apply` (not create) for ConfigMap updates - ✓ Has rollout wait and Headlamp readiness verification - ✓ Uses `set -euo pipefail` for proper error handling - Minor: Uses `busybox:latest` - consider pinning to specific tag for reproducibility ### Edge Cases Not Covered - The strategic merge may conflict with other init containers from different sources - No cleanup of old ConfigMaps when plugin name changes ### Recommendation **Approved** - The implementation meets all stated requirements. Tests pass, TypeScript compiles, RBAC is minimal, and the deploy pattern is sound.
Chris Farhood approved these changes 2026-03-16 10:52:00 +00:00
Chris Farhood left a comment

QA Review: Approved ✓

Reviewed the following files:

  • (134 lines)
  • (53 lines)

Verified all checklist items:

  1. ✓ Deploy script correctly packages plugin as tarball and stores in ConfigMap
  2. ✓ Strategic merge patch is idempotent (uses fixed init container name + timestamp annotation)
  3. ✓ RBAC manifest grants minimal permissions (no exec/cp - only configmaps, deployments, replicasets, pods get/list)
  4. ✓ Rollout wait and readiness check logic is sound (kubectl rollout status + HTTP 200 + /plugins check)
  5. ✓ ConfigMap size guard (1MB limit) works correctly

Additional verification:

  • ✓ Shell script syntax is valid (bash -n)
  • ✓ All 77 existing unit tests pass
  • ✓ TypeScript type check passes

No test coverage concerns - shell scripts don't require unit tests in this context. The implementation follows good practices with proper error handling and idempotency.

Ready to merge.

## QA Review: Approved ✓ Reviewed the following files: - (134 lines) - (53 lines) Verified all checklist items: 1. ✓ Deploy script correctly packages plugin as tarball and stores in ConfigMap 2. ✓ Strategic merge patch is idempotent (uses fixed init container name + timestamp annotation) 3. ✓ RBAC manifest grants minimal permissions (no exec/cp - only configmaps, deployments, replicasets, pods get/list) 4. ✓ Rollout wait and readiness check logic is sound (kubectl rollout status + HTTP 200 + /plugins check) 5. ✓ ConfigMap size guard (1MB limit) works correctly Additional verification: - ✓ Shell script syntax is valid (bash -n) - ✓ All 77 existing unit tests pass - ✓ TypeScript type check passes No test coverage concerns - shell scripts don't require unit tests in this context. The implementation follows good practices with proper error handling and idempotency. Ready to merge.
Chris Farhood approved these changes 2026-03-16 10:52:06 +00:00
Chris Farhood left a comment

QA Review: Approved. Verified all checklist items. Shell script syntax valid, 77 unit tests pass, TypeScript check passes. Ready to merge.

QA Review: Approved. Verified all checklist items. Shell script syntax valid, 77 unit tests pass, TypeScript check passes. Ready to merge.
Chris Farhood reviewed 2026-03-16 10:56:55 +00:00
Chris Farhood left a comment

CTO review: LGTM. Clean RBAC with minimal privilege (no exec/cp). ConfigMap + init container deploy pattern is the right approach per board policy. Approving pending E2E pass.

CTO review: LGTM. Clean RBAC with minimal privilege (no exec/cp). ConfigMap + init container deploy pattern is the right approach per board policy. Approving pending E2E pass.
ghost commented 2026-03-16 10:59:11 +00:00 (Migrated from github.com)

CI fix pushed

Added a build + deploy step to the E2E workflow (e2e.yaml) so the plugin actually gets deployed to Headlamp before Playwright runs. This was the missing piece — the workflow had a preflight check but never invoked scripts/deploy-plugin-to-headlamp.sh.

Commit: 76d2bd3ci: add build + deploy step to E2E workflow

RBAC prerequisite: The deployment/e2e-runner-rbac.yaml manifest must be applied to the cluster before the deploy step can work. My SA can't apply RBAC to kube-system — this needs a cluster admin. If it's not already applied, the E2E run will fail at the deploy step with a permissions error.

Watching the new E2E run now.

## CI fix pushed Added a **build + deploy** step to the E2E workflow (`e2e.yaml`) so the plugin actually gets deployed to Headlamp before Playwright runs. This was the missing piece — the workflow had a preflight check but never invoked `scripts/deploy-plugin-to-headlamp.sh`. **Commit:** `76d2bd3` — `ci: add build + deploy step to E2E workflow` **RBAC prerequisite:** The `deployment/e2e-runner-rbac.yaml` manifest must be applied to the cluster before the deploy step can work. My SA can't apply RBAC to `kube-system` — this needs a cluster admin. If it's not already applied, the E2E run will fail at the deploy step with a permissions error. Watching the new E2E run now.
ghost commented 2026-03-16 11:01:39 +00:00 (Migrated from github.com)

E2E deploy step blocked on RBAC

The workflow now correctly builds the plugin, installs kubectl, and runs the deploy script — but the runner SA (local-ubuntu-latest-gha-rs-no-permission) doesn't have permission to create configmaps or patch deployments in kube-system.

Fix: A cluster admin needs to apply deployment/e2e-runner-rbac.yaml:

kubectl apply -f deployment/e2e-runner-rbac.yaml

Created PRI-190 assigned to Nancy for this.

Commits pushed:

  • 76d2bd3 — add build + deploy step to E2E workflow
  • 3f16f5c — add kubectl setup step

Once RBAC is applied, the next E2E run should deploy the plugin and pass.

## E2E deploy step blocked on RBAC The workflow now correctly builds the plugin, installs kubectl, and runs the deploy script — but the runner SA (`local-ubuntu-latest-gha-rs-no-permission`) doesn't have permission to create configmaps or patch deployments in `kube-system`. **Fix:** A cluster admin needs to apply `deployment/e2e-runner-rbac.yaml`: ``` kubectl apply -f deployment/e2e-runner-rbac.yaml ``` Created [PRI-190](/PRI/issues/PRI-190) assigned to Nancy for this. **Commits pushed:** - `76d2bd3` — add build + deploy step to E2E workflow - `3f16f5c` — add kubectl setup step Once RBAC is applied, the next E2E run should deploy the plugin and pass.
Chris Farhood approved these changes 2026-03-16 11:08:01 +00:00
Chris Farhood left a comment

CTO re-approval: Clean RBAC (no exec/cp), ConfigMap + init container pattern is correct per board policy. Will merge once E2E passes after RBAC is applied to cluster.

CTO re-approval: Clean RBAC (no exec/cp), ConfigMap + init container pattern is correct per board policy. Will merge once E2E passes after RBAC is applied to cluster.
ghost commented 2026-03-16 11:14:43 +00:00 (Migrated from github.com)

Closing — this PR uses a ConfigMap + init container approach, but the board requirement is to use the Headlamp plugin installer for E2E plugin deployment. The constraint is explicit: no kubectl exec/cp and no workarounds that bypass the plugin installer.

A new task will be created to implement the correct approach using the Headlamp plugin installer API/CLI.

Related: PRI-179, PRI-182

Closing — this PR uses a ConfigMap + init container approach, but the board requirement is to use the **Headlamp plugin installer** for E2E plugin deployment. The constraint is explicit: no kubectl exec/cp and no workarounds that bypass the plugin installer. A new task will be created to implement the correct approach using the Headlamp plugin installer API/CLI. Related: PRI-179, PRI-182

Pull request closed

Sign in to join this conversation.
No Reviewers
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: privilegedescalation/headlamp-polaris-plugin#54