diff options
Diffstat (limited to 'scripts')
| -rwxr-xr-x | scripts/testing/cleanup-tests.sh | 24 | ||||
| -rwxr-xr-x | scripts/testing/create-base-vm.sh | 19 | ||||
| -rwxr-xr-x | scripts/testing/debug-vm.sh | 18 | ||||
| -rwxr-xr-x | scripts/testing/lib/logging.sh | 9 | ||||
| -rw-r--r-- | scripts/testing/lib/network-diagnostics.sh | 2 | ||||
| -rwxr-xr-x | scripts/testing/lib/vm-utils.sh | 24 | ||||
| -rwxr-xr-x | scripts/testing/run-test-baremetal.sh | 38 | ||||
| -rwxr-xr-x | scripts/testing/run-test.sh | 34 | ||||
| -rwxr-xr-x | scripts/testing/setup-testing-env.sh | 26 |
9 files changed, 146 insertions, 48 deletions
diff --git a/scripts/testing/cleanup-tests.sh b/scripts/testing/cleanup-tests.sh index fd2f8de..5c0153b 100755 --- a/scripts/testing/cleanup-tests.sh +++ b/scripts/testing/cleanup-tests.sh @@ -20,6 +20,12 @@ FORCE=false while [[ $# -gt 0 ]]; do case $1 in --keep) + case "${2:-}" in + ''|*[!0-9]*) + echo "Error: --keep requires a non-negative integer (got: '${2:-}')" >&2 + exit 1 + ;; + esac KEEP_LAST="$2" shift 2 ;; @@ -68,13 +74,17 @@ QEMU_PIDS=$(pgrep -f "qemu-system.*archsetup-test" 2>/dev/null || true) if [ -n "$QEMU_PIDS" ]; then info "Found orphaned QEMU processes: $QEMU_PIDS" if $FORCE; then - echo "$QEMU_PIDS" | xargs kill -9 2>/dev/null || true + echo "$QEMU_PIDS" | xargs -r kill 2>/dev/null || true + sleep 2 + echo "$QEMU_PIDS" | xargs -r kill -9 2>/dev/null || true success "Orphaned processes killed" else read -p "Kill orphaned QEMU processes? [y/N] " -n 1 -r echo "" if [[ $REPLY =~ ^[Yy]$ ]]; then - echo "$QEMU_PIDS" | xargs kill -9 2>/dev/null || true + echo "$QEMU_PIDS" | xargs -r kill 2>/dev/null || true + sleep 2 + echo "$QEMU_PIDS" | xargs -r kill -9 2>/dev/null || true success "Orphaned processes killed" fi fi @@ -88,7 +98,7 @@ section "Cleaning Up Disk Images" step "Finding temporary disk images" if [ -d "$PROJECT_ROOT/vm-images" ]; then - TEMP_DISKS=$(find "$PROJECT_ROOT/vm-images" -name "debug-overlay-*.qcow2" -o -name "archsetup-test-*.qcow2" 2>/dev/null || true) + TEMP_DISKS=$(find "$PROJECT_ROOT/vm-images" \( -name "debug-overlay-*.qcow2" -o -name "archsetup-test-*.qcow2" \) 2>/dev/null || true) if [ -z "$TEMP_DISKS" ]; then info "No temporary disk images found" @@ -98,7 +108,7 @@ if [ -d "$PROJECT_ROOT/vm-images" ]; then info "Found $DISK_COUNT temporary disk image(s) totaling $DISK_SIZE" if $FORCE; then - echo "$TEMP_DISKS" | while read disk; do + echo "$TEMP_DISKS" | while IFS= read -r disk; do rm -f "$disk" done success "Temporary disk images deleted" @@ -109,7 +119,7 @@ if [ -d "$PROJECT_ROOT/vm-images" ]; then read -p "Delete these disk images? [y/N] " -n 1 -r echo "" if [[ $REPLY =~ ^[Yy]$ ]]; then - echo "$TEMP_DISKS" | while read disk; do + echo "$TEMP_DISKS" | while IFS= read -r disk; do rm -f "$disk" done success "Temporary disk images deleted" @@ -143,7 +153,7 @@ else info "Keeping last $KEEP_LAST, deleting $DELETE_COUNT old result(s)" if $FORCE; then - echo "$TO_DELETE" | while read dir; do + echo "$TO_DELETE" | while IFS= read -r dir; do rm -rf "$dir" done success "Old test results deleted" @@ -155,7 +165,7 @@ else read -p "Delete these test results? [y/N] " -n 1 -r echo "" if [[ $REPLY =~ ^[Yy]$ ]]; then - echo "$TO_DELETE" | while read dir; do + echo "$TO_DELETE" | while IFS= read -r dir; do rm -rf "$dir" done success "Old test results deleted" diff --git a/scripts/testing/create-base-vm.sh b/scripts/testing/create-base-vm.sh index 11c40a7..4ecf4d6 100755 --- a/scripts/testing/create-base-vm.sh +++ b/scripts/testing/create-base-vm.sh @@ -29,6 +29,14 @@ 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" +# Kill QEMU if we exit before reaching the controlled stop_qemu calls below. +cleanup_create_base() { + if declare -f vm_is_running >/dev/null 2>&1 && vm_is_running; then + kill_qemu 2>/dev/null || true + fi +} +trap cleanup_create_base EXIT + section "Creating Base VM for ArchSetup Testing" # ─── Prerequisites ──────────────────────────────────────────────────── @@ -45,8 +53,8 @@ fi # Find archangel ISO in vm-images/ ISO_PATH=$(find "$VM_IMAGES_DIR" -maxdepth 1 -name "archangel-*.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 archangel-*.iso file to: $VM_IMAGES_DIR/" + fatal "No archangel ISO found in $VM_IMAGES_DIR/" fi info "Using ISO: $(basename "$ISO_PATH")" @@ -150,10 +158,11 @@ create_snapshot "$DISK_PATH" "$SNAPSHOT_NAME" || fatal "Failed to create snapsho section "Base VM Created Successfully" info "" -info " Disk: $DISK_PATH" -info " Snapshot: $SNAPSHOT_NAME" -info " Config: $(basename "$CONFIG_FILE")" -info " Log: $LOGFILE" +info " Disk: $DISK_PATH" +info " Snapshot: $SNAPSHOT_NAME" +info " Config: $(basename "$CONFIG_FILE")" +info " Log: $LOGFILE" +info " Serial log: $SERIAL_LOG" info "" info "Next step: Run ./scripts/testing/run-test.sh" info "" diff --git a/scripts/testing/debug-vm.sh b/scripts/testing/debug-vm.sh index 5b2b197..32f377c 100755 --- a/scripts/testing/debug-vm.sh +++ b/scripts/testing/debug-vm.sh @@ -25,7 +25,13 @@ if [ $# -eq 0 ]; then elif [ "$1" = "--base" ]; then USE_BASE=true elif [ -f "$1" ]; then - VM_DISK="$1" + case "$1" in + *.qcow2) VM_DISK="$1" ;; + *) + echo "Error: disk image must be qcow2 (got: $1)" >&2 + exit 1 + ;; + esac else echo "Usage: $0 [disk-image.qcow2 | --base]" echo "" @@ -48,6 +54,16 @@ LOGFILE="/tmp/debug-vm-$TIMESTAMP.log" init_logging "$LOGFILE" init_vm_paths "$VM_IMAGES_DIR" +cleanup_debug() { + if declare -f vm_is_running >/dev/null 2>&1 && vm_is_running; then + kill_qemu 2>/dev/null || true + fi + if [ -n "$OVERLAY_DISK" ] && [ -f "$OVERLAY_DISK" ]; then + rm -f "$OVERLAY_DISK" + fi +} +trap cleanup_debug EXIT + section "Launching Debug VM" # Determine which disk to use diff --git a/scripts/testing/lib/logging.sh b/scripts/testing/lib/logging.sh index eda9eb1..ed20707 100755 --- a/scripts/testing/lib/logging.sh +++ b/scripts/testing/lib/logging.sh @@ -135,8 +135,13 @@ start_timer() { stop_timer() { local name="${1:-default}" - local start=${TIMERS[$name]} - local end=$(date +%s) + local start="${TIMERS[$name]:-}" + if [ -z "$start" ]; then + log "TIMER STOP: $name (never started, skipping)" + return 0 + fi + local end + end=$(date +%s) local duration=$((end - start)) local mins=$((duration / 60)) local secs=$((duration % 60)) diff --git a/scripts/testing/lib/network-diagnostics.sh b/scripts/testing/lib/network-diagnostics.sh index d73ffe5..674aeba 100644 --- a/scripts/testing/lib/network-diagnostics.sh +++ b/scripts/testing/lib/network-diagnostics.sh @@ -53,7 +53,7 @@ run_network_diagnostics() { # Show network info info "Network configuration:" - $ssh_base "ip addr show | grep 'inet ' | grep -v '127.0.0.1'" 2>/dev/null | while read line; do + $ssh_base "ip addr show | grep 'inet ' | grep -v '127.0.0.1'" 2>/dev/null | while IFS= read -r line; do info " $line" done diff --git a/scripts/testing/lib/vm-utils.sh b/scripts/testing/lib/vm-utils.sh index 47bd391..a8736a3 100755 --- a/scripts/testing/lib/vm-utils.sh +++ b/scripts/testing/lib/vm-utils.sh @@ -72,6 +72,11 @@ check_ovmf() { info "Install with: sudo pacman -S edk2-ovmf" return 1 fi + if [ ! -f "$OVMF_VARS_TEMPLATE" ]; then + error "OVMF vars template not found: $OVMF_VARS_TEMPLATE" + info "Install with: sudo pacman -S edk2-ovmf" + return 1 + fi return 0 } @@ -132,6 +137,15 @@ start_qemu() { local iso_path="${3:-}" local display="${4:-none}" + if [ -z "$disk" ] || [ ! -f "$disk" ]; then + error "Disk image not found: ${disk:-<unset>}" + return 1 + fi + if [ "$mode" = "iso" ] && { [ -z "$iso_path" ] || [ ! -f "$iso_path" ]; }; then + error "ISO not found: ${iso_path:-<unset>}" + return 1 + fi + # Stop any existing instance stop_qemu 2>/dev/null || true @@ -223,7 +237,7 @@ stop_qemu() { # Wait for graceful shutdown local elapsed=0 - while [ $elapsed -lt $timeout ]; do + while [ "$elapsed" -lt "$timeout" ]; do if ! vm_is_running; then success "VM stopped gracefully" _cleanup_qemu_files @@ -319,7 +333,11 @@ list_snapshots() { snapshot_exists() { local disk="${1:-$DISK_PATH}" local snapshot_name="${2:-clean-install}" - qemu-img snapshot -l "$disk" 2>/dev/null | grep -q "$snapshot_name" + # Match on the TAG column (field 2) so a name appearing inside a timestamp + # or filename elsewhere in the output can't false-positive the check. + qemu-img snapshot -l "$disk" 2>/dev/null \ + | awk 'NR > 2 { print $2 }' \ + | grep -Fxq "$snapshot_name" } # ─── SSH Operations ─────────────────────────────────────────────────── @@ -331,7 +349,7 @@ wait_for_ssh() { local elapsed=0 progress "Waiting for SSH on localhost:$SSH_PORT..." - while [ $elapsed -lt $timeout ]; do + while [ "$elapsed" -lt "$timeout" ]; do if sshpass -p "$password" ssh $SSH_OPTS -p "$SSH_PORT" root@localhost true 2>/dev/null; then success "SSH is available" return 0 diff --git a/scripts/testing/run-test-baremetal.sh b/scripts/testing/run-test-baremetal.sh index c108e6f..3beaefc 100755 --- a/scripts/testing/run-test-baremetal.sh +++ b/scripts/testing/run-test-baremetal.sh @@ -47,11 +47,11 @@ VALIDATE_ONLY=false while [[ $# -gt 0 ]]; do case $1 in --host) - TARGET_HOST="$2" + TARGET_HOST="${2:?--host requires a value}" shift 2 ;; --password) - ROOT_PASSWORD="$2" + ROOT_PASSWORD="${2:?--password requires a value}" shift 2 ;; --rollback-first) @@ -86,6 +86,12 @@ fi TIMESTAMP=$(date +'%Y%m%d-%H%M%S') TEST_RESULTS_DIR="$PROJECT_ROOT/test-results/baremetal-$TIMESTAMP" ARCHZFS_INBOX="$HOME/code/archzfs/inbox" +BUNDLE_FILE="" + +cleanup_baremetal() { + [ -n "$BUNDLE_FILE" ] && [ -f "$BUNDLE_FILE" ] && rm -f "$BUNDLE_FILE" +} +trap cleanup_baremetal EXIT # Override VM_IP for validation.sh ssh_cmd function VM_IP="$TARGET_HOST" @@ -121,12 +127,13 @@ if $ROLLBACK_FIRST; then DATASETS=$(ssh_cmd "zfs list -H -o name -t snapshot | grep '@genesis$' | sed 's/@genesis$//'") step "Rolling back all datasets to genesis" - for ds in $DATASETS; do + while IFS= read -r ds; do + [ -z "$ds" ] && continue info "Rolling back $ds@genesis" - if ! ssh_cmd "zfs rollback -r $ds@genesis" &>> "$LOGFILE"; then + if ! ssh_cmd "zfs rollback -r \"$ds@genesis\"" &>> "$LOGFILE"; then warn "Failed to rollback $ds@genesis" fi - done + done <<< "$DATASETS" success "Rollback complete" # Need to reconnect after rollback @@ -246,11 +253,11 @@ fi # Generate reports generate_issue_report "$TEST_RESULTS_DIR" "$ARCHZFS_INBOX" -# Set validation result -if [ $VALIDATION_FAILED -eq 0 ]; then - VALIDATION_PASSED=true +# Set validation result (TEST_PASSED is the boolean; VALIDATION_PASSED stays the counter) +if [ "$VALIDATION_FAILED" -eq 0 ]; then + TEST_PASSED=true else - VALIDATION_PASSED=false + TEST_PASSED=false fi # Generate test report @@ -269,10 +276,10 @@ Test Method: Bare Metal ZFS 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_COUNT + Passed: $VALIDATION_PASSED Failed: $VALIDATION_FAILED Warnings: $VALIDATION_WARNINGS @@ -290,17 +297,18 @@ if $ROLLBACK_AFTER; then section "Rolling Back to Genesis (cleanup)" DATASETS=$(ssh_cmd "zfs list -H -o name -t snapshot | grep '@genesis$' | sed 's/@genesis$//'") - for ds in $DATASETS; do + while IFS= read -r ds; do + [ -z "$ds" ] && continue info "Rolling back $ds@genesis" - ssh_cmd "zfs rollback -r $ds@genesis" &>> "$LOGFILE" || true - done + ssh_cmd "zfs rollback -r \"$ds@genesis\"" &>> "$LOGFILE" || true + done <<< "$DATASETS" success "Rollback complete" fi # Final summary section "Test Complete" -if [ $ARCHSETUP_EXIT_CODE -eq 0 ] && $VALIDATION_PASSED; then +if [ "$ARCHSETUP_EXIT_CODE" -eq 0 ] && $TEST_PASSED; then success "TEST PASSED" exit 0 else diff --git a/scripts/testing/run-test.sh b/scripts/testing/run-test.sh index c0eaf50..18f4fdf 100755 --- a/scripts/testing/run-test.sh +++ b/scripts/testing/run-test.sh @@ -36,11 +36,11 @@ while [[ $# -gt 0 ]]; do shift ;; --script) - ARCHSETUP_SCRIPT="$2" + ARCHSETUP_SCRIPT="${2:?--script requires a value}" shift 2 ;; --snapshot) - SNAPSHOT_NAME="$2" + SNAPSHOT_NAME="${2:?--snapshot requires a value}" shift 2 ;; *) @@ -53,6 +53,28 @@ while [[ $# -gt 0 ]]; do esac done +# Failure-path cleanup. Normal completion (further down) sets CLEANUP_DONE=1 +# so the trap becomes a no-op. If we abort partway through, the trap is the +# safety net that stops a leaked QEMU and reverts the base disk. +CLEANUP_DONE=0 +BUNDLE_FILE="" +cleanup_run_test() { + [ "$CLEANUP_DONE" = "1" ] && return 0 + CLEANUP_DONE=1 + [ -n "$BUNDLE_FILE" ] && [ -f "$BUNDLE_FILE" ] && rm -f "$BUNDLE_FILE" + if [ "$KEEP_VM" = "true" ]; then + return 0 + fi + if declare -f stop_qemu >/dev/null 2>&1; then + stop_qemu 2>/dev/null || true + fi + if [ -n "${DISK_PATH:-}" ] && [ -n "${SNAPSHOT_NAME:-}" ] \ + && declare -f restore_snapshot >/dev/null 2>&1; then + restore_snapshot "$DISK_PATH" "$SNAPSHOT_NAME" 2>/dev/null || true + fi +} +trap cleanup_run_test EXIT + # Configuration TIMESTAMP=$(date +'%Y%m%d-%H%M%S') VM_IMAGES_DIR="$PROJECT_ROOT/vm-images" @@ -79,8 +101,8 @@ fi # 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" + fatal "Base disk not found: $DISK_PATH" fi # Check if snapshot exists @@ -88,11 +110,11 @@ section "Preparing Test Environment" step "Checking for snapshot: $SNAPSHOT_NAME" if ! snapshot_exists "$DISK_PATH" "$SNAPSHOT_NAME"; then - fatal "Snapshot '$SNAPSHOT_NAME' not found on $DISK_PATH" info "Available snapshots:" list_snapshots "$DISK_PATH" info "" info "Create base VM with: ./scripts/testing/create-base-vm.sh" + fatal "Snapshot '$SNAPSHOT_NAME' not found on $DISK_PATH" fi success "Snapshot $SNAPSHOT_NAME exists" @@ -127,7 +149,6 @@ section "Simulating Git Clone" step "Creating shallow git clone on VM" info "This simulates: git clone --depth 1 <repo> /home/cjennings/code/archsetup" -# Create a temporary git bundle from current repo BUNDLE_FILE=$(mktemp) git -C "$PROJECT_ROOT" bundle create "$BUNDLE_FILE" HEAD >> "$LOGFILE" 2>&1 @@ -276,7 +297,7 @@ analyze_log_diff "$TEST_RESULTS_DIR" generate_issue_report "$TEST_RESULTS_DIR" "$ARCHZFS_INBOX" # Set validation result based on failure count -if [ $VALIDATION_FAILED -eq 0 ]; then +if [ "$VALIDATION_FAILED" -eq 0 ]; then TEST_PASSED=true else TEST_PASSED=false @@ -350,6 +371,7 @@ else warn "Failed to revert snapshot - VM may be in modified state" fi fi +CLEANUP_DONE=1 # Final summary section "Test Complete" diff --git a/scripts/testing/setup-testing-env.sh b/scripts/testing/setup-testing-env.sh index f0e63aa..fb0628b 100755 --- a/scripts/testing/setup-testing-env.sh +++ b/scripts/testing/setup-testing-env.sh @@ -45,25 +45,35 @@ PACKAGES=( socat ) +to_install=() for pkg in "${PACKAGES[@]}"; do if pacman -Qi "$pkg" &>/dev/null; then info "$pkg is already installed" else - step "Installing $pkg" - if sudo pacman -S --noconfirm "$pkg" >> "$LOGFILE" 2>&1; then - success "$pkg installed" - else - error "Failed to install $pkg" - fatal "Package installation failed" - fi + to_install+=("$pkg") fi done +if [ "${#to_install[@]}" -gt 0 ]; then + step "Installing in one transaction: ${to_install[*]}" + if sudo pacman -S --needed --noconfirm "${to_install[@]}" >> "$LOGFILE" 2>&1; then + success "All required packages installed" + else + fatal "Package installation failed" + fi +fi + # Verify KVM support section "Verifying KVM Support" if [ -e /dev/kvm ]; then - success "KVM is available" + if [ -r /dev/kvm ] && [ -w /dev/kvm ]; then + success "KVM is available and accessible" + else + warn "KVM exists but is not readable/writable by user $(id -un)" + info "Add the user to the kvm group: sudo gpasswd -a $(id -un) kvm" + info "Then log out and back in so the new group takes effect" + fi else error "KVM is not available" info "Check if virtualization is enabled in BIOS" |
