🤖 Pull request created from `various-fixes` containing PageHead
breadcrumbs and navigation alignment fixes, Umami analytics proxy, and
infrastructure updates.
131 lines
4.7 KiB
Bash
Executable File
131 lines
4.7 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
# ==============================================================================
|
|
# End-to-End QEMU Verification Script for Golden Image & Cloud-Init User-Data
|
|
# ==============================================================================
|
|
set -euo pipefail
|
|
|
|
IMAGE_PATH="${1:-output-qemu/com-ermnvldmr-debian-12-k3s-amd64.qcow2}"
|
|
USER_DATA="${2:-ci/cloud-init/user-data.yaml}"
|
|
SSH_KEY="${3:-$HOME/.ssh/adam@ermnvldmr.com:ed25519}"
|
|
SSH_PORT="${4:-22222}"
|
|
|
|
if [ ! -f "$IMAGE_PATH" ]; then
|
|
echo "[-] Golden image not found at: $IMAGE_PATH"
|
|
echo " Build it first using: packer build -var 'accelerator=kvm' ci/packer/debian-12-k3s.pkr.hcl"
|
|
exit 1
|
|
fi
|
|
|
|
if [ ! -f "$USER_DATA" ]; then
|
|
echo "[-] User-data file not found at: $USER_DATA"
|
|
exit 1
|
|
fi
|
|
|
|
if [ ! -f "$SSH_KEY" ]; then
|
|
echo "[-] SSH private key not found at: $SSH_KEY"
|
|
exit 1
|
|
fi
|
|
|
|
# Ensure prerequisite tools exist
|
|
for cmd in qemu-system-x86_64 qemu-img genisoimage ssh; do
|
|
if ! command -v "$cmd" &>/dev/null; then
|
|
echo "[-] Required command not found in PATH: $cmd"
|
|
exit 1
|
|
fi
|
|
done
|
|
|
|
TMPDIR=$(mktemp -d /tmp/qemu-test-k3s-XXXXXX)
|
|
echo "=== [QEMU Test] Workdir: $TMPDIR ==="
|
|
|
|
QEMU_PID=""
|
|
cleanup() {
|
|
echo "=== [QEMU Test] Cleaning up resources ==="
|
|
if [ -n "$QEMU_PID" ] && kill -0 "$QEMU_PID" 2>/dev/null; then
|
|
echo "Stopping QEMU process (PID: $QEMU_PID)..."
|
|
kill "$QEMU_PID" 2>/dev/null || true
|
|
wait "$QEMU_PID" 2>/dev/null || true
|
|
fi
|
|
rm -rf "$TMPDIR"
|
|
echo "=== [QEMU Test] Cleanup complete ==="
|
|
}
|
|
trap cleanup EXIT
|
|
|
|
echo "=== [QEMU Test] 1. Preparing Cloud-Init cidata ISO ==="
|
|
cp "$USER_DATA" "$TMPDIR/user-data"
|
|
cat << 'EOF' > "$TMPDIR/meta-data"
|
|
instance-id: qemu-test-vm-01
|
|
local-hostname: com-ermnvldmr
|
|
EOF
|
|
|
|
genisoimage -output "$TMPDIR/cidata.iso" -volid cidata -joliet -rock "$TMPDIR/user-data" "$TMPDIR/meta-data" 2>/dev/null
|
|
|
|
echo "=== [QEMU Test] 2. Creating Ephemeral Disks ==="
|
|
# CoW overlay for root OS disk (preserves original golden image intact)
|
|
qemu-img create -f qcow2 -b "$(realpath "$IMAGE_PATH")" -F qcow2 "$TMPDIR/test-os.qcow2" 20G >/dev/null
|
|
# Secondary data disk (/dev/vdb) to test persistent storage auto-mounting
|
|
qemu-img create -f qcow2 "$TMPDIR/test-vdb.qcow2" 10G >/dev/null
|
|
|
|
echo "=== [QEMU Test] 3. Launching VM in QEMU (KVM accelerated) ==="
|
|
qemu-system-x86_64 \
|
|
-enable-kvm \
|
|
-m 4096 \
|
|
-smp 2 \
|
|
-drive file="$TMPDIR/test-os.qcow2",if=virtio,format=qcow2 \
|
|
-drive file="$TMPDIR/test-vdb.qcow2",if=virtio,format=qcow2 \
|
|
-drive file="$TMPDIR/cidata.iso",if=virtio,format=raw \
|
|
-netdev user,id=net0,hostfwd=tcp::${SSH_PORT}-:22 \
|
|
-device virtio-net,netdev=net0 \
|
|
-nographic \
|
|
-serial file:"$TMPDIR/serial.log" &
|
|
QEMU_PID=$!
|
|
|
|
echo "QEMU started with PID $QEMU_PID. Forwarding guest SSH to localhost:$SSH_PORT"
|
|
|
|
echo "=== [QEMU Test] 4. Waiting for SSH Availability (up to 120s) ==="
|
|
MAX_WAIT=120
|
|
WAIT_COUNT=0
|
|
SSH_OPTS=(-i "$SSH_KEY" -p "$SSH_PORT" -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -o ConnectTimeout=3 -o BatchMode=yes)
|
|
|
|
until ssh "${SSH_OPTS[@]}" adam@127.0.0.1 "echo ssh_ready" 2>/dev/null; do
|
|
sleep 3
|
|
WAIT_COUNT=$((WAIT_COUNT + 3))
|
|
if [ "$WAIT_COUNT" -ge "$MAX_WAIT" ]; then
|
|
echo "[-] Timed out waiting for SSH on localhost:$SSH_PORT!"
|
|
echo "--- Last 50 lines of serial console log ---"
|
|
tail -n 50 "$TMPDIR/serial.log" || true
|
|
exit 1
|
|
fi
|
|
done
|
|
echo "[+] SSH connection established!"
|
|
|
|
echo "=== [QEMU Test] 5. Waiting for Cloud-Init Completion ==="
|
|
ssh "${SSH_OPTS[@]}" adam@127.0.0.1 "sudo cloud-init status --wait"
|
|
|
|
echo "=== [QEMU Test] 6. Verifying System Assertions ==="
|
|
|
|
# Check Hostname
|
|
HOSTNAME_ACTUAL=$(ssh "${SSH_OPTS[@]}" adam@127.0.0.1 "hostname")
|
|
echo "Hostname: $HOSTNAME_ACTUAL (Expected: com-ermnvldmr)"
|
|
[ "$HOSTNAME_ACTUAL" = "com-ermnvldmr" ]
|
|
|
|
# Check Secondary Disk Mount
|
|
MOUNT_CHECK=$(ssh "${SSH_OPTS[@]}" adam@127.0.0.1 "grep -qs '/var/lib/rancher/k3s/storage' /proc/mounts && echo MOUNTED || echo NOT_MOUNTED")
|
|
echo "Secondary disk mount: $MOUNT_CHECK"
|
|
[ "$MOUNT_CHECK" = "MOUNTED" ]
|
|
|
|
# Check K3s Node Status
|
|
NODE_STATUS=$(ssh "${SSH_OPTS[@]}" adam@127.0.0.1 "kubectl get nodes -o jsonpath='{.items[0].status.conditions[?(@.type==\"Ready\")].status}'")
|
|
echo "K3s Node Ready Status: $NODE_STATUS"
|
|
[ "$NODE_STATUS" = "True" ]
|
|
|
|
# Check Non-root Kubectl access
|
|
echo "Checking non-root kubectl execution without sudo:"
|
|
ssh "${SSH_OPTS[@]}" adam@127.0.0.1 "kubectl get nodes -o wide"
|
|
|
|
# Check Flux namespace and Age secret
|
|
echo "Checking Flux namespace and SOPS Age secret:"
|
|
ssh "${SSH_OPTS[@]}" adam@127.0.0.1 "kubectl get secret sops-age -n flux-system"
|
|
|
|
echo "=========================================================================="
|
|
echo " [SUCCESS] All Golden Image & Cloud-Init Assertions Passed in QEMU!"
|
|
echo "=========================================================================="
|