fb6bdb50c9
E2E still failing at step 11 after --namespace fix. Adding detailed diagnostic output to capture actual kubectl error message before it cascades. Also verifying RoleExists before apply to detect if Flux already synced the RBAC from infra repo. Ref: PRI-851
223 lines
8.2 KiB
YAML
223 lines
8.2 KiB
YAML
name: E2E Tests
|
|
|
|
on:
|
|
push:
|
|
branches: [main]
|
|
pull_request:
|
|
branches: [main]
|
|
workflow_dispatch:
|
|
|
|
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
|
|
|
|
env:
|
|
E2E_NAMESPACE: headlamp-dev
|
|
E2E_RELEASE: headlamp-e2e-argocd
|
|
HEADLAMP_VERSION: v0.40.1
|
|
|
|
jobs:
|
|
e2e:
|
|
runs-on: runners-privilegedescalation
|
|
timeout-minutes: 15
|
|
|
|
steps:
|
|
- name: Checkout
|
|
uses: actions/checkout@v6
|
|
|
|
- name: Detect package manager
|
|
id: pkg-manager
|
|
run: |
|
|
if [ -f "pnpm-lock.yaml" ]; then
|
|
echo "manager=pnpm" >> $GITHUB_OUTPUT
|
|
PM=$(python3 -c "import json,sys; d=json.load(open('package.json')); print('true' if d.get('packageManager','').startswith('pnpm@') else 'false')" 2>/dev/null || echo "false")
|
|
echo "has_package_manager=$PM" >> $GITHUB_OUTPUT
|
|
else
|
|
echo "manager=npm" >> $GITHUB_OUTPUT
|
|
echo "has_package_manager=false" >> $GITHUB_OUTPUT
|
|
fi
|
|
|
|
- name: Setup Node
|
|
uses: actions/setup-node@v6
|
|
with:
|
|
node-version: '22'
|
|
cache: ${{ steps.pkg-manager.outputs.manager == 'npm' && 'npm' || '' }}
|
|
|
|
- name: Setup pnpm (Corepack, respects packageManager field)
|
|
if: steps.pkg-manager.outputs.manager == 'pnpm' && steps.pkg-manager.outputs.has_package_manager == 'true'
|
|
run: |
|
|
npm install -g corepack
|
|
corepack enable pnpm
|
|
corepack prepare $(node -p "require('./package.json').packageManager") --activate
|
|
|
|
- name: Setup pnpm (version latest, no packageManager field)
|
|
if: steps.pkg-manager.outputs.manager == 'pnpm' && steps.pkg-manager.outputs.has_package_manager == 'false'
|
|
uses: pnpm/action-setup@v5
|
|
with:
|
|
run_install: false
|
|
version: latest
|
|
|
|
- name: Get pnpm store directory
|
|
id: pnpm-store
|
|
if: steps.pkg-manager.outputs.manager == 'pnpm'
|
|
run: echo "dir=$(pnpm store path --silent)" >> $GITHUB_OUTPUT
|
|
|
|
- name: Cache pnpm store
|
|
if: steps.pkg-manager.outputs.manager == 'pnpm'
|
|
uses: actions/cache@v5
|
|
with:
|
|
path: ${{ steps.pnpm-store.outputs.dir }}
|
|
key: ${{ runner.os }}-pnpm-${{ hashFiles('**/pnpm-lock.yaml') }}
|
|
restore-keys: |
|
|
${{ runner.os }}-pnpm-
|
|
|
|
- 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
|
|
echo "=== Verifying RBAC is available in headlamp-dev namespace ==="
|
|
kubectl get role e2e-ci-runner -n headlamp-dev && echo "Role e2e-ci-runner already exists" || echo "Role e2e-ci-runner not found"
|
|
kubectl get rolebinding e2e-ci-runner-binding -n headlamp-dev && echo "RoleBinding e2e-ci-runner-binding already exists" || echo "RoleBinding not found"
|
|
echo "Applying RBAC from deployment/e2e-ci-runner-rbac.yaml..."
|
|
kubectl apply -f deployment/e2e-ci-runner-rbac.yaml || echo "apply returned exit code $?"
|
|
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: |
|
|
if [ "${{ steps.pkg-manager.outputs.manager }}" = "pnpm" ]; then
|
|
pnpm install --frozen-lockfile
|
|
else
|
|
npm ci
|
|
fi
|
|
|
|
- name: Build plugin
|
|
run: npx @kinvolk/headlamp-plugin build
|
|
|
|
- name: Deploy E2E Headlamp instance
|
|
run: scripts/deploy-e2e-headlamp.sh
|
|
|
|
- name: Load E2E environment
|
|
run: |
|
|
if [ -f .env.e2e ]; then
|
|
cat .env.e2e >> "$GITHUB_ENV"
|
|
else
|
|
echo "::error::deploy-e2e-headlamp.sh did not produce .env.e2e"
|
|
exit 1
|
|
fi
|
|
|
|
- name: Install Playwright browsers
|
|
run: |
|
|
if [ "${{ steps.pkg-manager.outputs.manager }}" = "pnpm" ]; then
|
|
pnpm exec playwright install --with-deps chromium
|
|
else
|
|
npx playwright install --with-deps chromium
|
|
fi
|
|
|
|
- name: Run E2E tests
|
|
run: |
|
|
if [ "${{ steps.pkg-manager.outputs.manager }}" = "pnpm" ]; then
|
|
pnpm run e2e
|
|
else
|
|
npm run e2e
|
|
fi
|
|
env:
|
|
HEADLAMP_URL: ${{ env.HEADLAMP_URL }}
|
|
HEADLAMP_TOKEN: ${{ env.HEADLAMP_TOKEN }}
|
|
|
|
- name: Collect deployment diagnostics on failure
|
|
if: failure()
|
|
run: |
|
|
echo "=== Pod state ==="
|
|
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
|
|
|
|
- name: Teardown E2E instance
|
|
if: always()
|
|
run: scripts/teardown-e2e-headlamp.sh
|
|
|
|
- name: Upload Playwright report
|
|
uses: actions/upload-artifact@v7
|
|
if: failure()
|
|
with:
|
|
name: playwright-report
|
|
path: playwright-report/
|
|
retention-days: 7
|
|
|
|
- name: Upload test results
|
|
uses: actions/upload-artifact@v7
|
|
if: failure()
|
|
with:
|
|
name: test-results
|
|
path: test-results/
|
|
retention-days: 7
|