From 70bb2d5ab1bf6787bc613e33f5398be2eca1f5fd Mon Sep 17 00:00:00 2001 From: Craig Jennings Date: Tue, 27 Jan 2026 17:22:55 -0600 Subject: feat(testing): rewrite test infrastructure from libvirt to direct QEMU MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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 --- scripts/testing/create-base-vm.sh | 249 ++++++++++++++++++-------------------- 1 file changed, 116 insertions(+), 133 deletions(-) (limited to 'scripts/testing/create-base-vm.sh') diff --git a/scripts/testing/create-base-vm.sh b/scripts/testing/create-base-vm.sh index 03409fe..7979bd2 100755 --- a/scripts/testing/create-base-vm.sh +++ b/scripts/testing/create-base-vm.sh @@ -1,10 +1,11 @@ #!/bin/bash -# Create base VM for archsetup testing - Manual Installation +# Create base VM for archsetup testing - Automated via Archangel ISO # Author: Craig Jennings # License: GNU GPLv3 # -# This script creates a VM booted from Arch ISO, then waits for you to -# manually install Arch using archinstall. +# This script boots an archangel ISO in QEMU, copies a config file into the +# live environment via SSH, and runs a fully unattended Arch Linux installation. +# The result is a base VM disk with a "clean-install" snapshot ready for testing. set -e @@ -17,157 +18,139 @@ source "$SCRIPT_DIR/lib/logging.sh" source "$SCRIPT_DIR/lib/vm-utils.sh" # Configuration -VM_NAME="archsetup-base" -VM_CPUS="${VM_CPUS:-4}" -VM_RAM="${VM_RAM:-8192}" # MB -VM_DISK="${VM_DISK:-50}" # GB VM_IMAGES_DIR="$PROJECT_ROOT/vm-images" -ISO_URL="https://mirrors.kernel.org/archlinux/iso/latest/archlinux-x86_64.iso" -ISO_PATH="$VM_IMAGES_DIR/arch-latest.iso" -DISK_PATH="$VM_IMAGES_DIR/archsetup-base.qcow2" +CONFIG_FILE="$SCRIPT_DIR/archsetup-test.conf" +LIVE_ISO_PASSWORD="archzfs" +SNAPSHOT_NAME="clean-install" # Initialize logging +mkdir -p "$PROJECT_ROOT/test-results" LOGFILE="$PROJECT_ROOT/test-results/create-base-vm-$(date +'%Y%m%d-%H%M%S').log" init_logging "$LOGFILE" +init_vm_paths "$VM_IMAGES_DIR" section "Creating Base VM for ArchSetup Testing" -# Verify prerequisites +# ─── Prerequisites ──────────────────────────────────────────────────── + step "Checking prerequisites" -check_libvirt || fatal "libvirt not running" -check_libvirt_group || fatal "User not in libvirt group" -check_kvm || fatal "KVM not available" +check_prerequisites || fatal "Missing prerequisites" success "Prerequisites satisfied" -# Create vm-images directory -mkdir -p "$VM_IMAGES_DIR" +# Verify config file exists +if [ ! -f "$CONFIG_FILE" ]; then + fatal "Config file not found: $CONFIG_FILE" +fi + +# Find archangel ISO in vm-images/ +ISO_PATH=$(find "$VM_IMAGES_DIR" -maxdepth 1 -name "archzfs-*.iso" -type f 2>/dev/null | sort -V | tail -1) +if [ -z "$ISO_PATH" ]; then + fatal "No archangel ISO found in $VM_IMAGES_DIR/" + info "Copy an archzfs-*.iso file to: $VM_IMAGES_DIR/" +fi +info "Using ISO: $(basename "$ISO_PATH")" -# Download Arch ISO if needed -section "Preparing Arch Linux ISO" +# ─── Prepare Disk ───────────────────────────────────────────────────── -if [ -f "$ISO_PATH" ]; then - info "Arch ISO exists: $ISO_PATH" +section "Preparing VM Disk" - # Check if ISO is older than 30 days - if [ $(find "$ISO_PATH" -mtime +30 | wc -l) -gt 0 ]; then - warn "ISO is older than 30 days" - info "Downloading latest version..." - rm -f "$ISO_PATH" - else - success "Using existing ISO" - fi +# Remove old disk and OVMF vars if they exist +if [ -f "$DISK_PATH" ]; then + warn "Removing existing disk: $DISK_PATH" + rm -f "$DISK_PATH" fi +rm -f "$OVMF_VARS" + +# Create fresh disk +step "Creating ${VM_DISK_SIZE}G qcow2 disk" +if qemu-img create -f qcow2 "$DISK_PATH" "${VM_DISK_SIZE}G" >> "$LOGFILE" 2>&1; then + success "Disk created: $DISK_PATH" +else + fatal "Failed to create disk image" +fi + +# ─── Phase 1: Install from ISO ─────────────────────────────────────── + +section "Phase 1: Archangel Installation" -if [ ! -f "$ISO_PATH" ]; then - step "Downloading latest Arch ISO" - info "URL: $ISO_URL" - info "This may take several minutes..." +start_timer "install" - if wget --progress=dot:giga -O "$ISO_PATH" "$ISO_URL" 2>&1 | tee -a "$LOGFILE"; then - success "ISO downloaded" - else - fatal "ISO download failed" - fi +# Boot from ISO +start_qemu "$DISK_PATH" "iso" "$ISO_PATH" "none" || fatal "Failed to start QEMU" + +# Wait for live ISO SSH +wait_for_ssh "$LIVE_ISO_PASSWORD" 120 || fatal "Live ISO SSH not available" + +# Copy config file into VM +copy_to_vm "$CONFIG_FILE" "/root/archsetup-test.conf" "$LIVE_ISO_PASSWORD" || \ + fatal "Failed to copy config to VM" + +# Run archangel installer (synchronous - typically 5-10 minutes) +step "Running archangel installer (unattended)..." +info "This will partition, install, and configure the base system" + +if vm_exec "$LIVE_ISO_PASSWORD" "archangel --config-file /root/archsetup-test.conf"; then + success "Archangel installation completed" +else + error "Archangel installation failed" + step "Capturing serial log for debugging" + info "Serial log: $SERIAL_LOG" + fatal "Base VM installation failed - check logs" fi -# Remove existing VM and disk -if vm_exists "$VM_NAME"; then - warn "VM $VM_NAME already exists - destroying it" - if vm_is_running "$VM_NAME"; then - virsh destroy "$VM_NAME" >> "$LOGFILE" 2>&1 - fi - virsh undefine "$VM_NAME" --nvram >> "$LOGFILE" 2>&1 || true +# Power off +stop_qemu +stop_timer "install" + +# ─── Phase 2: Verify Installation ──────────────────────────────────── + +section "Phase 2: Verifying Installation" + +start_timer "verify" + +# Boot from installed disk +start_qemu "$DISK_PATH" "disk" "" "none" || fatal "Failed to boot installed system" + +# Wait for SSH on installed system +wait_for_ssh "$ROOT_PASSWORD" 120 || fatal "Installed system SSH not available" + +# Basic verification +step "Verifying base system" + +KERNEL=$(vm_exec "$ROOT_PASSWORD" "uname -r" 2>/dev/null) +success "Kernel: $KERNEL" + +if vm_exec "$ROOT_PASSWORD" "systemctl is-active sshd" &>/dev/null; then + success "SSH service active" +else + warn "SSH service not active" fi -[ -f "$DISK_PATH" ] && rm -f "$DISK_PATH" - -# Create and start VM -section "Creating and Starting VM" - -info "Creating VM: $VM_NAME" -info " CPUs: $VM_CPUS | RAM: ${VM_RAM}MB | Disk: ${VM_DISK}GB" - -virt-install \ - --connect qemu:///system \ - --name "$VM_NAME" \ - --memory "$VM_RAM" \ - --vcpus "$VM_CPUS" \ - --disk path="$DISK_PATH",size="$VM_DISK",format=qcow2,bus=virtio \ - --cdrom "$ISO_PATH" \ - --os-variant archlinux \ - --network network=default,model=virtio \ - --graphics vnc,listen=127.0.0.1 \ - --console pty,target.type=serial \ - --boot uefi \ - --noreboot \ - --check path_in_use=off \ - --filesystem type=mount,mode=mapped,source="$PROJECT_ROOT/scripts",target=host-scripts \ - >> "$LOGFILE" 2>&1 & - -VIRT_INSTALL_PID=$! - -progress "Waiting for VM to boot from ISO" -sleep 30 - -# Check if VM started -if ! vm_is_running "$VM_NAME"; then - wait $VIRT_INSTALL_PID - EXIT_CODE=$? - fatal "VM failed to start (exit code: $EXIT_CODE)" +if vm_exec "$ROOT_PASSWORD" "systemctl is-active NetworkManager" &>/dev/null; then + success "NetworkManager active" +else + warn "NetworkManager not active" fi -success "VM started successfully" - -# Display manual installation instructions -section "Manual Installation Required" - -cat << 'EOF' - -[i] -[i] Base VM is running from Arch ISO -[i] -[i] NEXT STEPS - Complete installation manually: -[i] -[i] 1. Open virt-viewer (should already be open): -[i] virt-viewer --connect qemu:///system archsetup-base -[i] -[i] 2. Login as 'root' (no password) -[i] -[i] 3. Run: archinstall -[i] -[i] 4. Configure with these settings: -[i] - Hostname: archsetup-test -[i] - Root password: archsetup -[i] - Profile: minimal -[i] - Network: dhcpcd (or NetworkManager) -[i] - Additional packages: openssh git vim sudo iperf3 mtr traceroute bind net-tools sshfs -[i] - Enable: sshd, dhcpcd (or NetworkManager) -[i] -[i] 5. After archinstall completes: -[i] - Chroot into /mnt: arch-chroot /mnt -[i] - Edit /etc/ssh/sshd_config: -[i] sed -i 's/#PermitRootLogin.*/PermitRootLogin yes/' /etc/ssh/sshd_config -[i] sed -i 's/#PasswordAuthentication.*/PasswordAuthentication yes/' /etc/ssh/sshd_config -[i] - Set up shared folder mount (9p filesystem): -[i] mkdir -p /mnt/host-scripts -[i] echo 'host-scripts /mnt/host-scripts 9p trans=virtio,version=9p2000.L,rw 0 0' >> /etc/fstab -[i] - Exit chroot: exit -[i] - Poweroff: poweroff -[i] -[i] 6. After VM powers off, run: -[i] ./scripts/testing/finalize-base-vm.sh -[i] -[i] Log file: $LOGFILE -[i] - -EOF - -info "Waiting for VM to power off..." -info "(This script will exit when you manually power off the VM)" - -# Wait for virt-install to finish (VM powers off) -wait $VIRT_INSTALL_PID || true - -success "VM has powered off" +# Power off for snapshot +stop_qemu +stop_timer "verify" + +# ─── Phase 3: Create Snapshot ──────────────────────────────────────── + +section "Phase 3: Creating Clean-Install Snapshot" + +create_snapshot "$DISK_PATH" "$SNAPSHOT_NAME" || fatal "Failed to create snapshot" + +# ─── Done ───────────────────────────────────────────────────────────── + +section "Base VM Created Successfully" + +info "" +info " Disk: $DISK_PATH" +info " Snapshot: $SNAPSHOT_NAME" +info " Config: $(basename "$CONFIG_FILE")" +info " Log: $LOGFILE" +info "" +info "Next step: Run ./scripts/testing/run-test.sh" info "" -info "Next step: Run ./scripts/testing/finalize-base-vm.sh" -- cgit v1.2.3