Arch Linux cloud images disable SSH password authentication by default. Added cloud-init directives to: - Enable ssh_pwauth - Set the darkforge user password via chpasswd (plaintext, non-expiring) - Write /etc/ssh/sshd_config.d/99-darkforge.conf enabling PasswordAuthentication - Restart sshd after config is written Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
235 lines
7.6 KiB
Bash
Executable File
235 lines
7.6 KiB
Bash
Executable File
#!/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
|
|
|
|
# Enable SSH password authentication (cloud images disable it by default)
|
|
ssh_pwauth: true
|
|
chpasswd:
|
|
expire: false
|
|
users:
|
|
- name: darkforge
|
|
password: darkforge
|
|
type: text
|
|
|
|
# Ensure sshd allows password auth
|
|
write_files:
|
|
- path: /etc/ssh/sshd_config.d/99-darkforge.conf
|
|
content: |
|
|
PasswordAuthentication yes
|
|
PermitRootLogin no
|
|
|
|
package_update: true
|
|
packages:
|
|
- base-devel
|
|
- git
|
|
- wget
|
|
- curl
|
|
- rust
|
|
- cargo
|
|
- qemu-full
|
|
- edk2-ovmf
|
|
- squashfs-tools
|
|
- xorriso
|
|
- dosfstools
|
|
- mtools
|
|
- python
|
|
- bc
|
|
- rsync
|
|
- openssh
|
|
- tmux
|
|
|
|
runcmd:
|
|
# Restart sshd to pick up the password auth config
|
|
- systemctl restart sshd
|
|
|
|
# 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"
|
|
'
|
|
|
|
# Create the darkforge-test convenience command
|
|
- |
|
|
cat > /usr/local/bin/darkforge-test << 'DTEOF'
|
|
#!/bin/bash
|
|
SCRIPT="/home/darkforge/darkforge/tests/proxmox/run-in-vm.sh"
|
|
if [ ! -f "$SCRIPT" ]; then
|
|
echo "ERROR: Test script not found. Is the repo cloned?"
|
|
echo " git clone --recurse-submodules https://git.dannyhaslund.dk/danny8632/darkforge.git ~/darkforge"
|
|
exit 1
|
|
fi
|
|
ARGS="$*"
|
|
exec tmux new-session -d -s darkforge \
|
|
"bash ${SCRIPT} --tmux ${ARGS}; echo ''; echo 'Tests finished. Press Enter to close.'; read" \; \
|
|
attach-session -t darkforge
|
|
DTEOF
|
|
chmod +x /usr/local/bin/darkforge-test
|
|
|
|
# Signal that provisioning is done
|
|
- touch /home/darkforge/.provisioned
|
|
- chown darkforge:darkforge /home/darkforge/.provisioned
|
|
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 " Cloud-init will install packages and clone the repo."
|
|
echo " Wait ~5 min for provisioning, then SSH in to run tests."
|
|
echo ""
|
|
echo " Get the VM IP:"
|
|
echo " qm guest cmd ${VMID} network-get-interfaces | grep -oP '\"ip-address\":\\s*\"\\K[0-9.]+'"
|
|
echo ""
|
|
echo " SSH in:"
|
|
echo " ssh darkforge@<IP> (password: darkforge)"
|
|
echo ""
|
|
echo " Run tests in a tmux session (detachable):"
|
|
echo " darkforge-test # starts tests in tmux"
|
|
echo " darkforge-test --quick # fast mode (30 min)"
|
|
echo ""
|
|
echo " Detach from tmux: Ctrl+B then D"
|
|
echo " Reattach later: tmux attach -t darkforge"
|
|
echo ""
|
|
echo " Collect report:"
|
|
echo " scp darkforge@<IP>:~/darkforge/tests/report.* ./"
|
|
echo "═══════════════════════════════════════════════"
|