#!/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 "═══════════════════════════════════════════════"