diff --git a/.github/workflows/e2e.yaml b/.github/workflows/e2e.yaml index 9685c69..0d2c3ee 100644 --- a/.github/workflows/e2e.yaml +++ b/.github/workflows/e2e.yaml @@ -65,6 +65,39 @@ jobs: echo "::warning::Version mismatch — repo has $EXPECTED but Headlamp runs $DEPLOYED_VERSION. Tests may fail due to stale plugin." fi + - name: Build plugin + run: npm run build + + - name: Setup kubectl + uses: azure/setup-kubectl@v4 + + - name: Configure kubeconfig + env: + KUBECONFIG_DATA: ${{ secrets.KUBECONFIG }} + run: | + if [ -n "$KUBECONFIG_DATA" ]; then + mkdir -p ~/.kube + echo "$KUBECONFIG_DATA" | base64 -d > ~/.kube/config + chmod 600 ~/.kube/config + elif [ -f /var/run/secrets/kubernetes.io/serviceaccount/token ]; then + SA_TOKEN=$(cat /var/run/secrets/kubernetes.io/serviceaccount/token) + CA_CERT=/var/run/secrets/kubernetes.io/serviceaccount/ca.crt + NAMESPACE=$(cat /var/run/secrets/kubernetes.io/serviceaccount/namespace) + kubectl config set-cluster in-cluster --server=https://kubernetes.default.svc --certificate-authority="$CA_CERT" + kubectl config set-credentials sa-user --token="$SA_TOKEN" + kubectl config set-context in-cluster --cluster=in-cluster --user=sa-user --namespace="$NAMESPACE" + kubectl config use-context in-cluster + fi + kubectl version --client + kubectl cluster-info || echo "::warning::Cannot reach cluster API — deploy step may fail" + + - name: Deploy plugin to Headlamp + env: + HEADLAMP_URL: ${{ secrets.HEADLAMP_URL || 'http://headlamp.kube-system.svc.cluster.local' }} + HEADLAMP_NS: ${{ secrets.HEADLAMP_NS || 'kube-system' }} + HEADLAMP_PLUGIN_DIR: ${{ secrets.HEADLAMP_PLUGIN_DIR || '/headlamp/static-plugins/polaris' }} + run: ./scripts/deploy-plugin-to-headlamp.sh + - name: Install Playwright browsers run: npx playwright install --with-deps chromium diff --git a/Dockerfile b/Dockerfile index 80272c0..9def731 100644 --- a/Dockerfile +++ b/Dockerfile @@ -6,5 +6,5 @@ COPY src/ src/ RUN npx @kinvolk/headlamp-plugin build FROM alpine:3.20 -COPY --from=build /app/dist/ /plugins/headlamp-polaris-plugin/ -COPY --from=build /app/package.json /plugins/headlamp-polaris-plugin/ +COPY --from=build /app/dist/ /plugins/polaris/ +COPY --from=build /app/package.json /plugins/polaris/ diff --git a/scripts/deploy-plugin-to-headlamp.sh b/scripts/deploy-plugin-to-headlamp.sh new file mode 100755 index 0000000..8175497 --- /dev/null +++ b/scripts/deploy-plugin-to-headlamp.sh @@ -0,0 +1,70 @@ +#!/usr/bin/env bash +# Deploy the built plugin to a live Headlamp instance via kubectl. +# +# Prerequisites: +# - kubectl configured with access to the Headlamp namespace +# - Plugin already built (npm run build → dist/) +# +# Environment variables (all optional, with defaults): +# HEADLAMP_URL — Headlamp URL for readiness check +# HEADLAMP_NS — Kubernetes namespace (default: kube-system) +# HEADLAMP_PLUGIN_DIR — Plugin path inside the pod (default: /headlamp/static-plugins/polaris) +# +# Usage: +# npm run build +# ./scripts/deploy-plugin-to-headlamp.sh +# +# Intended to be called from the E2E workflow before running Playwright tests, +# so that E2E always tests the current commit's plugin code rather than whatever +# was previously deployed. + +set -euo pipefail + +HEADLAMP_URL="${HEADLAMP_URL:-http://headlamp.kube-system.svc.cluster.local}" +HEADLAMP_NS="${HEADLAMP_NS:-kube-system}" +HEADLAMP_PLUGIN_DIR="${HEADLAMP_PLUGIN_DIR:-/headlamp/static-plugins/polaris}" + +if [ ! -d "dist" ]; then + echo "Error: dist/ not found. Run 'npm run build' first." >&2 + exit 1 +fi + +# Find the Headlamp pod +POD=$(kubectl get pod -n "$HEADLAMP_NS" -l app.kubernetes.io/name=headlamp \ + -o jsonpath='{.items[0].metadata.name}') +echo "Headlamp pod: $POD" + +# Remove stale plugin and copy current build +kubectl exec -n "$HEADLAMP_NS" "$POD" -c headlamp -- \ + rm -rf "$HEADLAMP_PLUGIN_DIR" 2>/dev/null || true +kubectl cp dist/. "$HEADLAMP_NS/$POD:$HEADLAMP_PLUGIN_DIR" -c headlamp + +# Copy package.json so Headlamp can read plugin metadata +kubectl cp package.json "$HEADLAMP_NS/$POD:$HEADLAMP_PLUGIN_DIR/package.json" -c headlamp + +# Verify the copy +echo "Deployed files:" +kubectl exec -n "$HEADLAMP_NS" "$POD" -c headlamp -- \ + ls -la "$HEADLAMP_PLUGIN_DIR" + +# Restart the Headlamp process to reload plugins. +# Killing PID 1 restarts the container without replacing the pod, +# so the emptyDir volume (and our copied files) is preserved. +echo "Restarting Headlamp process..." +kubectl exec -n "$HEADLAMP_NS" "$POD" -c headlamp -- kill 1 || true + +# Wait for Headlamp to come back +echo "Waiting for Headlamp to restart..." +sleep 10 +for i in $(seq 1 30); do + HTTP_CODE=$(curl -s -o /dev/null -w '%{http_code}' --connect-timeout 5 "$HEADLAMP_URL" || true) + if [ "$HTTP_CODE" = "200" ]; then + echo "Headlamp is ready" + exit 0 + fi + echo " attempt $i/30 — HTTP $HTTP_CODE" + sleep 5 +done + +echo "Error: Headlamp did not recover after plugin deploy" >&2 +exit 1 diff --git a/src/components/AppBarScoreBadge.tsx b/src/components/AppBarScoreBadge.tsx index 02b6bbb..c18eac4 100644 --- a/src/components/AppBarScoreBadge.tsx +++ b/src/components/AppBarScoreBadge.tsx @@ -1,4 +1,4 @@ -import { K8s, Router } from '@kinvolk/headlamp-plugin/lib'; +import { K8s } from '@kinvolk/headlamp-plugin/lib'; import { useTheme } from '@mui/material/styles'; import React from 'react'; import { useHistory } from 'react-router-dom'; @@ -36,7 +36,8 @@ export default function AppBarScoreBadge() { }; const handleClick = () => { - history.push(Router.createRouteURL('polaris', { cluster: cluster ?? '' })); + const prefix = cluster ? `/c/${cluster}` : ''; + history.push(`${prefix}/polaris`); }; return ( diff --git a/src/index.tsx b/src/index.tsx index 5a90bbe..e5d068f 100644 --- a/src/index.tsx +++ b/src/index.tsx @@ -99,7 +99,7 @@ registerRoute({ }); // Register plugin settings -registerPluginSettings('headlamp-polaris', PolarisSettings, true); +registerPluginSettings('polaris', PolarisSettings, true); // Register details view section for supported controller types registerDetailsViewSection(({ resource }) => {