Files
headlamp-polaris-plugin/scripts/deploy-plugin-via-volume.sh
T
Hugh Hackman bf5614f268 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>
2026-03-17 18:13:03 +00:00

136 lines
4.7 KiB
Bash
Executable File

#!/usr/bin/env bash
# deploy-plugin-via-volume.sh
#
# Copies the built plugin into the shared PVC so Headlamp picks it up.
# Uses a temporary Kubernetes Job to write to the PVC — the CI runner
# does NOT need the PVC mounted locally.
#
# Usage:
# scripts/deploy-plugin-via-volume.sh
#
# Environment:
# HEADLAMP_NAMESPACE — namespace where Headlamp runs (default: kube-system)
# HEADLAMP_DEPLOY — Headlamp deployment name (default: headlamp)
set -euo pipefail
REPO_ROOT="$(cd "$(dirname "$0")/.." && pwd)"
HEADLAMP_NAMESPACE="${HEADLAMP_NAMESPACE:-kube-system}"
HEADLAMP_DEPLOY="${HEADLAMP_DEPLOY:-headlamp}"
# The deployed directory name must match the package.json name and
# the registerPluginSettings name. Headlamp identifies plugins by
# reading package.json from each subdirectory of the plugins dir.
PLUGIN_DIR_NAME="headlamp-polaris"
DIST_DIR="$REPO_ROOT/dist"
if [ ! -d "$DIST_DIR" ]; then
echo "ERROR: dist/ not found. Run 'npm run build' first." >&2
exit 1
fi
echo "Deploying plugin to shared volume via temporary job..."
echo " Source: $DIST_DIR"
echo " PVC: headlamp-plugins"
echo " Plugin: $PLUGIN_DIR_NAME"
# Create tarball of plugin dist + package.json
TAR_FILE=$(mktemp /tmp/plugin-XXXXXX.tar.gz)
tar -czf "$TAR_FILE" -C "$DIST_DIR" . -C "$REPO_ROOT" package.json
echo " Tarball: $TAR_FILE ($(du -h "$TAR_FILE" | cut -f1))"
# Find the node where Headlamp is running — the PVC is ReadWriteOnce so
# the deploy job must land on the same node to mount it.
HEADLAMP_NODE=$(kubectl get pods -n "$HEADLAMP_NAMESPACE" \
-l "app.kubernetes.io/name=headlamp" \
-o jsonpath='{.items[0].spec.nodeName}' 2>/dev/null || true)
if [ -z "$HEADLAMP_NODE" ]; then
HEADLAMP_NODE=$(kubectl get pods -n "$HEADLAMP_NAMESPACE" \
-l "app.kubernetes.io/instance=headlamp" \
-o jsonpath='{.items[0].spec.nodeName}' 2>/dev/null || true)
fi
if [ -n "$HEADLAMP_NODE" ]; then
echo " Headlamp node: $HEADLAMP_NODE (scheduling deploy job there)"
fi
# Clean up any previous deploy resources
kubectl delete pod plugin-deploy -n "$HEADLAMP_NAMESPACE" --ignore-not-found --wait=true 2>/dev/null || true
kubectl delete configmap plugin-tarball -n "$HEADLAMP_NAMESPACE" --ignore-not-found 2>/dev/null || true
sleep 2
# Store the tarball in a ConfigMap (binary-safe via --from-file)
echo "Creating ConfigMap with plugin tarball..."
kubectl create configmap plugin-tarball \
-n "$HEADLAMP_NAMESPACE" \
--from-file=plugin.tar.gz="$TAR_FILE"
# Build the Pod manifest as a temp file to avoid heredoc YAML escaping issues
POD_FILE=$(mktemp /tmp/plugin-deploy-pod-XXXXXX.yaml)
cat > "$POD_FILE" <<'YAMLDOC'
apiVersion: v1
kind: Pod
metadata:
name: plugin-deploy
spec:
restartPolicy: Never
containers:
- name: deploy
image: busybox:1.36
command: ["sh", "-c"]
args:
- |
echo "Cleaning up stale plugin directories..."
rm -rf /plugins/polaris /plugins/headlamp-polaris
echo "Extracting plugin to shared volume..."
mkdir -p /plugins/PLUGIN_DIR_PLACEHOLDER
tar -xzf /tarball/plugin.tar.gz -C /plugins/PLUGIN_DIR_PLACEHOLDER
echo "Files deployed:"
ls -la /plugins/PLUGIN_DIR_PLACEHOLDER/
volumeMounts:
- name: plugins
mountPath: /plugins
- name: tarball
mountPath: /tarball
readOnly: true
volumes:
- name: plugins
persistentVolumeClaim:
claimName: headlamp-plugins
- name: tarball
configMap:
name: plugin-tarball
YAMLDOC
# Substitute plugin dir name
sed -i "s/PLUGIN_DIR_PLACEHOLDER/${PLUGIN_DIR_NAME}/g" "$POD_FILE"
# Add nodeName if we know which node Headlamp is on
if [ -n "$HEADLAMP_NODE" ]; then
sed -i "/restartPolicy: Never/i\\ nodeName: ${HEADLAMP_NODE}" "$POD_FILE"
fi
echo "Starting deploy pod..."
kubectl apply -n "$HEADLAMP_NAMESPACE" -f "$POD_FILE"
rm -f "$POD_FILE"
# Wait for the pod to complete (Succeeded phase)
echo "Waiting for deploy pod to complete..."
kubectl wait --for=jsonpath='{.status.phase}'=Succeeded pod/plugin-deploy \
-n "$HEADLAMP_NAMESPACE" --timeout=120s
# Show logs
kubectl logs plugin-deploy -n "$HEADLAMP_NAMESPACE" 2>/dev/null || true
# Clean up
kubectl delete pod plugin-deploy -n "$HEADLAMP_NAMESPACE" --ignore-not-found 2>/dev/null || true
kubectl delete configmap plugin-tarball -n "$HEADLAMP_NAMESPACE" --ignore-not-found 2>/dev/null || true
rm -f "$TAR_FILE"
# Restart Headlamp to pick up the new plugin
echo "Restarting Headlamp deployment to load plugin..."
kubectl rollout restart "deployment/$HEADLAMP_DEPLOY" -n "$HEADLAMP_NAMESPACE"
kubectl rollout status "deployment/$HEADLAMP_DEPLOY" -n "$HEADLAMP_NAMESPACE" --timeout=120s
echo "Plugin deployed successfully."