#!/usr/bin/env bash # farm-run.sh [N] — el ciclo EFÍMERO de una tanda, de punta a punta y DESATENDIDO: levanta N # workers desde el snapshot golden, los alinea con la cola actual del laptop, espera a que MUELAN # la cola entera (un ciclo completo del worker-loop), y en cuanto terminan COSECHA su store y los # DESTRUYE. "Subí la cola, corré esto, andá a hacer otra cosa; se apaga solo cuando termina." # # Es el pegamento up→esperar→down: farm-up.sh + farm-down.sh existen sueltos (control manual/parcial); # farm-run.sh los encadena con la detección de "ya no queda nada nuevo por construir". # # Detección de "terminó": el worker-loop (farm-worker-loop.sh) imprime "ciclo terminado" al journal # tras construir TODAS las colas una vez. Contamos esas líneas ANTES de arrancar (baseline, persisten # en el journal) y esperamos a que el contador suba: +1 ⇒ el worker intentó la cola entera una vez # (lo que se pudo sellar, sellado; lo que falla, falla y se reintentaría en vano). Robusto a skew de # reloj (contamos ocurrencias, no comparamos timestamps) y a builds largos (no hay falso-positivo por # "store estable": sólo el marcador de fin-de-ciclo cuenta). Con varios workers, espera a TODOS. # # Uso: scripts/farm/farm-run.sh # 1 worker, cola actual, cosecha+destruye+firma al terminar # scripts/farm/farm-run.sh 3 # 3 workers en paralelo sobre la misma cola # Env: POLL (def 60s entre sondeos), MAXWAIT (def 21600s=6h tope duro), + los de farm-up/down # (IMAGE/TYPE/LOCATION/SSHKEY/SSH_KEY/REMOTE/NO_PROMOTE). set -euo pipefail N="${1:-1}" POLL="${POLL:-60}" MAXWAIT="${MAXWAIT:-21600}" SSH_KEY="${SSH_KEY:-$HOME/.ssh/github5}" ROOT="$(cd "$(dirname "$0")/../.." && pwd)" FLEET="$ROOT/scripts/farm/.fleet" # Ver nota en farm-up.sh: workers efímeros reciclan IPs ⇒ known_hosts a /dev/null (hub-and-spoke, # worker sin secretos, sin superficie MITM que proteger). SSH="ssh -i $SSH_KEY -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no -o LogLevel=ERROR -o ConnectTimeout=10" cd "$ROOT" cycles() { $SSH "root@$1" 'journalctl -u takana-farm -o cat --no-pager 2>/dev/null | grep -c "ciclo terminado"' 2>/dev/null || echo 0; } echo "==> [farm-run] levantando $N worker(s) y alineando la cola…" scripts/farm/farm-up.sh "$N" # Baseline por worker de la flota recién levantada (los que YA existían también entran: esperamos # a que cada uno complete un ciclo nuevo desde el restart que hizo farm-up). declare -A base while read -r name ip; do [ -n "${name:-}" ] || continue base["$name|$ip"]=$(cycles "$ip") done < "$FLEET" echo "==> [farm-run] esperando a que la flota muela la cola (POLL=${POLL}s, tope ${MAXWAIT}s)…" waited=0 while :; do pending=0 while read -r name ip; do [ -n "${name:-}" ] || continue b=${base["$name|$ip"]:-0} c=$(cycles "$ip") if [ "$c" -le "$b" ]; then pending=$((pending + 1)) fi done < "$FLEET" [ "$pending" -eq 0 ] && { echo "==> [farm-run] la flota completó un ciclo — cola molida."; break; } if [ "$waited" -ge "$MAXWAIT" ]; then echo " ⚠ [farm-run] tope de espera (${MAXWAIT}s) alcanzado con $pending worker(s) sin cerrar ciclo — cosecho igual lo que haya." break fi sleep "$POLL"; waited=$((waited + POLL)) done echo "==> [farm-run] cosechando + destruyendo + firmando…" scripts/farm/farm-down.sh echo "==> [farm-run] listo. Baseline €0 (sin cajas vivas de la tanda)."