#!/usr/bin/env bash # Test d'installation de Lyra dans des VM propres (roadmap #44), pilote par les # scripts vm-controller de fedora-agents (les memes que les outils MCP vm_*). # # Par VM : restauration du snapshot vierge, clone du depot au commit teste, # installeur SANS interface (--headless), verifications, rapport date. # Protocole et baselines : docs/user/VM_INSTALL_TESTS.md. # # tests/installer/vm_install_test.sh --dry-run # affiche le plan, ne lance rien # tests/installer/vm_install_test.sh --ollama-host 192.0.2.1 # campagne reelle (Fedora + Ubuntu) # # Options : --vm NOM (repetable ; defaut fedora-base et ubuntu-base) # --snapshot NOM (defaut installer-clean-20260926) # --ref REF (defaut : commit courant, qui doit etre pousse) # --repo URL --ollama-host HOTE --scripts-dir DIR --keep (ne pas re-restaurer) set -euo pipefail ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)" VMS=() SNAPSHOT="installer-clean-20260926" REPO="https://github.com/amineutron/lyra.git" REF="" OLLAMA_HOST_ARG="" SCRIPTS_DIR="${LYRA_SCRIPTS_DIR:-/usr/local/lib/lyra/scripts}/agents/vm-controller" DRY_RUN=false KEEP=false usage() { sed -n '2,17p' "$0" | sed 's/^# \{0,1\}//'; } while [ $# -gt 0 ]; do case "$1" in --vm) VMS+=("$2"); shift ;; --snapshot) SNAPSHOT="$2"; shift ;; --repo) REPO="$2"; shift ;; --ref) REF="$2"; shift ;; --ollama-host) OLLAMA_HOST_ARG="$2"; shift ;; --scripts-dir) SCRIPTS_DIR="$2"; shift ;; --dry-run) DRY_RUN=true ;; --keep) KEEP=true ;; -h|--help) usage; exit 0 ;; *) echo "option inconnue : $1" >&2; usage >&2; exit 2 ;; esac shift done [ ${#VMS[@]} -gt 0 ] || VMS=(fedora-base ubuntu-base) REF="${REF:-$(git -C "$ROOT" rev-parse HEAD)}" if ! $DRY_RUN && [ -z "$OLLAMA_HOST_ARG" ]; then echo "--ollama-host requis : les VM n'ont pas de GPU, Ollama tourne sur l'hote" >&2; exit 2 fi STAMP="$(date +%Y-%m-%d)" REPORT_DIR="$ROOT/docs/user/vm-install-reports" # les VM testees dans le nom : deux campagnes separees du meme commit ne s'ecrasent plus REPORT="$REPORT_DIR/$STAMP-${REF:0:7}-$(IFS=-; echo "${VMS[*]}").md" # Une etape = une commande ; en --dry-run on l'affiche seulement. step() { if $DRY_RUN; then printf '[plan]'; printf ' %q' "$@"; printf '\n'; return 0; fi "$@" } # Commande jouee DANS la VM (utilisateur de la baseline, sans mot de passe sudo) install_cmd() { local host_opt="" [ -n "$OLLAMA_HOST_ARG" ] && host_opt="--ollama-host $OLLAMA_HOST_ARG" printf '%s' "rm -rf ~/lyra && git clone --quiet $REPO ~/lyra && cd ~/lyra && git checkout --quiet $REF \ && ./installer/install.sh --headless $host_opt" } CHECKS=( "systemctl --user is-active lyra-daemon" "test -f ~/lyra/config.yaml && echo config.yaml present" # la commande la ou l'installeur la pose (et l'annonce) : ~/.local/bin/lyra "cd ~ && .local/bin/lyra --version" "cd ~ && timeout 120 .local/bin/lyra -y 'liste les taches'" ) # Jusqu'a 3 min, un essai toutes les 10 s : apres restauration, ubuntu-base a mis plus # de 2 min a retrouver le reseau (campagne du 2026-09-26) ; fedora-base ~15 s. SSH_WAIT_S=180 ssh_ready() { if $DRY_RUN; then step "$SCRIPTS_DIR/vm-exec.sh" "$1" "true" --timeout=30; return 0; fi local deadline=$(( $(date +%s) + SSH_WAIT_S )) while [ "$(date +%s)" -lt "$deadline" ]; do "$SCRIPTS_DIR/vm-exec.sh" "$1" "true" --timeout=15 >/dev/null 2>&1 && return 0 sleep 10 done return 1 } # Horloge de la VM a l'heure de l'hote : agent invite QEMU (virsh domtime), sinon # sudo date dans la VM. Echec si l'ecart reste superieur a 5 minutes. sync_clock() { local vm="$1" now if $DRY_RUN; then step virsh -c qemu:///system domtime "$vm" --now; return 0; fi virsh -c qemu:///system domtime "$vm" --now >/dev/null 2>&1 \ || "$SCRIPTS_DIR/vm-exec.sh" "$vm" "sudo -n date -u -s @$(date +%s)" --timeout=30 >/dev/null 2>&1 now="$("$SCRIPTS_DIR/vm-exec.sh" "$vm" "date +%s" --timeout=30 2>/dev/null | grep -Eo '^[0-9]{9,}$' | head -1)" [ -n "$now" ] && [ $(( $(date +%s) - now )) -lt 300 ] && [ $(( now - $(date +%s) )) -lt 300 ] } run_vm() { local vm="$1" status=OK echo "== $vm" step "$SCRIPTS_DIR/vm-snapshot.sh" "$vm" restore "$SNAPSHOT" -y || return 1 step sleep 20 # reseau de la VM apres restauration (~15 s mesure) # Acces SSH d'abord : une baseline qui ne connait pas la cle des VM faisait echouer # toutes les etapes une par une (2026-09-26 : cle creee le 15/09, baseline du 24/08). if ! ssh_ready "$vm"; then echo "SSH refuse sur $vm : la baseline $SNAPSHOT doit autoriser la cle des VM (~/.ssh/config)" >&2 RESULTS+=("| $vm | acces SSH | ECHEC |"); SUMMARY+=("| $vm | ECHEC (SSH refuse) |") $KEEP || step "$SCRIPTS_DIR/vm-snapshot.sh" "$vm" restore "$SNAPSHOT" -y return 1 fi # Un snapshot a chaud restaure aussi l'horloge de la VM (mars 2026 pour la baseline) : # sans remise a l'heure, TLS refuse les certificats « pas encore valides » (git clone). if ! sync_clock "$vm"; then RESULTS+=("| $vm | remise a l'heure | ECHEC |"); SUMMARY+=("| $vm | ECHEC (horloge) |") $KEEP || step "$SCRIPTS_DIR/vm-snapshot.sh" "$vm" restore "$SNAPSHOT" -y return 1 fi if ! step "$SCRIPTS_DIR/vm-exec.sh" "$vm" "$(install_cmd)" --timeout=3600; then status=ECHEC; fi for check in "${CHECKS[@]}"; do if step "$SCRIPTS_DIR/vm-exec.sh" "$vm" "$check" --timeout=180; then RESULTS+=("| $vm | \`$check\` | ok |") else RESULTS+=("| $vm | \`$check\` | ECHEC |"); status=ECHEC fi done $KEEP || step "$SCRIPTS_DIR/vm-snapshot.sh" "$vm" restore "$SNAPSHOT" -y SUMMARY+=("| $vm | $status |") } if ! $DRY_RUN; then for s in vm-snapshot.sh vm-exec.sh; do [ -x "$SCRIPTS_DIR/$s" ] || { echo "introuvable : $SCRIPTS_DIR/$s (--scripts-dir)" >&2; exit 2; } done git -C "$ROOT" branch -r --contains "$REF" | grep -q . \ || { echo "le commit $REF n'est pas pousse : les VM clonent depuis $REPO" >&2; exit 2; } fi RESULTS=(); SUMMARY=(); FAILED=0 for vm in "${VMS[@]}"; do run_vm "$vm" || FAILED=1; done if $DRY_RUN; then echo "[plan] rapport : ${REPORT#"$ROOT"/}"; exit 0; fi mkdir -p "$REPORT_DIR" { echo "# Test d'installation en VM : $STAMP" echo echo "- Commit : \`$REF\`" echo "- Snapshot de depart : \`$SNAPSHOT\` ; Ollama : hote de la VM" echo "- Script : \`tests/installer/vm_install_test.sh\` (installeur \`--headless\`)" echo echo "| VM | Resultat |"; echo "|---|---|"; printf '%s\n' "${SUMMARY[@]}" echo echo "| VM | Verification | Resultat |"; echo "|---|---|---|"; printf '%s\n' "${RESULTS[@]}" } > "$REPORT" echo "rapport : ${REPORT#"$ROOT"/}" grep -q "ECHEC" "$REPORT" && FAILED=1 exit "$FAILED"