diff options
| author | Craig Jennings <c@cjennings.net> | 2026-01-27 17:22:55 -0600 |
|---|---|---|
| committer | Craig Jennings <c@cjennings.net> | 2026-01-27 17:22:55 -0600 |
| commit | 70bb2d5ab1bf6787bc613e33f5398be2eca1f5fd (patch) | |
| tree | 0d8cd5057dd32f5f312a7f3534d590b99c2f0f91 /scripts/testing/run-test.sh | |
| parent | 0c6175bfc98f2c5ff2debc665fd8bf91f9171f4e (diff) | |
feat(testing): rewrite test infrastructure from libvirt to direct QEMU
Replace the never-fully-operational libvirt-based VM test infrastructure
with direct QEMU management and archangel ISO for fully automated,
unattended base VM creation.
Key changes:
- vm-utils.sh: complete rewrite — QEMU process mgmt via PID file,
monitor socket for graceful shutdown, qemu-img snapshots, SSH
port forwarding (localhost:2222)
- create-base-vm.sh: boots archangel ISO, SSHs in, runs unattended
install via config file, verifies, creates clean-install snapshot
- run-test.sh: snapshot revert, git bundle transfer, detached archsetup
execution with setsid, polling, validation, and report generation
- debug-vm.sh: CoW overlay disk, GTK display, auto-cleanup on close
- setup-testing-env.sh: reduced deps to qemu-full/sshpass/edk2-ovmf/socat
- cleanup-tests.sh: PID-based process management, orphan detection
- validation.sh: port-based SSH (backward compatible), fuzzel/foot for
Hyprland, corrected package list paths
- network-diagnostics.sh: getent/curl instead of nslookup/ping (SLIRP)
New files:
- archsetup-test.conf: archangel config for base VM (btrfs, no encrypt)
- archsetup-vm.conf: archsetup config for unattended test execution
- assets/archangel.conf.example: reference archangel config
Deleted:
- finalize-base-vm.sh: merged into create-base-vm.sh
- archinstall-config.json: replaced by archangel .conf format
Tested: full end-to-end run — 51 validations passed, 0 failures.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Diffstat (limited to 'scripts/testing/run-test.sh')
| -rwxr-xr-x | scripts/testing/run-test.sh | 173 |
1 files changed, 79 insertions, 94 deletions
diff --git a/scripts/testing/run-test.sh b/scripts/testing/run-test.sh index 4c41cc3..a600870 100755 --- a/scripts/testing/run-test.sh +++ b/scripts/testing/run-test.sh @@ -5,7 +5,7 @@ # # This script: # 1. Reverts base VM to clean snapshot -# 2. Starts the base VM +# 2. Boots the VM via QEMU # 3. Transfers archsetup and dotfiles # 4. Executes archsetup in the VM # 5. Captures logs and validates results @@ -55,15 +55,20 @@ done # Configuration TIMESTAMP=$(date +'%Y%m%d-%H%M%S') -VM_NAME="archsetup-base" +VM_IMAGES_DIR="$PROJECT_ROOT/vm-images" TEST_RESULTS_DIR="$PROJECT_ROOT/test-results/$TIMESTAMP" ROOT_PASSWORD="archsetup" ARCHZFS_INBOX="$HOME/code/archzfs/inbox" +ARCHSETUP_VM_CONF="$SCRIPT_DIR/archsetup-vm.conf" -# Initialize logging +# Set VM_IP for validation.sh backward compatibility +VM_IP="localhost" + +# Initialize logging and VM paths mkdir -p "$TEST_RESULTS_DIR" LOGFILE="$TEST_RESULTS_DIR/test.log" init_logging "$LOGFILE" +init_vm_paths "$VM_IMAGES_DIR" section "ArchSetup Test Run: $TIMESTAMP" @@ -72,9 +77,9 @@ if [ ! -f "$ARCHSETUP_SCRIPT" ]; then fatal "ArchSetup script not found: $ARCHSETUP_SCRIPT" fi -# Check if VM exists -if ! vm_exists "$VM_NAME"; then - fatal "Base VM not found: $VM_NAME" +# Check disk exists +if [ ! -f "$DISK_PATH" ]; then + fatal "Base disk not found: $DISK_PATH" info "Create it first: ./scripts/testing/create-base-vm.sh" fi @@ -82,72 +87,34 @@ fi section "Preparing Test Environment" step "Checking for snapshot: $SNAPSHOT_NAME" -if ! virsh --connect "$LIBVIRT_URI" snapshot-list "$VM_NAME" --name 2>/dev/null | grep -q "^$SNAPSHOT_NAME$"; then - fatal "Snapshot '$SNAPSHOT_NAME' not found on VM $VM_NAME" +if ! snapshot_exists "$DISK_PATH" "$SNAPSHOT_NAME"; then + fatal "Snapshot '$SNAPSHOT_NAME' not found on $DISK_PATH" info "Available snapshots:" - virsh --connect "$LIBVIRT_URI" snapshot-list "$VM_NAME" 2>/dev/null || info " (none)" + list_snapshots "$DISK_PATH" info "" - info "Create snapshot with:" - info " virsh snapshot-create-as $VM_NAME $SNAPSHOT_NAME --description 'Clean Arch install'" + info "Create base VM with: ./scripts/testing/create-base-vm.sh" fi success "Snapshot $SNAPSHOT_NAME exists" -# Shut down VM if running -if vm_is_running "$VM_NAME"; then - warn "VM $VM_NAME is currently running - shutting down for snapshot revert" - stop_vm "$VM_NAME" -fi +# Stop VM if running and restore snapshot +stop_qemu 2>/dev/null || true -# Revert to clean snapshot step "Reverting to snapshot: $SNAPSHOT_NAME" -if restore_snapshot "$VM_NAME" "$SNAPSHOT_NAME"; then +if restore_snapshot "$DISK_PATH" "$SNAPSHOT_NAME"; then success "Reverted to clean state" else fatal "Failed to revert snapshot" fi -# Start VM +# Start VM and wait for SSH start_timer "boot" step "Starting VM and waiting for SSH..." -if ! start_vm "$VM_NAME"; then - fatal "Failed to start VM" -fi - -sleep 10 # Give VM time to boot - -# Get VM IP address -VM_IP="" -for i in {1..30}; do - VM_IP=$(get_vm_ip "$VM_NAME" 2>/dev/null || true) - if [ -n "$VM_IP" ]; then - break - fi - sleep 2 -done - -if [ -z "$VM_IP" ]; then - error "Could not get VM IP address" - info "VM may not have booted correctly" - fatal "VM boot failed" -fi - -success "VM is running at $VM_IP" +start_qemu "$DISK_PATH" "disk" "" "none" || fatal "Failed to start VM" +wait_for_ssh "$ROOT_PASSWORD" 120 || fatal "VM SSH not available" stop_timer "boot" -# Wait for SSH -step "Waiting for SSH to become available..." -for i in {1..60}; do - if sshpass -p "$ROOT_PASSWORD" ssh -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null \ - -o ConnectTimeout=2 "root@$VM_IP" "echo connected" &>/dev/null; then - break - fi - sleep 2 -done - -success "SSH is available" - # Run network diagnostics -if ! run_network_diagnostics "$VM_IP"; then +if ! run_network_diagnostics; then fatal "Network diagnostics failed - aborting test" fi @@ -165,39 +132,59 @@ BUNDLE_FILE=$(mktemp) git bundle create "$BUNDLE_FILE" HEAD >> "$LOGFILE" 2>&1 # Transfer bundle and extract on VM -sshpass -p "$ROOT_PASSWORD" ssh -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null \ - "root@$VM_IP" "rm -rf /tmp/archsetup-test && mkdir -p /tmp/archsetup-test" >> "$LOGFILE" 2>&1 +vm_exec "$ROOT_PASSWORD" "rm -rf /tmp/archsetup-test && mkdir -p /tmp/archsetup-test" >> "$LOGFILE" 2>&1 -sshpass -p "$ROOT_PASSWORD" scp -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null \ - "$BUNDLE_FILE" "root@$VM_IP:/tmp/archsetup.bundle" >> "$LOGFILE" 2>&1 +copy_to_vm "$BUNDLE_FILE" "/tmp/archsetup.bundle" "$ROOT_PASSWORD" # Clone from bundle on VM (simulates git clone) -sshpass -p "$ROOT_PASSWORD" ssh -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null \ - "root@$VM_IP" "cd /tmp && git clone --depth 1 /tmp/archsetup.bundle archsetup-test && rm /tmp/archsetup.bundle" >> "$LOGFILE" 2>&1 +vm_exec "$ROOT_PASSWORD" \ + "cd /tmp && git clone --depth 1 /tmp/archsetup.bundle archsetup-test && rm /tmp/archsetup.bundle" \ + >> "$LOGFILE" 2>&1 rm -f "$BUNDLE_FILE" success "Repository cloned to VM (simulating git clone --depth 1)" +# Transfer archsetup VM config file +step "Copying archsetup VM config" +copy_to_vm "$ARCHSETUP_VM_CONF" "/tmp/archsetup-test/archsetup-vm.conf" "$ROOT_PASSWORD" + +# Pre-archsetup VM setup +section "Pre-ArchSetup VM Setup" + +step "Importing archzfs PGP key" +vm_exec "$ROOT_PASSWORD" \ + "curl -sL https://archzfs.com/archzfs.gpg | pacman-key --add - && pacman-key --lsign-key DDF7DB817396A49B2A2723F7403BD972F75D9D76" \ + >> "$LOGFILE" 2>&1 && success "archzfs PGP key imported" || warn "Failed to import archzfs key (may already be present)" + +step "Syncing package databases" +vm_exec "$ROOT_PASSWORD" "pacman -Sy --noconfirm" >> "$LOGFILE" 2>&1 && \ + success "Package databases synced" || warn "Package database sync had issues" + # Execute archsetup section "Executing ArchSetup" start_timer "archsetup" step "Starting archsetup script in detached session on VM..." -info "This will take 30-60 minutes depending on network speed" info "Log file: $LOGFILE" -# Start archsetup in a detached session on the VM (resilient to SSH disconnections) +# Start archsetup fully detached. +# Use ssh -T -n to prevent PTY allocation and stdin forwarding, which allows +# the SSH session to close immediately after the remote shell exits. REMOTE_LOG="/tmp/archsetup-test/archsetup-output.log" -sshpass -p "$ROOT_PASSWORD" ssh -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null \ - "root@$VM_IP" "cd /tmp/archsetup-test && nohup bash archsetup > $REMOTE_LOG 2>&1 & echo \$!" \ + +sshpass -p "$ROOT_PASSWORD" ssh -T -n $SSH_OPTS \ + -p "$SSH_PORT" root@localhost \ + "setsid bash -c 'cd /tmp/archsetup-test && bash archsetup --config-file /tmp/archsetup-test/archsetup-vm.conf > $REMOTE_LOG 2>&1' < /dev/null > /dev/null 2>&1 &" \ >> "$LOGFILE" 2>&1 -if [ $? -ne 0 ]; then - fatal "Failed to start archsetup on VM" +# Verify the process started +sleep 3 +if vm_exec "$ROOT_PASSWORD" "pgrep -f 'bash archsetup'" >> "$LOGFILE" 2>/dev/null; then + success "ArchSetup started in background on VM" +else + fatal "ArchSetup process not found after launch" fi -success "ArchSetup started in background on VM" - # Poll for completion step "Monitoring archsetup progress (polling every 30 seconds)..." POLL_COUNT=0 @@ -205,9 +192,7 @@ MAX_POLLS=180 # 90 minutes max (180 * 30 seconds) while [ $POLL_COUNT -lt $MAX_POLLS ]; do # Check if archsetup process is still running - # Use ps to avoid pgrep matching its own SSH command - if sshpass -p "$ROOT_PASSWORD" ssh -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null \ - "root@$VM_IP" "ps aux | grep '[b]ash archsetup' > /dev/null" 2>/dev/null; then + if vm_exec "$ROOT_PASSWORD" "ps aux | grep '[b]ash archsetup' > /dev/null" 2>/dev/null; then # Still running, wait and continue sleep 30 POLL_COUNT=$((POLL_COUNT + 1)) @@ -229,8 +214,9 @@ if [ $POLL_COUNT -ge $MAX_POLLS ]; then else # Get exit code from the remote log step "Retrieving archsetup exit status..." - ARCHSETUP_EXIT_CODE=$(sshpass -p "$ROOT_PASSWORD" ssh -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null \ - "root@$VM_IP" "grep -q 'ARCHSETUP_EXECUTION_COMPLETE' /var/log/archsetup-*.log 2>/dev/null && echo 0 || echo 1" 2>/dev/null) + ARCHSETUP_EXIT_CODE=$(vm_exec "$ROOT_PASSWORD" \ + "grep -q 'ARCHSETUP_EXECUTION_COMPLETE' /var/log/archsetup-*.log 2>/dev/null && echo 0 || echo 1" \ + 2>/dev/null) if [ "$ARCHSETUP_EXIT_CODE" = "0" ]; then success "ArchSetup completed successfully" @@ -241,8 +227,7 @@ fi # Copy the remote output log step "Retrieving archsetup output from VM..." -sshpass -p "$ROOT_PASSWORD" scp -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null \ - "root@$VM_IP:$REMOTE_LOG" "$TEST_RESULTS_DIR/archsetup-output.log" 2>> "$LOGFILE" || \ +copy_from_vm "$REMOTE_LOG" "$TEST_RESULTS_DIR/archsetup-output.log" "$ROOT_PASSWORD" || \ warn "Could not copy remote output log" # Append remote output to main test log @@ -256,15 +241,17 @@ stop_timer "archsetup" section "Capturing Test Artifacts" step "Copying archsetup log from VM" -sshpass -p "$ROOT_PASSWORD" scp -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null \ - "root@$VM_IP:/var/log/archsetup-*.log" "$TEST_RESULTS_DIR/" 2>> "$LOGFILE" || \ +copy_from_vm "/var/log/archsetup-*.log" "$TEST_RESULTS_DIR/" "$ROOT_PASSWORD" || \ warn "Could not copy archsetup log" step "Copying package lists from VM" -sshpass -p "$ROOT_PASSWORD" scp -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null \ - "root@$VM_IP:/root/.local/src/archsetup-*.txt" "$TEST_RESULTS_DIR/" 2>> "$LOGFILE" || \ +copy_from_vm "/var/log/archsetup-*-package-list.txt" "$TEST_RESULTS_DIR/" "$ROOT_PASSWORD" || \ warn "Could not copy package lists" +step "Copying installed packages list" +copy_from_vm "/var/log/archsetup-installed-packages.txt" "$TEST_RESULTS_DIR/" "$ROOT_PASSWORD" || \ + warn "Could not copy installed packages list" + # Capture post-install state capture_post_install_state "$TEST_RESULTS_DIR" @@ -282,9 +269,9 @@ generate_issue_report "$TEST_RESULTS_DIR" "$ARCHZFS_INBOX" # Set validation result based on failure count if [ $VALIDATION_FAILED -eq 0 ]; then - VALIDATION_PASSED=true + TEST_PASSED=true else - VALIDATION_PASSED=false + TEST_PASSED=false fi # Generate test report @@ -298,16 +285,16 @@ ArchSetup Test Report Test ID: $TIMESTAMP Date: $(date +'%Y-%m-%d %H:%M:%S') -Test Method: Snapshot-based +Test Method: QEMU snapshot-based VM Configuration: - Name: $VM_NAME - IP: $VM_IP + Disk: $DISK_PATH Snapshot: $SNAPSHOT_NAME + SSH: localhost:$SSH_PORT Results: ArchSetup Exit Code: $ARCHSETUP_EXIT_CODE - Validation: $(if $VALIDATION_PASSED; then echo "PASSED"; else echo "FAILED"; fi) + Validation: $(if $TEST_PASSED; then echo "PASSED"; else echo "FAILED"; fi) Validation Summary: Passed: $VALIDATION_PASSED @@ -338,16 +325,14 @@ section "Cleanup" if $KEEP_VM; then info "VM is still running in post-test state (--keep flag was used)" info "Connect with:" - info " Console: virsh console $VM_NAME" - info " SSH: ssh root@$VM_IP" + info " SSH: sshpass -p '$ROOT_PASSWORD' ssh -p $SSH_PORT root@localhost" info "" - info "To revert to clean state when done:" - info " virsh shutdown $VM_NAME" - info " virsh snapshot-revert $VM_NAME $SNAPSHOT_NAME" + info "To stop VM: kill \$(cat $PID_FILE)" + info "To revert: qemu-img snapshot -a $SNAPSHOT_NAME $DISK_PATH" else step "Shutting down VM and reverting to clean snapshot" - stop_vm "$VM_NAME" - if restore_snapshot "$VM_NAME" "$SNAPSHOT_NAME"; then + stop_qemu + if restore_snapshot "$DISK_PATH" "$SNAPSHOT_NAME"; then success "VM reverted to clean state" else warn "Failed to revert snapshot - VM may be in modified state" @@ -357,7 +342,7 @@ fi # Final summary section "Test Complete" -if [ $ARCHSETUP_EXIT_CODE -eq 0 ] && $VALIDATION_PASSED; then +if [ "$ARCHSETUP_EXIT_CODE" = "0" ] && $TEST_PASSED; then success "TEST PASSED" exit 0 else |
