summaryrefslogtreecommitdiff
path: root/scripts/testing/create-base-vm.sh
diff options
context:
space:
mode:
authorCraig Jennings <c@cjennings.net>2026-01-27 17:22:55 -0600
committerCraig Jennings <c@cjennings.net>2026-01-27 17:22:55 -0600
commit70bb2d5ab1bf6787bc613e33f5398be2eca1f5fd (patch)
tree0d8cd5057dd32f5f312a7f3534d590b99c2f0f91 /scripts/testing/create-base-vm.sh
parent0c6175bfc98f2c5ff2debc665fd8bf91f9171f4e (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/create-base-vm.sh')
-rwxr-xr-xscripts/testing/create-base-vm.sh249
1 files changed, 116 insertions, 133 deletions
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 <craigmartinjennings@gmail.com>
# 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"