diff options
| author | Craig Jennings <c@cjennings.net> | 2026-10-24 05:52:42 -0500 |
|---|---|---|
| committer | Craig Jennings <c@cjennings.net> | 2026-10-24 05:52:42 -0500 |
| commit | 02066b7106eef6f2ab3ed12de4ad2fbf3a83d669 (patch) | |
| tree | 1b1e712223a5a3e7c0769bd2c357f0ed8d5bb745 | |
| parent | 1ea3434be0bf6f66f1a3828ef0e4a6e0a6b8a761 (diff) | |
| download | archangel-02066b7106eef6f2ab3ed12de4ad2fbf3a83d669.tar.gz archangel-02066b7106eef6f2ab3ed12de4ad2fbf3a83d669.zip | |
I read the exact completion banner for every config and check exported
ZFS pools with zpool import. Failed pre-reboot checks stop the scenario
before it can count as passed.
I moved checks that need the running target into the post-reboot path.
| -rwxr-xr-x | scripts/test-install.sh | 144 | ||||
| -rw-r--r-- | tests/unit/test_test_install.bats | 113 |
2 files changed, 196 insertions, 61 deletions
diff --git a/scripts/test-install.sh b/scripts/test-install.sh index 3783ce8..6cde83f 100755 --- a/scripts/test-install.sh +++ b/scripts/test-install.sh @@ -718,72 +718,62 @@ run_install() { return 0 } -# Verify installation +# Succeed when an install log carries the installer's completion banner. +# Pure: takes the log text. +# +# Usage: install_log_reports_success "$log_text" +install_log_reports_success() { + grep -qF 'Installation Complete!' <<<"$1" +} + +# Succeed when `zpool import` output lists <pool> as importable. Matches the +# "pool:" header line only, so a vdev or a longer pool name can't match. +# Pure: takes the command's output. +# +# Usage: zpool_import_lists_pool "$zpool_import_output" <pool> +zpool_import_lists_pool() { + grep -Eq "^[[:space:]]*pool: ${2}\$" <<<"$1" +} + +# Pre-reboot checks, run on the live ISO right after the installer returns. +# Everything here goes over the live ISO's own SSH, which every config has: +# ENABLE_SSH only governs the installed system. Checks of the running +# installed system (Avahi, the Btrfs genesis snapshot) live in +# verify_reboot_survival, because the target isn't there to look at yet. verify_install() { local config="$1" - local enable_ssh - local filesystem - enable_ssh=$(grep "^ENABLE_SSH=" "$config" | cut -d= -f2) + local filesystem install_log import_out filesystem=$(grep "^FILESYSTEM=" "$config" | cut -d= -f2) filesystem="${filesystem:-zfs}" # Default to ZFS - # Basic checks via SSH (if enabled) - if [[ "$enable_ssh" == "yes" ]]; then - # Check install log for success indicators - if ssh_cmd "grep -q 'Installation complete' /tmp/archangel-*.log 2>/dev/null"; then - info "Install log shows success" - else - warn "Could not verify install log" - fi - - if [[ "$filesystem" == "zfs" ]]; then - # ZFS-specific checks - if ssh_cmd "zpool list zroot" >/dev/null 2>&1; then - info "ZFS pool 'zroot' exists" - else - error "ZFS pool 'zroot' not found" - return 1 - fi - - if ssh_cmd "zfs list -t snapshot | grep -q genesis"; then - info "ZFS genesis snapshot exists" - else - warn "ZFS genesis snapshot not found" - fi - elif [[ "$filesystem" == "btrfs" ]]; then - # Btrfs-specific checks - if ssh_cmd "btrfs subvolume list /mnt" >/dev/null 2>&1; then - info "Btrfs subvolumes exist" - else - error "Btrfs subvolumes not found" - return 1 - fi - - if ssh_cmd "arch-chroot /mnt snapper -c root list 2>/dev/null | grep -q genesis"; then - info "Btrfs genesis snapshot exists" - else - warn "Btrfs genesis snapshot not found" - fi - fi - - # Check Avahi mDNS packages - if ssh_cmd "arch-chroot /mnt pacman -Q avahi nss-mdns >/dev/null 2>&1"; then - info "Avahi packages installed" - else - warn "Avahi packages not found" - fi + install_log=$(ssh_cmd "cat \"\$(ls -t /tmp/archangel-*.log 2>/dev/null | head -1)\"" 2>/dev/null) || true + if install_log_reports_success "$install_log"; then + info "Install log shows success" + else + error "Install log has no \"Installation Complete!\" banner" + return 1 + fi - if ssh_cmd "arch-chroot /mnt systemctl is-enabled avahi-daemon >/dev/null 2>&1"; then - info "Avahi daemon enabled" + if [[ "$filesystem" == "zfs" ]]; then + # The installer exports zroot on its way out, so `zpool list` can't + # see it here. A clean export leaves it importable, and that's the + # check. The @genesis snapshot is asserted after the reboot. + import_out=$(ssh_cmd "zpool import 2>&1") || true + if zpool_import_lists_pool "$import_out" zroot; then + info "ZFS pool 'zroot' exported cleanly and importable" else - warn "Avahi daemon not enabled" + error "ZFS pool 'zroot' is not importable after the install" + return 1 fi - else - # For no-SSH tests, check serial console output - if grep -q "Installation complete" "$SERIAL_LOG" 2>/dev/null; then - info "Serial console shows installation complete" + elif [[ "$filesystem" == "btrfs" ]]; then + # This reads /mnt, which a successful Btrfs install currently leaves + # mounted. Once the installer's success path unmounts properly, this + # check has to move after the reboot or it fails every Btrfs run. + if ssh_cmd "btrfs subvolume list /mnt" >/dev/null 2>&1; then + info "Btrfs subvolumes exist" else - warn "Could not verify installation via serial console" + error "Btrfs subvolumes not found" + return 1 fi fi @@ -846,6 +836,34 @@ verify_reboot_survival() { else warn "Snapper timeline timer not active" fi + + # snapper-firstboot creates the genesis snapshot on the first boot, + # possibly after SSH is already up, so give it a minute. + local i genesis=no + for ((i = 0; i < 12; i++)); do + if ssh_cmd "snapper -c root list 2>/dev/null | grep -q genesis"; then + genesis=yes + break + fi + sleep 5 + done + if [[ "$genesis" == yes ]]; then + info "Btrfs genesis snapshot exists" + else + warn "Btrfs genesis snapshot not found after first boot" + fi + fi + + # mDNS on the installed system + if ssh_cmd "pacman -Q avahi nss-mdns >/dev/null 2>&1"; then + info "Avahi packages installed" + else + warn "Avahi packages not found" + fi + if ssh_cmd "systemctl is-enabled avahi-daemon >/dev/null 2>&1"; then + info "Avahi daemon enabled" + else + warn "Avahi daemon not enabled" fi return 0 @@ -1245,11 +1263,15 @@ run_test() { # Verify installation (while still in live ISO) step "Verifying installation..." - if verify_install "$config"; then - info "Verification passed" - else - warn "Verification had issues (may be expected if install rebooted)" + if ! verify_install "$config"; then + error "Pre-reboot verification failed (see above)" + stop_vm "$config_name" + cleanup_disks "$config_name" + TESTS_FAILED=$((TESTS_FAILED + 1)) + FAILED_TESTS+=("$config_name") + return 1 fi + info "Verification passed" # Stop VM to prepare for reboot test (keep OVMF vars for EFI boot) step "Stopping VM for reboot test..." diff --git a/tests/unit/test_test_install.bats b/tests/unit/test_test_install.bats index ab3541b..8e1e9d1 100644 --- a/tests/unit/test_test_install.bats +++ b/tests/unit/test_test_install.bats @@ -57,6 +57,119 @@ setup() { } ############################# +# install_log_reports_success +############################# +# verify_install grepped for "Installation complete", but the installer's +# banner is "Installation Complete!", so the check never passed on any run. + +@test "install_log_reports_success matches the installer's completion banner" { + local log="==> Cleaning Up +╔═══════════════════════════════════════════════════════════════╗ +║ Installation Complete! ║ +╚═══════════════════════════════════════════════════════════════╝" + run install_log_reports_success "$log" + [ "$status" -eq 0 ] +} + +@test "install_log_reports_success rejects a failed install's log" { + local log="[WARN] Installation failed (exit code 1) — cleaning up... +[ERROR] Installation failed; system cleaned up. Re-run the installer to retry." + run install_log_reports_success "$log" + [ "$status" -eq 1 ] +} + +@test "install_log_reports_success rejects a lowercase near-miss" { + run install_log_reports_success "Installation complete" + [ "$status" -eq 1 ] +} + +@test "install_log_reports_success rejects an empty log" { + run install_log_reports_success "" + [ "$status" -eq 1 ] +} + +############################# +# zpool_import_lists_pool +############################# +# The installer exports the pool on its way out, so before the reboot the +# only pool state the live ISO can see is `zpool import`'s list of +# importable pools. verify_install used `zpool list`, which never found it. + +@test "zpool_import_lists_pool finds an exported pool by name" { + local out=" pool: zroot + id: 1446521829418224377 + state: ONLINE + action: The pool can be imported using its name or numeric identifier. + config: + + zroot ONLINE + vda2 ONLINE" + run zpool_import_lists_pool "$out" zroot + [ "$status" -eq 0 ] +} + +@test "zpool_import_lists_pool ignores a pool whose name only starts with the target" { + local out=" pool: zrootbak + id: 99 + state: ONLINE" + run zpool_import_lists_pool "$out" zroot + [ "$status" -eq 1 ] +} + +@test "zpool_import_lists_pool ignores the pool name appearing only in the config tree" { + local out=" pool: tank + config: + + zroot ONLINE" + run zpool_import_lists_pool "$out" zroot + [ "$status" -eq 1 ] +} + +@test "zpool_import_lists_pool fails when no pools are available" { + run zpool_import_lists_pool "no pools available to import" zroot + [ "$status" -eq 1 ] +} + +@test "zpool_import_lists_pool fails on empty output" { + run zpool_import_lists_pool "" zroot + [ "$status" -eq 1 ] +} + +############################# +# run_test verification gate +############################# +# A failed live-ISO verification must fail the scenario before reboot. This +# matters most when ENABLE_SSH=no: the later console marker proves only that +# something reached a login prompt, not that the installed ZFS root was the +# pool the installer just created. + +@test "run_test fails when pre-reboot verification fails" { + local cfg="$BATS_TEST_TMPDIR/no-ssh.conf" + printf '%s\n' \ + 'DISKS=/dev/vda' \ + 'FILESYSTEM=zfs' \ + 'ENABLE_SSH=no' > "$cfg" + + LOG_DIR="$BATS_TEST_TMPDIR/logs" + SERIAL_LOG="$BATS_TEST_TMPDIR/serial.log" + create_disks() { :; } + port_in_use() { return 1; } + start_vm() { printf '12345\n'; } + wait_for_ssh() { return 0; } + timeout() { return 0; } + verify_install() { return 1; } + stop_vm() { :; } + cleanup_disks() { :; } + boot_from_disk() { return 0; } + wait_for_boot_console() { return 0; } + + run run_test "$cfg" + [ "$status" -eq 1 ] + [[ "$output" == *"Pre-reboot verification failed"* ]] + [[ "$output" != *"TEST PASSED"* ]] +} + +############################# # is_transient_install_failure ############################# |
