Add Proxmox test environment (VM creation + automated test suite)

Tests everything possible without the target hardware:

create-vm.sh (runs on Proxmox host):
- Creates Arch Linux VM (VMID 900, 8 cores, 16GB RAM, 100GB disk)
- UEFI boot with OVMF (for nested QEMU testing)
- Cloud-init auto-installs packages, clones repo, runs tests
- Nested virtualization enabled for QEMU-in-QEMU boot tests

run-in-vm.sh (runs inside the VM, 9 test suites):
1. Host environment validation
2. dpack build + unit tests + CLI smoke tests
3. Package definition validation (154 packages, dep resolution)
4. Script syntax checking (toolchain, init, installer, ISO)
5. Kernel config validation (critical options)
6. Package signing test (download zlib, compute SHA256)
7. Toolchain bootstrap (LFS Ch.5 cross-compiler build)
8. ISO generation
9. Nested QEMU boot test (UEFI boot, kernel + userspace check)

Modes: --quick (30min, suites 1-5), --no-build (1hr), full (2-6hr)
Generates report.json + report.txt for automated debugging.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-03-19 13:32:01 +01:00
parent c6a3f33746
commit c464e0eec9
3 changed files with 764 additions and 0 deletions

209
tests/proxmox/create-vm.sh Executable file
View File

@@ -0,0 +1,209 @@
#!/bin/bash
# ============================================================================
# DarkForge — Proxmox VM Creation Script
# ============================================================================
# Run this on the Proxmox host to create an Arch Linux test VM.
#
# Requirements:
# - Proxmox VE 8.x or 9.x
# - ~100GB free on a storage pool
# - Internet access
#
# Usage:
# bash create-vm.sh # use defaults
# bash create-vm.sh --vmid 200 # custom VM ID
# bash create-vm.sh --storage local-lvm # custom storage
# ============================================================================
set -euo pipefail
# --- Configuration (override via environment or flags) -----------------------
VMID="${VMID:-900}"
VM_NAME="${VM_NAME:-darkforge-test}"
STORAGE="${STORAGE:-local-lvm}"
DISK_SIZE="${DISK_SIZE:-100G}"
RAM="${RAM:-16384}" # 16GB
CORES="${CORES:-8}"
BRIDGE="${BRIDGE:-vmbr0}"
# Arch Linux cloud image
ARCH_IMG_URL="https://geo.mirror.pkgbuild.com/images/latest/Arch-Linux-x86_64-cloudimg.qcow2"
ARCH_IMG_FILE="/var/lib/vz/template/iso/arch-cloudimg.qcow2"
# Git repo to clone inside the VM
DARKFORGE_REPO="gitea@git.dannyhaslund.dk:danny8632/darkforge.git"
# Fallback if SSH key isn't available in the VM
DARKFORGE_REPO_HTTPS="https://git.dannyhaslund.dk/danny8632/darkforge.git"
# Parse args
for arg in "$@"; do
case "$arg" in
--vmid=*) VMID="${arg#*=}" ;;
--storage=*) STORAGE="${arg#*=}" ;;
--cores=*) CORES="${arg#*=}" ;;
--ram=*) RAM="${arg#*=}" ;;
--bridge=*) BRIDGE="${arg#*=}" ;;
esac
done
echo "═══════════════════════════════════════════════"
echo " DarkForge Test VM Creator"
echo " VMID: ${VMID} | Cores: ${CORES} | RAM: ${RAM}MB"
echo " Storage: ${STORAGE} | Disk: ${DISK_SIZE}"
echo "═══════════════════════════════════════════════"
echo ""
# --- Check if VM already exists ----------------------------------------------
if qm status "${VMID}" &>/dev/null; then
echo "VM ${VMID} already exists."
read -p "Destroy and recreate? [y/N] " confirm
if [[ "${confirm}" =~ ^[Yy]$ ]]; then
qm stop "${VMID}" 2>/dev/null || true
sleep 3
qm destroy "${VMID}" --purge
echo "Old VM destroyed."
else
echo "Aborted."
exit 0
fi
fi
# --- Download Arch cloud image if not cached ---------------------------------
if [ ! -f "${ARCH_IMG_FILE}" ]; then
echo ">>> Downloading Arch Linux cloud image..."
mkdir -p "$(dirname "${ARCH_IMG_FILE}")"
wget -q --show-progress -O "${ARCH_IMG_FILE}" "${ARCH_IMG_URL}"
echo ">>> Downloaded: ${ARCH_IMG_FILE}"
else
echo ">>> Using cached image: ${ARCH_IMG_FILE}"
fi
# --- Create the VM -----------------------------------------------------------
echo ">>> Creating VM ${VMID}..."
qm create "${VMID}" \
--name "${VM_NAME}" \
--ostype l26 \
--machine q35 \
--bios ovmf \
--cpu host \
--cores "${CORES}" \
--memory "${RAM}" \
--net0 "virtio,bridge=${BRIDGE}" \
--agent enabled=1 \
--serial0 socket \
--vga serial0
# Add EFI disk (required for OVMF BIOS)
qm set "${VMID}" --efidisk0 "${STORAGE}:1,efitype=4m,pre-enrolled-keys=0"
# Import the cloud image as the boot disk
echo ">>> Importing cloud image as boot disk..."
qm importdisk "${VMID}" "${ARCH_IMG_FILE}" "${STORAGE}" --format qcow2 2>/dev/null
qm set "${VMID}" --scsi0 "${STORAGE}:vm-${VMID}-disk-1,size=${DISK_SIZE}"
qm set "${VMID}" --boot order=scsi0
qm set "${VMID}" --scsihw virtio-scsi-single
# --- Configure cloud-init ----------------------------------------------------
echo ">>> Configuring cloud-init..."
qm set "${VMID}" --ide2 "${STORAGE}:cloudinit"
qm set "${VMID}" --ciuser "darkforge"
qm set "${VMID}" --cipassword "darkforge"
qm set "${VMID}" --ipconfig0 "ip=dhcp"
# Enable nested virtualization (for QEMU-in-QEMU boot tests)
qm set "${VMID}" --args "-cpu host,+vmx"
# Resize the disk to our desired size
echo ">>> Resizing disk to ${DISK_SIZE}..."
qm resize "${VMID}" scsi0 "${DISK_SIZE}" 2>/dev/null || true
# --- Generate cloud-init user-data snippet -----------------------------------
# This runs on first boot inside the VM
SNIPPET_DIR="/var/lib/vz/snippets"
mkdir -p "${SNIPPET_DIR}"
cat > "${SNIPPET_DIR}/darkforge-test-init.yaml" << 'CLOUDINIT'
#cloud-config
package_update: true
packages:
- base-devel
- git
- wget
- curl
- rust
- cargo
- qemu-full
- edk2-ovmf
- squashfs-tools
- xorriso
- dosfstools
- mtools
- python
- bc
- rsync
- openssh
runcmd:
# Grow the partition to fill the disk
- growpart /dev/sda 2 || true
- resize2fs /dev/sda2 || btrfs filesystem resize max / || true
# Clone the DarkForge project
- |
su - darkforge -c '
cd /home/darkforge
git clone --recurse-submodules https://git.dannyhaslund.dk/danny8632/darkforge.git 2>/dev/null || \
git clone --recurse-submodules https://github.com/danny8632/darkforge.git 2>/dev/null || \
echo "CLONE FAILED — manually clone the repo"
'
# Copy the test runner
- |
if [ -f /home/darkforge/darkforge/tests/proxmox/run-in-vm.sh ]; then
cp /home/darkforge/darkforge/tests/proxmox/run-in-vm.sh /home/darkforge/
chown darkforge:darkforge /home/darkforge/run-in-vm.sh
chmod +x /home/darkforge/run-in-vm.sh
fi
# Run the test suite automatically
- |
su - darkforge -c '
if [ -f /home/darkforge/run-in-vm.sh ]; then
bash /home/darkforge/run-in-vm.sh 2>&1 | tee /home/darkforge/test-output.log
fi
'
CLOUDINIT
qm set "${VMID}" --cicustom "user=local:snippets/darkforge-test-init.yaml"
# --- Start the VM ------------------------------------------------------------
echo ""
echo ">>> Starting VM ${VMID}..."
qm start "${VMID}"
echo ""
echo "═══════════════════════════════════════════════"
echo " VM ${VMID} created and starting."
echo ""
echo " The VM will:"
echo " 1. Boot Arch Linux"
echo " 2. Install required packages via cloud-init"
echo " 3. Clone the DarkForge repository"
echo " 4. Run the full test suite"
echo ""
echo " Monitor progress:"
echo " qm terminal ${VMID} (serial console)"
echo ""
echo " SSH access (after boot):"
echo " ssh darkforge@\$(qm guest cmd ${VMID} network-get-interfaces | grep -oP '\"ip-address\":\\s*\"\\K[0-9.]+')"
echo " Password: darkforge"
echo ""
echo " Or get IP:"
echo " qm guest cmd ${VMID} network-get-interfaces"
echo ""
echo " Collect report after tests finish:"
echo " VM_IP=\$(qm guest cmd ${VMID} network-get-interfaces | grep -oP '\"ip-address\":\\s*\"\\K[0-9.]+')"
echo " scp darkforge@\$VM_IP:/home/darkforge/darkforge/tests/report.* ./"
echo "═══════════════════════════════════════════════"