refactor: redesign E2E to use custom Docker image instead of PVC/kubectl

Replace the PVC + kubectl-patch approach for E2E plugin deployment with
a custom Docker image that has the plugin pre-installed. This eliminates
all policy-violating operations:

- No PVCs in kube-system
- No kubectl exec/cp to Headlamp pods
- No deployment patching via kubectl
- No temporary pods or ConfigMap-based file transfers

The new approach builds a Headlamp image with the plugin baked in
(Dockerfile.e2e), deploys it as a dedicated instance in the headlamp-e2e
namespace via Helm, and tears it down after tests complete.

RBAC is scoped to the headlamp-e2e namespace instead of kube-system.

Note: .github/workflows/e2e.yaml still needs updating to use the new
scripts — that change is delegated to Hugh (CI/CD owner).

Closes: privilegedescalation/headlamp-polaris-plugin#72

Co-Authored-By: Paperclip <noreply@paperclip.ing>
This commit is contained in:
Gandalf the Greybeard
2026-03-20 00:33:09 +00:00
parent 4296eb97fb
commit 6189f2b983
9 changed files with 219 additions and 221 deletions
+20 -32
View File
@@ -1,57 +1,45 @@
---
# RBAC for the GitHub Actions CI runner to perform E2E test setup.
# RBAC for the GitHub Actions CI runner to manage the E2E Headlamp instance.
# CI-only test fixture — NOT for production use.
#
# Grants the ARC runner service account namespace-scoped permissions in
# kube-system to patch the Headlamp deployment (add shared volume mount),
# manage PVCs, run temporary pods, and restart deployments.
# Grants the ARC runner service account permissions in the headlamp-e2e
# namespace to deploy and tear down a dedicated Headlamp instance via Helm.
#
# No cluster-scoped permissions needed — the E2E workflow uses kubectl patch
# instead of helm upgrade, avoiding the need to read ClusterRole/ClusterRoleBinding.
# No kube-system access needed — E2E tests use a separate namespace.
# No PVC management — plugin is baked into the Docker image.
#
# Apply with: kubectl apply -f deployment/e2e-ci-runner-rbac.yaml
# Prerequisites:
# kubectl create namespace headlamp-e2e
# kubectl apply -f deployment/e2e-ci-runner-rbac.yaml
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: e2e-ci-runner
namespace: kube-system
namespace: headlamp-e2e
rules:
# Helm needs to manage these resources for the Headlamp chart
- apiGroups: ["apps"]
resources: ["deployments"]
verbs: ["get", "list", "create", "update", "patch", "delete", "watch"]
- apiGroups: [""]
resources: ["persistentvolumeclaims"]
resources: ["services", "serviceaccounts", "configmaps", "secrets"]
verbs: ["get", "list", "create", "update", "patch", "delete"]
- apiGroups: [""]
resources: ["pods"]
verbs: ["get", "list", "create", "delete", "watch"]
verbs: ["get", "list", "watch"]
# Token creation for E2E test auth
- apiGroups: [""]
resources: ["pods/attach"]
verbs: ["create", "get"]
- apiGroups: ["apps"]
resources: ["deployments"]
verbs: ["get", "list", "patch", "watch"]
- apiGroups: ["apps"]
resources: ["deployments/scale"]
verbs: ["patch"]
- apiGroups: [""]
resources: ["secrets"]
verbs: ["get", "list", "create", "update", "patch", "delete"]
- apiGroups: [""]
resources: ["configmaps"]
verbs: ["get", "list", "create", "update", "patch", "delete"]
- apiGroups: [""]
resources: ["services"]
verbs: ["get", "list", "create", "update", "patch", "delete"]
- apiGroups: [""]
resources: ["serviceaccounts"]
verbs: ["get", "list"]
resources: ["serviceaccounts/token"]
verbs: ["create"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: e2e-ci-runner-binding
namespace: kube-system
namespace: headlamp-e2e
subjects:
- kind: ServiceAccount
name: local-ubuntu-latest-gha-rs-no-permission
name: runners-privilegedescalation-gha-rs-no-permission
namespace: arc-runners
roleRef:
kind: Role
+17 -16
View File
@@ -1,22 +1,23 @@
---
# Headlamp Helm values for E2E testing with shared volume plugin deployment.
# Headlamp Helm values for E2E testing.
#
# The CI runner and Headlamp pod share a PVC so that the runner can copy
# built plugin artifacts directly into Headlamp's plugins directory.
# This is a CI-only mechanism — production plugin distribution uses ArtifactHub.
# Uses a custom Docker image (built from Dockerfile.e2e) with the plugin
# pre-installed. No PVCs, no volume mounts, no deployment patching.
#
# The E2E workflow builds the image, pushes to ghcr.io, and deploys this
# Helm release in the headlamp-e2e namespace.
#
# Usage:
# helm install headlamp-e2e headlamp/headlamp \
# -n headlamp-e2e --create-namespace \
# -f deployment/headlamp-e2e-values.yaml \
# --set image.registry=ghcr.io \
# --set image.repository=privilegedescalation/headlamp-polaris-e2e \
# --set image.tag=<sha>
# Point Headlamp at the shared plugins mount
config:
pluginsDir: /headlamp/plugins
watchPlugins: false
# PVC-backed volume shared with the CI runner
volumes:
- name: plugins
persistentVolumeClaim:
claimName: headlamp-plugins
# Mount into the Headlamp container
volumeMounts:
- name: plugins
mountPath: /headlamp/plugins
readOnly: true
service:
type: ClusterIP
-14
View File
@@ -1,14 +0,0 @@
---
# PVC for sharing built plugin artifacts between the CI runner and Headlamp.
# Used only in E2E test environments — not for production.
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: headlamp-plugins
namespace: kube-system
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 128Mi