mirror of
https://github.com/NVIDIA/OpenShell.git
synced 2026-10-23 06:30:28 +08:00
perf(test-guest): accelerate cached guest startup
Signed-off-by: Drew Newberry <anewberry@nvidia.com>
This commit is contained in:
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user