mirror of
https://github.com/techno-tim/k3s-ansible.git
synced 2026-08-09 15:33:18 +02:00
68d03acc68
- 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.
212 lines
8.1 KiB
Bash
Executable File
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'
|