big update based on real wokr

This commit is contained in:
2026-09-23 18:52:57 -03:00
parent 45018e4743
commit 8fad2223f3
23 changed files with 652 additions and 670 deletions

View File

@@ -1,6 +1,7 @@
#!/usr/bin/env bash
# Readiness check: is this machine ready to run rig? Reports and instructs; never fixes.
# Usage: check.sh [all | mem [status|push|all|backup|restore]] (all = every detail)
# Prints the memory budget and what to do next first, then the configuration as it is.
# Notes: docs/notes/check.md
set -euo pipefail
cd "$(dirname "$0")"
@@ -12,13 +13,11 @@ if [ "${1:-}" = mem ]; then
exec bash ./mem.sh "${@:-status}"
fi
# Compact by default: facts only with `all`; problems (!) always print.
# Facts only with `all`; problems (!) always print.
VERBOSE=""
if [ "${1:-}" = all ]; then VERBOSE=1; fi
fact() { if [ -n "$VERBOSE" ]; then echo "$@"; fi; }
bash ./deps.sh detect ${VERBOSE:+all}
source ./lib/config.sh
load_config
@@ -66,49 +65,64 @@ port_busy() {
grep -qi "^ *[0-9]*: [0-9A-F]*$hex " /proc/net/tcp /proc/net/tcp6 2>/dev/null
}
echo
echo "rig"
echo " cluster ${CLUSTER} (${KUBECONTEXT}) profile ${PROFILE_NAME}, ${NODES} node(s), registry ${REGISTRY_MODE}"
if [ -n "${OVERLAY:-}" ]; then
echo " overlay $(basename "$(_abs_from_ctrl "$OVERLAY_DIR")") ($(_abs_from_ctrl "$OVERLAY_DIR"))"
elif [ -n "$OVERLAY_DIR" ]; then
fact " overlay none named — rig's own ${OVERLAY_DIR}"
fi
if [ -n "$VERBOSE" ] && [ -n "$OVERLAY_DIR" ]; then
ov=$(_from_ctrl "$OVERLAY_DIR") pieces=""
for piece in rig.env k8s/overlays/dev kind-config.yaml.tpl addons Tiltfile; do
[ -e "$ov/$piece" ] && pieces+="$piece "
done
echo " provides: ${pieces:-nothing rig reads}"
fi
fact " manifests ${MANIFESTS_DIR:-none}"
fact " kind config ${KIND_CONFIG}"
fact " ingress ${INGRESS_MODE}"
if [ ! -f ./.env ]; then
fact " .env none — built-in defaults (cp ctrl/.env.example ctrl/.env to set values)"
fi
if [ -n "${STALE_MANIFESTS_DIR:-}" ]; then
echo " ! .env MANIFESTS_DIR=${STALE_MANIFESTS_DIR} is the old default; rig's examples moved"
echo " to examples/ — delete that line from ctrl/.env (ignored until then)"
fi
# registry.sh points containerd at certs.d, which only works if the kind config says so,
# and a kind config is fixed at creation: a project's own file that drops it fails silently.
if [ "$REGISTRY_MODE" != none ] && ! grep -q 'config_path *= *"/etc/containerd/certs.d"' "$KIND_CONFIG"; then
echo " ! kind ${KIND_CONFIG} lacks the containerd config_path patch that registry mode"
echo " '${REGISTRY_MODE}' needs — copy it from ctrl/k8s/kind-config.yaml.tpl"
# Who listens on a port, as "pid name", when ss can tell (only for our own processes).
port_owner() {
command -v ss >/dev/null 2>&1 || return 0
ss -ltnpH "sport = :$1" 2>/dev/null \
| sed -n 's/.*users:(("\([^"]*\)",pid=\([0-9]*\).*/\2 \1/p' | head -1
}
# This environment's Tilt: a tilt started from this ctrl/ (the Makefile runs it there).
tilt_pid=""
if tilt_owner=$(port_owner "$TILT_PORT") && [ -n "$tilt_owner" ]; then
pid=${tilt_owner%% *}
if [ "${tilt_owner#* }" = tilt ] && [ "$(readlink "/proc/$pid/cwd" 2>/dev/null)" = "$(pwd -P)" ]; then
tilt_pid=$pid
fi
fi
# ── memory: does this cluster fit right now? Warns; never blocks. ──────────
# none | stopped | up | unknown (no docker)
cluster_state() {
docker info >/dev/null 2>&1 || { echo unknown; return; }
local f="label=io.x-k8s.kind.cluster=${CLUSTER}"
if [ -n "$(docker ps -q --filter "$f")" ]; then echo up
elif [ -n "$(docker ps -aq --filter "$f")" ]; then echo stopped
else echo none; fi
}
state=$(cluster_state)
# ── memory budget: what this environment needs, against what the machine has ──
# The workload's share comes from the overlay: MEMORY_BUDGET="name=MB ..." in its rig.env,
# and MEMORY_BUDGET_TIP, optionally, says how to switch its largest part off.
# rig only knows what a node costs. Warns; never blocks.
total_mb=$(mb_of MemTotal)
avail_mb=$(mb_of MemAvailable)
swap_used_mb=$(( $(mb_of SwapTotal) - $(mb_of SwapFree) ))
overcommit=$(cat "${OVERCOMMIT_FILE:-/proc/sys/vm/overcommit_memory}" 2>/dev/null || echo '?')
need_mb=$(( NODES * NODE_MB ))
node_mb=$(( NODES * NODE_MB ))
budget="" work_mb=0 big_mb=0 big=""
for item in ${MEMORY_BUDGET:-}; do
name=${item%=*} mb=${item##*=}
if [ "$name" = "$item" ] || ! [[ "$mb" =~ ^[0-9]+$ ]]; then
echo " ! budget '$item' in MEMORY_BUDGET is not name=MB"
continue
fi
[ "$mb" -gt 0 ] || continue
budget+=$(printf ' %-18s %6d MB' "$name" "$mb")$'\n'
work_mb=$(( work_mb + mb ))
if [ "$mb" -gt "$big_mb" ]; then big_mb=$mb big=$name; fi
done
need_mb=$(( work_mb + node_mb ))
rows=$(container_mb)
# If our cluster is already up, its memory is already out of MemAvailable: need nothing more.
# If our cluster is already up, its memory is already out of MemAvailable.
ours_mb=$(awk -F'\t' -v c="$CLUSTER" '$2 == c { s += $1 } END { print s + 0 }' <<< "$rows")
still_mb=$(( ours_mb > 0 ? 0 : need_mb ))
if [ "$state" = up ]; then
still_mb=$(( need_mb > ours_mb ? need_mb - ours_mb : 0 ))
else
still_mb=$need_mb
fi
headroom=$(( avail_mb - still_mb ))
# The biggest things holding memory, other than this cluster: kind clusters summed, the rest by name.
@@ -117,82 +131,164 @@ others=$(awk -F'\t' -v c="$CLUSTER" '
$2 == "-" { k["container \x27" $3 "\x27"] += $1 }
END { for (n in k) printf "%d\t%s\n", k[n], n }' <<< "$rows" | sort -rn)
if [ "$still_mb" -eq 0 ] && [ "$headroom" -ge 512 ]; then
printf " memory up, holding %d MB — %d MB headroom for what you deploy\n" "$ours_mb" "$headroom"
elif [ "$still_mb" -eq 0 ]; then
printf " ! memory up, but only %d MB headroom for anything you deploy\n" "$headroom"
elif [ "$headroom" -ge 512 ]; then
printf " memory fits — ~%d MB for %s node(s), %d MB headroom\n" "$need_mb" "$NODES" "$headroom"
echo
echo "memory budget (${CLUSTER})"
printf '%s' "$budget"
if [ -z "$budget" ]; then
echo " workload ? (the overlay lists none: MEMORY_BUDGET in its rig.env)"
fi
printf ' %-18s %6d MB (kind control plane, kube-system; %s node(s))\n' "node overhead" "$node_mb" "$NODES"
echo " ---------"
printf ' %-18s %6d MB\n' "required" "$need_mb"
if [ "$state" = up ]; then
printf ' %-18s %6d MB (%s is up)\n' "held by it now" "$ours_mb" "$CLUSTER"
fi
printf ' %-18s %6d MB\n' "this machine has" "$total_mb"
printf ' %-18s %6d MB (right now)\n' "available" "$avail_mb"
echo
if [ "$headroom" -ge 512 ]; then
if [ "$state" = up ]; then
echo "fits: ${CLUSTER} holds ${ours_mb} MB of its ${need_mb} MB; ${avail_mb} MB still available — ${headroom} MB headroom."
else
echo "fits: ${need_mb} MB needed, ${avail_mb} MB available — ${headroom} MB headroom."
fi
elif [ "$headroom" -ge 0 ]; then
printf " ! memory fits, but only %d MB headroom (~%d MB for %s node(s))\n" "$headroom" "$need_mb" "$NODES"
echo "! tight: ${still_mb} MB still needed, ${avail_mb} MB available — only ${headroom} MB headroom."
else
printf " ! memory does not fit: ~%d MB needed, %d MB available\n" "$still_mb" "$avail_mb"
echo "! does not fit: ${still_mb} MB still needed, ${avail_mb} MB available."
# Two failures with opposite fixes, and telling them apart is the point.
if [ "$still_mb" -le "$total_mb" ]; then
echo " something else holds it (below) — stopping that helps, a bigger VM would not."
if [ "$need_mb" -le "$total_mb" ]; then
echo " Something else holds the memory (below): stopping it helps, a bigger machine would not."
if grep -q 'kind cluster' <<< "$others"; then
echo " 'make cluster free' stops the other kind clusters. It stops, never deletes."
echo " 'make cluster free' stops the other kind clusters. It stops, never deletes."
fi
else
echo " the machine itself is too small: ${total_mb} MB total."
echo " The machine itself is too small: ${total_mb} MB in all. Give it more memory"
echo " ('make check mem' has the WSL steps), or switch parts of the workload off."
fi
fi
if [ -n "$others" ] && { [ -n "$VERBOSE" ] || [ "$headroom" -lt 512 ]; }; then
echo " held elsewhere:"
head -6 <<< "$others" | awk -F'\t' '{ printf " %6d MB %s\n", $1, $2 }'
echo " held elsewhere:"
head -6 <<< "$others" | awk -F'\t' '{ printf " %6d MB %s\n", $1, $2 }'
n_others=$(wc -l <<< "$others")
if [ "$n_others" -gt 6 ]; then
echo " ... and $((n_others - 6)) more"
echo " ... and $((n_others - 6)) more"
fi
fi
# One part that outweighs the rest is the first thing to switch off on a smaller machine.
if [ "$work_mb" -gt 0 ] && [ $(( big_mb * 2 )) -gt "$work_mb" ] && [ "$big_mb" -ge 1024 ]; then
echo
echo "${big} is ${big_mb} of the ${work_mb} MB the workload needs — more than the rest"
echo "combined. If you are not working on it, ${MEMORY_BUDGET_TIP:-switching it off in the overlay} gets that back."
fi
if [ "$swap_used_mb" -gt 0 ]; then
fact " ${swap_used_mb} MB already in swap, which 'available' does not count: expect slow before failing"
fact " ${swap_used_mb} MB already in swap, which 'available' does not count: expect slow before failing"
fi
if [ "$overcommit" = "1" ]; then
fact " overcommit=1: allocations never fail, so read 'fits' as a ceiling (OOM killer settles up)"
fact " overcommit=1: allocations never fail, so read 'fits' as a ceiling (OOM killer settles up)"
fi
# ── where things stand, and the next step ──────────────────────────────────
echo
case "$state" in
none)
echo "${CLUSTER} does not exist yet. 'make cluster up' creates it${ADDONS:+ (with ${ADDONS})},"
echo "then 'make tilt' starts the dev loop." ;;
stopped)
echo "${CLUSTER} exists but is stopped (nothing is lost). Start it again with:"
echo " docker start \$(docker ps -aq -f label=io.x-k8s.kind.cluster=${CLUSTER})" ;;
up)
if [ -n "$tilt_pid" ]; then
echo "${CLUSTER} is up, and its Tilt is running (pid ${tilt_pid}): http://localhost:${TILT_PORT}"
else
echo "${CLUSTER} is up. 'make tilt' starts the dev loop, at http://localhost:${TILT_PORT}."
fi
echo "Through the gateway: http://localhost:${HTTP_PORT}" ;;
*)
echo "docker is not reachable, so the cluster's state is unknown (the machine block below says why)." ;;
esac
# ── the configuration, as it is ────────────────────────────────────────────
echo
bash ./deps.sh detect ${VERBOSE:+all}
echo
echo "config"
echo " cluster ${CLUSTER} ($([ "$state" = none ] && echo 'not created' || echo "$state"))"
echo " context ${KUBECONTEXT}"
if [ -n "${OVERLAY:-}" ]; then
echo " overlay $(_abs_from_ctrl "$OVERLAY_DIR")"
elif [ -n "$OVERLAY_DIR" ]; then
echo " overlay none named — rig's own ${OVERLAY_DIR}"
fi
if [ -n "$VERBOSE" ] && [ -n "$OVERLAY_DIR" ]; then
ov=$(_from_ctrl "$OVERLAY_DIR") pieces=""
for piece in rig.env k8s/overlays/dev kind-config.yaml.tpl addons Tiltfile; do
[ -e "$ov/$piece" ] && pieces+="$piece "
done
echo " provides: ${pieces:-nothing rig reads}"
fi
echo " profile ${PROFILE_NAME}"
echo " nodes ${NODES}"
echo " kind config ${KIND_CONFIG}"
echo " manifests ${MANIFESTS_DIR:-none}"
echo " ingress ${INGRESS_MODE}"
if [ ! -f ./.env ]; then
fact " .env none — built-in defaults (cp ctrl/.env.example ctrl/.env to set values)"
fi
if [ -n "${STALE_MANIFESTS_DIR:-}" ]; then
echo " ! .env MANIFESTS_DIR=${STALE_MANIFESTS_DIR} is the old default; rig's examples moved"
echo " to examples/ — delete that line from ctrl/.env (ignored until then)"
fi
# registry.sh points containerd at certs.d, which only works if the kind config says so,
# and a kind config is fixed at creation: a project's own file that drops it fails silently.
if [ "$REGISTRY_MODE" != none ] && ! grep -q 'config_path *= *"/etc/containerd/certs.d"' "$KIND_CONFIG"; then
echo " ! kind ${KIND_CONFIG} lacks the containerd config_path patch that registry mode"
echo " '${REGISTRY_MODE}' needs — copy it from ctrl/k8s/kind-config.yaml.tpl"
fi
# ── ports: checked before creation; docker reports a clash only halfway through. ──
# Ports held by our own cluster are not clashes. Second grep, not `tr -d ':->'` (a tr range).
ours=$(docker ps --filter "label=io.x-k8s.kind.cluster=${CLUSTER}" \
--format '{{.Ports}}' 2>/dev/null | tr ',' '\n' \
| grep -oE ':[0-9]+->' | grep -oE '[0-9]+' || true)
clash=0 list="" mine=0
for entry in "HTTP:${HTTP_PORT}" "HTTPS:${HTTPS_PORT}" \
"TILT:${TILT_PORT}" "REGISTRY:${REGISTRY_PORT}"; do
# Ports held by our own cluster are not clashes: its node containers, and its registry,
# which is a container of its own. Second grep, not `tr -d ':->'` (a tr range).
ours=$({ docker ps --filter "label=io.x-k8s.kind.cluster=${CLUSTER}" --format '{{.Ports}}'
docker ps --filter "name=^${CLUSTER}-registry\$" --format '{{.Ports}}'; } 2>/dev/null \
| tr ',' '\n' | grep -oE ':[0-9]+->' | grep -oE '[0-9]+' || true)
clash=0 list=""
for entry in "http:${HTTP_PORT}" "https:${HTTPS_PORT}" \
"tilt:${TILT_PORT}" "registry:${REGISTRY_PORT}"; do
name="${entry%%:*}"; p="${entry#*:}"
[ -n "$p" ] || continue
list+="$p "
list+="$name $p "
if ! port_busy "$p"; then
fact "$(printf " %-9s %-6s free" "$name" "$p")"
elif echo "$ours" | grep -qx "$p"; then
mine=1
fact "$(printf " %-9s %-6s in use by this environment's cluster" "$name" "$p")"
elif [ "$name" = tilt ] && [ -n "$tilt_pid" ]; then
fact "$(printf " %-9s %-6s in use by this environment's Tilt" "$name" "$p")"
else
printf " ! ports %s %s IN USE by something else\n" "$name" "$p"
clash=1
owner=$(port_owner "$p")
if [ "$name" = tilt ] && [ "${owner#* }" = tilt ]; then
printf " ! ports tilt %s is held by another Tilt (pid %s, in %s)\n" "$p" "${owner%% *}" \
"$(readlink "/proc/${owner%% *}/cwd" 2>/dev/null || echo '?')"
echo " stop it (kill ${owner%% *}) before 'make tilt'"
else
printf " ! ports %s %s IN USE by %s\n" "$name" "$p" "${owner:+pid ${owner}}${owner:-something else}"
clash=1
fi
fi
done
echo " ports ${list% }"
if [ "$clash" -eq 1 ]; then
echo " override it in ctrl/.env (e.g. HTTP_PORT=21080), or rename this directory"
elif [ "$mine" -eq 1 ]; then
echo " ports ${list% } held by this cluster"
else
echo " ports ${list% } free"
fi
if [ -n "${OVERLAY:-}" ]; then
fact " derived from the overlay's folder name"
else
fact " derived from the directory name; pin them: bash ctrl/ports.sh persist"
fi
# ── what `make cluster up` wires in beside the cluster ─────────────────────
REG_NAME="${CLUSTER}-registry"
if state=$(docker inspect -f '{{.State.Status}}' "$REG_NAME" 2>/dev/null); then
echo " registry localhost:${REGISTRY_PORT} ($state)"
if reg_state=$(docker inspect -f '{{.State.Status}}' "$REG_NAME" 2>/dev/null); then
echo " registry ${REGISTRY_MODE} localhost:${REGISTRY_PORT} (${reg_state})"
else
fact " registry no container yet — 'make cluster up' starts it"
echo " registry ${REGISTRY_MODE} (no container yet)"
fi
echo " addons ${ADDONS:-none}"
if [ -n "$VERBOSE" ]; then