init rig
This commit is contained in:
149
rig/ctrl/cluster.sh
Executable file
149
rig/ctrl/cluster.sh
Executable file
@@ -0,0 +1,149 @@
|
||||
#!/usr/bin/env bash
|
||||
# Cluster lifecycle, plus what else is running on this machine.
|
||||
#
|
||||
# `list` and `free` live here rather than in a separate script because a
|
||||
# near-identical second name (cluster / clusters) is a trap — you reach for one
|
||||
# and get the other. One target, one file, unambiguous subcommands.
|
||||
#
|
||||
# "Idempotent" here means CONVERGENT, not "exits early if the cluster exists".
|
||||
# That distinction matters: an interrupted first run can leave a cluster created
|
||||
# but not finished, and returning early on the re-run would strand it there.
|
||||
# The create step is conditional; every step after it always runs, and each one
|
||||
# is individually idempotent.
|
||||
#
|
||||
# Usage: cluster.sh up | down | reset | list | free
|
||||
set -euo pipefail
|
||||
cd "$(dirname "$0")"
|
||||
|
||||
source ./lib/config.sh
|
||||
load_config
|
||||
|
||||
up() {
|
||||
if kind get clusters 2>/dev/null | grep -qx "$CLUSTER"; then
|
||||
echo "cluster '$CLUSTER' exists — converging"
|
||||
else
|
||||
# Say what this profile locks in BEFORE spending minutes building it:
|
||||
# the audit policy is an apiserver flag and cannot be changed later.
|
||||
echo "creating cluster '$CLUSTER' from profile '$PROFILE_NAME'"
|
||||
echo " shape ctrl/k8s/$KIND_CONFIG"
|
||||
echo " nodes $NODES"
|
||||
echo " image $NODE_IMAGE"
|
||||
echo " audit $AUDIT"
|
||||
echo " ingress $INGRESS_MODE"
|
||||
echo " registry $REGISTRY_MODE"
|
||||
echo " (audit is fixed at creation — 'make cluster reset' to change it)"
|
||||
echo
|
||||
|
||||
render_kind_config | kind create cluster --config -
|
||||
fi
|
||||
|
||||
# The cluster can exist while its context does not — a reset or a switched
|
||||
# KUBECONFIG loses it, and then nothing works despite a healthy cluster.
|
||||
if ! kubectl config get-contexts -o name 2>/dev/null | grep -qx "$KUBECONTEXT"; then
|
||||
echo "context '$KUBECONTEXT' missing from kubeconfig — re-exporting"
|
||||
kind export kubeconfig --name "$CLUSTER"
|
||||
fi
|
||||
kubectl config use-context "$KUBECONTEXT" >/dev/null
|
||||
|
||||
bash registry.sh up
|
||||
|
||||
if [ -n "${ADDONS// /}" ]; then
|
||||
bash addons.sh install
|
||||
fi
|
||||
|
||||
echo
|
||||
echo "cluster '$CLUSTER' ready (context $KUBECONTEXT)"
|
||||
}
|
||||
|
||||
down() {
|
||||
# The registry is a standalone container outside the cluster; take it down
|
||||
# first so a reset doesn't leave it orphaned and holding a port.
|
||||
bash registry.sh down || true
|
||||
|
||||
if kind get clusters 2>/dev/null | grep -qx "$CLUSTER"; then
|
||||
echo "deleting cluster '$CLUSTER'..."
|
||||
kind delete cluster --name "$CLUSTER"
|
||||
else
|
||||
echo "no cluster '$CLUSTER' to delete"
|
||||
fi
|
||||
}
|
||||
|
||||
# The escape hatch for a wedged cluster, and the only way to change a
|
||||
# creation-time setting such as the audit policy.
|
||||
reset() {
|
||||
down
|
||||
echo
|
||||
up
|
||||
}
|
||||
|
||||
# ── the whole machine ──────────────────────────────────────────────────────
|
||||
# Every cluster is a running container tree whether or not you are using it, and
|
||||
# an idle one is the usual reason a new one will not fit.
|
||||
|
||||
list() {
|
||||
local total avail
|
||||
total=$(awk '/^MemTotal:/{printf "%.1f", $2/1024/1024}' /proc/meminfo)
|
||||
avail=$(awk '/^MemAvailable:/{printf "%.1f", $2/1024/1024}' /proc/meminfo)
|
||||
echo "memory: ${avail} GB available of ${total} GB"
|
||||
echo
|
||||
|
||||
local names; names=$(kind get clusters 2>/dev/null || true)
|
||||
if [ -z "$names" ]; then
|
||||
echo "no clusters"
|
||||
return
|
||||
fi
|
||||
|
||||
printf "%-16s %-10s %8s %6s %-13s %s\n" CLUSTER STATE MEM NODES PORTS ""
|
||||
local c nodes state mem base
|
||||
for c in $names; do
|
||||
nodes=$(docker ps -a --filter "label=io.x-k8s.kind.cluster=$c" --format '{{.Names}}' | wc -l)
|
||||
state=$(docker inspect -f '{{.State.Status}}' "${c}-control-plane" 2>/dev/null || echo unknown)
|
||||
if [ "$state" = "running" ]; then
|
||||
mem=$(docker stats --no-stream --format '{{.MemUsage}}' \
|
||||
$(docker ps --filter "label=io.x-k8s.kind.cluster=$c" -q) 2>/dev/null \
|
||||
| awk '{gsub(/GiB/,"");gsub(/MiB/,"e-3");s+=$1} END {printf "%.1fG", s}')
|
||||
else
|
||||
mem="-"
|
||||
fi
|
||||
# A cluster's name is its directory slug, so its port block is derivable
|
||||
# here without reading that directory's config.
|
||||
base=$(derive_port_base "$c")
|
||||
printf "%-16s %-10s %8s %6s %-13s %s\n" "$c" "$state" "$mem" "$nodes" \
|
||||
"${base}-$((base + 3))" \
|
||||
"$([ "$c" = "$CLUSTER" ] && echo "<- this one")"
|
||||
done
|
||||
}
|
||||
|
||||
# Stop the OTHER clusters to free memory. Stops, never deletes — a stopped
|
||||
# cluster restarts with `docker start`, so nothing is lost.
|
||||
free() {
|
||||
local targets=("$@")
|
||||
if [ ${#targets[@]} -eq 0 ]; then
|
||||
mapfile -t targets < <(kind get clusters 2>/dev/null | grep -vx "$CLUSTER" || true)
|
||||
fi
|
||||
if [ ${#targets[@]} -eq 0 ]; then
|
||||
echo "nothing to stop"
|
||||
return
|
||||
fi
|
||||
|
||||
local c ids
|
||||
for c in "${targets[@]}"; do
|
||||
ids=$(docker ps --filter "label=io.x-k8s.kind.cluster=$c" -q)
|
||||
if [ -z "$ids" ]; then
|
||||
echo "cluster '$c' is not running"
|
||||
continue
|
||||
fi
|
||||
echo "stopping '$c' (restart with: docker start \$(docker ps -aq -f label=io.x-k8s.kind.cluster=$c))"
|
||||
# shellcheck disable=SC2086
|
||||
docker stop $ids >/dev/null
|
||||
done
|
||||
}
|
||||
|
||||
case "${1:-up}" in
|
||||
up) up ;;
|
||||
down) down ;;
|
||||
reset) reset ;;
|
||||
list) list ;;
|
||||
free) shift; free "$@" ;;
|
||||
*) echo "usage: $0 [up|down|reset|list|free]" >&2; exit 1 ;;
|
||||
esac
|
||||
Reference in New Issue
Block a user