Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
146 changes: 146 additions & 0 deletions headlamp-demo.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,146 @@
#!/usr/bin/env bash
set -e

# Colors
BOLD='\033[1m'
CYAN='\033[0;36m'
GREEN='\033[0;32m'
RESET='\033[0m'

SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
HEADLAMP_DIR="${SCRIPT_DIR}/headlamp"
PORT_FORWARD_PID=""

cleanup() {
local exit_code=$?
if [[ $exit_code -ne 0 ]]; then
echo ""
echo -e "\033[0;31m${BOLD}An error occurred. Running cleanup...${RESET}"
fi
if [[ -n "${PORT_FORWARD_PID}" ]]; then
kill "${PORT_FORWARD_PID}" 2>/dev/null || true
Copy link

Copilot AI Mar 22, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cleanup does kill "${PORT_FORWARD_PID}" without confirming the PID is still the port-forward process. If kubectl port-forward exits early, that PID could theoretically be reused and the cleanup might kill an unrelated process. Consider checking the process is still running and matches the expected command before killing (or run port-forward in the foreground and terminate it explicitly).

Suggested change
kill "${PORT_FORWARD_PID}" 2>/dev/null || true
if ps -p "${PORT_FORWARD_PID}" -o command= 2>/dev/null | grep -qE 'kubectl(\.exe)?[[:space:]].*port-forward'; then
kill "${PORT_FORWARD_PID}" 2>/dev/null || true
fi

Copilot uses AI. Check for mistakes.
fi
kind delete cluster --name kpt-demo 2>/dev/null || true
rm -rf "${HEADLAMP_DIR}"
}
Comment on lines +20 to +25
Copy link

Copilot AI Mar 22, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

rm -rf "${HEADLAMP_DIR}" runs on every script exit and will unconditionally delete a headlamp/ directory next to the script, even if it existed before the demo. This is destructive if the repo already contains user data at that path. Consider using a unique temp directory (e.g., via mktemp -d) or track whether the directory was created by this run and only remove it in that case (optionally prompting before deletion).

Copilot uses AI. Check for mistakes.
trap cleanup EXIT

banner() {
echo ""
echo -e "${CYAN}${BOLD}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${RESET}"
echo -e "${CYAN}${BOLD} $1${RESET}"
echo -e "${CYAN}${BOLD}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${RESET}"
echo ""
}

run() {
echo -e "${GREEN}> $*${RESET}"
"$@"
}

pause() {
echo ""
read -rp "Press Enter to continue to the next step..."
echo ""
}

# ─── Step 1 ───────────────────────────────────────────────────────────────────
banner "Step 1: Create kind cluster 'kpt-demo'"

run kind create cluster --name kpt-demo

pause

# ─── Step 2 ───────────────────────────────────────────────────────────────────
banner "Step 2: Download headlamp KRM file and initialize kpt package"

run mkdir -p "${HEADLAMP_DIR}"
cd "${HEADLAMP_DIR}"

run curl -sLO https://raw.githubusercontent.com/kubernetes-sigs/headlamp/main/kubernetes-headlamp.yaml
Copy link

Copilot AI Mar 22, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The manifest is fetched from Headlamp’s main branch, which can change at any time and break the demo unexpectedly. For a repeatable demo, consider pinning the URL to a specific release tag or commit SHA (and optionally verifying a checksum).

Copilot uses AI. Check for mistakes.

echo -e "${BOLD}# Generating security configuration${RESET}"
echo -e "${GREEN}> Generating headlamp-admin-sa.yaml${RESET}"
cat > headlamp-admin-sa.yaml <<'EOF'
apiVersion: v1
kind: ServiceAccount
metadata:
name: headlamp-admin
namespace: kube-system
EOF

echo -e "${GREEN}> Generating headlamp-admin-crb.yaml${RESET}"
cat > headlamp-admin-crb.yaml <<'EOF'
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: headlamp-admin
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: cluster-admin
subjects:
- kind: ServiceAccount
name: headlamp-admin
namespace: kube-system
EOF
Comment on lines +72 to +86
Copy link

Copilot AI Mar 22, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The script installs a ClusterRoleBinding to cluster-admin for the headlamp-admin service account. Even for a demo, this grants full cluster control and is risky if someone runs the script against a non-throwaway kubeconfig context. Prefer using the minimal RBAC recommended by Headlamp (or at least add a prominent warning and a safety check to ensure the current context is the kind-kpt-demo cluster before applying).

Copilot uses AI. Check for mistakes.

run kpt pkg init

pause

# ─── Step 3 ───────────────────────────────────────────────────────────────────
banner "Step 3: Add set-label mutator pipeline to Kptfile"

echo -e "${GREEN}> Appending pipeline block to Kptfile${RESET}"
cat >> Kptfile <<'EOF'
pipeline:
mutators:
- image: ghcr.io/kptdev/krm-functions-catalog/set-labels:v0.1.5
configMap:
k8s-app: headlamp-kpt-demo
EOF

echo "Kptfile now contains:"
cat Kptfile

pause

# ─── Step 4 ───────────────────────────────────────────────────────────────────
banner "Step 4: Run kpt fn render"

run kpt fn render

pause

# ─── Step 5 ───────────────────────────────────────────────────────────────────
banner "Step 5: Deploy to cluster, expose port, and create service account token"

run kpt live init
run kpt live apply --output=table --reconcile-timeout=5m

echo ""
echo -e "${GREEN}> kubectl port-forward -n kube-system service/headlamp 8080:80 &${RESET}"
kubectl port-forward -n kube-system service/headlamp 8080:80 &
PORT_FORWARD_PID=$!
Copy link

Copilot AI Mar 22, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

kubectl port-forward ... & is started in the background, but set -e won’t catch failures from a backgrounded process (e.g., service not ready, port 8080 already in use). The script will still print the URL/token even if port-forward immediately exits. Consider waiting briefly and verifying the process is still alive (kill -0 $PORT_FORWARD_PID), or retrying until the service endpoints are ready, failing the script if port-forward can’t be established.

Suggested change
PORT_FORWARD_PID=$!
PORT_FORWARD_PID=$!
# Give port-forward a moment to start and then verify it is still running.
sleep 2
if ! kill -0 "${PORT_FORWARD_PID}" 2>/dev/null; then
echo "Error: kubectl port-forward failed to start or exited early. See output above for details." >&2
exit 1
fi

Copilot uses AI. Check for mistakes.
echo "Port-forward running (PID: ${PORT_FORWARD_PID})"

echo ""
echo -e "${GREEN}> kubectl create token headlamp-admin -n kube-system${RESET}"
TOKEN=$(kubectl create token headlamp-admin -n kube-system)

echo ""
echo -e "${BOLD}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${RESET}"
echo -e "${BOLD} Headlamp is available at: http://localhost:8080${RESET}"
echo -e "${BOLD} Access token:${RESET}"
echo ""
echo "${TOKEN}"
echo -e "${BOLD}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${RESET}"

pause

# ─── Step 6 ───────────────────────────────────────────────────────────────────
banner "Step 6: Cleanup"

# The EXIT trap handles cleanup; just print a message and exit cleanly.
echo -e "${BOLD}Demo complete. Cluster deleted and package removed.${RESET}"