Compare commits

..

14 Commits

Author SHA1 Message Date
Chris Farhood 5997fb5e7e fix(e2e): remove pods/log from RBAC to fix escalation error
The CI runner SA cannot grant permissions it doesn't hold.
Adding pods/log caused: "attempting to grant RBAC permissions
not currently held". Remove it to match the infra-managed Role.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-05-06 16:54:49 +00:00
Chris Farhood 420504a43d chore: trigger E2E workflow 2026-05-06 16:54:49 +00:00
privilegedescalation-ceo[bot] 15069c368a ci(e2e): trigger PR workflow check via API commit 2026-05-06 16:54:49 +00:00
Chris Farhood 9143847019 fix(e2e): add kubeconfig setup, RBAC, kubectl logs diagnostics
- Add Get kubeconfig step (matches polaris pattern for ARC runners)
- Add Apply RBAC for E2E pipeline step + deployment/e2e-ci-runner-rbac.yaml
- Pin kubectl to latest (addresses azure/setup-kubectl@v4 Node.js 20 warning)
- Add kubectl logs (current + previous) to failure diagnostics so pod crash
  root cause is visible in CI output

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-05-06 16:54:49 +00:00
Chris Farhood 5931220ee3 fix(e2e): set executable bit on deploy/teardown scripts
Scripts were committed as 100644 which causes exit code 126
(Permission denied) when the CI runner tries to execute them.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-05-06 16:54:49 +00:00
Chris Farhood fe158777ce fix(e2e): use pnpm with proper detection and E2E_RELEASE config
The argocd-plugin uses pnpm (packageManager: pnpm@10.32.1) but the
original inline workflow used npm commands (npm ci, cache: npm).
This caused the workflow to fail.

Switch to pnpm detection and commands while preserving the
E2E_RELEASE=headlamp-e2e-argocd setting required by the deploy script.
2026-05-06 16:54:49 +00:00
Chris Farhood 3e73cc376d fix(e2e): use pnpm-capable reusable workflow
The argocd plugin uses pnpm (packageManager: pnpm@10.32.1) but the
inline workflow was using npm-based commands (npm ci, cache: npm).
This caused 'Setup Node.js' to fail because setup-node@v6 with
cache: npm has issues when no package-lock.json exists.

Switch to the reusable plugin-e2e workflow which properly detects
and uses pnpm for projects with pnpm-lock.yaml.
2026-05-06 16:54:22 +00:00
privilegedescalation-engineer[bot] 1c27b354af Add @playwright/test devDependency 2026-05-06 16:54:22 +00:00
privilegedescalation-engineer[bot] 6cac81159d Add playwright.config.ts 2026-05-06 16:53:59 +00:00
privilegedescalation-engineer[bot] 5b030e4c25 Add e2e/auth.setup.ts 2026-05-06 16:53:59 +00:00
privilegedescalation-engineer[bot] fe8397172a Add e2e/argocd.spec.ts 2026-05-06 16:53:59 +00:00
privilegedescalation-engineer[bot] dc7a5fd23c Add scripts/teardown-e2e-headlamp.sh 2026-05-06 16:53:59 +00:00
privilegedescalation-engineer[bot] eb8b965e71 Add scripts/deploy-e2e-headlamp.sh 2026-05-06 16:53:59 +00:00
privilegedescalation-engineer[bot] 090943f7d9 Add .github/workflows/e2e.yaml 2026-05-06 16:53:59 +00:00
4 changed files with 105 additions and 42 deletions
+69 -1
View File
@@ -10,6 +10,9 @@ on:
permissions:
contents: read
# Only one E2E run at a time — the shared E2E_RELEASE in headlamp-dev cannot
# be shared across concurrent runs. cancel-in-progress: false queues rather
# than cancels to avoid skipping the teardown step.
concurrency:
group: e2e-${{ github.repository }}
cancel-in-progress: false
@@ -76,6 +79,65 @@ jobs:
- name: Setup kubectl
uses: azure/setup-kubectl@v4
with:
version: 'latest'
- name: Get kubeconfig
run: |
set -euo pipefail
echo "=== Runner kubeconfig diagnostic ==="
echo "KUBECONFIG=${KUBECONFIG:-}"
for path in /runner/config /home/runner/.kube/config "${HOME:-}/.kube/config"; do
if [ -f "$path" ]; then
echo "FOUND kubeconfig at: $path"
fi
done
echo ""
echo "=== In-cluster service account check ==="
in_cluster=false
if [ -f /var/run/secrets/kubernetes.io/serviceaccount/token ]; then
echo "Service account token present — in-cluster mode available"
in_cluster=true
fi
if [ -f /runner/config ]; then
echo "KUBECONFIG=/runner/config" >> "$GITHUB_ENV"
elif [ -f /home/runner/.kube/config ]; then
echo "KUBECONFIG=/home/runner/.kube/config" >> "$GITHUB_ENV"
elif [ -f "${HOME:-}/.kube/config" ]; then
echo "KUBECONFIG=${HOME:-}/.kube/config" >> "$GITHUB_ENV"
elif [ "$in_cluster" = true ]; then
echo "No static kubeconfig found — generating in-cluster kubeconfig"
KUBECFG_DIR="${HOME:-}/.kube"
mkdir -p "$KUBECFG_DIR"
kubectl config set-cluster in-cluster \
--server="https://${KUBERNETES_SERVICE_HOST:-kubernetes.default.svc}:${KUBERNETES_SERVICE_PORT:-443}" \
--certificate-authority=/var/run/secrets/kubernetes.io/serviceaccount/ca.crt \
--embed-certs=true \
--kubeconfig="$KUBECFG_DIR/config"
kubectl config set-credentials in-cluster \
--token="$(cat /var/run/secrets/kubernetes.io/serviceaccount/token)" \
--kubeconfig="$KUBECFG_DIR/config"
kubectl config set-context in-cluster \
--cluster=in-cluster \
--user=in-cluster \
--kubeconfig="$KUBECFG_DIR/config"
kubectl config use-context in-cluster \
--kubeconfig="$KUBECFG_DIR/config"
echo "KUBECONFIG=$KUBECFG_DIR/config" >> "$GITHUB_ENV"
else
echo "::error::No kubeconfig found"
exit 1
fi
- name: Apply RBAC for E2E pipeline
run: |
set -x
kubectl apply -f deployment/e2e-ci-runner-rbac.yaml
echo "Waiting for RBAC propagation..."
sleep 5
kubectl get role e2e-ci-runner -n headlamp-dev
kubectl get rolebinding e2e-ci-runner-binding -n headlamp-dev 2>&1 | tail -3 || true
set +x
- name: Install dependencies
run: |
@@ -126,6 +188,12 @@ jobs:
kubectl get pods -n "$E2E_NAMESPACE" -l "app.kubernetes.io/instance=$E2E_RELEASE" 2>&1 || true
echo "=== Pod describe ==="
kubectl describe pods -n "$E2E_NAMESPACE" -l "app.kubernetes.io/instance=$E2E_RELEASE" 2>&1 || true
echo "=== Container logs (current) ==="
kubectl logs -n "$E2E_NAMESPACE" -l "app.kubernetes.io/instance=$E2E_RELEASE" \
--tail=100 2>&1 || true
echo "=== Container logs (previous, if crashed) ==="
kubectl logs -n "$E2E_NAMESPACE" -l "app.kubernetes.io/instance=$E2E_RELEASE" \
--previous --tail=100 2>&1 || true
echo "=== Recent namespace events ==="
kubectl get events -n "$E2E_NAMESPACE" --sort-by='.lastTimestamp' 2>&1 | tail -20 || true
@@ -147,4 +215,4 @@ jobs:
with:
name: test-results
path: test-results/
retention-days: 7
retention-days: 7
+33
View File
@@ -0,0 +1,33 @@
---
# 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 permissions in the headlamp-dev
# namespace to deploy and tear down a dedicated Headlamp instance.
# E2E resources run in `headlamp-dev` — nothing persists beyond a test run.
#
# Plugin is loaded via ConfigMap volume mount — no custom Docker images.
#
# Note: This RBAC is mirrored in privilegedescalation/infra (base/rbac/)
# and managed by Flux GitOps. The infra repo is the source of truth.
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: e2e-ci-runner
namespace: headlamp-dev
rules:
- apiGroups: ["apps"]
resources: ["deployments"]
verbs: ["get", "list", "create", "update", "patch", "delete", "watch"]
- apiGroups: [""]
resources: ["services", "serviceaccounts", "configmaps", "secrets", "events"]
verbs: ["get", "list", "watch", "create", "update", "patch", "delete"]
- apiGroups: [""]
resources: ["pods"]
verbs: ["get", "list", "watch"]
- apiGroups: [""]
resources: ["serviceaccounts/token"]
verbs: ["create"]
- apiGroups: ["rbac.authorization.k8s.io"]
resources: ["roles", "rolebindings"]
verbs: ["get", "list", "create", "update", "patch", "delete"]
+3 -3
View File
@@ -1,7 +1,7 @@
{
"name": "@privilegedescalation/headlamp-argocd-plugin",
"version": "0.1.2",
"description": "Headlamp plugin for ArgoCD visibility \u2014 monitors ArgoCD Applications, Rollouts, and health status",
"description": "Headlamp plugin for ArgoCD visibility monitors ArgoCD Applications, Rollouts, and health status",
"repository": {
"type": "git",
"url": "https://github.com/privilegedescalation/headlamp-argocd-plugin.git"
@@ -40,6 +40,7 @@
}
},
"devDependencies": {
"@playwright/test": "^1.58.2",
"@kinvolk/headlamp-plugin": "^0.13.0",
"@mui/material": "^5.15.14",
"@testing-library/jest-dom": "^6.4.8",
@@ -56,7 +57,6 @@
"react-dom": "^18.3.1",
"react-router-dom": "^5.3.0",
"typescript": "~5.6.2",
"vitest": "^3.0.5",
"@playwright/test": "^1.58.2"
"vitest": "^3.0.5"
}
}
-38
View File
@@ -23,9 +23,6 @@ importers:
'@mui/material':
specifier: ^5.15.14
version: 5.18.0(@emotion/react@11.14.0(@types/react@19.2.14)(react@18.3.1))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.2.14)(react@18.3.1))(@types/react@19.2.14)(react@18.3.1))(@types/react@19.2.14)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
'@playwright/test':
specifier: ^1.58.2
version: 1.59.1
'@testing-library/jest-dom':
specifier: ^6.4.8
version: 6.9.1
@@ -1005,11 +1002,6 @@ packages:
resolution: {integrity: sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==}
engines: {node: '>=14'}
'@playwright/test@1.59.1':
resolution: {integrity: sha512-PG6q63nQg5c9rIi4/Z5lR5IVF7yU5MqmKaPOe0HSc0O2cX1fPi96sUQu5j7eo4gKCkB2AnNGoWt7y4/Xx3Kcqg==}
engines: {node: '>=18'}
hasBin: true
'@popperjs/core@2.11.8':
resolution: {integrity: sha512-P1st0aksCrn9sGZhp8GMYwBnQsbvAWsZAX44oXNNvLHGqAOcoVxmjZiohstwQ7SqKnbR47akdNi+uleWD8+g6A==}
@@ -3091,11 +3083,6 @@ packages:
fs.realpath@1.0.0:
resolution: {integrity: sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==}
fsevents@2.3.2:
resolution: {integrity: sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==}
engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0}
os: [darwin]
fsevents@2.3.3:
resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==}
engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0}
@@ -4235,16 +4222,6 @@ packages:
resolution: {integrity: sha512-NPE8TDbzl/3YQYY7CSS228s3g2ollTFnc+Qi3tqmqJp9Vg2ovUpixcJEo2HJScN2Ez+kEaal6y70c0ehqJBJeA==}
engines: {node: '>=10'}
playwright-core@1.59.1:
resolution: {integrity: sha512-HBV/RJg81z5BiiZ9yPzIiClYV/QMsDCKUyogwH9p3MCP6IYjUFu/MActgYAvK0oWyV9NlwM3GLBjADyWgydVyg==}
engines: {node: '>=18'}
hasBin: true
playwright@1.59.1:
resolution: {integrity: sha512-C8oWjPR3F81yljW9o5OxcWzfh6avkVwDD2VYdwIGqTkl+OGFISgypqzfu7dOe4QNLL2aqcWBmI3PMtLIK233lw==}
engines: {node: '>=18'}
hasBin: true
possible-typed-array-names@1.1.0:
resolution: {integrity: sha512-/+5VFTchJDoVj3bhoqi6UeymcD00DAwb1nJwamzPvHEszJ4FpF6SNNbUbOS8yI56qHzdV8eK0qEfOSiodkTdxg==}
engines: {node: '>= 0.4'}
@@ -6651,10 +6628,6 @@ snapshots:
'@pkgjs/parseargs@0.11.0':
optional: true
'@playwright/test@1.59.1':
dependencies:
playwright: 1.59.1
'@popperjs/core@2.11.8': {}
'@reduxjs/toolkit@2.11.2(react-redux@9.2.0(@types/react@18.3.28)(react@18.3.1)(redux@5.0.1))(react@18.3.1)':
@@ -9213,9 +9186,6 @@ snapshots:
fs.realpath@1.0.0: {}
fsevents@2.3.2:
optional: true
fsevents@2.3.3:
optional: true
@@ -10598,14 +10568,6 @@ snapshots:
dependencies:
find-up: 5.0.0
playwright-core@1.59.1: {}
playwright@1.59.1:
dependencies:
playwright-core: 1.59.1
optionalDependencies:
fsevents: 2.3.2
possible-typed-array-names@1.1.0: {}
postcss-modules-extract-imports@3.1.0(postcss@8.5.10):