Files
takana/scripts/farm/vps-setup.sh
T
Sergio 476168bb07 takana etapa 5c: comentarios de scripts, MOTD, y un BUG que introdujo la etapa 4
250 líneas de comentario en 151 scripts. Control verificado: el diff no toca
NI UNA línea que no empiece por #, y la sintaxis de los 151 pasa.

El barrido saltea heredocs y cadenas triples, y el guardián DISPARÓ 3 veces:
las tres eran el MOTD que el script escribe DENTRO de la imagen construida —
texto del producto, no comentario del script. Se cambiaron aparte y a
propósito, que es rebranding, no limpieza.

Y el hallazgo caro:  casaba contra ,
que es el TARGET de tracing — o sea el module_path!, o sea el nombre del crate.
La etapa 4 lo movió a  y el script quedó casando NADA. No fallaba:
imprimía cero atribuciones, indistinguible de un log sin problemas. Comprobado
con el binario (RUST_LOG=info sobre zlib), no deducido. Ahora acepta las dos, y
tiene que seguir aceptándolas porque los logs viejos en disco dicen la vieja.

Además 14 rutas de módulo  en docs, que el barrido anterior no tocó
porque  no es frontera de palabra.
2026-09-09 19:28:48 +00:00

153 lines
8.7 KiB
Bash
Executable File

#!/usr/bin/env bash
# vps-setup.sh — provisiona un WORKER de granja takana en un VPS o LXC fresco.
#
# DOS FAMILIAS SOPORTADAS, detectadas por gestor de paquetes:
# · apt → Debian/Ubuntu: los workers efímeros de Hetzner (`farm-up.sh`, imagen golden).
# · dnf → Fedora: el LXC prestado `dev.gioser.net` (2026-08-28), que reemplaza al worker
# de pago mientras esté disponible. NO se retira el camino Debian: la capacidad de
# abrir workers en Hetzner se conserva intacta.
#
# Modelo hub-and-spoke: el LAPTOP es el hub (tiene la clave de firma + gitea). El worker
# NO toca gitea ni firma nada — sólo construye la cola `recipes/incoming/` y sella al store
# local (PROMOTE=0). El hub le rsync-ea código+cola y le baja el store con `farm-sync.sh`.
#
# El hub corre esto por SSH tras rsync-ear el repo a $HAMMER_DIR. Idempotente (re-correr
# actualiza in-place). Necesita root (paquetes + systemd).
#
# ── LO QUE UN CONTENEDOR NO DEJA HACER, Y NO ES UN FALLO ─────────────────────────────────────
# En LXC `sysctl -w` es read-only y `swapon` devuelve `Operation not permitted` (medido en
# dev.gioser.net, 2026-08-28). Los dos pasos degradan a aviso en vez de abortar:
# · sysctl: en Fedora los userns no están capados de fábrica, así que no hace falta.
# · swap: NO se puede crear desde dentro. Si la caja va justa de RAM, el swap lo tiene que
# dar el HOST (en Proxmox es el parámetro `swap:` del contenedor). Un `swapon` que
# 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
#
# ── 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:
#
# 1. `rsync` no está instalado ⇒ no se puede subir el repo con rsync. Huevo y gallina:
# ssh root@caja 'dnf install -y rsync git' # o apt-get install -y rsync git
#
# 2. El lab (`.dev-fs/`) lo EMPUJA EL HUB, no lo baja el worker. `lab-image.sh --traer`
# 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/
# 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}}"
cd "$HAMMER_DIR"
# ── detección de familia ────────────────────────────────────────────────────────────────────
if command -v apt-get >/dev/null 2>&1; then PKG=apt
elif command -v dnf5 >/dev/null 2>&1; then PKG=dnf; DNF=dnf5
elif command -v dnf >/dev/null 2>&1; then PKG=dnf; DNF=dnf
else echo "!! ni apt ni dnf: familia no soportada" >&2; exit 1
fi
echo "==> 0. familia detectada: $PKG ($(. /etc/os-release 2>/dev/null; echo "${PRETTY_NAME:-?}"))"
echo "==> 1. deps del sistema ($PKG)"
if [ "$PKG" = apt ]; then
export DEBIAN_FRONTEND=noninteractive
apt-get update -q
apt-get install -y -q git build-essential curl pkg-config libssl-dev \
bubblewrap rsync ca-certificates uidmap xz-utils
else
# Equivalencias Fedora. `build-essential` no existe: gcc/g++/make sueltos.
# uidmap → shadow-utils (trae newuidmap/newgidmap); xz-utils → xz; libssl-dev → openssl-devel.
#
# ⚠ `patch` VA EXPLÍCITO Y NO ES OBVIO (2026-08-29). En Debian entra de regalo dentro de
# `build-essential`; al desglosar a gcc/g++/make sueltos se cayó sin que nadie lo notara. El
# campo `patches = [...]` de una receta lo aplica takana DEL LADO DEL HOST, fuera del sandbox
# ⇒ no alcanza con que el rootfs lo traiga (lo trae: `/usr/bin/patch` está en el alpine). Sin
# él, `cairo-shared` muere con `spawn patch: No such file or directory` y detrás caen `pango`
# y `gtk4`, o sea el stack GUI entero de GNOME por un binario de 129 KB.
# Regla: toda herramienta que takana invoque HOST-SIDE va en esta lista, no en `[deps]` de la
# receta — declararla en la receta re-hashearía cairo y todo GNOME debajo para nada.
$DNF install -y -q git gcc gcc-c++ make patch curl pkgconf-pkg-config openssl-devel \
bubblewrap rsync ca-certificates shadow-utils xz tar findutils
fi
echo "==> 2. user namespaces para bwrap"
# Debian/Ubuntu los capan de fábrica; Fedora no. En un contenedor `sysctl -w` es read-only y
# esto no importa, porque el userns lo concede el HOST. Todo el paso es best-effort.
if [ "$PKG" = apt ]; then
sysctl -w kernel.unprivileged_userns_clone=1 2>/dev/null || true
sysctl -w kernel.apparmor_restrict_unprivileged_userns=0 2>/dev/null || true
cat > /etc/sysctl.d/99-hammer-userns.conf <<'EOF'
kernel.unprivileged_userns_clone=1
kernel.apparmor_restrict_unprivileged_userns=0
EOF
sysctl --system >/dev/null 2>&1 || true
fi
# El veredicto no es el sysctl: es que el userns REALMENTE se pueda crear. Se comprueba.
if ! unshare -Umr true 2>/dev/null; then
echo "!! FATAL: no se pueden crear user namespaces sin privilegios." >&2
echo " Sin esto el sandbox del lab (bwrap --unshare-all) no arranca y esta caja no" >&2
echo " puede ser worker. En LXC lo habilita el host." >&2
exit 1
fi
echo " userns: OK"
echo "==> 2b. bubblewrap con soporte overlay (el de la distro suele ser viejo/sin overlayfs)"
# El sandbox del lab usa `bwrap --overlay-src --tmp-overlay`; bubblewrap <0.10 (p.ej. el 0.9.0 de
# Ubuntu 24.04) responde "Unknown option --overlay-src". Compilamos 0.11.2 a /usr/local/bin (gana
# el PATH de systemd) si el instalado no lo soporta.
if ! bwrap --help 2>&1 | grep -q overlay-src; then
if [ "$PKG" = apt ]; then apt-get install -y -q meson ninja-build libcap-dev
else $DNF install -y -q meson ninja-build libcap-devel; fi
BWV=0.11.2
curl -sL "https://github.com/containers/bubblewrap/releases/download/v${BWV}/bubblewrap-${BWV}.tar.xz" -o /tmp/bwrap.tar.xz
( cd /tmp && rm -rf "bubblewrap-${BWV}" && tar xf bwrap.tar.xz && cd "bubblewrap-${BWV}" \
&& meson setup _build --prefix=/usr/local -Dman=disabled -Dtests=false >/dev/null \
&& ninja -C _build >/dev/null && ninja -C _build install >/dev/null )
hash -r
echo " bwrap actualizado: $(/usr/local/bin/bwrap --version)"
fi
echo "==> 3. swap 16G (8GB de RAM es justo para builds Rust con JOBS=2)"
# En un contenedor esto FALLA por diseño (swapon exige CAP_SYS_ADMIN del host). No se aborta,
# pero se avisa fuerte: sin swap el límite de RAM es duro y el OOM killer no avisa antes.
if ! swapon --show 2>/dev/null | grep -q /swapfile; then
if fallocate -l 16G /swapfile 2>/dev/null || dd if=/dev/zero of=/swapfile bs=1M count=16384 status=none; then
chmod 600 /swapfile; mkswap /swapfile >/dev/null 2>&1
if swapon /swapfile 2>/dev/null; then
grep -q '/swapfile' /etc/fstab || echo '/swapfile none swap sw 0 0' >> /etc/fstab
echo " swap activo: $(swapon --show=NAME,SIZE --noheadings | tr '\n' ' ')"
else
rm -f /swapfile
echo " ⚠ SIN SWAP: el kernel no deja activarlo (normal en LXC)."
echo " RAM disponible: $(free -g | awk '/Mem/{print $2}') G — es un techo DURO."
echo " Un build Rust grande que se pase NO se pone lento: lo mata el OOM killer."
echo " Si hace falta más, lo da el host (en Proxmox: parámetro 'swap:' del contenedor)."
fi
fi
fi
echo "==> 4. rust host toolchain (para buildear el binario hammer)"
if ! command -v cargo >/dev/null 2>&1; then
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | \
sh -s -- -y --profile minimal --default-toolchain stable
fi
. "$HOME/.cargo/env"
echo "==> 5. build hammer (target/release/takana)"
cargo build --release --bin takana --bin hammer
echo "==> 6. rootfs Alpine edge + zig (bootstrap reproducible; varios min la 1ra vez)"
./scripts/bootstrap-devfs.sh
echo "==> 7. systemd unit del loop"
cp scripts/farm/hammer-farm.service /etc/systemd/system/hammer-farm.service
systemctl daemon-reload
echo "✓ worker provisionado."
echo " arrancar: systemctl enable --now hammer-farm"
echo " logs: journalctl -u hammer-farm -f"