aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorCraig Jennings <c@cjennings.net>2026-10-24 05:52:42 -0500
committerCraig Jennings <c@cjennings.net>2026-10-24 05:52:42 -0500
commit02066b7106eef6f2ab3ed12de4ad2fbf3a83d669 (patch)
tree1b1e712223a5a3e7c0769bd2c357f0ed8d5bb745
parent1ea3434be0bf6f66f1a3828ef0e4a6e0a6b8a761 (diff)
downloadarchangel-02066b7106eef6f2ab3ed12de4ad2fbf3a83d669.tar.gz
archangel-02066b7106eef6f2ab3ed12de4ad2fbf3a83d669.zip
fix(test): verify installations from the live ISOHEADmain
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-xscripts/test-install.sh144
-rw-r--r--tests/unit/test_test_install.bats113
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
#############################