Merge dev workflow fix (remove container/install step) and add python3
JSON roundtrip to handle Gitea API responses with control characters
that break jq parsing.
Co-Authored-By: Paperclip <noreply@paperclip.ing>
The ubuntu-latest runner host already has curl, jq, and ca-certificates
pre-installed. The apt-get update call inside the Docker container was
failing due to broken container networking on the runner host (runs 577,
578), blocking PR #182 (dev→uat promotion).
Co-Authored-By: Paperclip <noreply@paperclip.ing>
The headlamp-plugin package command outputs filenames with .tar.gz extension,
not .tgz. This caused the "Get tarball path" step to fail (exit code 1) on
the v1.0.1 release run #554.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Add explicit pnpm installation before Install dependencies step.
Without this, ubuntu-latest runner fails with 'pnpm: command not found'
since pnpm is not bundled with the Node 20 action.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
PR #170 merged conflict with old uat version instead of inlined dev version.
Restore inlined dual-approval.yaml to match main, fixing uat->main promotion gate.
Co-Authored-By: Paperclip <noreply@paperclip.ing>
CI triggers on dev/uat/main. Promotion gate replaces dual-approval.
Co-authored-by: Chris Farhood <chris@farhood.org>
Co-authored-by: Paperclip <noreply@paperclip.ing>
The E2E workflow and deploy scripts were targeting the legacy
privilegedescalation-dev namespace, which is not managed by Flux GitOps
in privilegedescalation/infra.
The infra repo (PR #11) already provisions the headlamp-dev namespace
and corresponding RBAC (e2e-ci-runner-headlamp-rbac.yaml) that grants
the ARC runner SA (runners-privilegedescalation-gha-rs-no-permission in
arc-runners) the permissions needed to deploy/teardown the E2E
Headlamp instance.
This change aligns all E2E infrastructure to use headlamp-dev:
- .github/workflows/e2e.yaml: E2E_NAMESPACE=headlamp-dev
- scripts/deploy-e2e-headlamp.sh: default namespace and comments
- scripts/teardown-e2e-headlamp.sh: default namespace
- deployment/e2e-ci-runner-rbac.yaml: namespace and add missing events
permission (already present in infra copy)
Refs: PRI-423
Co-authored-by: Chris Farhood <chris@farhood.org>
Co-authored-by: Paperclip <noreply@paperclip.ing>
When the E2E deploy step fails (rollout timeout, pod not ready, etc.),
previously required manual cluster investigation to diagnose the root
cause. This heartbeat had to grep CI logs and query kubectl separately
to determine a :latest image drift issue.
The new step captures pod state, pod describe output, and recent namespace
events immediately when a failure occurs — surfacing the root cause
directly in the CI run log.
Co-Authored-By: Paperclip <noreply@paperclip.ing>
The :latest tag caused E2E flakiness when a newer Headlamp image was
pulled on some cluster nodes (IfNotPresent policy) but not others.
Concurrent E2E runs on main saw different image versions, and the newest
:latest (sha256:89c6c65) failed to pass the readiness probe within 120s.
Pin to v0.40.1 — the same version running in production (kube-system) —
so all nodes use the same cached digest and CI is deterministic. Update
this pin when Headlamp is upgraded in production.
Co-Authored-By: Paperclip <noreply@paperclip.ing>
cancel-in-progress: true would cancel in-flight E2E runs when a new one
arrives. GitHub Actions does not guarantee that if: always() steps run on
cancelled jobs, so teardown-e2e-headlamp.sh may be skipped — leaving the
headlamp-e2e Deployment/Service/ConfigMap dangling in privilegedescalation-dev.
Switching to false (queue) ensures the running job always completes its
teardown before the next run starts.
Co-Authored-By: Paperclip <noreply@paperclip.ing>
Prevents parallel E2E runs from conflicting over the shared
headlamp-e2e Helm release in privilegedescalation-dev. With
cancel-in-progress: true, a new push cancels any in-progress
run on the same repo — only one E2E suite runs at a time.
Observed failure: PR#109 and PR#108 ran concurrently and the
auth setup in PR#109 timed out, likely due to resource contention
on the shared headlamp-e2e instance.
Co-Authored-By: Paperclip <noreply@paperclip.ing>
Calls the shared privilegedescalation/.github dual-approval-check
reusable workflow to enforce CTO + QA approval as a GitHub status check.
Once privilegedescalation/.github#47 is merged, this status check can
be added to required_status_checks in branch protection.
Co-Authored-By: Paperclip <noreply@paperclip.ing>
The E2E Headlamp instance is deployed without OIDC configuration, so
Headlamp redirects / → /token directly instead of / → /login. The
authenticateWithToken function was hardcoded to expect /login first,
causing a 60s timeout on every run.
- e2e.yaml: remove unused Setup Helm step (deploy script uses kubectl)
- e2e.yaml: remove AUTHENTIK_USERNAME/PASSWORD (no OIDC in E2E instance)
- auth.setup.ts: waitForURL accepts both /login and /token; only clicks
"use a token" if landed on /login (OIDC-configured Headlamp)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Align workflow with org RBAC policy — agents have read-write access only
in privilegedescalation-dev, not the default namespace.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Board directive: E2E tests must run in the `default` namespace.
Nothing should persist beyond a test run; no dedicated namespace needed.
Changes:
- e2e-ci-runner-rbac.yaml: retarget Role/RoleBinding to `default`,
remove ClusterRole/ClusterRoleBinding (no longer needed since we
don't need cluster-scoped namespace read permission)
- e2e.yaml: set E2E_NAMESPACE=default
- deploy-e2e-headlamp.sh: default namespace to `default`, remove
namespace existence check (default always exists)
- teardown-e2e-headlamp.sh: default namespace to `default`, remove
namespace existence check guard
- headlamp-e2e-values.yaml: update usage comment
- e2e/README.md: remove namespace creation prerequisite
Closes#78#79
Co-Authored-By: Paperclip <noreply@paperclip.ing>
The shared release workflow now requires RELEASE_APP_ID and
RELEASE_APP_PRIVATE_KEY secrets for PR creation, since the org
blocks GITHUB_TOKEN from creating PRs.
Depends on privilegedescalation/.github#31
Co-authored-by: privilegedescalation-paperclip[bot] <268365651+privilegedescalation-paperclip[bot]@users.noreply.github.com>
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Replace PVC/kubectl-patch E2E workflow with the new Docker image approach:
- Build custom Headlamp image with plugin pre-installed (Dockerfile.e2e)
- Push to ghcr.io/privilegedescalation/headlamp-polaris-e2e
- Deploy dedicated instance in headlamp-e2e namespace via Helm
- Auto-generate auth token via deploy-e2e-headlamp.sh
- Teardown after tests (always runs)
No more PVCs, kubectl exec/cp, or kube-system deployment patching.
ARC runner scale set label changed from local-ubuntu-latest to
runners-privilegedescalation. The shared workflows were updated in
.github PR #28 but this per-repo e2e workflow was missed.
Co-authored-by: Hugh Hackman <hugh@privilegedescalation.com>
* e2e: shared volume plugin deployment replacing init container approach
Replace the init container plugin installation with a shared PVC volume
between the CI runner and Headlamp pod. The runner builds the plugin and
copies it to the shared mount; Headlamp reads from the same volume.
- Add deployment/headlamp-e2e-values.yaml (PVC-backed shared volume)
- Add deployment/headlamp-plugins-pvc.yaml (PVC manifest)
- Add scripts/deploy-plugin-via-volume.sh (build + copy + restart)
- Remove deployment/headlamp-static-plugin-values.yaml (init container)
This is CI-only test infrastructure — ArtifactHub remains the sole
user-facing distribution channel.
Co-Authored-By: Paperclip <noreply@paperclip.ing>
* ci: update e2e workflow for shared volume plugin deployment
Replace the old preflight-only approach with a build-and-deploy flow
that uses a shared volume (hostPath) between the CI runner and the
Headlamp pod. The workflow now builds the plugin from source, copies
the artifact to a shared volume path, and optionally calls Gandalf's
deploy script for Headlamp rollout coordination.
Removes kubectl exec/cp references and version-match preflight in
favor of deploying the PR's actual build artifact.
Refs: PRI-216, PRI-195
Co-Authored-By: Paperclip <noreply@paperclip.ing>
* ci: align e2e workflow with Gandalf's deploy script interface
Simplify deploy step to call scripts/deploy-plugin-via-volume.sh
directly instead of duplicating copy logic. Align env var names
(PLUGIN_VOLUME_PATH, HEADLAMP_DEPLOY) with the deploy script's
expected interface from PR #59.
Co-Authored-By: Paperclip <noreply@paperclip.ing>
* fix: deploy plugin via temporary pod instead of assuming local PVC mount
The deploy script assumed the PVC was mounted on the CI runner at
/mnt/headlamp-plugins, but the runner pod doesn't have that mount.
Fix by using a temporary pod (kubectl run) that mounts the PVC,
receives the plugin tarball via stdin, and extracts it.
Also adds missing workflow steps to create the PVC and upgrade
Headlamp with the shared volume helm values before deploying.
Co-Authored-By: Paperclip <noreply@paperclip.ing>
* fix: add kubectl, helm, and helm repo setup steps to e2e workflow
The self-hosted runner doesn't have kubectl or helm pre-installed.
Add setup steps using azure/setup-kubectl and azure/setup-helm
actions, and add the Headlamp helm repo before the upgrade step.
Co-Authored-By: Paperclip <noreply@paperclip.ing>
* fix: update Headlamp Helm repo URL from headlamp-k8s to kubernetes-sigs
The Headlamp project moved to the kubernetes-sigs org. The old Helm chart
repository URL (headlamp-k8s.github.io) returns 404, causing E2E workflow
failure at the `helm repo add` step.
Co-Authored-By: Paperclip <noreply@paperclip.ing>
* chore: add RBAC manifest for E2E CI runner
Documents the Role and RoleBinding applied to the cluster for the ARC
runner service account. Grants permissions in kube-system needed for
shared volume plugin deployment (PVCs, pods, Helm resources).
Co-Authored-By: Paperclip <noreply@paperclip.ing>
* fix: remove .github/workflows/e2e.yaml changes from PR
The workflow changes should be handled separately by Hugh Hackman
per PRI-215. This PR should only contain deployment manifests and
scripts, not CI workflow modifications.
Co-Authored-By: Paperclip <noreply@paperclip.ing>
* ci: add shared volume plugin deployment to E2E workflow
Adds the build, Helm, PVC, and plugin deploy steps needed for the
shared volume E2E approach. Uses the correct kubernetes-sigs Helm repo
URL and overrides config.sessionTTL=0 to avoid schema validation error.
This is the workflow counterpart to the deployment manifests and scripts
already in this PR (PVC, values overlay, deploy script).
Co-Authored-By: Paperclip <noreply@paperclip.ing>
* fix(e2e): set sessionTTL=1 to satisfy Helm schema minimum
The Headlamp Helm chart schema enforces a minimum of 1 for
config.sessionTTL. Setting it to 0 caused helm upgrade to fail
with a schema validation error.
Co-Authored-By: Paperclip <noreply@paperclip.ing>
* fix(e2e): add cluster-scoped RBAC for CI runner
The Headlamp Helm chart manages ClusterRole and ClusterRoleBinding
resources. The CI runner SA needs cluster-level permissions to
get/update these during helm upgrade. Added ClusterRole and
ClusterRoleBinding alongside the existing namespace-scoped Role.
Co-Authored-By: Paperclip <noreply@paperclip.ing>
* fix(e2e): replace helm upgrade with kubectl patch to avoid cluster RBAC
The CI runner SA cannot access cluster-scoped resources (ClusterRole,
ClusterRoleBinding) needed by helm upgrade's 3-way merge. Replace the
helm upgrade step with kubectl patch commands that add the shared volume
mount directly to the Headlamp deployment.
This eliminates the need for cluster-admin intervention:
- kubectl patch adds PVC volume + volumeMount to the deployment
- kubectl set env configures the plugins directory
- kubectl rollout status waits for the update
Also removes the now-unnecessary ClusterRole/ClusterRoleBinding from the
RBAC manifest — only namespace-scoped Role/RoleBinding is needed.
Co-Authored-By: Paperclip <noreply@paperclip.ing>
* fix(e2e): improve volume mount idempotency check
Check for existing volume mount by mountPath and PVC claimName, not
just by volume name. A prior helm upgrade may have created mounts
with different names but the same path, causing kubectl patch to fail
with "mountPath must be unique".
Co-Authored-By: Paperclip <noreply@paperclip.ing>
* fix(e2e): schedule deploy pod on same node as Headlamp
The headlamp-plugins PVC is ReadWriteOnce, so the temporary deploy
pod must run on the same node as the Headlamp pod to mount it.
Look up the Headlamp pod's node and set nodeName in the pod spec.
Co-Authored-By: Paperclip <noreply@paperclip.ing>
* fix(e2e): use Job with base64 tarball instead of kubectl run stdin
The kubectl run --rm -i stdin pipe times out in the ARC runner
environment. Replace with a Kubernetes Job that receives the plugin
tarball as base64-encoded data in the container command. This avoids
the unreliable attach/stdin mechanism entirely.
Co-Authored-By: Paperclip <noreply@paperclip.ing>
* fix(e2e): use ConfigMap for tarball instead of inline base64
Embedding base64 data in the YAML spec broke parsing. Store the plugin
tarball in a ConfigMap via --from-file and mount it in the deploy Job.
This avoids both the stdin pipe issue and the YAML escaping issue.
Co-Authored-By: Paperclip <noreply@paperclip.ing>
* fix(e2e): use temp file for Job YAML to avoid heredoc escaping
Variable expansion inside heredocs breaks YAML parsing when values
contain colons and quotes (like nodeName). Write the Job manifest to
a temp file with literal YAML, then sed-substitute the dynamic values.
Co-Authored-By: Paperclip <noreply@paperclip.ing>
* fix(e2e): use Pod instead of Job for plugin deploy
The CI runner SA has permission to create Pods but not Jobs in
kube-system. Switch from a Job to a plain Pod with restartPolicy:Never.
Use ConfigMap mount for tarball data (no stdin piping needed).
Co-Authored-By: Paperclip <noreply@paperclip.ing>
* fix: align registerPluginSettings name with deployed plugin directory
The plugin is deployed to the 'polaris' directory but was registered with
'headlamp-polaris', causing Headlamp to not match the settings component
with the loaded plugin. This fixes all 5 failing E2E settings tests.
Co-Authored-By: Paperclip <noreply@paperclip.ing>
* fix: use package name for registerPluginSettings, not directory name
Headlamp identifies plugins by their package.json name (headlamp-polaris),
not the deploy directory name (polaris). The previous commit incorrectly
changed this to 'polaris', causing the settings component to never render
in the plugin settings page — breaking all 5 E2E settings tests.
Co-Authored-By: Paperclip <noreply@paperclip.ing>
* fix: align registerPluginSettings name with deploy directory 'polaris'
The shared volume deploy script places the plugin at /headlamp/plugins/polaris/,
so Headlamp matches settings by directory name 'polaris', not the package.json
name 'headlamp-polaris'. This reverts commit b9d718b which incorrectly changed
the registration name back to 'headlamp-polaris'.
Co-Authored-By: Paperclip <noreply@paperclip.ing>
* fix: align plugin deploy dir with package.json name, clean stale dirs
The PVC had a stale headlamp-polaris directory from a previous install.
Headlamp loads plugins by scanning the plugins dir and reading package.json
from each subdirectory — it was loading the old build from headlamp-polaris/
while the deploy script was writing to polaris/. The settings registration
name needs to match the plugin name Headlamp identifies.
Changes:
- Deploy script now uses headlamp-polaris as the directory name (matching
package.json name field)
- Deploy pod cleans up both polaris/ and headlamp-polaris/ before deploying
to ensure no stale copies remain
- registerPluginSettings uses headlamp-polaris to match Headlamp's plugin
identifier
Co-Authored-By: Paperclip <noreply@paperclip.ing>
* fix: align registerPluginSettings and E2E test with package.json name
Headlamp identifies plugins by reading package.json from the plugin
directory. Since package.json name is 'headlamp-polaris', both the
registerPluginSettings call and the E2E settings test must use
'headlamp-polaris', not 'polaris'.
- registerPluginSettings('polaris') → registerPluginSettings('headlamp-polaris')
- E2E test locator: text=polaris → text=headlamp-polaris
Co-Authored-By: Paperclip <noreply@paperclip.ing>
* fix(e2e): load main page before settings to ensure plugin list is populated
Headlamp's PluginSettings component initializes its state from
localStorage on mount and never syncs when props.plugins updates later.
If the settings page loads before fetchAndExecutePlugins completes,
the plugin list stays empty and the test can't find "headlamp-polaris".
Fix: navigate to the main page first, wait for the Polaris sidebar
entry to confirm the plugin is loaded (which populates localStorage),
then navigate to the settings page.
Co-Authored-By: Paperclip <noreply@paperclip.ing>
* fix(e2e): use client-side routing for settings navigation
The PluginSettings component reads the plugin registry once on mount
and never re-renders when new plugins register. Using page.goto() for
the settings URL re-initializes the SPA, causing PluginSettings to
mount before async plugin scripts finish calling registerPluginSettings().
Replace page.goto() with pushState + popstate to do client-side routing.
This preserves the already-loaded plugin registrations from the main
page, so PluginSettings sees the plugin immediately on mount.
Co-Authored-By: Paperclip <noreply@paperclip.ing>
* fix(e2e): use correct HOME-context URL for plugin settings page
The settings page is at /settings/plugins (HOME sidebar context), not
/c/main/settings/plugins (in-cluster context). The in-cluster URL
doesn't match any route, so PluginSettings never mounted and the
plugin entry was never visible.
With the correct URL, no preloading or client-side routing hacks are
needed — PluginSettings uses useTypedSelector on the Redux plugin store,
so it re-renders automatically when registerPluginSettings() fires.
Co-Authored-By: Paperclip <noreply@paperclip.ing>
---------
Co-authored-by: Gandalf the Greybeard <gandalf@privilegedescalation.dev>
Co-authored-by: Paperclip <noreply@paperclip.ing>
Co-authored-by: Hugh Hackman <hugh@privilegedescalation.com>
Co-authored-by: Hugh Hackman <hugh-hackman[bot]@users.noreply.github.com>
Polaris is already installed on the CI cluster. The E2E workflow
was failing because the runner SA lacks RBAC to deploy to the
polaris namespace. Remove Setup Helm, Setup kubectl, Deploy Polaris,
Apply RBAC, and Wait for readiness steps.
Resolves: PRI-28, PRI-109
Co-authored-by: Null Pointer Nancy <nancy@privilegedescalation.dev>
Adds Helm-based Polaris dashboard deployment step to E2E workflow, fixing the long-standing E2E failure where Polaris was not accessible in the CI cluster.
Enhances the preflight step to:
- Check the deployed plugin version against the repo version
- Emit a clear warning annotation when there's a mismatch
- Report the plugin name from artifacthub metadata
- Still runs tests (warning, not error) so we catch other issues
This makes plugin version mismatches immediately visible in the
CI summary instead of requiring investigators to dig through
14 timeout failures.
Co-authored-by: hugh-hackman[bot] <hugh-hackman[bot]@users.noreply.github.com>
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Adds a diagnostic step before E2E tests that:
- Logs the expected plugin version from package.json
- Verifies Headlamp is reachable (fails fast if not)
- Attempts to list installed plugins for debugging
This surfaces version mismatches and connectivity issues immediately
instead of requiring analysis of cryptic test timeout failures.
Co-authored-by: gandalf-the-greybeard[bot] <gandalf-the-greybeard[bot]@users.noreply.github.com>
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Configures the reusable release workflow to fetch the latest release
tag from FairwindsOps/polaris and set appVersion in artifacthub-pkg.yml.
This keeps our Artifact Hub listing in sync with the upstream project.
Co-authored-by: Hugh Hackman <hugh@privilegedescalation.dev>
Enable manual triggering of the CI workflow via GitHub Actions UI.
The release workflow already supports workflow_dispatch.
Co-authored-by: hugh-hackman[bot] <hugh-hackman[bot]@users.noreply.github.com>
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
The CI and release workflows use Node 22, but E2E was still on Node 20.
This aligns all workflows to the same Node version for consistency.
Co-authored-by: gandalf-the-greybeard[bot] <gandalf-the-greybeard[bot]@users.noreply.github.com>
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
gh CLI is not installed on the self-hosted runner. Switch to
softprops/action-gh-release@v2 which was used before the
standardization broke it.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>