Files
kubesolo-os/test/qemu/run-vm.sh
Adolfo Delorenzo 958524e6d8
Some checks failed
CI / Go Tests (push) Has been cancelled
CI / Build Go Binaries (amd64, linux, linux-amd64) (push) Has been cancelled
CI / Build Go Binaries (arm64, linux, linux-arm64) (push) Has been cancelled
CI / Shellcheck (push) Has been cancelled
fix: Go version, test scripts, and shellcheck warnings from validation
- Dockerfile.builder: Go 1.24.0 → 1.25.5 (go.mod requires it)
- test-boot.sh: use direct kernel boot via ISO extraction instead of
  broken -cdrom + -append; fix boot marker to "KubeSolo is running"
  (Stage 90 blocks on wait, never emits "complete")
- test-security-hardening.sh: same direct kernel boot and marker fixes
- run-vm.sh, dev-vm.sh, dev-vm-arm64.sh: quote QEMU -net args to
  silence shellcheck SC2054
- fetch-components.sh, fetch-rpi-firmware.sh, dev-vm-arm64.sh: fix
  trap quoting (SC2064)

Validated: full Docker build, 94 Go tests pass, QEMU boot (73s),
security hardening test (6/6 pass, 1 AppArmor skip pending kernel
rebuild).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-12 13:30:55 -06:00

161 lines
4.7 KiB
Bash
Executable File

#!/bin/bash
# run-vm.sh — Launch QEMU VM for testing (reusable by other test scripts)
# Usage: ./test/qemu/run-vm.sh <iso-or-img> [options]
#
# Options:
# --arch <arch> Architecture: x86_64 (default) or arm64
# --data-disk <path> Use existing data disk (default: create temp)
# --data-size <MB> Size of temp data disk (default: 1024)
# --memory <MB> VM memory (default: 2048)
# --cpus <n> VM CPUs (default: 2)
# --serial-log <path> Write serial output to file
# --api-port <port> Forward K8s API to host port (default: 6443)
# --ssh-port <port> Forward SSH to host port (default: 2222)
# --background Run in background, print PID
# --append <args> Extra kernel append args
# --kernel <path> Kernel image (required for arm64)
# --initrd <path> Initramfs image (required for arm64)
#
# Outputs (on stdout):
# QEMU_PID=<pid>
# DATA_DISK=<path>
# SERIAL_LOG=<path>
set -euo pipefail
IMAGE="${1:?Usage: $0 <iso-or-img> [options]}"
shift
# Defaults
ARCH="x86_64"
DATA_DISK=""
DATA_SIZE_MB=1024
MEMORY=2048
CPUS=2
SERIAL_LOG=""
API_PORT=6443
SSH_PORT=2222
BACKGROUND=0
EXTRA_APPEND=""
CREATED_DATA_DISK=""
VM_KERNEL=""
VM_INITRD=""
# Parse options
while [ $# -gt 0 ]; do
case "$1" in
--arch) ARCH="$2"; shift 2 ;;
--data-disk) DATA_DISK="$2"; shift 2 ;;
--data-size) DATA_SIZE_MB="$2"; shift 2 ;;
--memory) MEMORY="$2"; shift 2 ;;
--cpus) CPUS="$2"; shift 2 ;;
--serial-log) SERIAL_LOG="$2"; shift 2 ;;
--api-port) API_PORT="$2"; shift 2 ;;
--ssh-port) SSH_PORT="$2"; shift 2 ;;
--background) BACKGROUND=1; shift ;;
--append) EXTRA_APPEND="$2"; shift 2 ;;
--kernel) VM_KERNEL="$2"; shift 2 ;;
--initrd) VM_INITRD="$2"; shift 2 ;;
*) echo "Unknown option: $1" >&2; exit 1 ;;
esac
done
# Create data disk if not provided
if [ -z "$DATA_DISK" ]; then
DATA_DISK=$(mktemp /tmp/kubesolo-data-XXXXXX.img)
CREATED_DATA_DISK="$DATA_DISK"
dd if=/dev/zero of="$DATA_DISK" bs=1M count="$DATA_SIZE_MB" 2>/dev/null
mkfs.ext4 -q -L KSOLODATA "$DATA_DISK" 2>/dev/null
fi
# Create serial log if not provided
if [ -z "$SERIAL_LOG" ]; then
SERIAL_LOG=$(mktemp /tmp/kubesolo-serial-XXXXXX.log)
fi
# Build QEMU command based on architecture
if [ "$ARCH" = "arm64" ] || [ "$ARCH" = "aarch64" ]; then
# ARM64: qemu-system-aarch64 with -machine virt
# No KVM for cross-arch emulation (TCG only)
CONSOLE="ttyAMA0"
# ARM64 requires explicit kernel + initrd (no -cdrom support with -machine virt)
if [ -z "$VM_KERNEL" ] || [ -z "$VM_INITRD" ]; then
echo "ERROR: ARM64 mode requires --kernel and --initrd options" >&2
exit 1
fi
QEMU_CMD=(
qemu-system-aarch64
-machine virt
-cpu cortex-a72
-m "$MEMORY"
-smp "$CPUS"
-nographic
-net "nic,model=virtio"
-net "user,hostfwd=tcp::${API_PORT}-:6443,hostfwd=tcp::${SSH_PORT}-:22"
-drive "file=$DATA_DISK,format=raw,if=virtio"
-serial "file:$SERIAL_LOG"
-kernel "$VM_KERNEL"
-initrd "$VM_INITRD"
-append "console=${CONSOLE} kubesolo.data=/dev/vda kubesolo.debug $EXTRA_APPEND"
)
else
# x86_64: standard QEMU
CONSOLE="ttyS0,115200n8"
# Detect KVM availability
KVM_FLAG=""
if [ -w /dev/kvm ] 2>/dev/null; then
KVM_FLAG="-enable-kvm"
fi
QEMU_CMD=(
qemu-system-x86_64
-m "$MEMORY"
-smp "$CPUS"
-nographic
-net "nic,model=virtio"
-net "user,hostfwd=tcp::${API_PORT}-:6443,hostfwd=tcp::${SSH_PORT}-:22"
-drive "file=$DATA_DISK,format=raw,if=virtio"
-serial "file:$SERIAL_LOG"
)
[ -n "$KVM_FLAG" ] && QEMU_CMD+=("$KVM_FLAG")
case "$IMAGE" in
*.iso)
QEMU_CMD+=(
-cdrom "$IMAGE"
-boot d
-append "console=${CONSOLE} kubesolo.data=/dev/vda kubesolo.debug $EXTRA_APPEND"
)
;;
*.img)
QEMU_CMD+=(
-drive "file=$IMAGE,format=raw,if=virtio"
)
;;
*)
echo "ERROR: Unrecognized image format: $IMAGE" >&2
exit 1
;;
esac
fi
# Launch
"${QEMU_CMD[@]}" &
QEMU_PID=$!
# Output metadata
echo "QEMU_PID=$QEMU_PID"
echo "DATA_DISK=$DATA_DISK"
echo "SERIAL_LOG=$SERIAL_LOG"
echo "CREATED_DATA_DISK=$CREATED_DATA_DISK"
if [ "$BACKGROUND" = "0" ]; then
# Foreground mode — wait for QEMU to exit
wait "$QEMU_PID" || true
# Clean up temp data disk
[ -n "$CREATED_DATA_DISK" ] && rm -f "$CREATED_DATA_DISK"
fi