diff --git a/AGENTS.md b/AGENTS.md index 3d67089..41c5e7b 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -174,6 +174,47 @@ Variante **WinBuild2022**: path `F:\CI\Templates\WinBuild2022\WinBuild2022.vmx`, | Toolchain | GCC, G++, Clang, CMake, Ninja, Python 3, Git, 7-Zip | | CI dirs | `/opt/ci/{build,output,scripts,cache}` — owner `ci_build` | +### Linux Rust (LinuxRust2404) + +Variante di `LinuxBuild2404` per workspace Rust (es. app Tauri): stessa cloud +image e stesso layout guest, provisioning via +`template/Install-CIToolchain-LinuxRust2404.sh` che aggiunge le librerie +webview/GTK di Tauri v2 e una toolchain Rust system-wide sotto `/opt/rust` +(rustup + rustfmt + clippy + binario `cargo-audit`; il DB advisory di +cargo-audit NON e' bakato, viene fetchato a runtime). + +| Parametro | Valore | +| -------------- | ------------------------------------------------------------- | +| OS | Ubuntu 24.04 LTS (cloud image) | +| VMX | `LinuxRust2404/LinuxRust2404.vmx` (sotto la dir templates) | +| Snapshot clone | `BaseClean-LinuxRust` | +| Transport | SSH porta 22, utente `ci_build`, stessa chiave di LinuxBuild2404 | +| Toolchain | quella di LinuxBuild2404 + webview/GTK Tauri + Rust in `/opt/rust` | +| Comandi | `ci template deploy-rust` / `ci template prepare-rust` | + +`--rust-channel` (default `stable`) controlla la toolchain bakata: pinnare una +versione esatta (es. `1.97.1`) per uno snapshot deterministico; con `stable` +rustup riscarica a runtime ad ogni nuova stable. Rigenerare lo snapshot quando +cambia il pin o la baseline apt di sicurezza. + +Due vincoli sull'albero `/opt/rust`, entrambi regressioni gia' occorse: + +1. **`chown` DOPO l'installazione.** Lo script gira come root, quindi tutto + cio' che rustup/cargo creano nasce root-owned: un `chown` fatto solo prima + copre le due directory vuote e nient'altro. Senza il `chown` finale + `ci_build` non puo' scrivere ne' in `RUSTUP_HOME` ne' in + `CARGO_HOME/registry`, e **ogni** build Rust con dipendenze esterne muore + con `Permission denied (os error 13)`. Gli assert devono verificare la + **scrivibilita'** (`sudo -u ci_build test -w`), non solo `test -x` sui + binari: altrimenti il template rotto passa la sua build e fallisce nei job + dei consumatori. +2. **`/etc/profile.d` da solo non basta.** L'orchestratore esegue i comandi + con paramiko `exec_command` + `sh -c` (`transport/ssh.py`, + `commands/build.py`): shell **non di login**, `profile.d` non viene mai + sorgiato. `RUSTUP_HOME`/`CARGO_HOME` vanno anche in `/etc/environment` + (letto da `pam_env` all'apertura della sessione SSH) e i proxy rustup + symlinkati in `/usr/local/bin`. + --- ## Transport layer @@ -236,3 +277,9 @@ Configurazione in `PSScriptAnalyzerSettings.psd1`. Regole principali: 11. **Machine-id identico tra clone Linux** — le cloud image Ubuntu hanno un `/etc/machine-id` fisso. VMware DHCP assegna l'IP tramite DHCP client-id derivato da machine-id: se tutti i clone hanno lo stesso machine-id ottengono lo stesso IP e si bloccano per "IP collision". Fix nel template (prima di prendere lo snapshot): `sudo truncate -s 0 /etc/machine-id && sudo rm -f /var/lib/dbus/machine-id && sudo shutdown -h now`, poi ricreare lo snapshot `BaseClean-Linux` da stato powered-off. 12. **`& nativecmd 2>$null` NON sopprime stderr in PS 5.1 con `$ErrorActionPreference='Stop'`** — la stderr del processo nativo viene convertita in ErrorRecord nel pipeline di PowerShell; sotto `'Stop'` questo diventa un errore terminante PRIMA che `2>$null` possa scartarlo. Esempio: `ssh-keygen -R -f ` stampa "Host X not found in file" su stderr → eccezione terminante. Fix: usare `$ErrorActionPreference = 'SilentlyContinue'` attorno alla chiamata oppure `2>&1 | Out-Null`. Per i job CI su VM ephemere locali, usare direttamente `StrictHostKeyChecking=no UserKnownHostsFile=NUL` (default in `_Transport.psm1` con `KnownHostsFile=''`) — nessun `ssh-keygen` necessario. 13. **Quote WinRM/desktop-heap troppo basse per builds Windows paralleli** — sintomo: `MSBuild error MSB6003: CL.exe could not be run. System.ComponentModel.Win32Exception (0x80004005): Not enough quota is available to process this command` (oppure le altre tool MSVC: link.exe, mt.exe) quando il build interno fa fan-out parallelo (es. matrix x86/x64/x86-ansi, MSBuild `/maxcpucount`). Causa: il plugin endpoint Microsoft.PowerShell del guest ha `MaxMemoryPerShellMB=150` di default e l'heap del desktop non-interattivo (Session 0) ha `SharedSection 3rd field=768 KB` — entrambi insufficienti per più CL.exe concorrenti via WinRM. **Non emerge** in pre-migration Win→Win perché `Invoke-Command` su `PSSession` localhost-style eredita quote più larghe; emerge appena il path passa per `pypsrp` Linux→Win. Fix nel template (Deploy-WinBuild2025.ps1 / 2022 lo fa, Install-CIToolchain lo valida): `Set-Item WSMan:\localhost\Plugin\Microsoft.PowerShell\Quotas\MaxMemoryPerShellMB 2048`, `Set-Item WSMan:\localhost\Shell\MaxProcessesPerShell 100`, registry `HKLM\SYSTEM\CurrentControlSet\Control\Session Manager\SubSystems\Windows` patch del 3° campo `SharedSection` a 4096, **reboot** (csrss legge SharedSection solo al boot), nuovo `BaseClean`. Dettagli e procedura completa in `docs/WINDOWS-TEMPLATE-SETUP.md § "WinRM quotas e desktop heap"`. + +14. **`deploy-* --force` lanciato mentre la VM target è ACCESA** — corrompe la VM in esecuzione e produce un timeout allo Step 7 che *sembra* un problema di rete. Occorso il 2026-07-22 su `LinuxRust2404`, con un `prepare-rust` ancora in corso sulla stessa VMX. Meccanica: (a) Step 1 vede `.vmdk.lck` e lo cancella come "stale artifact" — **non era stale**, apparteneva al processo vivo; (b) Step 4 riscrive il VMDK base sotto il guest in esecuzione, che muore; (c) Step 6 lancia il power-on, il nuovo `vmware-vmx` muore subito perché la VMX è già occupata, ma il controllo `[OK] VM appears in vmrun list` **passa lo stesso** perché vede l'istanza vecchia; (d) Step 7 interroga per 10 minuti un guest morto e riporta `Error: The VMware Tools are not running in the virtual machine`. Diagnosi in 5 secondi: `ps -eo pid,lstart,etime,args | grep vmware-vmx` — se il `vmware-vmx` vivo è più vecchio del deploy appena lanciato, è questo il caso. Recupero: `vmrun stop hard`, rimuovere i delta orfani `-00000*.vmdk`, verificare `vmrun list` = 0, poi rilanciare. **Non** interpretarlo come DHCP incastrato (vedi #15) e non risolverlo con un retry automatico: riavviare una VM già corrotta ripete il fallimento con la causa ormai illeggibile. + +15. **Step 7 in timeout: distinguere tre cause diverse che danno lo stesso messaggio** — `getGuestIPAddress` risponde `The VMware Tools are not running` sia quando il guest non ha IP, sia quando ha l'IP ma i Tools non rispondono. Ordine di verifica, dal più veloce: (a) **collisione** — `ps | grep vmware-vmx`, processo più vecchio del deploy corrente → vedi #14; (b) **DHCP host incastrato** — `ip -4 addr show vmnet8` deve dare `192.168.79.1/24` e `ps -ef | grep vmnet-dhcpd` deve mostrarlo vivo; se no, `sudo systemctl restart vmware-networks` (o `vmware-host.service`); (c) **guest senza lease o Tools non pronti** — cercare il MAC della VMX in `/etc/vmware/vmnet8/dhcpd/dhcpd.leases` e in `ip neigh show dev vmnet8`: se un lease c'è, il guest è vivo e mancano solo i Tools, quindi sondare TCP/22 direttamente invece di aspettare `getGuestIPAddress`. + +16. **Delta di snapshot orfani dopo `--force`** — `deploy-*` cancella `.vmsd` (il database snapshot) per evitare l'errore di resize sulla catena, ma **non** cancella i file delta `-00000N.vmdk`. Restano su disco con un `parentCID` che non corrisponde più al base ricreato: innocui (fuori dal percorso di boot, la VMX punta al base con `parentCID=ffffffff`) ma confondono la diagnosi e vanno rimossi a mano. Verifica della catena: `head -c 2000 .vmdk | strings | grep -E '^CID|parentCID'`. diff --git a/CLAUDE.md b/CLAUDE.md index 740494b..0df598c 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -106,6 +106,14 @@ The host **never executes build tools directly** — it only orchestrates VM lif | WinBuild2025 | `F:\CI\Templates\WinBuild2025\WinBuild2025.vmx` | `BaseClean` | WinRM HTTPS/5986 | | WinBuild2022 | `F:\CI\Templates\WinBuild2022\WinBuild2022.vmx` | `BaseClean` | WinRM HTTPS/5986 | | LinuxBuild2404 | `F:\CI\Templates\LinuxBuild2404\LinuxBuild2404.vmx` | `BaseClean-Linux` | SSH/22 (`ci_build`, key `F:\CI\keys\ci_linux`) | +| LinuxRust2404 | `LinuxRust2404/LinuxRust2404.vmx` (templates dir) | `BaseClean-LinuxRust` | SSH/22 (same user/key as LinuxBuild2404) | + +`LinuxRust2404` is a LinuxBuild2404 variant for Rust workspaces (Tauri): +`ci template deploy-rust` / `ci template prepare-rust` provision it via +`template/Install-CIToolchain-LinuxRust2404.sh` (webview/GTK libs + system-wide +rustup under `/opt/rust` + cargo-audit binary; the audit advisory DB is fetched +at runtime, never baked). `--rust-channel` pins the baked toolchain (default +`stable`, a moving target). Snapshots **must be taken from powered-off state** — presence of `.vmem` in the template dir means the snapshot captured running memory and `vmrun clone ... linked` will fail. diff --git a/TODO.md b/TODO.md index c8dcc82..2a81318 100644 --- a/TODO.md +++ b/TODO.md @@ -312,6 +312,34 @@ mai rilasciati. Workaround manuale: reset a `{}` con `build-vms/` vuoto. cui clone-dir non esiste più in `CloneBaseDir`. Opzionale: allargare il pool (es. 201–210) per dare margine oltre parallelism. Aggiungere test. +### 2.9 [P2] [ ] `retention run` non copre backup/template/ISO — APERTO (2026-07-22) + +`ci-retention-policy` agisce solo su **artifact, log e lease IP**. Su questo host +quei tre insiemi pesano 52 KB in tutto, mentre il disco era pieno al 94% per +`backups/` (195 G), `templates/` (128 G) e `iso/` (69 G) — tutti fuori dal suo +raggio d'azione. + +Sintomo osservato: il timer girava, rilevava meno di 50 GB liberi, dichiarava +`WARNING: Free space below 50 GB — aggressive retention: 7d` e liberava +**0.0 GB**, due esecuzioni di fila. L'allarme suonava ma il meccanismo non +poteva agire su ciò che pesava. Un guardiano che non sorveglia nulla è peggio di +nessun guardiano: dà l'impressione che il problema sia sotto controllo. + +Mitigato il 2026-07-22 (backup svuotati, `--keep-count 1 --exclude WinBuildTest` +nella unit), ma è una correzione manuale: la policy automatica resta cieca. + +**Fix proposto**: estendere `retention run` ai backup (per numero di generazioni +o per età) e almeno segnalare — non cancellare — ISO e template che superano una +soglia. In alternativa, se i backup restano governati solo da `--keep-count`, +far sì che il warning sullo spazio dica **quali** directory pesano, invece di +lamentarsi senza indicare dove guardare. + +**Nota**: `iso/` contiene coppie originale + `.patched` e versioni April +superate da May (~38 G recuperabili); le `.patched` sono rigenerate da +`deploy-win`. `templates/WinBuildTest` (41 G, ultimo uso 10 giugno, citato solo +in `docs/archived/2026-05-10/`) è candidato alla dismissione: escluso dai backup +ma ancora su disco. + --- ## 3. Performance & throughput @@ -518,6 +546,54 @@ Già supportato lato Gitea, basta replicare il setup. **Deferred**: in un home lab single-host questa funzionalità non serve. Non pianificare. +### 6.5 [P3] [ ] Upgrade act_runner v1.0.4 → v2.x — VALUTATO E RIMANDATO (2026-07-22) + +Versione installata: **v1.0.4** (18 mag 2026). Ultima upstream al momento della +valutazione: **v2.1.0** (16 lug 2026). +Release: https://gitea.com/gitea/runner/releases + +**Decisione: non aggiornare ora.** Il beneficio netto per questo sistema è +marginale. Valutazione fatta ispezionando il binario v2.1.0 (checksum +verificato), non solo il changelog. + +Cosa **non** cambia — verificato sul binario, non dedotto: + +- v2.1.0 esporta al job esattamente le stesse variabili di v1.0.4 + (`RUNNER_ARCH/TEMP/DEBUG/OS/TOOL_CACHE`), **nessun `RUNNER_LABELS`**. Il + `GITEA_RUNNER_LABELS` del changelog v2.1.0 è operator-side: imposta le label + del daemon all'avvio, non le espone al workflow. Il routing label → template + continua a richiedere un'istanza runner per label (vedi RUNBOOK-PhaseC, + sezione `deploy-rust`). + +Cosa non ci riguarda: il grosso di v2.x è lavoro su Docker/container +(credenziali di pull, service container, dind, IPv6). Qui `container.enabled` +è `false`, host mode puro. Anche l'unico breaking change di v2.0.0 +(`DOCKER_USERNAME`/`DOCKER_PASSWORD` non più impliciti nei pull) è inapplicabile. + +Cosa ci riguarderebbe: + +| Novità | Valore reale | +|---|---| +| Cancellation handling completa (v2.0.0) + reap processi (v1.0.7/1.0.8) | Un job cancellato può lasciare una build VM orfana — ma `ci-cleanup-orphans.timer` gira già ogni 6h | +| `runner.post_task_script` (v2.0.0) | Aggancerebbe `vm cleanup` dopo ogni task: latenza orfani da ≤6h a immediata | +| `jobs..timeout-minutes` / `continue-on-error` (v2.0.0) | Ergonomia workflow | +| `register --token-file` (v2.1.0) | Già implementato a mano in `scripts/install-rust-runner.sh` | + +Schema di configurazione retrocompatibile: il diff v2.1.0 vs il nostro config è +solo additivo, nessuna chiave rimossa. + +**Trigger per riaprire**: quando serve `runner.post_task_script` per azzerare la +latenza del cleanup orfani, oppure quando la 2.x è matura (era vecchia di 6 +giorni alla valutazione). Non aggiornare "per stare aggiornati". + +**Vincolo operativo da ricordare**: i due runner (`act-runner.service` e +`act-runner-rust.service`) condividono **lo stesso binario** +`/opt/ci/act_runner/act_runner`. Sostituirlo aggiorna entrambi insieme. Per un +rollout graduale serve un secondo binario affiancato +(`act_runner-2.1.0`), l'`ExecStart` del solo servizio rust puntato lì, un job di +prova su `runs-on: rust-build`, e solo dopo la promozione per il runner +principale — tenendo v1.0.4 per il rollback. + ### 6.4 [P3] [x] Build matrix nel workflow File: [gitea/workflows/build-ns7zip.yml](gitea/workflows/build-ns7zip.yml). diff --git a/docs/RUNBOOK-PhaseC.md b/docs/RUNBOOK-PhaseC.md index 93ea27a..0f154ca 100644 --- a/docs/RUNBOOK-PhaseC.md +++ b/docs/RUNBOOK-PhaseC.md @@ -77,10 +77,12 @@ sudo); `--uninstall` removes the block. The script defines two helpers: | `ci vmware --vmx ...` | Open a VMX in the VMware Workstation GUI | [§4](#4-manual-vm-lifecycle) | | `ci creds set --user ...` | Store guest credentials in the keyring | [§5](#5-credentials--template-provisioning) | | `ci template deploy-linux` | Build the Linux template VM | [§5](#5-credentials--template-provisioning) | +| `ci template deploy-rust` | Build the Rust Linux template VM (LinuxRust2404) | [§5](#5-credentials--template-provisioning) | | `ci template check-win-iso` | Report the newest WS2025 ISO from massgrave + cache status | [§5](#5-credentials--template-provisioning) | | `ci template patch-win-iso` | Inject autounattend + no-prompt boot into a Windows ISO | [§5](#5-credentials--template-provisioning) | | `ci template deploy-win` | Build the Windows Server 2025 template VM (download→patch→deploy) | [§5](#5-credentials--template-provisioning) | | `ci template prepare-linux` | Provision the Linux template over SSH | [§5](#5-credentials--template-provisioning) | +| `ci template prepare-rust` | Provision the Rust Linux template over SSH | [§5](#5-credentials--template-provisioning) | | `ci template prepare-win` | Provision a Windows template over WinRM | [§5](#5-credentials--template-provisioning) | | `ci template backup` | Timestamped copy of the template directories | [§5](#5-credentials--template-provisioning) | | `ci retention run` | Purge old artifacts and logs | [§6](#6-maintenance) | @@ -412,12 +414,139 @@ Run `ci template --help` for the (many) per-template options. ```bash ci template deploy-linux # build the Linux template VM from scratch (Ubuntu 24.04) +ci template deploy-rust # build the Rust Linux template VM (LinuxRust2404) ci template deploy-win # build the Windows Server 2025 template VM (see below) ci template prepare-linux # provision an existing Linux template over SSH +ci template prepare-rust # provision the Rust Linux template over SSH ci template prepare-win # provision a Windows template over WinRM (Linux-host driven) ci template backup # timestamped backup of the template directories ``` +### `deploy-rust` / `prepare-rust` — the Rust Linux template (LinuxRust2404) + +A separate template for Rust workspaces (e.g. Tauri apps): same Ubuntu 24.04 +cloud image and guest layout as `LinuxBuild2404`, but `prepare-rust` runs +`template/Install-CIToolchain-LinuxRust2404.sh`, which additionally bakes: + +- the Tauri v2 webview/GTK development libraries + (`libwebkit2gtk-4.1-dev`, `libgtk-3-dev`, `libayatana-appindicator3-dev`, + `librsvg2-dev`); +- a system-wide Rust toolchain via rustup under `/opt/rust` + (`RUSTUP_HOME=/opt/rust/rustup`, `CARGO_HOME=/opt/rust/cargo`) with the + `rustfmt` and `clippy` components. The tree is `chown`ed to `ci_build` + **after** the installs, not only before — otherwise every write the build + user makes (toolchain sync, dependency download into + `CARGO_HOME/registry`, `cargo install`) fails with `Permission denied`. + Exposure is threefold because the orchestrator runs build commands in a + **non-login** shell (`exec_command` + `sh -c`), which never sources + `profile.d`: `/etc/profile.d/rust.sh` for interactive shells, + `/etc/environment` (read by `pam_env` at SSH session open) for the two + home variables, and `/usr/local/bin` symlinks for the rustup proxies; +- the `cargo-audit` binary (skip with `--no-cargo-audit`). The audit + **advisory DB is never baked** — `cargo audit` fetches it at runtime. + +```bash +ci template deploy-rust # same flags as deploy-linux; 60 GB disk default +ci template prepare-rust # default VMX: /LinuxRust2404/... + # snapshot: BaseClean-LinuxRust +ci template prepare-rust --rust-channel 1.97.1 # pin an exact toolchain + +# Both phases in one run, unattended. deploy and prepare stay separate +# commands — --then-prepare only chains them, reusing the VM deploy left +# running (no second power-on, no second IP detection). +# +# Run from the repo root: the guest toolchain script is looked up under +# /template/. With --then-prepare this is checked BEFORE the deploy +# starts, so a wrong directory costs two seconds instead of failing the +# hand-off after a ~45-minute deploy. Pass --toolchain-script to run from +# anywhere. +cd /opt/ci/local-ci-cd-system +ci template deploy-rust --then-prepare --rust-channel 1.97.1 +ci template deploy-linux --then-prepare + +# from any directory +ci template deploy-rust --then-prepare --rust-channel 1.97.1 \ + --toolchain-script /opt/ci/local-ci-cd-system/template/Install-CIToolchain-LinuxRust2404.sh +``` + +`--snapshot-name` on `deploy-linux`/`deploy-rust` names the snapshot that +`prepare-*` will create (`BaseClean-Linux` / `BaseClean-LinuxRust`) — the +deploy commands take no snapshot of their own, unlike `deploy-win`. With +`--then-prepare` the value is handed to `prepare-*`, so overriding it on the +deploy side changes what actually gets created. + +Both `prepare-linux` and `prepare-rust` default `--vmx` to their template VMX +under `` (`LinuxBuild2404` / `LinuxRust2404`) when neither +`--vmx` nor `--ip` is passed — so the plain `ci template prepare-rust` just +works after `deploy-rust`. Pass `--ip` to provision a running guest without +VM lifecycle (no auto power-on/shutdown/snapshot). + +`--rust-channel` defaults to `stable` — a moving target: when a new stable +ships, `rustup show` in a job re-downloads the toolchain at runtime. Pin an +exact version for a fully deterministic snapshot (the consumer repo decides +this via its `rust-toolchain.toml`). Refresh the snapshot when the pinned +channel or the apt security baseline moves. + +Two ways to route jobs to it: + +1. **Per job (no host change).** Pass `template-path` + `snapshot-name` to the + `local-ci-build` composite action. This cannot leave a job unschedulable, + which is why consumer repos default to it. +2. **By label** — `runs-on: rust-build`. This needs a **second act_runner + instance**, not just an extra label on the existing one: act_runner reads a + single `runner.envs` block per process and never tells the workflow which + label picked the job (there is no `RUNNER_LABELS` variable — v1.0.4 exposes + only `RUNNER_OS/ARCH/TEMP/TOOL_CACHE/DEBUG/VERSION`). A label can select a + template only by selecting a runner whose envs name that template. + + ```bash + # 1. mint a registration token from the stored GiteaPAT (0600 file, never + # printed — pass it by path so it stays out of `ps` and shell history) + sudo -u ci-runner /opt/ci/venv/bin/python tools/gitea-runner-token.py \ + --out /var/lib/ci/.rust-runner-regtoken + + # 2. provision + register + enable the unit + sudo ./scripts/install-rust-runner.sh \ + --token-file /var/lib/ci/.rust-runner-regtoken + + # 3. the token is single-use — destroy it + sudo shred -u /var/lib/ci/.rust-runner-regtoken + ``` + + Provisions `/var/lib/ci/runner-rust/` from `deploy/runner-rust-config.yaml` + (label `rust-build:host`, `GITEA_CI_LINUX_TEMPLATE_PATH` → + `LinuxRust2404`, `GITEA_CI_LINUX_SNAPSHOT_NAME` → `BaseClean-LinuxRust`), + registers it, and installs `act-runner-rust.service`. Its `capacity` adds + to the main runner's — keep the sum inside the host RAM budget (currently + 2 + 1 = 3 concurrent build VMs). + + **Verify the label is announced before any workflow depends on it**: a + `runs-on` value that no runner advertises leaves the job unschedulable with + no error anywhere — silently zero runs. + + ```bash + sudo -u ci-runner /opt/ci/venv/bin/python tools/gitea-runner-list.py \ + --label rust-build # exit 1 unless an ONLINE runner announces it + ``` + + Note that `runner.file` in the config is an **absolute** path. act_runner + resolves a relative `file:` against the process CWD, so registering from + anywhere else drops the registration state (which contains the runner + token) into that directory and the daemon then fails with + `registration file not found`. + + Both runner services share **one binary**, `/opt/ci/act_runner/act_runner`. + Replacing it upgrades `act-runner.service` and `act-runner-rust.service` + together — a staged rollout needs a second binary alongside it and the + `ExecStart` of one unit pointed at it. Current version is v1.0.4; the + upgrade to v2.x was evaluated and deferred (TODO §6.5). + +Note that `guest-os: 'Auto'` cannot pick a template by itself, for the same +reason: the label is invisible to the action, and the VMX `guestOS` field is +only readable *after* the clone. With `Auto` the action requires +`template-path` **and** `snapshot-name` and fails fast otherwise, instead of +silently falling back to the Windows template as it did before. + ### `deploy-win` — build the Windows Server 2025 template Takes a Windows Server 2025 install ISO — downloaded from `--iso-url` (cached diff --git a/docs/RUNBOOK.md b/docs/RUNBOOK.md index 5bed577..8c2be21 100644 --- a/docs/RUNBOOK.md +++ b/docs/RUNBOOK.md @@ -250,6 +250,7 @@ Estimated time: 2-4 hours including Windows Update. | Template corrupt | Remove `*.lck` files; if persistent, restore from `F:\CI\Backups\` | | Snapshot missing | `vmrun listSnapshots `; update `GITEA_CI_SNAPSHOT_NAME` | | IP collision | `Remove-Item F:\CI\State\ip-leases\*.lease`; lower `capacity` | +| Step 7: no guest IP | `ps -eo pid,lstart,args \| grep vmware-vmx` — see §16 | --- @@ -868,3 +869,92 @@ This is **optional** — the default boots-to-Linux behaviour is intentional so the machine comes back as the Linux CI host after any unattended reboot (power-loss, kernel update). Leave it as-is unless Windows becomes the daily driver. + +--- + +## 16. `deploy-*` step 7 in timeout — nessun IP dal guest + +**Sintomo.** `deploy-linux` / `deploy-rust` cicla su +`... waiting for guest IP (open-vm-tools) ...` per 10 minuti e chiude con +`Error: [Deploy Step 7] Timed out waiting for guest IP.` Interrogando a mano: + +``` +$ vmrun -T ws getGuestIPAddress +Error: The VMware Tools are not running in the virtual machine +``` + +Quel messaggio è ambiguo: esce identico sia quando il guest **non ha** un IP, +sia quando ce l'ha ma i Tools non rispondono. Tre cause distinte, in ordine di +verifica dal più veloce al più lento. + +### 16.1 Collisione con una VM già accesa (la più insidiosa) + +Succede quando si lancia `deploy-* --force` mentre la stessa VMX è in uso — +tipicamente un `prepare-*` ancora in corso. Occorso il 2026-07-22 su +`LinuxRust2404`. + +```bash +ps -eo pid,lstart,etime,args | grep vmware-vmx | grep -v grep +``` + +Se il `vmware-vmx` vivo è **più vecchio** del deploy appena lanciato, è questo +il caso: il deploy ha cancellato il `.vmdk.lck` della VM viva credendolo stale, +ha riscritto il VMDK base sotto il guest in esecuzione, e il suo power-on è +morto subito perché la VMX era occupata. Il controllo +`[OK] VM appears in vmrun list` **non protegge**: vede l'istanza vecchia. + +Recupero: + +```bash +sudo -u ci-runner vmrun -T ws stop hard +sudo rm -f /-00000*.vmdk # delta orfani, vedi 16.4 +sudo -u ci-runner vmrun -T ws list # deve dire 0 +# poi rilanciare il deploy, con nient'altro in esecuzione sulla stessa VMX +``` + +Non risolvere con un retry: riavviare una VM già corrotta ripete il fallimento +con la causa ormai illeggibile. + +### 16.2 DHCP dell'host incastrato + +```bash +ip -4 addr show vmnet8 # atteso: inet 192.168.79.1/24 +ps -ef | grep vmnet-dhcpd # atteso: due istanze (vmnet1 + vmnet8) +``` + +Se `vmnet8` manca o `vmnet-dhcpd` non c'è: + +```bash +sudo systemctl restart vmware-networks # oppure vmware-host.service +``` + +### 16.3 Guest senza lease, oppure Tools non ancora pronti + +```bash +grep -A1 '^lease' /etc/vmware/vmnet8/dhcpd/dhcpd.leases | tail -20 +ip neigh show dev vmnet8 +``` + +Se compare un lease per il MAC della VM, **il guest è vivo e ha rete**: mancano +solo i Tools. Sondare direttamente la porta 22 invece di aspettare +`getGuestIPAddress`: + +```bash +timeout 5 bash -c 'cat < /dev/null > /dev/tcp//22' && echo "SSH aperta" +``` + +Se invece non c'è nessun lease e le due verifiche precedenti sono pulite, il +guest non sta completando il boot: cloud-init fallito o immagine corrotta. + +### 16.4 Delta di snapshot orfani + +`deploy-*` cancella `.vmsd` per evitare l'errore di resize sulla catena +snapshot, ma **non** rimuove i file delta `-00000N.vmdk`. Restano con un +`parentCID` che non corrisponde più al base ricreato. Sono innocui — la VMX +punta al base, riconoscibile da `parentCID=ffffffff` — ma confondono la +diagnosi. Verifica della catena: + +```bash +grep -i 'scsi0:0.fileName' +head -c 2000 .vmdk | strings | grep -E '^CID|parentCID' +```