Files
k3s-ansible/.github/scripts/prepare-vagrant-box-masters.sh
Timothy Stewart 68d03acc68 test(molecule): update Vagrant boxes to current releases
- Use published Bento boxes for Ubuntu 26.04, Debian 13, and Rocky Linux 10.1.\n- Pin exact VirtualBox amd64 box versions in CI.\n- Remove the obsolete Ubuntu SSH workaround and update fixtures.
2026-08-01 19:13:48 -05:00

212 lines
8.1 KiB
Bash
Executable File

#!/usr/bin/env bash
set -Eeuo pipefail
fail() {
printf 'Vagrant box master preparation refused: %s\n' "$1" >&2
exit 3
}
root_contains() {
local root="$1"
local path="$2"
[[ "$path" == "$root"/* ]]
}
read_machine_value() {
local machine_info="$1"
local key="$2"
awk -F= -v key="$key" '$1 == key {gsub(/"/, "", $2); print $2; exit}' <<< "$machine_info"
}
read_extra_data() {
local uuid="$1"
local key="$2"
local value
value="$(VBoxManage getextradata "$uuid" "$key" 2>/dev/null || true)"
[[ "$value" == 'Value: '* ]] || return 1
printf '%s\n' "${value#Value: }"
}
validate_owned_master() {
local uuid="$1"
local box="$2"
local version="$3"
local architecture="$4"
local machine_info cfg_file cfg_file_real vm_state groups disk_path disk_path_real
[[ "$uuid" =~ ^[0-9a-fA-F-]{36}$ ]] || return 1
machine_info="$(VBoxManage showvminfo "$uuid" --machinereadable 2>/dev/null)" || return 1
vm_state="$(read_machine_value "$machine_info" VMState)"
groups="$(read_machine_value "$machine_info" groups)"
cfg_file="$(read_machine_value "$machine_info" CfgFile)"
[[ "$vm_state" == poweroff ]] || return 1
[[ ",$groups," == *,/k3s-ansible/box-masters,* ]] || return 1
[[ -n "$cfg_file" ]] || return 1
cfg_file_real="$(readlink -f -- "$cfg_file" 2>/dev/null || true)"
[[ -n "$cfg_file_real" ]] || return 1
root_contains "$virtualbox_root_real" "$cfg_file_real" || return 1
[[ "$(read_extra_data "$uuid" k3s-ansible/owner || true)" == box-master ]] || return 1
[[ "$(read_extra_data "$uuid" k3s-ansible/box || true)" == "$box" ]] || return 1
[[ "$(read_extra_data "$uuid" k3s-ansible/version || true)" == "$version" ]] || return 1
[[ "$(read_extra_data "$uuid" k3s-ansible/architecture || true)" == "$architecture" ]] || return 1
while IFS= read -r disk_path; do
[[ -z "$disk_path" || "$disk_path" == none ]] && continue
disk_path_real="$(readlink -f -- "$disk_path" 2>/dev/null || true)"
[[ -n "$disk_path_real" ]] || return 1
root_contains "$virtualbox_root_real" "$disk_path_real" || return 1
done < <(awk -F= '$1 ~ /^(SATA|IDE|SCSI|SAS|VirtioSCSI|NVMe)-[0-9]+-[0-9]+$/ {
gsub(/"/, "", $2); print $2
}' <<< "$machine_info")
}
write_prewarm_vagrantfile() {
local destination="$1"
local box="$2"
local version="$3"
{
printf '%s\n' "Vagrant.configure('2') do |config|"
printf ' config.vm.box = "%s"\n' "$box"
printf ' config.vm.box_version = "%s"\n' "$version"
printf '%s\n' \
' config.vm.synced_folder ".", "/vagrant", disabled: true' \
' config.vm.hostname = "k3s-ansible-box-prewarm"' \
' config.vm.boot_timeout = 600' \
' config.vm.provider "virtualbox" do |virtualbox|' \
' virtualbox.linked_clone = true' \
' virtualbox.memory = 1024' \
' virtualbox.cpus = 2' \
' end' \
'end'
} > "$destination"
}
cleanup_prewarm() {
local rc=$?
trap - EXIT
if [[ -n "${prewarm_dir:-}" && -d "$prewarm_dir" ]]; then
VAGRANT_CWD="$prewarm_dir" vagrant destroy --force >/dev/null 2>&1 || true
rm -rf -- "$prewarm_dir"
fi
exit "$rc"
}
create_owned_master() {
local box="$1"
local version="$2"
local architecture="$3"
local master_id_file="$4"
local mapping_file="$5"
local uuid machine_info cfg_file cfg_file_real vm_state mapping_tmp
# A master_id restored from an immutable cache is only a hint. Without the
# runner-local ownership record and matching VirtualBox metadata it is not
# trusted, adopted, modified, or deleted.
rm -f -- "$master_id_file"
prewarm_dir="$(mktemp -d "${master_root}/prewarm.XXXXXX")"
trap cleanup_prewarm EXIT
write_prewarm_vagrantfile "$prewarm_dir/Vagrantfile" "$box" "$version"
printf 'Creating runner-owned linked-clone master for %s %s %s\n' \
"$box" "$version" "$architecture"
VAGRANT_CWD="$prewarm_dir" vagrant up --provider virtualbox --no-provision
[[ -r "$master_id_file" ]] || fail "Vagrant did not record a master UUID for $box"
uuid="$(tr -d '[:space:]' < "$master_id_file")"
[[ "$uuid" =~ ^[0-9a-fA-F-]{36}$ ]] || fail "Vagrant recorded an invalid master UUID for $box"
machine_info="$(VBoxManage showvminfo "$uuid" --machinereadable 2>/dev/null)" || \
fail "Vagrant master $uuid for $box is not registered"
vm_state="$(read_machine_value "$machine_info" VMState)"
cfg_file="$(read_machine_value "$machine_info" CfgFile)"
cfg_file_real="$(readlink -f -- "$cfg_file" 2>/dev/null || true)"
[[ "$vm_state" == poweroff ]] || fail "new Vagrant master $uuid is not powered off"
if [[ -z "$cfg_file_real" ]] || ! root_contains "$virtualbox_root_real" "$cfg_file_real"; then
fail "new Vagrant master $uuid is outside the runner VirtualBox root"
fi
VAGRANT_CWD="$prewarm_dir" vagrant destroy --force
VBoxManage modifyvm "$uuid" --groups /k3s-ansible/box-masters
VBoxManage setextradata "$uuid" k3s-ansible/owner box-master
VBoxManage setextradata "$uuid" k3s-ansible/box "$box"
VBoxManage setextradata "$uuid" k3s-ansible/version "$version"
VBoxManage setextradata "$uuid" k3s-ansible/architecture "$architecture"
validate_owned_master "$uuid" "$box" "$version" "$architecture" || \
fail "new Vagrant master $uuid failed ownership validation"
rm -rf -- "$prewarm_dir"
prewarm_dir=""
trap - EXIT
mapping_tmp="${mapping_file}.tmp"
printf '%s\n' "$uuid" > "$mapping_tmp"
chmod 600 "$mapping_tmp"
mv -- "$mapping_tmp" "$mapping_file"
printf '%s\n' "$uuid" > "$master_id_file"
chmod 600 "$master_id_file"
printf 'Created and recorded owned master %s for %s\n' "$uuid" "$box"
}
repository_root="${K3S_CI_REPOSITORY_ROOT:-$(git rev-parse --show-toplevel)}"
lock_file="${VAGRANT_BOX_LOCK_FILE:-${repository_root}/.github/vagrant-boxes.lock}"
vagrant_home="${VAGRANT_HOME:?VAGRANT_HOME must be set}"
master_root="${K3S_CI_VAGRANT_MASTER_ROOT:-${HOME:?HOME must be set}/.cache/k3s-ci/vagrant-masters}"
virtualbox_root="${K3S_CI_VIRTUALBOX_ROOT:-${HOME}/VirtualBox VMs}"
[[ -r "$lock_file" ]] || fail "box lock file is missing or unreadable: $lock_file"
[[ -d "$vagrant_home/boxes" ]] || fail "Vagrant box directory is missing: $vagrant_home/boxes"
[[ -d "$virtualbox_root" ]] || fail "VirtualBox root is missing: $virtualbox_root"
box_root_real="$(readlink -f -- "$vagrant_home/boxes")"
virtualbox_root_real="$(readlink -f -- "$virtualbox_root")"
mkdir -p -- "$master_root"
chmod 700 "$master_root"
exec 9> "${master_root}/prepare.lock"
flock 9
lock_entries="$(awk '
/^[[:space:]]*#/ || NF == 0 { next }
NF != 3 { invalid = 1; next }
{ print $1 " " $2 " " $3 }
END { exit invalid }
' "$lock_file")" || fail 'invalid Vagrant box lock entry'
[[ -n "$lock_entries" ]] || fail 'Vagrant box lock is empty'
while read -r box version architecture; do
[[ "$box" =~ ^[A-Za-z0-9._-]+/[A-Za-z0-9._-]+$ ]] || fail "invalid box name: $box"
[[ "$version" =~ ^[A-Za-z0-9._-]+$ ]] || fail "invalid box version: $version"
[[ "$architecture" =~ ^[A-Za-z0-9._-]+$ ]] || fail "invalid box architecture: $architecture"
box_slug="${box//\//-VAGRANTSLASH-}"
record_slug="${box//\//_}-${version}-${architecture}"
box_dir="${vagrant_home}/boxes/${box_slug}/${version}/${architecture}/virtualbox"
[[ -d "$box_dir" ]] || fail "pinned box is not installed: $box $version $architecture"
box_dir_real="$(readlink -f -- "$box_dir")"
root_contains "$box_root_real" "$box_dir_real" || fail "box directory is outside VAGRANT_HOME: $box_dir_real"
master_id_file="${box_dir_real}/master_id"
mapping_file="${master_root}/${record_slug}.uuid"
uuid=""
if [[ -r "$mapping_file" ]]; then
uuid="$(tr -d '[:space:]' < "$mapping_file")"
fi
if [[ -n "$uuid" ]] && validate_owned_master "$uuid" "$box" "$version" "$architecture"; then
printf '%s\n' "$uuid" > "$master_id_file"
chmod 600 "$master_id_file"
printf 'Reusing owned master %s for %s %s %s\n' "$uuid" "$box" "$version" "$architecture"
continue
fi
if [[ -e "$mapping_file" ]]; then
printf 'Owned master record is stale for %s; rebuilding without deleting any VM or disk.\n' "$box"
fi
create_owned_master "$box" "$version" "$architecture" "$master_id_file" "$mapping_file"
done <<< "$lock_entries"
printf 'All pinned Vagrant box masters are ready.\n'