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 1389Shell 137Markdown 80Rust 802602 Definiciones

scripts/product-image: imagen de disco auto-booteable del producto (Etapa E)

Cierra el pipeline de release engineering para el product-rootfs: del
artefacto sellado por `hammer bootstrap product` a un disco GRUB-booteable
que arranca solo en QEMU (`-drive file=img`, SIN -kernel) y sirve SSH.

- product-image.sh: hidrata el product-rootfs sellado a un dir escribible,
  provisiona authorized_keys de prueba y delega el armado del disco a
  install-image.sh (GRUB BIOS, particiones dedicadas vda2=/ vda3=/store
  vda4=/var/lib/hammer). A diferencia de install-image por defecto (que
  empaqueta el BUILDER con todo el toolchain), la root es el product-rootfs
  LEAN ⇒ imagen de PRODUCTO. Con BOOT=1 auto-bootea + handshake SSH (slirp
  hostfwd) como validación.
  GOTCHA: BOOT=1 (para el boot propio) se hereda por entorno a
  install-image.sh, que haría su PROPIO `exec qemu` foreground y bloquearía
  ⇒ se pasa BOOT=0 explícito al delegar.
- hammer-bootstrap: el producto crea los mountpoints /store y /var/lib/hammer
  (disk-ready) para el wrapper /sbin/init de la imagen. Test actualizado.

Validado in-VM: la imagen auto-bootea por GRUB (sin -kernel) → arje PID1 →
hammerd+getty+sshd; SSH OK, ls = "uutils coreutils 0.9.0" (userland Rust del
disco), /dev/vda3→/store y /dev/vda4→/var/lib/hammer montadas. 36 tests verde.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

8ba571aebc1058fe5cfc7c33d98fd16600f2bdfa · por sergio · (hace 4 meses) · padre 20936e1f9 · ver el árbol en este commit

Delta semántico · 2

crates/hammer-bootstrap/src/lib.rs

2 archivos · +76 −0

diff --git a/crates/hammer-bootstrap/src/lib.rs b/crates/hammer-bootstrap/src/lib.rs
index a411bbc9..b3483ec1 100644
--- a/crates/hammer-bootstrap/src/lib.rs
+++ b/crates/hammer-bootstrap/src/lib.rs
@@ -565,6 +565,10 @@ fn assemble_product_rootfs(
     let dotssh = staging.join("root/.ssh"); // authorized_keys provisionadas en deploy.
     std::fs::create_dir_all(&dotssh)?;
     std::fs::set_permissions(&dotssh, std::fs::Permissions::from_mode(0o700))?;
+    // Mountpoints de las particiones dedicadas del disco de producto (vacíos; el wrapper /sbin/init de
+    // la imagen monta vda3→/store y vda4→/var/lib/hammer). `/var/lib/hammer` ya viene de la base.
+    std::fs::create_dir_all(staging.join("store"))?;
+    std::fs::create_dir_all(staging.join("var/lib/hammer"))?;
     Ok(())
 }
 
@@ -1666,6 +1670,9 @@ mod tests {
         let mode = std::fs::metadata(staging.join("var/empty")).unwrap().permissions().mode() & 0o777;
         assert_eq!(mode, 0o711, "/var/empty debe ser 0711");
         assert!(staging.join("root/.ssh").is_dir(), "/root/.ssh para authorized_keys");
+        // Mountpoints de disco (el producto es disk-ready): /store + /var/lib/hammer existen vacíos.
+        assert!(staging.join("store").is_dir() && staging.join("var/lib/hammer").is_dir(),
+            "/store y /var/lib/hammer como mountpoints del disco de producto");
     }
 
     #[test]
diff --git a/scripts/product-image.sh b/scripts/product-image.sh
new file mode 100755
index 00000000..944d3049
--- /dev/null
+++ b/scripts/product-image.sh
@@ -0,0 +1,69 @@
+#!/usr/bin/env bash
+# product-image.sh — empaqueta el `product-rootfs` (lean: 4/4 + userland Rust + sshd, SIN toolchain)
+# como imagen de disco AUTO-BOOTEABLE (GRUB BIOS), reusando scripts/install-image.sh. Es el cierre del
+# pipeline: del artefacto sellado por `hammer bootstrap product` a un disco que arranca solo en QEMU
+# (`-drive file=img`, sin -kernel) y, con red, sirve SSH.
+#
+# A diferencia de install-image.sh (que por defecto empaqueta el BUILDER rootfs con todo el toolchain),
+# acá la root es el product-rootfs lean ⇒ imagen de PRODUCTO, no de self-host.
+#
+# Uso:   PRODUCT=<hash|prefijo> ./scripts/product-image.sh         # crea work/hammer-product.img
+#        PRODUCT=<...> BOOT=1 ./scripts/product-image.sh            # además auto-bootea + handshake SSH
+# Vars:  KERNEL  IMG (def work/hammer-product.img)  KVM (def 1)  MEM (def 2048)  PORT (def 2224)
+#        DEADLINE (def 150, sólo con BOOT=1)
+set -euo pipefail
+ROOT="$(cd "$(dirname "$0")/.." && pwd)"; cd "$ROOT"
+IMG="${IMG:-work/hammer-product.img}"; KVM="${KVM:-1}"; MEM="${MEM:-2048}"; PORT="${PORT:-2224}"
+DEADLINE="${DEADLINE:-150}"; WORK="$ROOT/work/product-image"
+
+if [ -n "${PRODUCT:-}" ]; then
+  PDIR=$(ls -d store/"${PRODUCT#b3:}"*-product-rootfs 2>/dev/null | head -1)
+else
+  PDIR=$(ls -dt store/*-product-rootfs 2>/dev/null | head -1)
+fi
+[ -n "$PDIR" ] && [ -d "$PDIR" ] || { echo "no encuentro product-rootfs (corré 'hammer bootstrap product')"; exit 1; }
+echo "==> product-rootfs: $PDIR"
+
+# copia escribible del artefacto sellado (read-only); no tocamos el store
+rm -rf "$WORK"; mkdir -p "$WORK"; RFS="$WORK/rootfs"
+cp -a "$PDIR"/. "$RFS"/; chmod -R u+w "$RFS"; rm -rf "$RFS/.hammer"
+
+# provisión de admin: authorized_keys de prueba (lo único no horneado en el producto)
+KG=$(ls store/*-openssh/usr/bin/ssh-keygen | head -1)
+"$KG" -t ed25519 -N '' -f "$WORK/clientkey" -q
+install -d -m 0700 "$RFS/root/.ssh"
+cat "$WORK/clientkey.pub" > "$RFS/root/.ssh/authorized_keys"; chmod 0600 "$RFS/root/.ssh/authorized_keys"
+
+# delegar el armado del disco GRUB-booteable a install-image.sh. BOOT=0 EXPLÍCITO: nuestro BOOT=1 está
+# en el entorno y se heredaría a install-image.sh, que haría su PROPIO `exec qemu` foreground (sin red)
+# y bloquearía acá para siempre. Lo aislamos para que install-image SÓLO arme la imagen.
+echo "==> armando imagen de disco (GRUB BIOS) vía install-image.sh"
+ROOTFS="$RFS" IMG="$IMG" BOOT=0 ROOT_SIZE="${ROOT_SIZE:-1024}" STORE_SIZE="${STORE_SIZE:-512}" \
+  STATE_SIZE="${STATE_SIZE:-512}" sh ./scripts/install-image.sh
+echo "==> imagen de producto: $IMG"
+
+[ "${BOOT:-0}" = 1 ] || { echo "(set BOOT=1 para auto-bootear + probar SSH)"; exit 0; }
+
+# auto-boot (sin -kernel, GRUB del disco) + red slirp hostfwd para el handshake
+accel=(-cpu Broadwell); { [ "$KVM" = 1 ] && [ -w /dev/kvm ]; } && accel=(-enable-kvm -cpu host)
+echo "==> AUTO-BOOT (sin -kernel) + hostfwd :$PORT->:22"
+qemu-system-x86_64 -m "$MEM" -no-reboot -nographic "${accel[@]}" \
+  -drive file="$IMG",format=raw,if=virtio \
+  -netdev "user,id=n0,hostfwd=tcp::$PORT-:22" -device e1000,netdev=n0 > "$WORK/console.log" 2>&1 &
+QPID=$!; trap 'kill $QPID 2>/dev/null || true' EXIT
+SSHOPTS=(-p "$PORT" -i "$WORK/clientkey" -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null
+         -o GlobalKnownHostsFile=/dev/null -o PasswordAuthentication=no -o ConnectTimeout=4 -o LogLevel=ERROR)
+echo "==> esperando handshake desde el disco auto-booteado (deadline ${DEADLINE}s)…"
+start=$(date +%s); ok=0
+while [ $(( $(date +%s) - start )) -lt "$DEADLINE" ]; do
+  kill -0 $QPID 2>/dev/null || { echo "!! QEMU murió"; break; }
+  if out=$(ssh "${SSHOPTS[@]}" root@localhost \
+        'echo PRODUCT_DISK_SSH_OK uid=$(id -u); uname -sr; ls --version 2>&1 | head -1; df -h /store /var/lib/hammer 2>/dev/null | tail -2' 2>/dev/null); then
+    echo "============ RESPUESTA DEL GUEST ============"; echo "$out"; echo "============================================="
+    echo "$out" | grep -q PRODUCT_DISK_SSH_OK && ok=1; break
+  fi
+  sleep 3
+done
+echo "---- consola (cola) ----"; tail -15 "$WORK/console.log" 2>/dev/null
+[ "$ok" = 1 ] && { echo; echo "*** PRODUCT DISK OK — imagen auto-booteable (GRUB, sin -kernel) sirve SSH ***"; exit 0; }
+echo; echo "*** PRODUCT DISK FALLÓ — ver $WORK/console.log ***"; exit 1