From 3bbfa413a3487d9f25206e19c4f9c0b6318f402e Mon Sep 17 00:00:00 2001 From: buenosairesam Date: Wed, 23 Sep 2026 18:52:57 -0300 Subject: [PATCH] big update based on real wokr --- rig/ctrl/addons/metallb.sh | 23 +- rig/ctrl/addons/metallb.yaml | 19 ++ rig/ctrl/check.sh | 254 ++++++++++++------ rig/ctrl/docs.sh | 18 ++ rig/ctrl/lib/config.sh | 23 ++ rig/ctrl/selftest.sh | 12 +- rig/docs/graphs/03-architecture.dot | 56 ---- rig/docs/graphs/03-architecture.svg | 94 ------- rig/docs/index.html | 352 +++++++++---------------- rig/docs/notes/addons.md | 6 + rig/docs/notes/check.md | 39 ++- rig/docs/notes/docs.md | 6 + rig/docs/notes/overlay.md | 6 +- rig/docs/viewer.html | 1 + rig/examples/data/addons/airflow.sh | 73 +---- rig/examples/data/addons/airflow.yaml | 69 +++++ rig/examples/data/addons/postgres.sh | 73 +---- rig/examples/data/addons/postgres.yaml | 69 +++++ rig/examples/data/addons/redis.sh | 39 +-- rig/examples/data/addons/redis.yaml | 35 +++ rig/examples/data/rig.env | 3 + rig/standalone/default/rigdeps.sh | 26 ++ rig/standalone/default/rigmini.sh | 26 ++ 23 files changed, 652 insertions(+), 670 deletions(-) create mode 100644 rig/ctrl/addons/metallb.yaml delete mode 100644 rig/docs/graphs/03-architecture.dot delete mode 100644 rig/docs/graphs/03-architecture.svg create mode 100644 rig/examples/data/addons/airflow.yaml create mode 100644 rig/examples/data/addons/postgres.yaml create mode 100644 rig/examples/data/addons/redis.yaml diff --git a/rig/ctrl/addons/metallb.sh b/rig/ctrl/addons/metallb.sh index bea1c48..5da0556 100755 --- a/rig/ctrl/addons/metallb.sh +++ b/rig/ctrl/addons/metallb.sh @@ -3,6 +3,7 @@ # The pool is derived from the kind Docker network at install time. # Notes: docs/notes/addons.md set -euo pipefail +ADDON_DIR="$(cd "$(dirname "$0")" && pwd)" # its manifest sits beside it cd "${RIG_CTRL:-$(dirname "$0")/..}" source ./lib/config.sh @@ -61,27 +62,7 @@ $K rollout status daemonset/speaker -n metallb-system --timeout=240s # on a race that resolves itself in seconds. echo " configuring the address pool" for attempt in 1 2 3 4 5 6 7 8 9 10; do - if $K apply -f - >/dev/null 2>&1 </dev/null 2>&1 then echo " pool ready: ${pool_start}-${pool_end}" exit 0 diff --git a/rig/ctrl/addons/metallb.yaml b/rig/ctrl/addons/metallb.yaml new file mode 100644 index 0000000..29cb8c2 --- /dev/null +++ b/rig/ctrl/addons/metallb.yaml @@ -0,0 +1,19 @@ +apiVersion: metallb.io/v1beta1 +kind: IPAddressPool +metadata: + name: default + namespace: metallb-system +spec: + addresses: + - ${pool_start}-${pool_end} +--- +# Layer 2 mode: one node answers ARP for each address. No BGP peer needed, which +# is what makes this work on a laptop. +apiVersion: metallb.io/v1beta1 +kind: L2Advertisement +metadata: + name: default + namespace: metallb-system +spec: + ipAddressPools: + - default diff --git a/rig/ctrl/check.sh b/rig/ctrl/check.sh index 344b807..03b3a32 100755 --- a/rig/ctrl/check.sh +++ b/rig/ctrl/check.sh @@ -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 diff --git a/rig/ctrl/docs.sh b/rig/ctrl/docs.sh index 58c4bcd..a7e7010 100755 --- a/rig/ctrl/docs.sh +++ b/rig/ctrl/docs.sh @@ -16,12 +16,30 @@ serve() { echo "no docs/index.html" >&2 exit 1 fi + # One worker and no access log: a few static pages need no more, and the + # terminal then shows only real errors. The config is a temp file, gone on exit. + # Global, not local: the trap runs after serve() has returned. + DOCS_CONF=$(mktemp) + trap 'rm -f "$DOCS_CONF"' EXIT + cat > "$DOCS_CONF" <<'NGINX' +worker_processes 1; +error_log stderr error; +events { worker_connections 64; } +http { + include /etc/nginx/mime.types; + access_log off; + server { listen 80; root /usr/share/nginx/html; } +} +NGINX + chmod 644 "$DOCS_CONF" echo "docs for '$CLUSTER' on http://localhost:${DOCS_PORT}" echo " (ctrl-c to stop; nothing is installed and nothing persists)" docker run --rm \ --name "${CLUSTER}-docs" \ + -e NGINX_ENTRYPOINT_QUIET_LOGS=1 \ -p "${DOCS_PORT}:80" \ -v "$REPO/docs:/usr/share/nginx/html:ro" \ + -v "$DOCS_CONF:/etc/nginx/nginx.conf:ro" \ nginx:alpine } diff --git a/rig/ctrl/lib/config.sh b/rig/ctrl/lib/config.sh index a645dde..f1bd78d 100644 --- a/rig/ctrl/lib/config.sh +++ b/rig/ctrl/lib/config.sh @@ -208,6 +208,29 @@ render_kind_config() { "$KIND_CONFIG" } +# Render an addon's manifest (a .yaml beside its .sh) to stdout. Every ${NAME}, or +# ${NAME:-default}, is filled from the loaded config or the calling script's variables; +# a NAME with neither is an error, so a typo never becomes an empty image. $VAR and +# $(VAR) are left alone: they belong to the container. Notes: docs/notes/addons.md +render_manifest() { + local file="$1" text ref key def val + text=$(<"$file") || return 1 + while IFS= read -r ref; do + key=${ref#\$\{}; key=${key%\}}; def="" + if [[ $key == *:-* ]]; then def=${key#*:-}; key=${key%%:-*}; fi + if [ -n "${!key:-}" ]; then + val=${!key} + elif [[ $ref == *:-* ]]; then + val=$def + else + echo "render_manifest: $file needs \${$key}, which nothing sets" >&2 + return 1 + fi + text=${text//"$ref"/"$val"} + done < <(grep -oE '\$\{[A-Za-z_][A-Za-z0-9_]*(:-[^}]*)?\}' "$file" | sort -u) + printf '%s\n' "$text" +} + _config_restore() { local line while IFS= read -r line; do diff --git a/rig/ctrl/selftest.sh b/rig/ctrl/selftest.sh index 3a7ee1a..0a501bd 100755 --- a/rig/ctrl/selftest.sh +++ b/rig/ctrl/selftest.sh @@ -307,7 +307,7 @@ HOUSE_PAT="$(printf '%s' 'semes' 'ter|local' '\.ar\b')" check "✖ S2 no example environment name from the copies era" "0" \ "$(cd .. && grep -rIlE "$COPIES_PAT" . --exclude-dir=def --exclude-dir=local --exclude=STALE.md 2>/dev/null | wc -l)" check "✖ S3 ctrl/addons makes the cluster work, nothing more" "cert-manager metallb metrics-server" \ - "$(ls addons/ | sed 's/\.sh$//' | sort | xargs)" + "$(ls addons/*.sh | xargs -n1 basename | sed 's/\.sh$//' | sort | xargs)" # the scripts; their manifests sit beside them check "✖ S3 versions.env pins no workload image" "0" \ "$(grep -cE '^(POSTGRES|REDIS|AIRFLOW)_IMAGE=' versions.env)" check "✖ S4 no namespace named after the cluster" "0" "$(grep -c "CLUSTER + ':namespace'" Tiltfile)" @@ -378,6 +378,16 @@ note "the examples are overlays that work as shipped" bad="" for f in ../examples/*/addons/*.sh; do [ -e "$f" ] && { bash -n "$f" 2>/dev/null || bad+="$f "; }; done check "every example addon parses" "" "$bad" +# An addon's manifest may only name what the config or its own script sets, so a typo +# fails here rather than as an empty value on a cluster. +bad="" +for y in addons/*.yaml ../examples/*/addons/*.yaml; do + [ -e "$y" ] || continue + for n in $(grep -oE '\$\{[A-Za-z_][A-Za-z0-9_]*\}' "$y" | tr -d '${}' | sort -u); do + grep -qE "(^|[[:space:]])$n=" "${y%.yaml}.sh" versions.env ../examples/*/rig.env 2>/dev/null || bad+="$y:$n " + done +done +check "every addon manifest names only what is set" "" "$bad" if command -v python3 >/dev/null; then bad="" for f in ../examples/*/dags/*.py; do diff --git a/rig/docs/graphs/03-architecture.dot b/rig/docs/graphs/03-architecture.dot deleted file mode 100644 index ae9e595..0000000 --- a/rig/docs/graphs/03-architecture.dot +++ /dev/null @@ -1,56 +0,0 @@ -// TODO: PLACEHOLDER — replace with the real estate topology. -// -// This is where the extracted platform diagrams land. The shape below is -// illustrative only: it shows how a mocked dependency, a real service and a -// remote system are meant to sit together, not what the system actually is. -// -// The intended end state is that this file stops being hand-written and is -// generated from the running cluster, so the diagram becomes a report of what -// exists rather than a drawing of what was once intended. -digraph estate { - rankdir=LR - bgcolor="#0a0e17" - fontname="Helvetica" - node [fontname="Helvetica" fontsize=11 style=filled color="#1e2a4a" fontcolor="#e8eaf0" shape=box] - edge [fontname="Helvetica" fontsize=9 fontcolor="#8892a8" color="#4a5568"] - - label="Estate topology — PLACEHOLDER" - labelloc=t - fontsize=16 - fontcolor="#ffc107" - - subgraph cluster_new { - label="New" - style=dashed - color="#1e2a4a" - fontcolor="#8892a8" - - api [label="service under work\n(real: built and hot-reloaded)" fillcolor="#1a3a1a" fontcolor="#00c853"] - } - - subgraph cluster_core { - label="Core (mocked)" - style=dashed - color="#1e2a4a" - fontcolor="#8892a8" - - svc_a [label="upstream service\n(mock: canned responses)" fillcolor="#121829"] - db [label="datastore\n(mock)" fillcolor="#121829" shape=cylinder] - } - - subgraph cluster_legacy { - label="Legacy estate (mocked)" - style=dashed - color="#1e2a4a" - fontcolor="#8892a8" - - batch [label="batch drop\n(mock: writes files on a timer)" fillcolor="#121829"] - } - - remote [label="external system\n(remote: ExternalName,\nreachable only from a VDI)" fillcolor="#3a1a1a" fontcolor="#ffc107" shape=octagon] - - api -> svc_a [label="HTTP"] - api -> db [label="query"] - api -> batch [label="file handoff" style=dashed] - api -> remote [label="only when reachable" style=dashed color="#ffc107"] -} diff --git a/rig/docs/graphs/03-architecture.svg b/rig/docs/graphs/03-architecture.svg deleted file mode 100644 index f02fbda..0000000 --- a/rig/docs/graphs/03-architecture.svg +++ /dev/null @@ -1,94 +0,0 @@ - - - - - - -estate - -Estate topology — PLACEHOLDER - -cluster_new - -New - - -cluster_core - -Core (mocked) - - -cluster_legacy - -Legacy estate (mocked) - - - -api - -service under work -(real: built and hot-reloaded) - - - -svc_a - -upstream service -(mock: canned responses) - - - -api->svc_a - - -HTTP - - - -db - - -datastore -(mock) - - - -api->db - - -query - - - -batch - -batch drop -(mock: writes files on a timer) - - - -api->batch - - -file handoff - - - -remote - -external system -(remote: ExternalName, -reachable only from a VDI) - - - -api->remote - - -only when reachable - - - diff --git a/rig/docs/index.html b/rig/docs/index.html index b49120c..bc95964 100644 --- a/rig/docs/index.html +++ b/rig/docs/index.html @@ -2,6 +2,7 @@ + rig — local environment installer