diff --git a/nix/test-guest/README.md b/nix/test-guest/README.md index e3c7f6ef6..f6de6d849 100644 --- a/nix/test-guest/README.md +++ b/nix/test-guest/README.md @@ -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: diff --git a/nix/test-guest/cache-lib.sh b/nix/test-guest/cache-lib.sh index 5b37f9189..1e6f76b19 100644 --- a/nix/test-guest/cache-lib.sh +++ b/nix/test-guest/cache-lib.sh @@ -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 diff --git a/nix/test-guest/cache.sh b/nix/test-guest/cache.sh index b986077e8..b94ecd7bd 100644 --- a/nix/test-guest/cache.sh +++ b/nix/test-guest/cache.sh @@ -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:-}" >&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}" diff --git a/nix/test-guest/default.nix b/nix/test-guest/default.nix index 9863f6d76..f34fb062a 100644 --- a/nix/test-guest/default.nix +++ b/nix/test-guest/default.nix @@ -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 { diff --git a/nix/test-guest/run.sh b/nix/test-guest/run.sh index bf92e639a..07d27abe8 100644 --- a/nix/test-guest/run.sh +++ b/nix/test-guest/run.sh @@ -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:-}" >&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}" < 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