gioser

sergio/takana

> Una distribución Linux construida con un laboratorio funcional y hermético en el > sótano, y una terminal mutable, clásica y anárquica en el piso de arriba — diseñada > desde el suelo para que una IA programadora entienda, modifique y co…

TOML 1430Shell 145Markdown 87Rust 852973 Sutichaykuna

⎇ 534014c3ec69daea789a1d0523e1d00a69c6ed65 scripts/farm/farm-down.sh Shell · 84 siq'ikuna · 4.4 KB Ñawpa willay ch'uya

takana: el worker pasa a /opt/takana y takana-farm.service aacee245d · Sergio ·

1#!/usr/bin/env bash
2# farm-down.sh [name...] — COSECHA el store sellado de cada worker efímero al laptop y luego lo
3# DESTRUYE. Sin args opera sobre toda la flota registrada en scripts/farm/.fleet. Con args, sólo
4# sobre los workers nombrados (para bajar la escala parcialmente).
5#
6# El orden importa: primero rsync-eamos el store (CAS, merge seguro con el del laptop), y SÓLO si
7# el pull salió bien destruimos el server. Tras destruir todo, promovemos+firmamos lo cosechado
8# (cache-hit en los artefactos recién bajados) — el store del laptop es la fuente canónica.
9#
10# Uso:   scripts/farm/farm-down.sh            # cosecha + destruye toda la flota
11#        scripts/farm/farm-down.sh hworker-2  # sólo ese
12# Env:   SSH_KEY (def ~/.ssh/github5), REMOTE (def /opt/takana), NO_PROMOTE=1 (sólo cosecha+destruye)
13set -uo pipefail
14
15SSH_KEY="${SSH_KEY:-$HOME/.ssh/github5}"
16REMOTE="${REMOTE:-/opt/takana}"
17ROOT="$(cd "$(dirname "$0")/../.." && pwd)"
18FLEET="$ROOT/scripts/farm/.fleet"
19# Ver nota en farm-up.sh: workers efímeros reciclan IPs ⇒ known_hosts a /dev/null (hub-and-spoke,
20# worker sin secretos, sin superficie MITM que proteger).
21SSH="ssh -i $SSH_KEY -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no -o LogLevel=ERROR -o ConnectTimeout=10"
22cd "$ROOT"
23[ -s "$FLEET" ] || { echo "flota vacía (scripts/farm/.fleet)"; exit 0; }
24
25want=" $* "   # si hay args, filtrar por nombre; si no, todos
26harvested=0
27newfleet="$(mktemp)"
28
29while read -r name ip; do
30  [ -n "${name:-}" ] || continue
31  if [ "$#" -gt 0 ] && [ "${want#* $name }" = "$want" ]; then
32    echo "$name $ip" >> "$newfleet"; continue   # no seleccionado: sigue vivo
33  fi
34  # ── BLINDAJE (2026-07-17, petición explícita del usuario): gioser.net es FIJO, vive en el MISMO
35  # proyecto hcloud y NO TIENE BACKUP. Este script borra por NOMBRE leído de .fleet, sin verificar
36  # nada: si un nombre equivocado entra a esa lista (a mano, por un bug, por un rsync), lo destruye
37  # sin preguntar. Dos capas iguales a las del deadman — la del label es la fuerte, porque no
38  # depende de mantener una lista al día. gioser no tiene labels (map[]) ⇒ nunca pasa.
39  case "$name" in
40    gioser|gioser.net|*gioser*)
41      echo "  ⛔ '$name' está en la LISTA NEGRA (server fijo sin backup) — NO se toca"
42      echo "$name $ip" >> "$newfleet"; continue ;;
43  esac
44  if ! hcloud server describe "$name" -o format='{{.Labels}}' 2>/dev/null | grep -q hammer-worker; then
45    echo "  ⛔ '$name' NO tiene label role=hammer-worker ⇒ no nació de la granja. NO se borra."
46    echo "$name $ip" >> "$newfleet"; continue
47  fi
48
49  echo "==> cosechando store de $name ($ip)"
50  # ⚠ `--exclude-from` del registro de podados: el worker NO se poda, así que devolvería los
51  # artefactos superados y desharía la poda (24 G por vuelta). Misma protección que en
52  # `cosecha-cron.sh`, `farm-sync.sh` y `store-gc.sh` — son CUATRO rutas que bajan el store del
53  # worker y la protección tiene que estar en las cuatro. Se descubrió tras arreglar sólo una y
54  # comprobar que la churn seguía.
55  LEDGER_GC="$ROOT/work/store-gc-superados.txt"
56  EXCL_GC=""; [ -s "$LEDGER_GC" ] && EXCL_GC="--exclude-from=$LEDGER_GC"
57  if rsync -az $EXCL_GC -e "$SSH" "root@$ip:$REMOTE/store/" "$ROOT/store/"; then
58    echo "==> destruyendo $name"
59    if hcloud server delete "$name" >/dev/null 2>&1; then
60      harvested=$((harvested + 1))
61    else
62      echo "  ⚠ no pude destruir $name — lo dejo en la flota"; echo "$name $ip" >> "$newfleet"
63    fi
64  else
65    echo "  ⚠ falló el pull de store de $name — NO lo destruyo (queda en la flota)"
66    echo "$name $ip" >> "$newfleet"
67  fi
68done < "$FLEET"
69
70mv "$newfleet" "$FLEET"
71echo "==> cosechados+destruidos: $harvested; flota restante:"; cat "$FLEET"
72
73if [ "${NO_PROMOTE:-}" != "1" ] && [ "$harvested" -gt 0 ]; then
74  # El worker muele TODAS las colas (ver QUEUES en farm-worker-loop.sh); build-farm.sh sin QUEUE
75  # sólo mira recipes/incoming ⇒ los artefactos de incoming-go/incoming-clib se cosechaban pero
76  # NO se promovían/firmaban. Recorrer las mismas colas que el worker (cache-hit en lo cosechado).
77  # El guard `ls "$Q"/*.toml` salta las colas vacías, así que listar todas es inocuo.
78  echo "==> promote+firma final (cache-hit en lo cosechado)"
79  for Q in ${QUEUES:-recipes/incoming recipes/incoming-go}; do
80    ls "$Q"/*.toml >/dev/null 2>&1 || continue
81    echo "  --- promoviendo $Q"
82    QUEUE="$Q" scripts/build-farm.sh || echo "  (build-farm $Q devolvió error; revisá logs)"
83  done
84fi