#!/bin/bash # test-boot.sh — Automated boot test: verify KubeSolo OS boots in QEMU # Usage: ./test/qemu/test-boot.sh # Exit 0 = PASS, Exit 1 = FAIL set -euo pipefail ISO="${1:?Usage: $0 }" TIMEOUT_BOOT=${TIMEOUT_BOOT:-120} # seconds to wait for boot success marker SERIAL_LOG=$(mktemp /tmp/kubesolo-boot-test-XXXXXX.log) # Temp data disk DATA_DISK=$(mktemp /tmp/kubesolo-data-XXXXXX.img) dd if=/dev/zero of="$DATA_DISK" bs=1M count=512 2>/dev/null mkfs.ext4 -q -L KSOLODATA "$DATA_DISK" 2>/dev/null EXTRACT_DIR="" QEMU_PID="" cleanup() { [ -n "$QEMU_PID" ] && kill "$QEMU_PID" 2>/dev/null || true rm -f "$DATA_DISK" "$SERIAL_LOG" [ -n "$EXTRACT_DIR" ] && rm -rf "$EXTRACT_DIR" } trap cleanup EXIT echo "==> Boot test: $ISO" echo " Timeout: ${TIMEOUT_BOOT}s" echo " Serial log: $SERIAL_LOG" # Extract kernel + initramfs from ISO (direct kernel boot required for -append) SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" PROJECT_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)" ROOTFS_DIR="${ROOTFS_DIR:-$PROJECT_ROOT/build/rootfs-work}" VMLINUZ="" INITRAMFS="" if [ -f "$ROOTFS_DIR/vmlinuz" ] && [ -f "$ROOTFS_DIR/kubesolo-os.gz" ]; then VMLINUZ="$ROOTFS_DIR/vmlinuz" INITRAMFS="$ROOTFS_DIR/kubesolo-os.gz" echo " Using kernel/initramfs from build directory" else EXTRACT_DIR="$(mktemp -d /tmp/kubesolo-extract-XXXXXX)" EXTRACTED=0 echo " Extracting kernel/initramfs from ISO..." # Method 1: bsdtar if [ $EXTRACTED -eq 0 ] && command -v bsdtar >/dev/null 2>&1; then if bsdtar -xf "$ISO" -C "$EXTRACT_DIR" boot/vmlinuz boot/kubesolo-os.gz 2>/dev/null; then echo " Extracted via bsdtar" EXTRACTED=1 fi fi # Method 2: isoinfo if [ $EXTRACTED -eq 0 ] && command -v isoinfo >/dev/null 2>&1; then mkdir -p "$EXTRACT_DIR/boot" isoinfo -i "$ISO" -x "/BOOT/VMLINUZ;1" > "$EXTRACT_DIR/boot/vmlinuz" 2>/dev/null || true isoinfo -i "$ISO" -x "/BOOT/KUBESOLO-OS.GZ;1" > "$EXTRACT_DIR/boot/kubesolo-os.gz" 2>/dev/null || true if [ -s "$EXTRACT_DIR/boot/vmlinuz" ] && [ -s "$EXTRACT_DIR/boot/kubesolo-os.gz" ]; then echo " Extracted via isoinfo" EXTRACTED=1 else rm -f "$EXTRACT_DIR/boot/vmlinuz" "$EXTRACT_DIR/boot/kubesolo-os.gz" fi fi # Method 3: loop mount (Linux only) if [ $EXTRACTED -eq 0 ] && [ "$(uname)" = "Linux" ]; then ISO_MOUNT="$EXTRACT_DIR/mnt" mkdir -p "$ISO_MOUNT" if mount -o loop,ro "$ISO" "$ISO_MOUNT" 2>/dev/null; then mkdir -p "$EXTRACT_DIR/boot" cp "$ISO_MOUNT/boot/vmlinuz" "$EXTRACT_DIR/boot/" 2>/dev/null || true cp "$ISO_MOUNT/boot/kubesolo-os.gz" "$EXTRACT_DIR/boot/" 2>/dev/null || true umount "$ISO_MOUNT" 2>/dev/null || true if [ -f "$EXTRACT_DIR/boot/vmlinuz" ] && [ -f "$EXTRACT_DIR/boot/kubesolo-os.gz" ]; then echo " Extracted via loop mount" EXTRACTED=1 fi fi fi if [ $EXTRACTED -eq 0 ]; then echo "ERROR: Failed to extract kernel/initramfs from ISO." exit 1 fi VMLINUZ="$EXTRACT_DIR/boot/vmlinuz" INITRAMFS="$EXTRACT_DIR/boot/kubesolo-os.gz" fi # Detect KVM KVM_FLAG="" if [ -w /dev/kvm ] 2>/dev/null; then KVM_FLAG="-enable-kvm" echo " KVM acceleration: enabled" fi # Launch QEMU in background with direct kernel boot qemu-system-x86_64 \ -m 2048 -smp 2 \ -nographic \ $KVM_FLAG \ -kernel "$VMLINUZ" \ -initrd "$INITRAMFS" \ -drive "file=$DATA_DISK,format=raw,if=virtio" \ -net "nic,model=virtio" \ -net user \ -serial "file:$SERIAL_LOG" \ -append "console=ttyS0,115200n8 kubesolo.data=/dev/vda kubesolo.debug" \ & QEMU_PID=$! # Wait for boot success marker in serial log echo " Waiting for boot..." ELAPSED=0 while [ "$ELAPSED" -lt "$TIMEOUT_BOOT" ]; do if grep -q "\[kubesolo-init\] \[OK\] KubeSolo is running" "$SERIAL_LOG" 2>/dev/null; then echo "" echo "==> PASS: KubeSolo OS booted successfully in ${ELAPSED}s" exit 0 fi if ! kill -0 "$QEMU_PID" 2>/dev/null; then echo "" echo "==> FAIL: QEMU exited prematurely" echo " Last 20 lines of serial log:" tail -20 "$SERIAL_LOG" 2>/dev/null exit 1 fi sleep 1 ELAPSED=$((ELAPSED + 1)) printf "\r Elapsed: %ds / %ds" "$ELAPSED" "$TIMEOUT_BOOT" done echo "" echo "==> FAIL: Boot did not complete within ${TIMEOUT_BOOT}s" echo " Last 30 lines of serial log:" tail -30 "$SERIAL_LOG" 2>/dev/null exit 1