This commit is contained in:
Stephen Simpson
2025-11-26 08:15:00 -06:00
parent 3cbd4525a0
commit bb829c9b63
18 changed files with 2440 additions and 349 deletions

107
scripts/bootstrap_golden.sh Executable file
View File

@@ -0,0 +1,107 @@
#!/bin/bash
# Bootstrap the golden image by booting it, running sparrowdo --bootstrap, then shutting down
# This should be run ONCE after creating the golden image
GOLDEN_IMAGE="$1"
SSH_PRIVATE_KEY="${2:-$HOME/.ssh/id_rsa}"
if [ -z "$GOLDEN_IMAGE" ]; then
echo "Usage: $0 <golden_image_path> [ssh_private_key]"
exit 1
fi
if [ ! -f "$GOLDEN_IMAGE" ]; then
echo "ERROR: Golden image not found: $GOLDEN_IMAGE"
exit 1
fi
BOOTSTRAP_VM="bootstrap-golden-$(date +%s)"
SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
echo "=========================================="
echo "Bootstrapping Golden Image"
echo "=========================================="
echo "Golden Image: $GOLDEN_IMAGE"
echo "Bootstrap VM: $BOOTSTRAP_VM"
echo "SSH Key: $SSH_PRIVATE_KEY"
echo ""
# Provision temporary VM from golden image
echo "[1/4] Provisioning temporary VM..."
VM_IP=$("${SCRIPT_DIR}/provision_vm.sh" "$BOOTSTRAP_VM" "$GOLDEN_IMAGE" 60)
if [ "$VM_IP" = "ERROR" ] || [ -z "$VM_IP" ]; then
echo "ERROR: Failed to provision bootstrap VM"
exit 1
fi
VM_IP=$(echo "$VM_IP" | tail -1)
echo "VM IP: $VM_IP"
# Wait for SSH
echo ""
echo "[2/4] Waiting for SSH to be ready..."
for i in {1..30}; do
if ssh -i "$SSH_PRIVATE_KEY" \
-o StrictHostKeyChecking=no \
-o ConnectTimeout=5 \
-o UserKnownHostsFile=/dev/null \
rocky@${VM_IP} 'echo "SSH ready"' 2>/dev/null; then
echo "SSH connection established"
break
fi
sleep 2
done
# Run sparrowdo bootstrap
echo ""
echo "[3/4] Running Sparrowdo bootstrap..."
if sparrowdo \
--host="${VM_IP}" \
--ssh_user=rocky \
--ssh_private_key="${SSH_PRIVATE_KEY}" \
--ssh_args="-o StrictHostKeyChecking=no -o ConnectTimeout=10 -o UserKnownHostsFile=/dev/null" \
--bootstrap \
--color; then
echo "Bootstrap completed successfully!"
else
echo "WARNING: Bootstrap may have failed"
fi
# Shutdown the VM cleanly to save changes
echo ""
echo "[4/4] Shutting down VM to save changes..."
ssh -i "$SSH_PRIVATE_KEY" \
-o StrictHostKeyChecking=no \
-o UserKnownHostsFile=/dev/null \
rocky@${VM_IP} 'sudo shutdown -h now' 2>/dev/null || true
# Wait for VM to shut down
echo "Waiting for VM to shut down..."
sleep 10
for i in {1..30}; do
if ! sudo virsh -c qemu:///system list --name 2>/dev/null | grep -q "$BOOTSTRAP_VM"; then
echo "VM has shut down"
break
fi
sleep 2
done
# Clean up VM definition (disk contains the bootstrapped state)
echo "Cleaning up VM definition..."
sudo virsh -c qemu:///system undefine "$BOOTSTRAP_VM" 2>/dev/null || true
echo ""
echo "=========================================="
echo "Bootstrap Complete"
echo "=========================================="
echo "Golden image has been bootstrapped with Sparrowdo"
echo "The image now contains:"
echo " - Raku/Rakudo runtime"
echo " - Sparrowdo and dependencies"
echo " - All required testing tools"
echo ""
echo "This image can now be used for testing without"
echo "running bootstrap on each test VM."
echo "=========================================="

51
scripts/build-golden.sh Executable file
View File

@@ -0,0 +1,51 @@
#!/bin/bash
BASE_IMAGE="$1"
GOLDEN_IMAGE="$2"
SSH_PUB_KEY="${3:-$HOME/.ssh/id_rsa.pub}"
if [ -z "$BASE_IMAGE" ] || [ -z "$GOLDEN_IMAGE" ]; then
echo "Usage: $0 <base_image> <golden_image> [ssh_pub_key]"
exit 1
fi
echo "Creating golden image: $GOLDEN_IMAGE"
cp "$BASE_IMAGE" "$GOLDEN_IMAGE"
export LIBGUESTFS_BACKEND=direct
sudo virt-customize -a "$GOLDEN_IMAGE" \
--install rakudo,rakudo-zef,perl,git,openssh-server \
--run-command 'useradd -m rocky && echo "rocky:rockypass" | chpasswd' \
--run-command 'echo "rocky ALL=(ALL) NOPASSWD:ALL" > /etc/sudoers.d/rocky && chmod 0440 /etc/sudoers.d/rocky' \
--ssh-inject root:file:"$SSH_PUB_KEY" \
--ssh-inject rocky:file:"$SSH_PUB_KEY" \
--root-password password:rockytesting \
--run-command 'systemctl enable sshd' \
--selinux-relabel
echo "Bootstrapping Sparrowdo..."
BOOTSTRAP_VM="bootstrap-$$"
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
VM_IP=$("$SCRIPT_DIR/provision_vm.sh" "$BOOTSTRAP_VM" "$GOLDEN_IMAGE" 60 | tail -1)
if [ -z "$VM_IP" ] || [ "$VM_IP" = "ERROR" ]; then
echo "ERROR: Failed to provision bootstrap VM"
exit 1
fi
sleep 5
SSH_KEY="${SSH_PUB_KEY%.pub}"
sparrowdo --host="$VM_IP" --ssh_user=rocky --ssh_private_key="$SSH_KEY" \
--ssh_args="-o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null" \
--bootstrap --color
ssh -i "$SSH_KEY" -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null \
rocky@"$VM_IP" 'sudo shutdown -h now' 2>/dev/null || true
sleep 10
"$SCRIPT_DIR/cleanup_vm.sh" "$BOOTSTRAP_VM"
echo "Golden image ready: $GOLDEN_IMAGE"

14
scripts/cleanup-all.sh Executable file
View File

@@ -0,0 +1,14 @@
#!/bin/bash
PATTERN="${1:-.*}"
echo "Cleaning up VMs matching: $PATTERN"
for vm in $(virsh -c qemu:///system list --all --name | grep -E "$PATTERN"); do
echo "Destroying $vm"
virsh -c qemu:///system destroy "$vm" 2>/dev/null || true
virsh -c qemu:///system undefine "$vm" 2>/dev/null || true
rm -f "/var/lib/libvirt/images/${vm}.qcow2" 2>/dev/null || true
done
echo "Cleanup complete"

View File

@@ -12,11 +12,11 @@ echo "[Cleanup] Starting cleanup for VM: $VM_NAME"
# Force stop VM
echo "[Cleanup] Destroying VM..."
sudo virsh destroy "$VM_NAME" 2>/dev/null || echo "[Cleanup] VM was not running"
sudo virsh -c qemu:///system destroy "$VM_NAME" 2>/dev/null || echo "[Cleanup] VM was not running"
# Remove VM definition
echo "[Cleanup] Undefining VM..."
sudo virsh undefine "$VM_NAME" 2>/dev/null || echo "[Cleanup] VM definition already removed"
sudo virsh -c qemu:///system undefine "$VM_NAME" 2>/dev/null || echo "[Cleanup] VM definition already removed"
# Remove disk image
echo "[Cleanup] Removing disk image..."

22
scripts/download-image.sh Executable file
View File

@@ -0,0 +1,22 @@
#!/bin/bash
URL="$1"
OUTPUT_DIR="${2:-/var/lib/libvirt/images}"
FORCE="${3:-false}"
if [ -z "$URL" ]; then
echo "Usage: $0 <url> [output_dir] [force]"
exit 1
fi
FILENAME=$(basename "$URL")
CACHED="$OUTPUT_DIR/$FILENAME"
if [ "$FORCE" = "true" ] || [ ! -f "$CACHED" ]; then
echo "Downloading $URL to $CACHED"
curl -L --progress-bar -o "$CACHED" "$URL"
else
echo "Using cached image: $CACHED"
fi
echo "$CACHED"

View File

@@ -1,5 +1,6 @@
#!/bin/bash
set -e
# Note: Not using 'set -e' here because we handle errors explicitly
# and arithmetic operations can cause false failures with set -e
VM_NAME="$1"
GOLDEN_IMAGE="$2"
@@ -20,7 +21,7 @@ sudo qemu-img create -f qcow2 -b "$GOLDEN_IMAGE" -F qcow2 "$VM_DISK" 2>/dev/null
# Define and start VM
echo "[Provision] Starting VM with virt-install..."
sudo virt-install \
VIRT_INSTALL_OUTPUT=$(sudo virt-install \
--name "$VM_NAME" \
--memory 2048 \
--vcpus 2 \
@@ -31,14 +32,21 @@ sudo virt-install \
--noautoconsole \
--wait 0 \
--transient \
2>&1 | grep -v "WARNING" || true
2>&1) || {
echo "[Provision] ERROR: virt-install failed"
echo "$VIRT_INSTALL_OUTPUT" | grep -v "WARNING"
echo "[Provision] Cleaning up disk..."
sudo rm -f "$VM_DISK"
echo "ERROR"
exit 1
}
# Wait for IP address
echo "[Provision] Waiting for VM to obtain IP address (max ${MAX_WAIT}s)..."
COUNTER=0
while [ $COUNTER -lt $MAX_WAIT ]; do
# Try to get IP from DHCP lease
IP=$(sudo virsh domifaddr "$VM_NAME" --source lease 2>/dev/null | awk '/ipv4/ {print $4}' | cut -d/ -f1 | head -1)
# Try to get IP from DHCP lease (explicitly use system connection)
IP=$(sudo virsh -c qemu:///system domifaddr "$VM_NAME" --source lease 2>/dev/null | awk '/ipv4/ {print $4}' | cut -d/ -f1 | head -1)
if [ -n "$IP" ] && [ "$IP" != "0.0.0.0" ]; then
echo "[Provision] IP obtained: $IP"

60
scripts/run-test.sh Executable file
View File

@@ -0,0 +1,60 @@
#!/bin/bash
TEST_NAME="$1"
TEST_REPO="$2"
GOLDEN_IMAGE="$3"
SSH_KEY="${4:-$HOME/.ssh/id_rsa}"
if [ -z "$TEST_NAME" ] || [ -z "$TEST_REPO" ] || [ -z "$GOLDEN_IMAGE" ]; then
echo "Usage: $0 <test_name> <test_repo_url> <golden_image> [ssh_key]"
exit 1
fi
VM_NAME="$TEST_NAME-$$"
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
WORK_DIR="/tmp/test-$VM_NAME"
mkdir -p "$WORK_DIR"
cd "$WORK_DIR"
cleanup() {
echo "Cleaning up..."
"$SCRIPT_DIR/cleanup_vm.sh" "$VM_NAME" 2>/dev/null || true
cd /tmp
rm -rf "$WORK_DIR"
}
trap cleanup EXIT
echo "Provisioning VM..."
VM_IP=$("$SCRIPT_DIR/provision_vm.sh" "$VM_NAME" "$GOLDEN_IMAGE" 60 | tail -1)
if [ -z "$VM_IP" ] || [ "$VM_IP" = "ERROR" ]; then
echo "ERROR: Failed to provision VM"
exit 1
fi
echo "VM ready at $VM_IP"
echo "Cloning test repository..."
git clone "$TEST_REPO" test-repo
SPARROWFILE=$(find test-repo -name "main.raku" -o -name "sparrowfile" | head -1)
if [ -z "$SPARROWFILE" ]; then
echo "ERROR: No sparrowfile found"
exit 1
fi
echo "Running test: $SPARROWFILE"
sparrowdo \
--host="$VM_IP" \
--ssh_user=rocky \
--ssh_private_key="$SSH_KEY" \
--ssh_args="-o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null" \
--no_sudo \
--sparrowfile="$SPARROWFILE" \
--verbose \
--color
echo "Test completed successfully"

View File

@@ -37,6 +37,7 @@ if [ -f "$PREP_SCRIPT_PATH" ]; then
sudo virt-customize -a "$GOLDEN_IMAGE" \
--run "$PREP_SCRIPT_PATH" \
--ssh-inject root:file:"$SSH_PUB_KEY" \
--ssh-inject rocky:file:"$SSH_PUB_KEY" \
--root-password password:rockytesting \
--selinux-relabel 2>&1 || {
echo "ERROR: virt-customize failed"