139 lines
4.9 KiB
Bash
Executable File
139 lines
4.9 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
# Cluster lifecycle (convergent, not exit-early), plus what else runs on this machine.
|
|
# Usage: cluster.sh up | down | reset | list | free
|
|
# Notes: docs/notes/cluster.md
|
|
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 kind config is fixed at creation and cannot be changed later.
|
|
echo "creating cluster '$CLUSTER' from profile '$PROFILE_NAME'"
|
|
echo " kind config ${KIND_CONFIG}"
|
|
echo " nodes $NODES"
|
|
echo " image $NODE_IMAGE"
|
|
echo " ingress $INGRESS_MODE"
|
|
echo " registry $REGISTRY_MODE"
|
|
echo " (fixed at creation — edit the kind config, then 'make cluster reset')"
|
|
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 node count or port mappings.
|
|
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
|