aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rwxr-xr-xscripts/build-release25
-rwxr-xr-xscripts/sanity-test.sh331
2 files changed, 336 insertions, 20 deletions
diff --git a/scripts/build-release b/scripts/build-release
index 0fc3693..f1cbe1e 100755
--- a/scripts/build-release
+++ b/scripts/build-release
@@ -79,28 +79,13 @@ build_iso() {
find_iso
}
-# Run sanity test in QEMU
+# Run sanity test in QEMU (automated)
sanity_test() {
- step "Sanity Test"
- info "Booting ISO in QEMU for verification..."
- info "Please verify:"
- echo " 1. System boots to login prompt"
- echo " 2. ZFS module loads (run 'zpool status' or 'lsmod | grep zfs')"
- echo " 3. Custom scripts are present (ls /usr/local/bin/)"
- echo ""
-
- # Start QEMU in background
- "$SCRIPT_DIR/test-vm.sh" &
- QEMU_PID=$!
-
- echo ""
- read -p "Press Enter once you've verified the ISO works (or Ctrl+C to abort)... "
+ step "Sanity Test (Automated)"
- # Kill QEMU if still running
- if kill -0 $QEMU_PID 2>/dev/null; then
- info "Shutting down test VM..."
- kill $QEMU_PID 2>/dev/null || true
- wait $QEMU_PID 2>/dev/null || true
+ if ! "$SCRIPT_DIR/sanity-test.sh"; then
+ error "Sanity test failed!"
+ exit 1
fi
info "Sanity test passed!"
diff --git a/scripts/sanity-test.sh b/scripts/sanity-test.sh
new file mode 100755
index 0000000..977f018
--- /dev/null
+++ b/scripts/sanity-test.sh
@@ -0,0 +1,331 @@
+#!/bin/bash
+# sanity-test.sh - Automated sanity test for archzfs ISO
+#
+# Boots the ISO in a headless QEMU VM, waits for SSH, runs verification
+# commands, and reports pass/fail. Fully automated - no human input required.
+#
+# Usage:
+# ./scripts/sanity-test.sh # Run sanity test
+# ./scripts/sanity-test.sh --verbose # Show detailed output
+#
+# Exit codes:
+# 0 - All tests passed
+# 1 - One or more tests failed
+# 2 - Setup/infrastructure error (QEMU, SSH, etc.)
+
+SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
+PROJECT_DIR="$(dirname "$SCRIPT_DIR")"
+
+# VM Configuration
+VM_DIR="$PROJECT_DIR/vm"
+VM_DISK="$VM_DIR/sanity-test.qcow2"
+VM_DISK_SIZE="10G"
+VM_RAM="2048"
+VM_CPUS="2"
+VM_NAME="archzfs-sanity"
+
+# UEFI firmware
+OVMF_CODE="/usr/share/edk2/x64/OVMF_CODE.4m.fd"
+OVMF_VARS_ORIG="/usr/share/edk2/x64/OVMF_VARS.4m.fd"
+OVMF_VARS="$VM_DIR/sanity-test-OVMF_VARS.fd"
+
+# SSH settings
+SSH_PORT=2223 # Different port to avoid conflicts with test-vm.sh
+SSH_USER="root"
+SSH_PASS="archzfs"
+SSH_TIMEOUT=180 # Max seconds to wait for SSH
+SSH_OPTS="-o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -o LogLevel=ERROR -o ConnectTimeout=5"
+
+# Colors
+RED='\033[0;31m'
+GREEN='\033[0;32m'
+YELLOW='\033[1;33m'
+CYAN='\033[0;36m'
+NC='\033[0m'
+
+# State
+QEMU_PID=""
+VERBOSE=false
+TESTS_PASSED=0
+TESTS_FAILED=0
+
+info() { echo -e "${GREEN}[INFO]${NC} $1"; }
+warn() { echo -e "${YELLOW}[WARN]${NC} $1"; }
+error() { echo -e "${RED}[ERROR]${NC} $1"; }
+pass() { echo -e "${GREEN}[PASS]${NC} $1"; ((TESTS_PASSED++)); }
+fail() { echo -e "${RED}[FAIL]${NC} $1"; ((TESTS_FAILED++)); }
+
+# Parse arguments
+while [[ $# -gt 0 ]]; do
+ case $1 in
+ --verbose|-v) VERBOSE=true; shift ;;
+ -h|--help)
+ echo "Usage: $0 [--verbose]"
+ echo ""
+ echo "Automated sanity test for archzfs ISO."
+ echo "Boots ISO in headless QEMU, verifies via SSH, reports results."
+ exit 0
+ ;;
+ *) error "Unknown option: $1"; exit 2 ;;
+ esac
+done
+
+# Find the ISO
+find_iso() {
+ ISO_FILE=$(ls -t "$PROJECT_DIR/out/"*.iso 2>/dev/null | head -1)
+ if [[ -z "$ISO_FILE" ]]; then
+ error "No ISO found in $PROJECT_DIR/out/"
+ exit 2
+ fi
+ info "Testing ISO: $(basename "$ISO_FILE")"
+}
+
+# Setup VM resources
+setup_vm() {
+ mkdir -p "$VM_DIR"
+
+ # Create a fresh disk for sanity testing
+ if [[ -f "$VM_DISK" ]]; then
+ rm -f "$VM_DISK"
+ fi
+ qemu-img create -f qcow2 "$VM_DISK" "$VM_DISK_SIZE" >/dev/null 2>&1
+
+ # Copy OVMF vars
+ cp "$OVMF_VARS_ORIG" "$OVMF_VARS"
+}
+
+# Cleanup on exit
+cleanup() {
+ if [[ -n "$QEMU_PID" ]] && kill -0 "$QEMU_PID" 2>/dev/null; then
+ info "Shutting down VM..."
+ kill "$QEMU_PID" 2>/dev/null || true
+ wait "$QEMU_PID" 2>/dev/null || true
+ fi
+ # Clean up sanity test disk (leave main test disk alone)
+ rm -f "$VM_DISK" "$OVMF_VARS" 2>/dev/null || true
+}
+trap cleanup EXIT
+
+# Start QEMU in headless mode
+start_vm() {
+ info "Starting headless VM..."
+
+ qemu-system-x86_64 \
+ -name "$VM_NAME" \
+ -machine q35,accel=kvm \
+ -cpu host \
+ -smp "$VM_CPUS" \
+ -m "$VM_RAM" \
+ -drive if=pflash,format=raw,readonly=on,file="$OVMF_CODE" \
+ -drive if=pflash,format=raw,file="$OVMF_VARS" \
+ -drive "file=$VM_DISK,format=qcow2,if=virtio" \
+ -cdrom "$ISO_FILE" \
+ -boot d \
+ -netdev user,id=net0,hostfwd=tcp::${SSH_PORT}-:22 \
+ -device virtio-net-pci,netdev=net0 \
+ -display none \
+ -serial null \
+ -daemonize \
+ -pidfile "$VM_DIR/sanity-test.pid"
+
+ sleep 1
+ if [[ -f "$VM_DIR/sanity-test.pid" ]]; then
+ QEMU_PID=$(cat "$VM_DIR/sanity-test.pid")
+ if kill -0 "$QEMU_PID" 2>/dev/null; then
+ info "VM started (PID: $QEMU_PID)"
+ else
+ error "VM failed to start"
+ exit 2
+ fi
+ else
+ error "VM failed to start - no PID file"
+ exit 2
+ fi
+}
+
+# Wait for SSH to become available
+wait_for_ssh() {
+ info "Waiting for SSH (timeout: ${SSH_TIMEOUT}s)..."
+ local elapsed=0
+ local interval=5
+
+ while [[ $elapsed -lt $SSH_TIMEOUT ]]; do
+ if sshpass -p "$SSH_PASS" ssh $SSH_OPTS -p "$SSH_PORT" "$SSH_USER@localhost" "true" 2>/dev/null; then
+ info "SSH available after ${elapsed}s"
+ return 0
+ fi
+ sleep $interval
+ ((elapsed += interval))
+ if $VERBOSE; then
+ echo -n "."
+ fi
+ done
+
+ error "SSH timeout after ${SSH_TIMEOUT}s"
+ return 1
+}
+
+# Run a test command via SSH
+run_test() {
+ local name="$1"
+ local cmd="$2"
+ local expect_output="$3" # Optional: string that should be in output
+
+ if $VERBOSE; then
+ echo -e "${CYAN}Testing:${NC} $name"
+ echo -e "${CYAN}Command:${NC} $cmd"
+ fi
+
+ local output
+ output=$(sshpass -p "$SSH_PASS" ssh $SSH_OPTS -p "$SSH_PORT" "$SSH_USER@localhost" "$cmd" 2>&1) || {
+ fail "$name (command failed)"
+ if $VERBOSE; then
+ echo " Output: $output"
+ fi
+ return 1
+ }
+
+ if [[ -n "$expect_output" ]]; then
+ if echo "$output" | grep -q "$expect_output"; then
+ pass "$name"
+ if $VERBOSE; then
+ echo " Output: $output"
+ fi
+ return 0
+ else
+ fail "$name (expected '$expect_output' not found)"
+ if $VERBOSE; then
+ echo " Output: $output"
+ fi
+ return 1
+ fi
+ else
+ pass "$name"
+ if $VERBOSE; then
+ echo " Output: $output"
+ fi
+ return 0
+ fi
+}
+
+# Run all sanity tests
+run_sanity_tests() {
+ echo ""
+ echo -e "${CYAN}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}"
+ echo -e "${CYAN} SANITY TESTS${NC}"
+ echo -e "${CYAN}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}"
+ echo ""
+
+ # Test 1: ZFS kernel module loaded
+ run_test "ZFS kernel module loaded" \
+ "lsmod | grep -q '^zfs' && echo 'zfs module loaded'" \
+ "zfs module loaded"
+
+ # Test 2: ZFS commands work
+ run_test "ZFS version command works" \
+ "zfs version | head -1" \
+ "zfs-"
+
+ # Test 3: zpool command works
+ run_test "zpool command works" \
+ "zpool version | head -1" \
+ "zfs-"
+
+ # Test 4: Custom scripts present
+ run_test "install-archzfs script present" \
+ "test -x /usr/local/bin/install-archzfs && echo 'exists'" \
+ "exists"
+
+ run_test "zfsrollback script present" \
+ "test -x /usr/local/bin/zfsrollback && echo 'exists'" \
+ "exists"
+
+ run_test "zfssnapshot script present" \
+ "test -x /usr/local/bin/zfssnapshot && echo 'exists'" \
+ "exists"
+
+ run_test "grub-zfs-snap script present" \
+ "test -x /usr/local/bin/grub-zfs-snap && echo 'exists'" \
+ "exists"
+
+ run_test "zfs-snap-prune script present" \
+ "test -x /usr/local/bin/zfs-snap-prune && echo 'exists'" \
+ "exists"
+
+ # Test 5: fzf installed (required by zfsrollback)
+ run_test "fzf installed" \
+ "command -v fzf && echo 'found'" \
+ "found"
+
+ # Test 6: SSH is working (implicit - we're connected)
+ pass "SSH connectivity"
+
+ # Test 7: Network manager available
+ run_test "NetworkManager available" \
+ "systemctl is-enabled NetworkManager 2>/dev/null || echo 'available'" \
+ ""
+
+ # Test 8: Kernel version (LTS)
+ run_test "Running LTS kernel" \
+ "uname -r" \
+ "lts"
+
+ # Test 9: archsetup directory present
+ run_test "archsetup directory present" \
+ "test -d /code/archsetup && echo 'exists'" \
+ "exists"
+
+ echo ""
+}
+
+# Print summary
+print_summary() {
+ echo -e "${CYAN}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}"
+ echo -e "${CYAN} SUMMARY${NC}"
+ echo -e "${CYAN}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}"
+ echo ""
+ echo -e " Tests passed: ${GREEN}$TESTS_PASSED${NC}"
+ echo -e " Tests failed: ${RED}$TESTS_FAILED${NC}"
+ echo ""
+
+ if [[ $TESTS_FAILED -eq 0 ]]; then
+ echo -e "${GREEN}All sanity tests passed!${NC}"
+ return 0
+ else
+ echo -e "${RED}Some tests failed.${NC}"
+ return 1
+ fi
+}
+
+# Main
+main() {
+ echo ""
+ echo -e "${CYAN}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}"
+ echo -e "${CYAN} ARCHZFS ISO SANITY TEST${NC}"
+ echo -e "${CYAN}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}"
+ echo ""
+
+ # Check dependencies
+ command -v qemu-system-x86_64 >/dev/null || { error "qemu-system-x86_64 not found"; exit 2; }
+ command -v sshpass >/dev/null || { error "sshpass not found"; exit 2; }
+ [[ -f "$OVMF_CODE" ]] || { error "OVMF firmware not found at $OVMF_CODE"; exit 2; }
+
+ find_iso
+ setup_vm
+ start_vm
+
+ if ! wait_for_ssh; then
+ error "Could not connect to VM via SSH"
+ exit 2
+ fi
+
+ run_sanity_tests
+
+ if print_summary; then
+ exit 0
+ else
+ exit 1
+ fi
+}
+
+main "$@"