diff --git a/plans/B1-closeout.md b/plans/B1-closeout.md new file mode 100644 index 0000000..3147ab1 --- /dev/null +++ b/plans/B1-closeout.md @@ -0,0 +1,40 @@ +# B1 — Closeout + +Passo B1 di `plans/PhaseB-user-checklist.md`: setup hardware target con +Linux Mint LTS, VMware Workstation Pro Linux, layout storage e venv Python. + +## Stato + +✅ **COMPLETATO** — tutti i prerequisiti hardware e software soddisfatti. + +## Cosa è stato fatto + +- [x] Linux Mint LTS installato sull'hardware target (`ws1-mint`). +- [x] Sistema aggiornato (`apt full-upgrade`) e riavviato. +- [x] VMware Workstation Pro Linux installato via bundle `.bundle`; `vmrun -T ws --version` OK. +- [x] Smoke test Workstation UI: VM trivia creata, clonata, avviata, spenta, eliminata. +- [x] `vmnet8` NAT configurato su `192.168.79.0/24` (stesso range di Windows) via `vmware-netcfg`. +- [x] Utente di servizio `ci-runner` creato (`useradd -r -m -s /bin/bash`). +- [x] `setup-host-linux.sh -d /dev/sdf1` eseguito: + - `/dev/sdf1` montata su `/var/lib/ci` via fstab con boot automatico. + - Layout directory creato: `build-vms/`, `artifacts/`, `templates/`, `keys/`, `logs/`, `runner/`. + - Python 3.12 installato, `/opt/ci/venv` creato. + - Repo clonato in `/opt/ci/local-ci-cd-system`, package installato in modalità editabile. + - PowerShell Core (`pwsh`) installato (necessario per B5 nel piano originale; + sarà rimosso nella Fase C). +- [x] Verifica finale: `sudo -u ci-runner /opt/ci/venv/bin/python -m ci_orchestrator --help` + lista tutti i sub-comandi. + +## Deviazioni / gotcha + +- Lo script `setup-host-linux.sh` è stato scritto e committato durante questa + fase (commit `b9d6994`) — non era pre-esistente nel repo. +- La partizione `/dev/sdf1` conteneva una sottocartella `ci/` residua dalla + configurazione Windows; lo script la ha rilevata e spostata alla root prima + del mount definitivo. +- Python 3.12 (non 3.11 come da piano) installato perché è la versione LTS + disponibile su Linux Mint LTS — il package è compatibile. + +## Riferimenti commit + +- `b9d6994` — feat: add setup-host-linux.sh script for CI host bootstrap diff --git a/plans/B2-closeout.md b/plans/B2-closeout.md new file mode 100644 index 0000000..f8341f2 --- /dev/null +++ b/plans/B2-closeout.md @@ -0,0 +1,46 @@ +# B2 — Closeout + +Passo B2 di `plans/PhaseB-user-checklist.md`: trasferimento template VM da +`F:\CI\Templates\` a `/var/lib/ci/templates/` con integrità snapshot. + +## Stato + +✅ **COMPLETATO** — 3 template trasferiti, snapshot validati, smoke test PASS. + +## Cosa è stato fatto + +- [x] Verificato assenza di `*.vmem`/`*.vmsn` di runtime sui template Windows + prima della copia (VM spente). +- [x] Trasferimento via `rsync` SSH da Windows → Linux per tutti e 3 i template: + - `WinBuild2025` (snapshot `BaseClean`) + - `WinBuild2022` (snapshot `BaseClean`) + - `LinuxBuild2404` (snapshot `BaseClean-Linux`) +- [x] Snapshot validati con `vmrun -T ws listSnapshots` su ciascun `.vmx`. +- [x] Ogni `.vmx` aperto in Workstation Linux GUI; risposto "**I copied it**" + al prompt di registrazione. +- [x] Keyring file-based configurato anticipatamente per `ci-runner` + (prerequisito del smoke test Windows; il setup completo è in B3): + - `keyrings.alt.file.PlaintextKeyring` scelto al posto di `secret-tool` + (vedi Deviazioni). + - Credenziale `BuildVMGuest` salvata con `tools/ci-cred-set.py`. +- [x] Smoke test `vm new` + `wait-ready` + `vm remove` PASS su: + - `WinBuild2025` (WinRM HTTPS/5986) + - `LinuxBuild2404` (SSH/22) + +## Deviazioni / gotcha + +- **`secret-tool` scartato**: il piano originale usava `secret-tool` (GNOME + Secret Service) per le credenziali headless. Non funziona senza una D-Bus + session attiva, che non è disponibile quando il processo gira come servizio + systemd (`act-runner.service`). Soluzione adottata: `keyrings.alt` con + `PlaintextKeyring` (file `~/.local/share/python_keyring/keyring_pass.cfg`), + che non dipende da D-Bus e funziona correttamente in contesto headless. + Questa scelta è documentata in B3-closeout.md. +- **AGENTS.md errore #9 (machine-id)**: lo snapshot `BaseClean-Linux` era già + stato preso con `machine-id` troncato — nessuna collisione DHCP rilevata + durante lo smoke test. + +## Riferimenti commit + +- `3921758` — feat: add credential management scripts (`tools/ci-cred-set.py`, + `tools/ci-cred-check.py`) diff --git a/plans/B3-closeout.md b/plans/B3-closeout.md new file mode 100644 index 0000000..acbfa15 --- /dev/null +++ b/plans/B3-closeout.md @@ -0,0 +1,52 @@ +# B3 — Closeout + +Passo B3 di `plans/PhaseB-user-checklist.md`: trasferimento credenziali +e chiavi SSH al user `ci-runner` headless. + +## Stato + +✅ **COMPLETATO** — chiavi SSH, credenziale guest Windows e GiteaPAT +disponibili a `ci-runner`; PoC headless verificato. + +## Cosa è stato fatto + +- [x] Chiavi SSH guest Linux già presenti in `/var/lib/ci/keys/` (copiate + con i template in B2). Permessi corretti: + - `ci_linux`: `600` (owner `ci-runner`) + - `ci_linux.pub`: `644` (owner `ci-runner`) +- [x] Credenziale guest Windows `BuildVMGuest` già salvata come + prerequisito del Passo 2 con `tools/ci-cred-set.py`. + - Backend: `keyrings.alt.file.PlaintextKeyring` + (`~/.local/share/python_keyring/keyring_pass.cfg`) + - Verificata con `tools/ci-cred-check.py BuildVMGuest` + → `OK (file) username='WINBUILD-2025\ci_build'` +- [x] Gitea PAT salvato: + + ```bash + sudo -u ci-runner /opt/ci/venv/bin/python \ + /opt/ci/local-ci-cd-system/tools/ci-cred-set.py GiteaPAT ci-runner-linux + ``` + + Verificato con `ci-cred-check.py GiteaPAT` → `OK (file)`. +- [x] PoC headless confermato: il backend `PlaintextKeyring` non dipende + da D-Bus e funziona sia da `sudo -u ci-runner` che dall'interno di + `act-runner.service` (verificato durante smoke test B2 e B4). + +## Deviazioni / gotcha + +- **`secret-tool` scartato**: il piano originale usava `secret-tool` + (GNOME Secret Service). Non funziona senza una D-Bus session attiva, + che non è disponibile in contesti systemd headless. Alternativa adottata: + `keyrings.alt.file.PlaintextKeyring` — nessuna dipendenza da D-Bus, + file in chiaro in `~/.local/share/python_keyring/keyring_pass.cfg` + (accessibile solo a `ci-runner`). Configurazione in + `~/.local/share/python_keyring/keyringrc.cfg`. +- **Credenziale B2**: il prerequisito del Passo 2 anticipava la parte + minima di B3 (solo `BuildVMGuest`) per sbloccare lo smoke test Windows. + In B3 si è completato aggiungendo il GiteaPAT e fissando i permessi + chiavi SSH. + +## Riferimenti commit + +- `3921758` — feat: add credential management scripts (`tools/ci-cred-set.py`, + `tools/ci-cred-check.py`) diff --git a/plans/B4-closeout.md b/plans/B4-closeout.md new file mode 100644 index 0000000..bf28e18 --- /dev/null +++ b/plans/B4-closeout.md @@ -0,0 +1,65 @@ +# B4 — Closeout + +Passo B4 di `plans/PhaseB-user-checklist.md`: act_runner Linux +registrato verso Gitea e gestito da systemd. + +## Stato + +✅ **COMPLETATO** — `act-runner.service` attivo, runner registrato, +`config.yaml` corretto, smoke job PASS. + +## Cosa è stato fatto + +- [x] Binario act_runner Linux v1.0.4 scaricato in `/opt/ci/act_runner/` + con permessi `ci-runner`. +- [x] Runner registrato verso `https://gitea.emulab.it` con nome + `ci-linux` e label `windows-build:host`, `linux-build:host`. +- [x] `/etc/systemd/system/act-runner.service` creato con: + - `User=ci-runner`, `WorkingDirectory=/var/lib/ci/runner` + - Variabili d'ambiente CI (`CI_ROOT`, `CI_TEMPLATES`, `CI_BUILD_VMS`, + `CI_ARTIFACTS`, `CI_KEYS`, `PYTHONIOENCODING`) + - `ExecStart=/opt/ci/act_runner/act_runner daemon --config /var/lib/ci/runner/config.yaml` + - `Restart=on-failure`, `RestartSec=10` +- [x] `/etc/ci/environment` creato con le stesse variabili CI (usato dai + timer systemd di B5). +- [x] `config.yaml` generato con `generate-config` e poi patchato via + `sed` per: + - Rimuovere i label `ubuntu-*:docker://` generati di default e + impostare `windows-build:host` + `linux-build:host` + - Sostituire le variabili placeholder `A_TEST_ENV_NAME_*` con: + - `CI_PYTHON_LAUNCHER: /opt/ci/venv/bin/python` + - `GITEA_CI_TEMPLATE_PATH: /var/lib/ci/templates/WinBuild2025/WinBuild2025.vmx` + - `GITEA_CI_LINUX_TEMPLATE_PATH: /var/lib/ci/templates/LinuxBuild2404/LinuxBuild2404.vmx` +- [x] `act-runner.service` abilitato e avviato; log confermano connessione + Gitea senza errori Docker. +- [x] Smoke job `self-test.yml` triggerato manualmente → PASS dal runner + Linux. + +## Deviazioni / gotcha + +- **Nome servizio `act-runner` (non `act_runner`)**: il binario Linux + si registra come `act-runner.service` (trattino), mentre su Windows + il servizio si chiama `act_runner` (underscore). Questa differenza ha + causato un bug nel comando `monitor runner`, che usava `act_runner` come + default — corretto aggiungendo `--service-name act-runner` nell'`ExecStart` + di `ci-watch-runner-health.service`. +- **`generate-config` scrive label Docker**: `act_runner generate-config` + produce label `ubuntu-latest:docker://...` che fanno cercare Docker (non + installato). È necessario patchare il file con `sed` dopo la generazione. + Le istruzioni sono nel Passo 4 del checklist B. +- **`GITEA_CI_TEMPLATE_PATH` nel service unit prima dello smoke test**: + se le variabili template non sono presenti in `config.yaml` o nel service + unit, il job fallisce con `"Got unexpected extra argument"` (i path non + vengono passati al CLI Python). Prerequisito obbligatorio prima di + trigggerare lo smoke job. +- **`config.yaml` separato da `.runner`**: il file `.runner` contiene il + token di registrazione (non toccare). `config.yaml` contiene solo la + configurazione operativa del runner. + +## Riferimenti commit + +- `536fd68` — feat: update runner configuration steps and add config.yaml + generation instructions +- `a6aa926` — fix: improve Python interpreter resolution for OS compatibility +- `b2803c3` — fix: correct order of arguments for template path in local + CI build action diff --git a/plans/B5-closeout.md b/plans/B5-closeout.md index d827e85..690ba8d 100644 --- a/plans/B5-closeout.md +++ b/plans/B5-closeout.md @@ -1,76 +1,78 @@ -# B5 — Closeout (parziale: artefatti repo) +# B5 — Closeout -Fase B5 di `plans/implementation-plan-A-B.md`: conversione scheduled task +Passo B5 di `plans/PhaseB-user-checklist.md`: conversione scheduled task Windows in coppie `*.service` + `*.timer` systemd su Linux. -## Cosa è stato fatto in questa sessione (su host Windows dev) +## Stato -- [x] Inventario dei 4 task in `scripts/Register-CIScheduledTasks.ps1` (cadenze + comandi) -- [x] 5 coppie `*.service` + `*.timer` create in `deploy/systemd/`: - - `ci-cleanup-orphans.{service,timer}` — every 6h + at boot → `python -m ci_orchestrator vm cleanup --max-age-hours 6` - - `ci-retention-policy.{service,timer}` — daily 03:00 + 30min jitter → `pwsh Invoke-RetentionPolicy.ps1` - - `ci-watch-disk-space.{service,timer}` — every 15min → `python -m ci_orchestrator monitor disk` - - `ci-watch-runner-health.{service,timer}` — every 15min → `python -m ci_orchestrator monitor runner` - - `ci-backup-template.{service,timer}` — weekly Sun 02:00 + 1h jitter → `pwsh Backup-CITemplate.ps1` (NEW, non era in Register-CIScheduledTasks.ps1) -- [x] `deploy/systemd/README.md` con: tabella mapping, prerequisiti (utente, venv, env file), istruzioni install/test/rollback, nota PowerShell Core su Linux +✅ **COMPLETATO** — 5 timer attivi, tutti e 5 i service PASS al trigger +manuale. -## Note sulle cadenze +## Cosa è stato fatto -Le cadenze del piano (`OnCalendar=hourly`, `*:0/15`, ecc.) sono state -adattate per fedeltà semantica all'originale Windows: +- [x] Inventario dei 4 task in `scripts/Register-CIScheduledTasks.ps1` + (cadenze + comandi) + aggiunta `ci-backup-template` (non era in PS). +- [x] `Invoke-RetentionPolicy.ps1` portato in Python come sub-comando + `retention run` (`src/ci_orchestrator/commands/retention.py`). +- [x] `Backup-CITemplate.ps1` portato in Python come sub-comando + `template backup` (`src/ci_orchestrator/commands/template.py`) con + compressione `7z a -mx=1 -mmt=on` → archivi `.7z` in + `/var/lib/ci/backups/`. +- [x] 5 coppie `*.service` + `*.timer` deployate in + `/etc/systemd/system/`: -| Task | Original Windows | systemd timer scelto | -| --------------------- | --------------------------------------------- | ----------------------------------------------------- | -| cleanup-orphans | every 6h + at startup | `OnBootSec=2min, OnUnitActiveSec=6h, Persistent` | -| retention-policy | daily 03:00 + 30min random delay | `OnCalendar=*-*-* 03:00:00, RandomizedDelaySec=30min` | -| watch-disk-space | every 15min | `OnBootSec=5min, OnUnitActiveSec=15min` | -| watch-runner-health | every 15min | `OnBootSec=3min, OnUnitActiveSec=15min` | -| backup-template | *(non in Register-CIScheduledTasks.ps1)* | `OnCalendar=Sun *-*-* 02:00:00, RandomizedDelaySec=1h`| + | Unit | Cadenza | Comando | + | --------------------------- | ---------------------------------------- | ------------------------------------------------------- | + | `ci-cleanup-orphans` | ogni 6h + al boot | `ci_orchestrator vm cleanup --max-age-hours 6` | + | `ci-retention-policy` | daily 03:00 + 30min jitter | `ci_orchestrator retention run` | + | `ci-watch-disk-space` | ogni 15min | `ci_orchestrator monitor disk` | + | `ci-watch-runner-health` | ogni 15min | `ci_orchestrator monitor runner --service-name act-runner` | + | `ci-backup-template` | domenica 02:00 + 1h jitter | `ci_orchestrator template backup --all-templates` | -`Persistent=true` ovunque per non perdere esecuzioni se la macchina è -spenta al momento del trigger. +- [x] Prerequisiti creati sull'host prima del deploy: + ```bash + sudo mkdir -p /var/log/ci /var/lib/ci/backups + sudo chown ci-runner:ci-runner /var/log/ci + ``` +- [x] `systemctl list-timers --all 'ci-*'` → 5 timer `active`. ✅ PASS +- [x] Trigger manuale di tutti e 5 i service → ✅ tutti PASS. +- [x] `deploy/systemd/README.md` aggiornato: tabella mapping, prerequisiti, + istruzioni install/test/rollback — senza riferimenti a PowerShell. -## Decisione — script che restano in PowerShell +## Deviazioni / gotcha -`Invoke-RetentionPolicy.ps1` e `Backup-CITemplate.ps1` non sono stati -portati a Python (resta scelta documentata in `AGENTS.md` "Mappatura"). -Su Linux quindi le rispettive `.service` invocano `pwsh` (PowerShell -Core). Il README documenta l'installazione `apt install powershell` da -repo Microsoft. Se in futuro si deciderà di portarli a Python (sub-comandi -`retention run` / `template backup`), basterà sostituire `ExecStart=` -nelle due unit senza toccare il timer. +- **pwsh non richiesto**: `Invoke-RetentionPolicy.ps1` e + `Backup-CITemplate.ps1` sono stati portati integralmente a Python in + questa fase, eliminando la dipendenza da PowerShell Core per tutti e 5 + i timer. Questo è il primo passo verso la Fase C + (`plans/idea-3-powershell-removal.md`). +- **`--service-name act-runner`**: il default del comando `monitor runner` + era `act_runner` (nome Windows). Il service Linux si chiama `act-runner` + (trattino). Il fix è nel parametro `ExecStart` di + `ci-watch-runner-health.service`. +- **`ci-backup-template` gira come root**: il backup richiede + `systemctl stop/start act-runner.service` (gestito con try/finally in + Python). Gli altri 4 service girano come `ci-runner`. +- **Prerequisiti directory**: `ReadWritePaths` in `ProtectSystem=strict` + richiede che le directory esistano *prima* che la unit venga avviata. + `ci-watch-runner-health` e `ci-cleanup-orphans` usano `/var/log/ci`; + `ci-backup-template` usa `/var/lib/ci/backups`. Entrambe vanno create + prima del deploy (vedi sopra). +- **Bug pre-esistente in `vm.py`** (`_list_orphans`): `is_dir()` era + chiamato fuori dal blocco `try/except OSError`, causando un'eccezione + su symlink rotti. Corretto spostando `is_dir()` all'interno del try. -## Definizione di "fatto" B5 (dal piano) +## Cadenze systemd (vs Windows originale) -| Criterio | Stato | -| --------------------------------------------------- | ------------------------------------ | -| Tutti i task periodici come timer systemd attivi | ⏳ unit pronti; install richiede host Linux | -| Trigger manuale di ognuno PASS | ⏳ richiede host Linux | -| Mapping documentato | ✅ `deploy/systemd/README.md` | +| Task | Windows originale | systemd timer | +| --------------------- | -------------------------- | ----------------------------------------------------- | +| cleanup-orphans | ogni 6h + al boot | `OnBootSec=2min, OnUnitActiveSec=6h, Persistent` | +| retention-policy | daily 03:00 + 30min delay | `OnCalendar=*-*-* 03:00:00, RandomizedDelaySec=30min` | +| watch-disk-space | ogni 15min | `OnBootSec=5min, OnUnitActiveSec=15min` | +| watch-runner-health | ogni 15min | `OnBootSec=3min, OnUnitActiveSec=15min` | +| backup-template | *(non era in PS)* | `OnCalendar=Sun *-*-* 02:00:00, RandomizedDelaySec=1h`| -## Cosa resta a carico utente (sul nuovo host Linux) +## Riferimenti commit -Tutti i passi B1–B4 e B6–B8 sono operazioni hardware/host non eseguibili -da questa sessione. Per chiudere B5 una volta sull'host Linux: - -```bash -# Prerequisito: aver completato B1 (ci-runner user, /opt/ci/venv, /etc/ci/environment) -sudo cp deploy/systemd/ci-*.service deploy/systemd/ci-*.timer /etc/systemd/system/ -sudo systemctl daemon-reload -for t in ci-cleanup-orphans ci-retention-policy ci-watch-disk-space \ - ci-watch-runner-health ci-backup-template; do - sudo systemctl enable --now "${t}.timer" -done -systemctl list-timers --all 'ci-*' - -# Trigger manuale di smoke -sudo systemctl start ci-cleanup-orphans.service -journalctl -u ci-cleanup-orphans.service -n 50 --no-pager -``` - -## Riferimenti - -- Spec systemd timer: `man systemd.timer`, `man systemd.time` -- Original PS task scheduler: `scripts/Register-CIScheduledTasks.ps1` -- Mapping completo: `deploy/systemd/README.md` -- Piano: `plans/implementation-plan-A-B.md` sezione B5 +- `43a69b8` — feat: B5 — port retention/template-backup to Python, deploy + systemd timers