perf(test-guest): accelerate cached guest startup

Signed-off-by: Drew Newberry <anewberry@nvidia.com>
This commit is contained in:
Drew Newberry
2026-07-28 12:54:43 -07:00
parent 52cb5e1b90
commit eebc077add
5 changed files with 131 additions and 119 deletions
+2 -2
View File
@@ -145,9 +145,9 @@ A cache build boots and configures a disposable VM, runs the internal sealing sc
The key includes the pinned base-image identity, guest architecture, ordered configuration file digests, Ansible version, cache generation, and sealing script digest. Installed packages, copied binaries, forwarded ports, and guest commands are never cached.
Normal `test-guest` runs automatically use an exact valid local entry and create a fresh writable overlay, cloud-init instance, machine ID, and SSH identity. On a local miss, the runner uses the pinned cloud image and applies configurations normally. Set `OPENSHELL_TEST_VM_CACHE_DISABLE=1` to bypass local lookup.
Normal `test-guest` runs automatically use an exact valid local entry and create a fresh writable overlay, cloud-init instance, machine ID, and SSH identity. On a local miss, the runner uses the pinned cloud image and applies configurations normally. Set `OPENSHELL_TEST_GUEST_CACHE_DISABLE=1` to bypass local lookup.
The default cache directory is `${XDG_CACHE_HOME:-$HOME/.cache}/openshell/test-guest`. Override it with `--cache-dir` on the cache command or `OPENSHELL_TEST_VM_CACHE_DIR` for either app.
The default cache directory is `${XDG_CACHE_HOME:-$HOME/.cache}/openshell/test-guest`. Override it with `--cache-dir` on the cache command or `OPENSHELL_TEST_GUEST_CACHE_DIR` for either app.
Cache command options:
+22 -22
View File
@@ -4,26 +4,26 @@
# Shared cache identity and validation helpers for the test guest runner.
TEST_VM_CACHE_SCHEMA_VERSION=1
TEST_VM_CACHE_DISK_LAYOUT=standalone-qcow2-zstd-v1
TEST_VM_CACHE_ARTIFACT_TYPE=application/vnd.nvidia.openshell.test-guest.cache.v1
TEST_VM_CACHE_METADATA_TYPE=application/vnd.nvidia.openshell.test-guest.cache.metadata.v1+json
TEST_VM_CACHE_DISK_TYPE=application/vnd.nvidia.openshell.test-guest.cache.disk.qcow2.v1+zstd
TEST_GUEST_CACHE_SCHEMA_VERSION=1
TEST_GUEST_CACHE_DISK_LAYOUT=standalone-qcow2-zstd-v1
TEST_GUEST_CACHE_ARTIFACT_TYPE=application/vnd.nvidia.openshell.test-guest.cache.v1
TEST_GUEST_CACHE_METADATA_TYPE=application/vnd.nvidia.openshell.test-guest.cache.metadata.v1+json
TEST_GUEST_CACHE_DISK_TYPE=application/vnd.nvidia.openshell.test-guest.cache.disk.qcow2.v1+zstd
test_vm_cache_root() {
if [ -n "${OPENSHELL_TEST_VM_CACHE_DIR:-}" ]; then
printf '%s\n' "${OPENSHELL_TEST_VM_CACHE_DIR}"
if [ -n "${OPENSHELL_TEST_GUEST_CACHE_DIR:-}" ]; then
printf '%s\n' "${OPENSHELL_TEST_GUEST_CACHE_DIR}"
else
printf '%s\n' "${XDG_CACHE_HOME:-${HOME}/.cache}/openshell/test-guest"
fi
}
test_vm_cache_oci_architecture() {
case "${TEST_VM_ARCHITECTURE}" in
case "${TEST_GUEST_ARCHITECTURE}" in
x86_64) printf '%s\n' amd64 ;;
aarch64) printf '%s\n' arm64 ;;
*)
echo "unsupported cache architecture: ${TEST_VM_ARCHITECTURE}" >&2
echo "unsupported cache architecture: ${TEST_GUEST_ARCHITECTURE}" >&2
return 1
;;
esac
@@ -48,25 +48,25 @@ test_vm_cache_key() {
local configuration_line
architecture=$(test_vm_cache_oci_architecture) || return 1
seal_hash=$(test_vm_cache_sha256 "${OPENSHELL_TEST_VM_CACHE_SEAL}") || return 1
seal_hash=$(test_vm_cache_sha256 "${OPENSHELL_TEST_GUEST_CACHE_SEAL}") || return 1
printf -v material \
'schema=%s\ngeneration=%s\ndisk_layout=%s\ndistro=%s\nos_version=%s\narchitecture=%s\nbase_url=%s\nbase_hash=%s\noverlay_growth=%s\nansible_version=%s\nseal_sha256=%s\n' \
"${TEST_VM_CACHE_SCHEMA_VERSION}" \
"${TEST_VM_CACHE_GENERATION}" \
"${TEST_VM_CACHE_DISK_LAYOUT}" \
"${TEST_GUEST_CACHE_SCHEMA_VERSION}" \
"${TEST_GUEST_CACHE_GENERATION}" \
"${TEST_GUEST_CACHE_DISK_LAYOUT}" \
"${distro_name}" \
"${TEST_VM_OS_VERSION}" \
"${TEST_GUEST_OS_VERSION}" \
"${architecture}" \
"${TEST_VM_IMAGE_URL}" \
"${TEST_VM_IMAGE_HASH}" \
"${TEST_GUEST_IMAGE_URL}" \
"${TEST_GUEST_IMAGE_HASH}" \
16G \
"${TEST_VM_ANSIBLE_VERSION}" \
"${TEST_GUEST_ANSIBLE_VERSION}" \
"${seal_hash}"
for configuration in "$@"; do
configuration_hash=$(
test_vm_cache_sha256 \
"${OPENSHELL_TEST_VM_CONFIGURATIONS}/${configuration}"
"${OPENSHELL_TEST_GUEST_CONFIGURATIONS}/${configuration}"
) || return 1
printf -v configuration_line \
'configuration=%s:%s\n' \
@@ -82,7 +82,7 @@ test_vm_cache_tag() {
local distro_name=$1
local key=$2
printf 'v%s-%s-%s-%s\n' \
"${TEST_VM_CACHE_SCHEMA_VERSION}" \
"${TEST_GUEST_CACHE_SCHEMA_VERSION}" \
"${distro_name}" \
"$(test_vm_cache_oci_architecture)" \
"${key}"
@@ -105,12 +105,12 @@ test_vm_cache_metadata_matches() {
expected_configurations=$(jq -cn --args '$ARGS.positional' "$@") || return 1
jq -e \
--argjson schema "${TEST_VM_CACHE_SCHEMA_VERSION}" \
--argjson schema "${TEST_GUEST_CACHE_SCHEMA_VERSION}" \
--arg key "${key}" \
--arg distro "${distro_name}" \
--arg os_version "${TEST_VM_OS_VERSION}" \
--arg os_version "${TEST_GUEST_OS_VERSION}" \
--arg architecture "$(test_vm_cache_oci_architecture)" \
--arg base_hash "${TEST_VM_IMAGE_HASH}" \
--arg base_hash "${TEST_GUEST_IMAGE_HASH}" \
--argjson configurations "${expected_configurations}" \
'
.schema == $schema and
+25 -25
View File
@@ -26,18 +26,18 @@ Publishing is explicit and requires both --repository and --push.
EOF
}
if [ "${OPENSHELL_TEST_VM_RUNTIME:-}" != 1 ] ||
[ ! -d "${OPENSHELL_TEST_VM_DISTROS:-}" ] ||
[ ! -d "${OPENSHELL_TEST_VM_CONFIGURATIONS:-}" ] ||
[ ! -r "${OPENSHELL_TEST_VM_CACHE_LIB:-}" ] ||
[ ! -r "${OPENSHELL_TEST_VM_CACHE_SEAL:-}" ] ||
[ ! -r "${OPENSHELL_TEST_VM_RUNNER:-}" ]; then
if [ "${OPENSHELL_TEST_GUEST_RUNTIME:-}" != 1 ] ||
[ ! -d "${OPENSHELL_TEST_GUEST_DISTROS:-}" ] ||
[ ! -d "${OPENSHELL_TEST_GUEST_CONFIGURATIONS:-}" ] ||
[ ! -r "${OPENSHELL_TEST_GUEST_CACHE_LIB:-}" ] ||
[ ! -r "${OPENSHELL_TEST_GUEST_CACHE_SEAL:-}" ] ||
[ ! -r "${OPENSHELL_TEST_GUEST_RUNNER:-}" ]; then
echo "run this script through 'nix run .#test-guest-cache -- ...'" >&2
exit 2
fi
# shellcheck disable=SC1090
. "${OPENSHELL_TEST_VM_CACHE_LIB}"
. "${OPENSHELL_TEST_GUEST_CACHE_LIB}"
require_value() {
if [ "$#" -lt 2 ] || [ -z "${2:-}" ]; then
@@ -47,7 +47,7 @@ require_value() {
}
distro=
repository=${OPENSHELL_TEST_VM_CACHE_REPOSITORY:-}
repository=${OPENSHELL_TEST_GUEST_CACHE_REPOSITORY:-}
cache_dir=
push=0
configurations=()
@@ -96,18 +96,18 @@ if [ -z "${distro}" ]; then
exit 2
fi
if [[ ! ${distro} =~ ^[a-z0-9][a-z0-9-]*$ ]] ||
[ ! -r "${OPENSHELL_TEST_VM_DISTROS}/${distro}" ]; then
[ ! -r "${OPENSHELL_TEST_GUEST_DISTROS}/${distro}" ]; then
echo "unknown distro: ${distro}" >&2
exit 2
fi
# Distro profiles contain only trusted values generated into the Nix store.
# shellcheck disable=SC1090
. "${OPENSHELL_TEST_VM_DISTROS}/${distro}"
. "${OPENSHELL_TEST_GUEST_DISTROS}/${distro}"
for item in "${configurations[@]}"; do
if [[ ! ${item} =~ ^[a-z0-9][a-z0-9-]*$ ]] ||
[ ! -r "${OPENSHELL_TEST_VM_CONFIGURATIONS}/${item}" ]; then
[ ! -r "${OPENSHELL_TEST_GUEST_CONFIGURATIONS}/${item}" ]; then
echo "unknown configuration: ${item:-<empty>}" >&2
exit 2
fi
@@ -123,8 +123,8 @@ if [[ ${repository} == *[[:space:]]* ]] || [[ ${repository} == *@* ]]; then
fi
if [ -n "${cache_dir}" ]; then
OPENSHELL_TEST_VM_CACHE_DIR=${cache_dir}
export OPENSHELL_TEST_VM_CACHE_DIR
OPENSHELL_TEST_GUEST_CACHE_DIR=${cache_dir}
export OPENSHELL_TEST_GUEST_CACHE_DIR
fi
umask 077
@@ -288,7 +288,7 @@ build_local() {
done
prepare_args+=(
--copy
"${OPENSHELL_TEST_VM_CACHE_SEAL}:/usr/local/sbin/openshell-test-guest-cache-seal"
"${OPENSHELL_TEST_GUEST_CACHE_SEAL}:/usr/local/sbin/openshell-test-guest-cache-seal"
--
sudo
/usr/local/sbin/openshell-test-guest-cache-seal
@@ -296,7 +296,7 @@ build_local() {
echo "==> Cache miss: preparing ${distro} ($(test_vm_cache_oci_architecture))"
TMPDIR="${build_stage}/prepare-tmp" \
"${TEST_VM_BASH}" "${OPENSHELL_TEST_VM_RUNNER}" "${prepare_args[@]}"
"${TEST_GUEST_BASH}" "${OPENSHELL_TEST_GUEST_RUNNER}" "${prepare_args[@]}"
shopt -s nullglob
run_dirs=("${build_stage}/prepare-tmp/openshell-test-guest"/run.*)
@@ -329,8 +329,8 @@ build_local() {
echo "==> Validating fresh boot from prepared cache disk"
TMPDIR="${build_stage}/validate-tmp" \
OPENSHELL_TEST_VM_IMAGE_OVERRIDE="${prepared_disk}" \
"${TEST_VM_BASH}" "${OPENSHELL_TEST_VM_RUNNER}" "${validate_args[@]}"
OPENSHELL_TEST_GUEST_IMAGE_OVERRIDE="${prepared_disk}" \
"${TEST_GUEST_BASH}" "${OPENSHELL_TEST_GUEST_RUNNER}" "${validate_args[@]}"
configuration_json=$(jq -cn --args '$ARGS.positional' "${configurations[@]}")
disk_sha=$(test_vm_cache_sha256 "${prepared_disk}")
@@ -341,14 +341,14 @@ build_local() {
created=$(date -u +%Y-%m-%dT%H:%M:%SZ)
jq -n \
--argjson schema "${TEST_VM_CACHE_SCHEMA_VERSION}" \
--argjson schema "${TEST_GUEST_CACHE_SCHEMA_VERSION}" \
--arg key "${cache_key}" \
--arg distro "${distro}" \
--arg os_version "${TEST_VM_OS_VERSION}" \
--arg os_version "${TEST_GUEST_OS_VERSION}" \
--arg architecture "$(test_vm_cache_oci_architecture)" \
--arg base_image_url "${TEST_VM_IMAGE_URL}" \
--arg base_image_hash "${TEST_VM_IMAGE_HASH}" \
--arg disk_layout "${TEST_VM_CACHE_DISK_LAYOUT}" \
--arg base_image_url "${TEST_GUEST_IMAGE_URL}" \
--arg base_image_hash "${TEST_GUEST_IMAGE_HASH}" \
--arg disk_layout "${TEST_GUEST_CACHE_DISK_LAYOUT}" \
--arg disk_sha256 "${disk_sha}" \
--arg created "${created}" \
--argjson virtual_size "${virtual_size}" \
@@ -400,10 +400,10 @@ push_remote() {
(
cd "${push_dir}"
oras push \
--artifact-type "${TEST_VM_CACHE_ARTIFACT_TYPE}" \
--artifact-type "${TEST_GUEST_CACHE_ARTIFACT_TYPE}" \
"${remote_ref}" \
"metadata.json:${TEST_VM_CACHE_METADATA_TYPE}" \
"disk.qcow2.zst:${TEST_VM_CACHE_DISK_TYPE}"
"metadata.json:${TEST_GUEST_CACHE_METADATA_TYPE}" \
"disk.qcow2.zst:${TEST_GUEST_CACHE_DISK_TYPE}"
)
rm -rf "${push_dir}"
echo "==> Cache push complete: ${remote_ref}"
+23 -23
View File
@@ -29,14 +29,14 @@ let
mkDistroProfile =
name: distro:
pkgs.writeText "openshell-test-guest-${name}" ''
TEST_VM_IMAGE_DRV=${builtins.unsafeDiscardStringContext distro.image.drvPath}
TEST_VM_IMAGE_URL=${pkgs.lib.escapeShellArg distro.imageUrl}
TEST_VM_IMAGE_HASH=${pkgs.lib.escapeShellArg distro.imageHash}
TEST_VM_OS_ID=${pkgs.lib.escapeShellArg distro.osId}
TEST_VM_OS_VERSION=${pkgs.lib.escapeShellArg distro.osVersion}
TEST_VM_PACKAGE_FAMILY=${pkgs.lib.escapeShellArg distro.packageFamily}
export TEST_VM_IMAGE_DRV TEST_VM_IMAGE_URL TEST_VM_IMAGE_HASH
export TEST_VM_OS_ID TEST_VM_OS_VERSION TEST_VM_PACKAGE_FAMILY
TEST_GUEST_IMAGE_DRV=${builtins.unsafeDiscardStringContext distro.image.drvPath}
TEST_GUEST_IMAGE_URL=${pkgs.lib.escapeShellArg distro.imageUrl}
TEST_GUEST_IMAGE_HASH=${pkgs.lib.escapeShellArg distro.imageHash}
TEST_GUEST_OS_ID=${pkgs.lib.escapeShellArg distro.osId}
TEST_GUEST_OS_VERSION=${pkgs.lib.escapeShellArg distro.osVersion}
TEST_GUEST_PACKAGE_FAMILY=${pkgs.lib.escapeShellArg distro.packageFamily}
export TEST_GUEST_IMAGE_DRV TEST_GUEST_IMAGE_URL TEST_GUEST_IMAGE_HASH
export TEST_GUEST_OS_ID TEST_GUEST_OS_VERSION TEST_GUEST_PACKAGE_FAMILY
'';
distroCatalog = pkgs.linkFarm "openshell-test-guest-distros" (
@@ -65,21 +65,21 @@ let
];
runtimeEnvironment = ''
export OPENSHELL_TEST_VM_RUNTIME=1
export OPENSHELL_TEST_VM_DISTROS=${distroCatalog}
export OPENSHELL_TEST_VM_CONFIGURATIONS=${configurationCatalog}
export OPENSHELL_TEST_VM_CACHE_LIB=${./cache-lib.sh}
export OPENSHELL_TEST_VM_CACHE_SEAL=${./cache-seal.sh}
export OPENSHELL_TEST_VM_RUNNER=${./run.sh}
export TEST_VM_BASH=${pkgs.bash}/bin/bash
export TEST_VM_QEMU=${qemuBinary}
export TEST_VM_FIRMWARE_CODE=${pkgs.OVMF.firmware}
export TEST_VM_FIRMWARE_VARS=${pkgs.OVMF.variables}
export TEST_VM_MACHINE=${if isAarch64 then "virt" else "q35"}
export TEST_VM_ACCELERATOR=${if isDarwin then "hvf" else "kvm"}
export TEST_VM_ARCHITECTURE=${architecture}
export TEST_VM_ANSIBLE_VERSION=${pkgs.python3Packages.ansible-core.version}
export TEST_VM_CACHE_GENERATION=1
export OPENSHELL_TEST_GUEST_RUNTIME=1
export OPENSHELL_TEST_GUEST_DISTROS=${distroCatalog}
export OPENSHELL_TEST_GUEST_CONFIGURATIONS=${configurationCatalog}
export OPENSHELL_TEST_GUEST_CACHE_LIB=${./cache-lib.sh}
export OPENSHELL_TEST_GUEST_CACHE_SEAL=${./cache-seal.sh}
export OPENSHELL_TEST_GUEST_RUNNER=${./run.sh}
export TEST_GUEST_BASH=${pkgs.bash}/bin/bash
export TEST_GUEST_QEMU=${qemuBinary}
export TEST_GUEST_FIRMWARE_CODE=${pkgs.OVMF.firmware}
export TEST_GUEST_FIRMWARE_VARS=${pkgs.OVMF.variables}
export TEST_GUEST_MACHINE=${if isAarch64 then "virt" else "q35"}
export TEST_GUEST_ACCELERATOR=${if isDarwin then "hvf" else "kvm"}
export TEST_GUEST_ARCHITECTURE=${architecture}
export TEST_GUEST_ANSIBLE_VERSION=${pkgs.python3Packages.ansible-core.version}
export TEST_GUEST_CACHE_GENERATION=1
'';
runner = pkgs.writeShellApplication {
+59 -47
View File
@@ -25,10 +25,10 @@ With no COMMAND, the runner opens an interactive SSH session.
EOF
}
if [ "${OPENSHELL_TEST_VM_RUNTIME:-}" != 1 ] ||
[ ! -d "${OPENSHELL_TEST_VM_DISTROS:-}" ] ||
[ ! -d "${OPENSHELL_TEST_VM_CONFIGURATIONS:-}" ] ||
[ ! -r "${OPENSHELL_TEST_VM_CACHE_LIB:-}" ]; then
if [ "${OPENSHELL_TEST_GUEST_RUNTIME:-}" != 1 ] ||
[ ! -d "${OPENSHELL_TEST_GUEST_DISTROS:-}" ] ||
[ ! -d "${OPENSHELL_TEST_GUEST_CONFIGURATIONS:-}" ] ||
[ ! -r "${OPENSHELL_TEST_GUEST_CACHE_LIB:-}" ]; then
echo "run this script through 'nix run .#test-guest -- ...'" >&2
exit 2
fi
@@ -91,11 +91,11 @@ done
if [ "${list}" -eq 1 ]; then
echo "Distros:"
for entry in "${OPENSHELL_TEST_VM_DISTROS}"/*; do
for entry in "${OPENSHELL_TEST_GUEST_DISTROS}"/*; do
printf ' %s\n' "${entry##*/}"
done
echo "Configurations:"
for entry in "${OPENSHELL_TEST_VM_CONFIGURATIONS}"/*; do
for entry in "${OPENSHELL_TEST_GUEST_CONFIGURATIONS}"/*; do
printf ' %s\n' "${entry##*/}"
done
exit 0
@@ -107,17 +107,17 @@ if [ -z "${distro}" ]; then
exit 2
fi
if [[ ! ${distro} =~ ^[a-z0-9][a-z0-9-]*$ ]] ||
[ ! -r "${OPENSHELL_TEST_VM_DISTROS}/${distro}" ]; then
[ ! -r "${OPENSHELL_TEST_GUEST_DISTROS}/${distro}" ]; then
echo "unknown distro: ${distro}" >&2
exit 2
fi
# Distro profiles contain only trusted values generated into the Nix store.
# shellcheck disable=SC1090
. "${OPENSHELL_TEST_VM_DISTROS}/${distro}"
. "${OPENSHELL_TEST_GUEST_DISTROS}/${distro}"
for item in "${configurations[@]}"; do
if [[ ! ${item} =~ ^[a-z0-9][a-z0-9-]*$ ]] ||
[ ! -r "${OPENSHELL_TEST_VM_CONFIGURATIONS}/${item}" ]; then
[ ! -r "${OPENSHELL_TEST_GUEST_CONFIGURATIONS}/${item}" ]; then
echo "unknown configuration: ${item:-<empty>}" >&2
exit 2
fi
@@ -137,10 +137,10 @@ for package in "${packages[@]}"; do
echo "package does not exist: ${package}" >&2
exit 2
fi
case "${TEST_VM_PACKAGE_FAMILY}:${package}" in
case "${TEST_GUEST_PACKAGE_FAMILY}:${package}" in
deb:*.deb | rpm:*.rpm) ;;
*)
echo "${package} does not match the ${TEST_VM_PACKAGE_FAMILY} package family" >&2
echo "${package} does not match the ${TEST_GUEST_PACKAGE_FAMILY} package family" >&2
exit 2
;;
esac
@@ -169,34 +169,34 @@ done
test_vm_cpu=host
ssh_wait_seconds=180
if [ "${TEST_VM_ACCELERATOR}" = kvm ] &&
if [ "${TEST_GUEST_ACCELERATOR}" = kvm ] &&
{ [ ! -c /dev/kvm ] || [ ! -r /dev/kvm ] || [ ! -w /dev/kvm ]; }; then
echo "==> /dev/kvm is unavailable; falling back to QEMU/TCG"
TEST_VM_ACCELERATOR=tcg
TEST_GUEST_ACCELERATOR=tcg
test_vm_cpu=max
ssh_wait_seconds=600
fi
# shellcheck disable=SC1090
. "${OPENSHELL_TEST_VM_CACHE_LIB}"
. "${OPENSHELL_TEST_GUEST_CACHE_LIB}"
prepared_image=0
TEST_VM_IMAGE=
if [ -n "${OPENSHELL_TEST_VM_IMAGE_OVERRIDE:-}" ]; then
if [ ! -f "${OPENSHELL_TEST_VM_IMAGE_OVERRIDE}" ]; then
echo "prepared guest image does not exist: ${OPENSHELL_TEST_VM_IMAGE_OVERRIDE}" >&2
TEST_GUEST_IMAGE=
if [ -n "${OPENSHELL_TEST_GUEST_IMAGE_OVERRIDE:-}" ]; then
if [ ! -f "${OPENSHELL_TEST_GUEST_IMAGE_OVERRIDE}" ]; then
echo "prepared guest image does not exist: ${OPENSHELL_TEST_GUEST_IMAGE_OVERRIDE}" >&2
exit 2
fi
TEST_VM_IMAGE=${OPENSHELL_TEST_VM_IMAGE_OVERRIDE}
TEST_GUEST_IMAGE=${OPENSHELL_TEST_GUEST_IMAGE_OVERRIDE}
prepared_image=1
echo "==> Using explicit prepared guest image"
elif [ "${OPENSHELL_TEST_VM_CACHE_DISABLE:-0}" -ne 1 ]; then
elif [ "${OPENSHELL_TEST_GUEST_CACHE_DISABLE:-0}" -ne 1 ]; then
cache_root=$(test_vm_cache_root)
cache_key=$(test_vm_cache_key "${distro}" "${configurations[@]}")
if test_vm_cache_local_entry_valid \
"${cache_root}" "${cache_key}" "${distro}" "${configurations[@]}"; then
cache_entry=$(test_vm_cache_entry_dir "${cache_root}" "${cache_key}")
TEST_VM_IMAGE="${cache_entry}/disk.qcow2"
TEST_GUEST_IMAGE="${cache_entry}/disk.qcow2"
prepared_image=1
echo "==> Cache local hit: ${cache_entry}"
fi
@@ -204,7 +204,7 @@ fi
if [ "${prepared_image}" -eq 0 ]; then
echo "==> Realizing the pinned ${distro} cloud image"
TEST_VM_IMAGE=$(nix build --no-link --print-out-paths "${TEST_VM_IMAGE_DRV}^out")
TEST_GUEST_IMAGE=$(nix build --no-link --print-out-paths "${TEST_GUEST_IMAGE_DRV}^out")
fi
umask 077
@@ -215,6 +215,7 @@ overlay=${run_dir}/disk.qcow2
seed=${run_dir}/seed.iso
vars=${run_dir}/firmware-vars.fd
private_key=${run_dir}/id_ed25519
ssh_control_path=${run_dir}/ssh-control
serial_log=${run_dir}/serial.log
qemu_log=${run_dir}/qemu.log
qemu_pid=
@@ -285,29 +286,30 @@ EOF
xorriso -as mkisofs -quiet -output "${seed}" -volid cidata -joliet -rock user-data meta-data
)
qemu-img create -q -f qcow2 -F qcow2 -b "${TEST_VM_IMAGE}" "${overlay}"
qemu-img create -q -f qcow2 -F qcow2 -b "${TEST_GUEST_IMAGE}" "${overlay}"
if [ "${prepared_image}" -eq 0 ]; then
qemu-img resize -q "${overlay}" +16G
fi
cp "${TEST_VM_FIRMWARE_VARS}" "${vars}"
cp "${TEST_GUEST_FIRMWARE_VARS}" "${vars}"
chmod 0600 "${vars}"
for attempt in $(seq 1 5); do
ssh_port=${requested_ssh_port:-$(pick_free_port)}
: >"${qemu_log}"
echo "==> Booting ${distro} (${TEST_VM_ARCHITECTURE}) with QEMU/${TEST_VM_ACCELERATOR}"
"${TEST_VM_QEMU}" \
echo "==> Booting ${distro} (${TEST_GUEST_ARCHITECTURE}) with QEMU/${TEST_GUEST_ACCELERATOR}"
"${TEST_GUEST_QEMU}" \
-name "openshell-test-${distro}" \
-machine "${TEST_VM_MACHINE},accel=${TEST_VM_ACCELERATOR}" \
-machine "${TEST_GUEST_MACHINE},accel=${TEST_GUEST_ACCELERATOR}" \
-cpu "${test_vm_cpu}" \
-smp 4 \
-m 4096 \
-drive "if=pflash,format=raw,readonly=on,file=${TEST_VM_FIRMWARE_CODE}" \
-drive "if=pflash,format=raw,readonly=on,file=${TEST_GUEST_FIRMWARE_CODE}" \
-drive "if=pflash,format=raw,file=${vars}" \
-drive "if=none,format=qcow2,file=${overlay},id=osdisk" \
-device virtio-blk-pci,drive=osdisk \
-device virtio-blk-pci,drive=osdisk,bootindex=1 \
-drive "if=none,format=raw,readonly=on,file=${seed},id=seed" \
-device virtio-blk-pci,drive=seed \
-boot strict=on \
-netdev "user,id=net0,hostfwd=tcp:127.0.0.1:${ssh_port}-:22" \
-device virtio-net-pci,netdev=net0 \
-display none \
@@ -317,7 +319,7 @@ for attempt in $(seq 1 5); do
>/dev/null 2>"${qemu_log}" &
qemu_pid=$!
sleep 1
sleep 0.25
if kill -0 "${qemu_pid}" 2>/dev/null; then
break
fi
@@ -342,6 +344,9 @@ ssh_args=(
-p "${ssh_port}"
-o BatchMode=yes
-o ConnectTimeout=5
-o ControlMaster=auto
-o ControlPersist=60
-o "ControlPath=${ssh_control_path}"
-o LogLevel=ERROR
-o StrictHostKeyChecking=no
-o UserKnownHostsFile=/dev/null
@@ -352,6 +357,9 @@ scp_args=(
-P "${ssh_port}"
-o BatchMode=yes
-o ConnectTimeout=5
-o ControlMaster=auto
-o ControlPersist=60
-o "ControlPath=${ssh_control_path}"
-o LogLevel=ERROR
-o StrictHostKeyChecking=no
-o UserKnownHostsFile=/dev/null
@@ -359,18 +367,18 @@ scp_args=(
echo "==> Waiting up to ${ssh_wait_seconds} seconds for SSH on 127.0.0.1:${ssh_port}"
ssh_ready=0
for _ in $(seq 1 "$((ssh_wait_seconds / 2))"); do
for _ in $(seq 1 "$((ssh_wait_seconds * 4))"); do
if ! kill -0 "${qemu_pid}" 2>/dev/null; then
wait "${qemu_pid}" || true
qemu_pid=
echo "QEMU exited before SSH became ready" >&2
exit 1
fi
if ssh "${ssh_args[@]}" openshell@127.0.0.1 true 2>/dev/null; then
if ssh "${ssh_args[@]}" -o ConnectTimeout=1 openshell@127.0.0.1 true 2>/dev/null; then
ssh_ready=1
break
fi
sleep 2
sleep 0.25
done
if [ "${ssh_ready}" -ne 1 ]; then
echo "SSH did not become ready within ${ssh_wait_seconds} seconds" >&2
@@ -381,12 +389,10 @@ echo "==> Validating ${distro}"
# Profile values come from the trusted Nix-generated catalog.
# shellcheck disable=SC2029
ssh "${ssh_args[@]}" openshell@127.0.0.1 \
"set -eu; . /etc/os-release; test \"\${ID}\" = '${TEST_VM_OS_ID}'; case \"\${VERSION_ID}\" in '${TEST_VM_OS_VERSION}'*) ;; *) exit 1 ;; esac; test \"\$(uname -m)\" = '${TEST_VM_ARCHITECTURE}'"
"set -eu; set +e; sudo cloud-init status --wait >/dev/null; status=\$?; set -e; [ \"\${status}\" -eq 0 ] || [ \"\${status}\" -eq 2 ]; . /etc/os-release; test \"\${ID}\" = '${TEST_GUEST_OS_ID}'; case \"\${VERSION_ID}\" in '${TEST_GUEST_OS_VERSION}'*) ;; *) exit 1 ;; esac; test \"\$(uname -m)\" = '${TEST_GUEST_ARCHITECTURE}'"
# cloud-init returns 2 when it completes with recoverable errors. Fedora can
# report that status for an initial transient-hostname warning even though the
# requested user and SSH configuration were applied successfully.
ssh "${ssh_args[@]}" openshell@127.0.0.1 \
'sudo cloud-init status --wait >/dev/null; status=$?; [ "$status" -eq 0 ] || [ "$status" -eq 2 ]'
cat >"${ansible_config}" <<EOF
[defaults]
@@ -407,7 +413,7 @@ if [ "${prepared_image}" -eq 0 ]; then
for item in "${configurations[@]}"; do
echo "==> Applying configuration: ${item}"
ANSIBLE_CONFIG="${ansible_config}" ANSIBLE_NOCOLOR=1 \
ansible-playbook "${OPENSHELL_TEST_VM_CONFIGURATIONS}/${item}"
ansible-playbook "${OPENSHELL_TEST_GUEST_CONFIGURATIONS}/${item}"
done
else
echo "==> Reusing cached configuration: ${configurations[*]:-base image}"
@@ -422,7 +428,7 @@ for package in "${packages[@]}"; do
remote_package="/home/openshell/${package_name}"
echo "==> Installing package: ${package_name}"
scp "${scp_args[@]}" "${package}" "openshell@127.0.0.1:${remote_package}"
case "${TEST_VM_PACKAGE_FAMILY}" in
case "${TEST_GUEST_PACKAGE_FAMILY}" in
deb)
# The remote path uses a restricted package filename.
# shellcheck disable=SC2029
@@ -466,12 +472,18 @@ else
fi
echo "==> Shutting down ${distro}"
ssh "${ssh_args[@]}" openshell@127.0.0.1 'sudo systemctl poweroff' >/dev/null 2>&1 || true
for _ in $(seq 1 30); do
if ! kill -0 "${qemu_pid}" 2>/dev/null; then
wait "${qemu_pid}" || true
qemu_pid=
break
fi
sleep 1
done
if [ "${keep}" -eq 1 ]; then
ssh "${ssh_args[@]}" openshell@127.0.0.1 'sudo systemctl poweroff' >/dev/null 2>&1 || true
for _ in $(seq 1 120); do
if ! kill -0 "${qemu_pid}" 2>/dev/null; then
wait "${qemu_pid}" || true
qemu_pid=
break
fi
sleep 0.25
done
else
kill "${qemu_pid}" 2>/dev/null || true
wait "${qemu_pid}" 2>/dev/null || true
qemu_pid=
fi