takana: el worker pasa a /opt/takana y takana-farm.service
Renombrado el directorio del worker y la unit, con las tres units del repo (farm, deadman service y timer). Todas las rutas /opt/hammer del hub pasan a /opt/takana: eran defaults REMOTE= y HAMMER_DIR=, mas los systemctl/journalctl que nombraban la unit sin el sufijo .service, que el primer sed no casaba. Orden, para que no quedara partido: el hub siembra por rsync a una ruta fija, asi que se saco el cron ANTES de mover. Si se movia el worker con el hub apuntando a la ruta vieja, la siguiente cosecha recreaba /opt/hammer y quedaban dos arboles. Verificado de punta a punta: - el worker cerro un ciclo de build REAL desde /opt/takana (dunst sellado, 1/1) - una cosecha completa del hub contra la ruta nueva: siembra, manifiesto, los nueve grafos, static-audit (691 estaticos, MIENTEN 0) y estado pusheado - la unit vieja quedo deshabilitada y borrada; /opt tiene una sola entrada NO se tocan dos referencias a /opt/hammer que siguen siendo CORRECTAS: docs/23-plan-rehasheo.md describe rutas EMBEBIDAS en artefactos ya construidos —que literalmente dicen /opt/hammer/work en sus secciones .debug— y el HANDOFF de la noche de KDE es registro. Cambiarlas haria que los documentos mientan.
This commit is contained in:
@@ -93,7 +93,7 @@ simplemente deja de cosechar. El directorio es lo **último** que se toca, y con
|
||||
- ADRs y docs de diseño: texto, y `hammer` sigue funcionando. Van con la etapa 5.
|
||||
|
||||
**Cómo converge el worker** (medido el 2026-09-09, no supuesto). Su checkout vive en
|
||||
`/opt/hammer`, **no es un clon git** —lo pone el rsync de la siembra— y `hammer-farm.service`
|
||||
`/opt/hammer`, **no es un clon git** —lo pone el rsync de la siembra— y `takana-farm.service`
|
||||
está `enabled` allá, corriendo `farm-worker-loop.sh` como servicio largo. En el momento del
|
||||
cambio el worker tenía scripts viejos y binario del 6-sep: **coherente**. Los dos estados
|
||||
intermedios también lo son, y por eso la 3a iba primero:
|
||||
@@ -105,7 +105,7 @@ simplemente deja de cosechar. El directorio es lo **último** que se toca, y con
|
||||
La unit apunta a la RUTA DEL SCRIPT, no al binario, así que nada de la etapa 3 la toca. El
|
||||
nombre del fichero de unit y `/opt/hammer` son etapa 6.
|
||||
|
||||
**Convergido y comprobado (2026-09-09 18:40–18:45Z).** Reiniciado `hammer-farm.service`, el
|
||||
**Convergido y comprobado (2026-09-09 18:40–18:45Z).** Reiniciado `takana-farm.service`, el
|
||||
worker compiló los dos binarios y **cerró un ciclo de build real con el nombre nuevo**:
|
||||
`✓ dunst b3:29a79859…`, BUILD-YIELD 1/1, 1 promovido. No es que «no se rompió nada visible»: el
|
||||
camino de construir y sellar se ejercitó entero.
|
||||
@@ -197,7 +197,7 @@ simplemente deja de cosechar. El directorio es lo **último** que se toca, y con
|
||||
reescribir historia es peor que un mensaje incompleto. El detalle vive acá.)*
|
||||
|
||||
**Congelados y verificados con controles:** `/opt/hammer`, `/var/lib/hammer`, `/usr/bin/hammer`,
|
||||
`/mnt/vvv/hammer`, la URL de gitea, `hammer-farm.service`, `hammer-live-install.sh`,
|
||||
`/mnt/vvv/hammer`, la URL de gitea, `takana-farm.service`, `hammer-live-install.sh`,
|
||||
`BRIEFING-hammer.md`, `hammerd`, `hammer-recover`, `HAMMER_LIVE`, `.hammer-zig-cc`,
|
||||
`.hammer-cargo-vendor` y dos identificadores más que aparecieron al barrer:
|
||||
- `hammer-build-state/1` — el `schema` del estado generado. Nadie lo valida, pero el valor de un
|
||||
@@ -236,7 +236,7 @@ simplemente deja de cosechar. El directorio es lo **último** que se toca, y con
|
||||
- 🔒 **`/usr/bin/hammer` dentro del rootfs del producto** — está **dentro de un árbol que se
|
||||
hashea**. Moverlo cambia el hash del producto y obliga a rehacer el baseline del selfhost.
|
||||
Va junto con esa deuda, no antes.
|
||||
- ⚠ **`hammer-farm.service` y `/opt/hammer`** — despliegue coordinado en el worker: parar,
|
||||
- ⚠ **`takana-farm.service` y `/opt/hammer`** — despliegue coordinado en el worker: parar,
|
||||
renombrar, `daemon-reload`, arrancar. Reversible, pero es hacia afuera.
|
||||
- ✅ **`/mnt/vvv/hammer` → `/mnt/vvv/takana`, y el repo renombrado en gitea a `sergio/takana`**
|
||||
(2026-09-09 ~20:00Z). Se coordinó primero: se avisó a las dos sesiones `hammer-*` y `hammer-9f`
|
||||
|
||||
+2
-2
@@ -54,8 +54,8 @@ while [ "$i" -le "$N" ]; do
|
||||
for _ in $(seq 1 30); do $SSH "root@$ip" true 2>/dev/null && break; sleep 6; done
|
||||
rsync -az -e "$SSH" --exclude /work --exclude /store --exclude '/store-*' --exclude /target \
|
||||
--exclude /dist --exclude /tandas --exclude /.dev-fs --exclude /.git --exclude /.scratch \
|
||||
--exclude '*.png' --exclude '/content*' ./ "root@$ip:/opt/hammer/" >/dev/null 2>&1
|
||||
hN=$($SSH "root@$ip" "export PATH=\$HOME/.cargo/bin:\$PATH; cd /opt/hammer && \
|
||||
--exclude '*.png' --exclude '/content*' ./ "root@$ip:/opt/takana/" >/dev/null 2>&1
|
||||
hN=$($SSH "root@$ip" "export PATH=\$HOME/.cargo/bin:\$PATH; cd /opt/takana && \
|
||||
cargo build --release -q -p takana-cli 2>/dev/null; \
|
||||
timeout ${POR:-900} ./target/release/takana build $RECETA --store \$PWD/store 2>/dev/null \
|
||||
| grep -oE '^b3:[a-f0-9]{64}' | head -1" 2>/dev/null | tr -d '\r')
|
||||
|
||||
@@ -21,11 +21,11 @@
|
||||
# Uso (en el worker): scripts/farm/campana-deuda.sh <receta> [receta…]
|
||||
# DEUDA="glib cairo" scripts/farm/campana-deuda.sh
|
||||
# Lanzarlo desde el hub para que sobreviva al cierre del SSH:
|
||||
# ssh root@<ip> 'systemd-run --unit=campana-deuda --working-directory=/opt/hammer \
|
||||
# /opt/hammer/scripts/farm/campana-deuda.sh <recetas…>'
|
||||
# ssh root@<ip> 'systemd-run --unit=campana-deuda --working-directory=/opt/takana \
|
||||
# /opt/takana/scripts/farm/campana-deuda.sh <recetas…>'
|
||||
# ssh root@<ip> 'journalctl -u campana-deuda -f'
|
||||
set -u
|
||||
HAMMER_DIR="${TAKANA_DIR:-${HAMMER_DIR:-/opt/hammer}}"
|
||||
HAMMER_DIR="${TAKANA_DIR:-${HAMMER_DIR:-/opt/takana}}"
|
||||
# Ruta absoluta a MÍ MISMO antes del `cd`: acá el `cd` va a HAMMER_DIR, que ni siquiera tiene por
|
||||
# qué contener a este script, así que un `$0` relativo se pierde seguro.
|
||||
YO="$(cd "$(dirname "$0")" && pwd)/$(basename "$0")"
|
||||
|
||||
@@ -21,7 +21,7 @@
|
||||
#
|
||||
# Uso: scripts/farm/cosecha-cron.sh # un ciclo (lo que dispara el cron)
|
||||
# */30 * * * * cd /home/sergio/takana && scripts/farm/cosecha-cron.sh >>work/cosecha-cron.log 2>&1
|
||||
# Env: SSH_KEY (def ~/.ssh/github5), REMOTE (def /opt/hammer), NO_COMMIT=1 (regenera pero no commitea)
|
||||
# Env: SSH_KEY (def ~/.ssh/github5), REMOTE (def /opt/takana), NO_COMMIT=1 (regenera pero no commitea)
|
||||
set -uo pipefail
|
||||
|
||||
ROOT="$(cd "$(dirname "$0")/../.." && pwd)"
|
||||
@@ -62,7 +62,7 @@ if [ -z "${COSECHA_CRON_CON_LOCK:-}" ]; then
|
||||
fi
|
||||
|
||||
SSH_KEY="${SSH_KEY:-$HOME/.ssh/github5}"
|
||||
REMOTE="${REMOTE:-/opt/hammer}"
|
||||
REMOTE="${REMOTE:-/opt/takana}"
|
||||
FLEET="$ROOT/scripts/farm/.fleet"
|
||||
# ── SIEMBRA DE LA FLOTA FIJA ────────────────────────────────────────────────────────────────────
|
||||
# `.fleet` es estado de EJECUCIÓN (gitignored, lo reescribe el reaper cada ciclo); `flota-permanente`
|
||||
@@ -156,7 +156,7 @@ else
|
||||
fi
|
||||
|
||||
# 2.5 REAPER HUB-SIDE (2026-07-23). El dead-man DEL WORKER probó FRÁGIL: un worker quedó 3.5h idle
|
||||
# quemando € porque su `/etc/hammer-deadman.env` no tenía token válido ⇒ el dead-man llegaba a
|
||||
# quemando € porque su `/etc/takana-deadman.env` no tenía token válido ⇒ el dead-man llegaba a
|
||||
# matarse pero salía 1 "sin HCLOUD_TOKEN" cada tick. La garantía "un vps idle es inadmisible" NO
|
||||
# puede depender de que cada worker se auto-provisione bien un token (falla en silencio). El HUB sí
|
||||
# tiene un token que funciona, y este latido corre cada 30 min aunque no haya sesión (setsid) ⇒ el
|
||||
|
||||
@@ -47,15 +47,15 @@
|
||||
set -eu
|
||||
|
||||
IDLE_MAX_TICKS="${IDLE_MAX_TICKS:-6}" # 6 ticks × 10 min = 1 h sin trabajo → borrar
|
||||
ESTADO="/var/lib/hammer-deadman.ticks"
|
||||
ESTADO="/var/lib/takana-deadman.ticks"
|
||||
HEARTBEAT="/run/hammer-heartbeat"
|
||||
HEARTBEAT_MAX_AGE=1800 # 30 min: más viejo que esto no cuenta como vivo
|
||||
LOG="/var/log/hammer-deadman.log"
|
||||
LOG="/var/log/takana-deadman.log"
|
||||
|
||||
log() { echo "$(date -u +%FT%TZ) deadman: $*" >> "$LOG"; }
|
||||
|
||||
# TOKEN ROBUSTO A CADA CAMINO DE CREACIÓN (incidente 2026-07-23). Hay DOS caminos que crean workers y
|
||||
# ponen el token en ficheros DISTINTOS: `farm-up` → /etc/hammer-deadman.env (EnvironmentFile de la
|
||||
# ponen el token en ficheros DISTINTOS: `farm-up` → /etc/takana-deadman.env (EnvironmentFile de la
|
||||
# service); `harkaq-vol.sh` → /root/.hcloud-token (cloud-init). La service del snapshot lee sólo el
|
||||
# primero ⇒ un worker nacido por el OTRO camino quedaba SIN token en su env, disparaba pero salía 1
|
||||
# "sin HCLOUD_TOKEN" y NUNCA se borraba (3.5h idle quemando €). El dead-man NO puede depender del hub
|
||||
@@ -65,8 +65,8 @@ log() { echo "$(date -u +%FT%TZ) deadman: $*" >> "$LOG"; }
|
||||
# La service lo recibe vía EnvironmentFile, pero corrido A MANO (farm-up --puede-borrar) ese fichero
|
||||
# NO se carga solo ⇒ hay que sourcearlo, o la verificación da falso-negativo y destruye un worker que
|
||||
# SÍ puede matarse (lo cazó el 1er worker real, 2026-07-23). Es formato KEY=value.
|
||||
if [ -z "$HCLOUD_TOKEN" ] && [ -r /etc/hammer-deadman.env ]; then
|
||||
. /etc/hammer-deadman.env 2>/dev/null || true
|
||||
if [ -z "$HCLOUD_TOKEN" ] && [ -r /etc/takana-deadman.env ]; then
|
||||
. /etc/takana-deadman.env 2>/dev/null || true
|
||||
: "${HCLOUD_TOKEN:=}"
|
||||
fi
|
||||
# Y ficheros de token DESNUDO (otros caminos de creación): el volumen primero, que es lo persistente.
|
||||
@@ -92,8 +92,8 @@ fi
|
||||
|
||||
if [ "${1:-}" = "--verificar" ]; then
|
||||
echo "══ ¿el dead-man switch está REALMENTE armado?"
|
||||
echo "── timer:"; systemctl is-active hammer-deadman.timer 2>/dev/null || echo " INACTIVO ⚠"
|
||||
systemctl list-timers hammer-deadman.timer --no-pager 2>/dev/null | head -3
|
||||
echo "── timer:"; systemctl is-active takana-deadman.timer 2>/dev/null || echo " INACTIVO ⚠"
|
||||
systemctl list-timers takana-deadman.timer --no-pager 2>/dev/null | head -3
|
||||
echo "── ticks idle acumulados: $(cat "$ESTADO" 2>/dev/null || echo 0) / $IDLE_MAX_TICKS"
|
||||
echo "── últimas líneas del log (evidencia de que DISPARA):"
|
||||
tail -5 "$LOG" 2>/dev/null | sed 's/^/ /' || echo " (sin log todavía ⇒ NUNCA disparó)"
|
||||
|
||||
@@ -15,7 +15,7 @@ if [ -s "$FLEET" ]; then
|
||||
echo "▶ $name ($ip)"
|
||||
$SSH "root@$ip" '
|
||||
echo " $(uptime | sed "s/.*load average/load/")"
|
||||
n=$(ls /opt/hammer/store 2>/dev/null | wc -l); echo " store: $n artefactos"
|
||||
n=$(ls /opt/takana/store 2>/dev/null | wc -l); echo " store: $n artefactos"
|
||||
b=$(pgrep -af "release/hammer" | grep -E "release/hammer (build|--store|hash)" | grep -oE "[^ /]+\.toml" | sort -u | tr "\n" " ")
|
||||
echo " moliendo: ${b:-(nada — idle o entre colas)}"
|
||||
' 2>/dev/null || echo " ⚠ sin respuesta (¿dead-man lo mató? ¿aún arrancando?)"
|
||||
|
||||
@@ -9,11 +9,11 @@
|
||||
#
|
||||
# Uso: scripts/farm/farm-down.sh # cosecha + destruye toda la flota
|
||||
# scripts/farm/farm-down.sh hworker-2 # sólo ese
|
||||
# Env: SSH_KEY (def ~/.ssh/github5), REMOTE (def /opt/hammer), NO_PROMOTE=1 (sólo cosecha+destruye)
|
||||
# Env: SSH_KEY (def ~/.ssh/github5), REMOTE (def /opt/takana), NO_PROMOTE=1 (sólo cosecha+destruye)
|
||||
set -uo pipefail
|
||||
|
||||
SSH_KEY="${SSH_KEY:-$HOME/.ssh/github5}"
|
||||
REMOTE="${REMOTE:-/opt/hammer}"
|
||||
REMOTE="${REMOTE:-/opt/takana}"
|
||||
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,
|
||||
|
||||
@@ -26,7 +26,7 @@ set -euo pipefail
|
||||
ROOT="$(cd "$(dirname "$0")/../.." && pwd)"; cd "$ROOT"
|
||||
IP="${1:?falta la IP del worker}"
|
||||
SSH_KEY="${SSH_KEY:-$HOME/.ssh/github5}"
|
||||
REMOTE="${REMOTE:-/opt/hammer}"
|
||||
REMOTE="${REMOTE:-/opt/takana}"
|
||||
SSH="ssh -i $SSH_KEY -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null"
|
||||
IMG="$ROOT/.dev-fs/lab-image.tar.zst"
|
||||
TESTIGO="${TESTIGO:-recipes/zlib.toml}"
|
||||
|
||||
@@ -31,7 +31,7 @@ FLEET="$ROOT/scripts/farm/.fleet"
|
||||
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 hammer-farm -o cat --no-pager 2>/dev/null | grep -c "ciclo terminado"' 2>/dev/null || echo 0; }
|
||||
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"
|
||||
|
||||
@@ -9,12 +9,12 @@
|
||||
#
|
||||
# Uso: scripts/farm/farm-sync.sh root@1.2.3.4
|
||||
# scripts/farm/farm-sync.sh root@1.2.3.4 --no-promote # sólo sincroniza, no firma
|
||||
# Env: SSH_KEY (def ~/.ssh/github5), REMOTE (def /opt/hammer)
|
||||
# Env: SSH_KEY (def ~/.ssh/github5), REMOTE (def /opt/takana)
|
||||
set -euo pipefail
|
||||
|
||||
VPS="${1:?uso: farm-sync.sh user@host [--no-promote]}"
|
||||
SSH_KEY="${SSH_KEY:-$HOME/.ssh/github5}"
|
||||
REMOTE="${REMOTE:-/opt/hammer}"
|
||||
REMOTE="${REMOTE:-/opt/takana}"
|
||||
ROOT="$(cd "$(dirname "$0")/../.." && pwd)"
|
||||
cd "$ROOT"
|
||||
SSH="ssh -i $SSH_KEY -o StrictHostKeyChecking=accept-new"
|
||||
|
||||
+10
-10
@@ -15,7 +15,7 @@
|
||||
# Env: IMAGE (snapshot, def 417847948=takana-golden-rust-go-2026-08-08), TYPE (def ccx23),
|
||||
# LOCATION
|
||||
# (def hel1), SSHKEY (llave del proyecto Hetzner, def desarrollo@jlsoltech.com), SSH_KEY
|
||||
# (privada local p/ SSH, def ~/.ssh/github5), REMOTE (def /opt/hammer).
|
||||
# (privada local p/ SSH, def ~/.ssh/github5), REMOTE (def /opt/takana).
|
||||
set -euo pipefail
|
||||
|
||||
N="${1:-1}"
|
||||
@@ -40,7 +40,7 @@ TYPE="${TYPE:-ccx23}"
|
||||
LOCATION="${LOCATION:-hel1}"
|
||||
SSHKEY="${SSHKEY:-desarrollo@jlsoltech.com}"
|
||||
SSH_KEY="${SSH_KEY:-$HOME/.ssh/github5}"
|
||||
REMOTE="${REMOTE:-/opt/hammer}"
|
||||
REMOTE="${REMOTE:-/opt/takana}"
|
||||
# MISMO volumen que harkaq-vol.sh, a propósito: eran DOS caminos de crear workers y sólo uno tenía
|
||||
# volumen+automuerte. hworker-4 nació por ÉSTE (farm-up) y por eso quedó 5 días idle con 37G que
|
||||
# nadie salvó — la protección existía y este camino la esquivaba. Compartir el volumen hace que
|
||||
@@ -141,7 +141,7 @@ for k in $(seq 1 "$N"); do
|
||||
# mientras eso no se reordene, esto garantiza al menos que se llegue a armarlo.
|
||||
[ "$rc" -ne 0 ] && echo " ⚠ rsync de código salió $rc — el worker puede tener la cola incompleta; sigo para ARMAR EL DEAD-MAN"
|
||||
# el servicio ya arrancó en el boot (baked/enabled); reiniciar fuerza rescan inmediato de la cola
|
||||
$SSH root@"$ip" 'systemctl restart hammer-farm' 2>/dev/null || true
|
||||
$SSH root@"$ip" 'systemctl restart takana-farm' 2>/dev/null || true
|
||||
|
||||
# VOLUMEN PERSISTENTE + STORE DENTRO. Sin esto la automuerte perdería lo construido: hworker-4
|
||||
# tenía 438 artefactos (37G) SÓLO en su disco local. El store vive en /mnt/cosecha/store ⇒ cada
|
||||
@@ -188,9 +188,9 @@ for k in $(seq 1 "$N"); do
|
||||
# fusionar es seguro por construcción: \`mv -n\` no pisa lo que el volumen ya tiene.
|
||||
# ⚠ mountpoint -q NO ES OPCIONAL — sin él este bloque BORRA EL VOLUMEN.
|
||||
# La guarda original sólo comprobaba «es directorio y no es symlink». Eso basta en un server
|
||||
# recién creado desde la golden ORIGINAL, donde /opt/hammer/store es un directorio normal.
|
||||
# recién creado desde la golden ORIGINAL, donde /opt/takana/store es un directorio normal.
|
||||
# Pero al re-snapshotear un worker (2026-08-08) la imagen se llevó también la CONFIGURACIÓN DE
|
||||
# MONTAJE: el server nuevo monta el volumen en /opt/hammer/store al arrancar, ANTES de que
|
||||
# MONTAJE: el server nuevo monta el volumen en /opt/takana/store al arrancar, ANTES de que
|
||||
# corra este rescate. Entonces rm -rf $REMOTE/store borra A TRAVÉS DEL MONTAJE y se lleva el
|
||||
# store del volumen entero — que es justo lo que el volumen existe para proteger.
|
||||
# Medido: el volumen pasó de ~900 artefactos a 2. No se perdió el filesystem (lost+found es de
|
||||
@@ -210,7 +210,7 @@ for k in $(seq 1 "$N"); do
|
||||
# el store resuelve a /mnt/cosecha/store ⇒ busca /mnt/cosecha/.dev-fs, que no existe, y muere
|
||||
# con 'no encuentro el ejecutable zig en …' apuntando al lugar equivocado. Un bind-mount deja
|
||||
# los datos en el volumen igual, pero \$REMOTE/store sigue siendo una ruta REAL bajo
|
||||
# \$REMOTE ⇒ el padre vuelve a ser /opt/hammer y .dev-fs se encuentra. Mantiene las dos
|
||||
# \$REMOTE ⇒ el padre vuelve a ser /opt/takana y .dev-fs se encuentra. Mantiene las dos
|
||||
# invariantes a la vez: sellar = persistir, y el lab donde takana lo espera.
|
||||
if [ -L $REMOTE/store ]; then rm -f $REMOTE/store; fi
|
||||
mkdir -p $REMOTE/store
|
||||
@@ -239,11 +239,11 @@ for k in $(seq 1 "$N"); do
|
||||
scp -q -i "$SSH_KEY" -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null \
|
||||
scripts/farm/deadman.sh "root@$ip:/usr/local/bin/deadman.sh" 2>/dev/null
|
||||
scp -q -i "$SSH_KEY" -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null \
|
||||
scripts/farm/hammer-deadman.service scripts/farm/hammer-deadman.timer \
|
||||
scripts/farm/takana-deadman.service scripts/farm/takana-deadman.timer \
|
||||
"root@$ip:/etc/systemd/system/" 2>/dev/null
|
||||
$SSH root@"$ip" "chmod +x /usr/local/bin/deadman.sh
|
||||
printf 'HCLOUD_TOKEN=%s\n' '$tok' > /etc/hammer-deadman.env; chmod 600 /etc/hammer-deadman.env
|
||||
systemctl daemon-reload && systemctl enable --now hammer-deadman.timer" >/dev/null 2>&1
|
||||
printf 'HCLOUD_TOKEN=%s\n' '$tok' > /etc/takana-deadman.env; chmod 600 /etc/takana-deadman.env
|
||||
systemctl daemon-reload && systemctl enable --now takana-deadman.timer" >/dev/null 2>&1
|
||||
# EVIDENCIA, no fe: que el timer esté ARMADO se comprueba, no se asume (el cron de aquella vez
|
||||
# estaba "validado" y nunca disparó porque el PID 1 no tenía crond).
|
||||
# FIRING ≠ CAN-DELETE (2026-07-23): un worker quedó idle 3.5h quemando € porque su token no
|
||||
@@ -252,7 +252,7 @@ for k in $(seq 1 "$N"); do
|
||||
# SOLO, sin el hub — así que si NO PUEDE, no lo dejamos vivo: lo DESTRUIMOS acá mismo. Un worker
|
||||
# que no se puede autodestruir es exactamente lo que el usuario prohíbe. `deadman.sh --puede-borrar`
|
||||
# busca el token en cadena (env, volumen, /root) y prueba que ve su id en la API.
|
||||
timer_ok=$($SSH root@"$ip" 'systemctl is-active hammer-deadman.timer' 2>/dev/null | grep -q active && echo 1)
|
||||
timer_ok=$($SSH root@"$ip" 'systemctl is-active takana-deadman.timer' 2>/dev/null | grep -q active && echo 1)
|
||||
puede=$($SSH root@"$ip" '/usr/local/bin/deadman.sh --puede-borrar' 2>/dev/null)
|
||||
if [ -n "$timer_ok" ] && printf '%s' "$puede" | grep -q '^SÍ'; then
|
||||
echo " ✓ dead-man ARMADO y PUEDE borrarse solo ($puede) — no depende del hub"
|
||||
|
||||
@@ -7,9 +7,9 @@
|
||||
# Idempotente: cada ciclo re-escanea incoming/; lo ya-sellado sale por cache-hit (instantáneo),
|
||||
# sólo construye lo nuevo. Cuando la cola está toda construida, duerme IDLE_SLEEP y reintenta.
|
||||
#
|
||||
# Lo lanza systemd (hammer-farm.service). Manual: JOBS=2 ./scripts/farm/farm-worker-loop.sh
|
||||
# Lo lanza systemd (takana-farm.service). Manual: JOBS=2 ./scripts/farm/farm-worker-loop.sh
|
||||
set -uo pipefail
|
||||
HAMMER_DIR="${TAKANA_DIR:-${HAMMER_DIR:-/opt/hammer}}"
|
||||
HAMMER_DIR="${TAKANA_DIR:-${HAMMER_DIR:-/opt/takana}}"
|
||||
cd "$HAMMER_DIR"
|
||||
. "$HOME/.cargo/env" 2>/dev/null || true
|
||||
# JOBS=1 MIENTRAS EL ADR 0012 ESTÉ SIN DECIDIR (2026-07-22). `build-farm.sh` usa `xargs -P$JOBS`, y
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
set -u
|
||||
|
||||
MI_NOMBRE="${1:?uso: harkaq-campana-vol.sh <mi-nombre>}"
|
||||
HAMMER_DIR="${TAKANA_DIR:-${HAMMER_DIR:-/opt/hammer}}"
|
||||
HAMMER_DIR="${TAKANA_DIR:-${HAMMER_DIR:-/opt/takana}}"
|
||||
cd "$HAMMER_DIR"
|
||||
LISTA="${LISTA:-work/harkaq-cola.txt}"
|
||||
VOL_OUT="/mnt/cosecha"
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
# script existe para no tener.
|
||||
set -u
|
||||
|
||||
HAMMER_DIR="${TAKANA_DIR:-${HAMMER_DIR:-/opt/hammer}}"
|
||||
HAMMER_DIR="${TAKANA_DIR:-${HAMMER_DIR:-/opt/takana}}"
|
||||
cd "$HAMMER_DIR"
|
||||
LISTA="${1:-work/harkaq-cola.txt}"
|
||||
OUT="work/harkaq-campana"
|
||||
|
||||
@@ -30,7 +30,7 @@ HELPER_TYPE="${HELPER_TYPE:-ccx13}" # para collect: el más barato que
|
||||
LOCATION="${LOCATION:-hel1}"
|
||||
SSHKEY="${SSHKEY:-desarrollo@jlsoltech.com}"
|
||||
SSH_KEY="${SSH_KEY:-$HOME/.ssh/github5}"
|
||||
REMOTE="${REMOTE:-/opt/hammer}"
|
||||
REMOTE="${REMOTE:-/opt/takana}"
|
||||
IDLE_CICLOS="${IDLE_CICLOS:-3}" # ciclos idle antes de auto-eliminarse
|
||||
SSH="ssh -i $SSH_KEY -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -o BatchMode=yes"
|
||||
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
# Todo se loguea a work/harvest-go.log para revisar el lunes.
|
||||
#
|
||||
# Uso (cron): */DohubVPS cd /home/sergio/takana && scripts/farm/harvest-go.sh root@1.2.3.4 >>work/harvest-go.log 2>&1
|
||||
# Env: SSH_KEY (def ~/.ssh/github5), REMOTE (def /opt/hammer), STORE (def ./store), REPO (def dist/repo),
|
||||
# Env: SSH_KEY (def ~/.ssh/github5), REMOTE (def /opt/takana), STORE (def ./store), REPO (def dist/repo),
|
||||
# KEY (def dist/keys/release.ed25519), DISTRO (def dev), HAMMER (def target/release/takana),
|
||||
# SEAL_TIMEOUT (def 60s: techo del cache-hit; más = sería build local ⇒ skip).
|
||||
set -u
|
||||
@@ -24,7 +24,7 @@ set -u
|
||||
VPS="${1:?uso: harvest-go.sh user@host}"
|
||||
ROOT="$(cd "$(dirname "$0")/../.." && pwd)"; cd "$ROOT"
|
||||
SSH_KEY="${SSH_KEY:-$HOME/.ssh/github5}"
|
||||
REMOTE="${REMOTE:-/opt/hammer}"
|
||||
REMOTE="${REMOTE:-/opt/takana}"
|
||||
STORE="${STORE:-$ROOT/store}"
|
||||
REPO="${REPO:-$ROOT/dist/repo}"
|
||||
KEY="${KEY:-$ROOT/dist/keys/release.ed25519}"
|
||||
|
||||
@@ -49,7 +49,7 @@ if [ -z "${HARVEST_HARKAQ_CON_LOCK:-}" ]; then
|
||||
exit "$rc"
|
||||
fi
|
||||
SSH_KEY="${SSH_KEY:-$HOME/.ssh/github5}"
|
||||
REMOTE="${REMOTE:-/opt/hammer}"
|
||||
REMOTE="${REMOTE:-/opt/takana}"
|
||||
SSH="ssh -i $SSH_KEY -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -o BatchMode=yes"
|
||||
REVIEW="tandas/needs-review-harkaq"
|
||||
mkdir -p "$REVIEW" work
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
[Unit]
|
||||
Description=hammer: tick del dead-man switch (borra el worker tras 1h idle)
|
||||
# El token se lee de /etc/hammer-deadman.env (0600, lo escribe farm-up al provisionar). Sin él el
|
||||
# El token se lee de /etc/takana-deadman.env (0600, lo escribe farm-up al provisionar). Sin él el
|
||||
# script LOGUEA EL ERROR y sale 1: no hace un poweroff "por si acaso", porque un server apagado en
|
||||
# Hetzner SIGUE COBRANDO y daría una falsa sensación de ahorro.
|
||||
|
||||
[Service]
|
||||
Type=oneshot
|
||||
EnvironmentFile=-/etc/hammer-deadman.env
|
||||
EnvironmentFile=-/etc/takana-deadman.env
|
||||
ExecStart=/usr/local/bin/deadman.sh
|
||||
@@ -1,5 +1,5 @@
|
||||
[Unit]
|
||||
Description=hammer build farm worker (loop autónomo)
|
||||
Description=takana build farm worker (loop autónomo)
|
||||
After=network-online.target
|
||||
Wants=network-online.target
|
||||
# ── FRENO AL CRASHLOOP (2026-08-31) ──────────────────────────────────────────────────────────────
|
||||
@@ -18,7 +18,7 @@ StartLimitBurst=3
|
||||
|
||||
[Service]
|
||||
Type=simple
|
||||
WorkingDirectory=/opt/hammer
|
||||
WorkingDirectory=/opt/takana
|
||||
Environment=HOME=/root
|
||||
# ADR 0016: se fijan LAS DOS mientras dure el renombre. Los scripts leen
|
||||
# ${TAKANA_DIR:-${HAMMER_DIR:-…}}, pero un script VIEJO —o una copia del repo
|
||||
@@ -27,12 +27,12 @@ Environment=HOME=/root
|
||||
# es lo que hoy impide retirar la caída. Cuando el worker corra con TAKANA_DIR
|
||||
# y el resto de la flota también, se borra la segunda línea y recién ahí se
|
||||
# puede retirar la caída del código (etapa 6).
|
||||
Environment=TAKANA_DIR=/opt/hammer
|
||||
Environment=HAMMER_DIR=/opt/hammer
|
||||
Environment=TAKANA_DIR=/opt/takana
|
||||
Environment=HAMMER_DIR=/opt/takana
|
||||
# JOBS no se fija: el loop usa nproc por defecto (cada build usa 1 core por codegen-units=1).
|
||||
# Así, al REPOTENCIAR la caja (CCX13→CCX23/33), el worker auto-escala los slots sin tocar config.
|
||||
# La línea CCX da ~4GB RAM/core ⇒ nproc slots quedan con margen para builds Rust + swap.
|
||||
ExecStart=/opt/hammer/scripts/farm/farm-worker-loop.sh
|
||||
ExecStart=/opt/takana/scripts/farm/farm-worker-loop.sh
|
||||
Restart=always
|
||||
RestartSec=30
|
||||
# El OOM del worker NO se reintenta como si fuera un fallo transitorio: si la receta no entra en la
|
||||
@@ -17,11 +17,11 @@
|
||||
# Uso (en el worker):
|
||||
# scripts/farm/tandas.sh "gui:pixman,cairo,pango" "kernels:linux,linux-generic"
|
||||
# Desde el hub:
|
||||
# ssh root@<ip> 'systemd-run --unit=tandas --working-directory=/opt/hammer \
|
||||
# /opt/hammer/scripts/farm/tandas.sh "gui:pixman,cairo" …'
|
||||
# ssh root@<ip> 'systemd-run --unit=tandas --working-directory=/opt/takana \
|
||||
# /opt/takana/scripts/farm/tandas.sh "gui:pixman,cairo" …'
|
||||
# ssh root@<ip> 'tail -f /var/log/tandas.log'
|
||||
set -u
|
||||
HAMMER_DIR="${TAKANA_DIR:-${HAMMER_DIR:-/opt/hammer}}"
|
||||
HAMMER_DIR="${TAKANA_DIR:-${HAMMER_DIR:-/opt/takana}}"
|
||||
cd "$HAMMER_DIR"
|
||||
LOG="${LOG:-/var/log/tandas.log}"
|
||||
ESPERA="${ESPERA:-30}"
|
||||
|
||||
@@ -23,7 +23,7 @@
|
||||
# falla se avisa RUIDOSO, porque sin swap un build Rust grande no se pone lento:
|
||||
# lo mata el OOM killer.
|
||||
#
|
||||
# Uso (en el VPS, como root): HAMMER_DIR=/opt/hammer ./scripts/farm/vps-setup.sh
|
||||
# Uso (en el VPS, como root): HAMMER_DIR=/opt/takana ./scripts/farm/vps-setup.sh
|
||||
#
|
||||
# ── CAJA FRESCA (no golden): DOS PASOS DEL HUB ANTES DE ESTO ────────────────────────────────
|
||||
# La imagen golden de Hetzner ya trae rsync y el lab horneados. Una caja virgen, no:
|
||||
@@ -35,13 +35,13 @@
|
||||
# tira del Storage Box y eso exige credenciales — que el worker NO debe tener (modelo
|
||||
# hub-and-spoke: compute puro, sin secretos). El hub le manda el tarball ya verificado
|
||||
# y el worker lo re-verifica por sha256 al extraerlo:
|
||||
# rsync -a .dev-fs/lab-image.tar.zst caja:/opt/hammer/.dev-fs/lab-<SHA>.tar.zst
|
||||
# rsync -a .dev-fs/tools/ caja:/opt/hammer/.dev-fs/tools/
|
||||
# rsync -a .dev-fs/lab-image.tar.zst caja:/opt/takana/.dev-fs/lab-<SHA>.tar.zst
|
||||
# rsync -a .dev-fs/tools/ caja:/opt/takana/.dev-fs/tools/
|
||||
# Mandar `tools/` entero (zig 0.13.0 + 0.16.0 + go) y no dejar que se descarguen solos
|
||||
# evita el zig-skew: el worker usa EXACTAMENTE las versiones del hub.
|
||||
set -euo pipefail
|
||||
|
||||
HAMMER_DIR="${TAKANA_DIR:-${HAMMER_DIR:-/opt/hammer}}"
|
||||
HAMMER_DIR="${TAKANA_DIR:-${HAMMER_DIR:-/opt/takana}}"
|
||||
cd "$HAMMER_DIR"
|
||||
|
||||
# ── detección de familia ────────────────────────────────────────────────────────────────────
|
||||
@@ -144,9 +144,9 @@ echo "==> 6. rootfs Alpine edge + zig (bootstrap reproducible; varios min la 1ra
|
||||
./scripts/bootstrap-devfs.sh
|
||||
|
||||
echo "==> 7. systemd unit del loop"
|
||||
cp scripts/farm/hammer-farm.service /etc/systemd/system/hammer-farm.service
|
||||
cp scripts/farm/takana-farm.service /etc/systemd/system/takana-farm.service
|
||||
systemctl daemon-reload
|
||||
|
||||
echo "✓ worker provisionado."
|
||||
echo " arrancar: systemctl enable --now hammer-farm"
|
||||
echo " logs: journalctl -u hammer-farm -f"
|
||||
echo " arrancar: systemctl enable --now takana-farm"
|
||||
echo " logs: journalctl -u takana-farm -f"
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
# DENTRO de una misma corrida: con un rsync por directorio, cada artefacto viaja como si fuera único
|
||||
# y el buzón llega inflado. Una sola invocación con la lista completa los ve todos.
|
||||
set -uo pipefail
|
||||
STORE="${STORE:-/opt/hammer/store}"
|
||||
STORE="${STORE:-/opt/takana/store}"
|
||||
KEY="${KEY:-/root/.ssh/sb_sub}"
|
||||
RO_HOST="${RO_HOST:-u647150-sub1.your-storagebox.de}"; RO_USER="${RO_USER:-u647150-sub1}"
|
||||
BZ_HOST="${BZ_HOST:-u647150-sub2.your-storagebox.de}"; BZ_USER="${BZ_USER:-u647150-sub2}"
|
||||
|
||||
@@ -11,7 +11,7 @@ set -eu
|
||||
|
||||
IP="${1:?uso: harkaq-farm-setup.sh <ip>}"
|
||||
KEY="${SSH_KEY:-$HOME/.ssh/github5}"
|
||||
REMOTE="${REMOTE:-/opt/hammer}"
|
||||
REMOTE="${REMOTE:-/opt/takana}"
|
||||
SSH="ssh -i $KEY -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -o BatchMode=yes"
|
||||
HERE="$(cd "$(dirname "$0")" && pwd)"
|
||||
|
||||
|
||||
+2
-2
@@ -153,9 +153,9 @@ def _base(n):
|
||||
# El primer predictor "obvio" (prefijo del hub) mentía con confianza — la lección de siempre.
|
||||
PAGINA_OVERLAY = 4096 # tope del string de opciones de un mount overlayfs = PAGE_SIZE
|
||||
# Prefijo que bwrap ve en runtime (bajo /oldroot, ruta del worker). Ajustable por host.
|
||||
_PREF_SANDBOX = os.environ.get("PREF_STORE_SANDBOX", "/oldroot/opt/hammer/store/")
|
||||
_PREF_SANDBOX = os.environ.get("PREF_STORE_SANDBOX", "/oldroot/opt/takana/store/")
|
||||
_OVERHEAD_OVERLAY = len("upperdir=/tmp-overlay-upper-0,workdir=/tmp-overlay-work-0,") \
|
||||
+ len("/oldroot/opt/hammer/.dev-fs/alpine") + len(",userxattr")
|
||||
+ len("/oldroot/opt/takana/.dev-fs/alpine") + len(",userxattr")
|
||||
|
||||
|
||||
def _lowerdir_estimado(deps):
|
||||
|
||||
Reference in New Issue
Block a user