test: reset Windows native runs from clean baseline

This commit is contained in:
2026-09-09 07:13:14 +00:00
parent 665901afcd
commit 985a2c2422
5 changed files with 170 additions and 81 deletions
+77 -12
View File
@@ -18,7 +18,8 @@ Actions:
status read-only VM/snapshot identity and state check
prepare restore the declared baseline, boot headless, and verify Guest Additions
stage copy a bundle containing rvbox.exe and client.toml into the guest test root
run create/update and start the RVBox SCM service from the staged bundle
install install and start RVBox from the staged bundle through a fixture-only full-admin principal
run start the already-installed RVBox SCM service from the staged bundle
collect copy bounded guest artifacts to the local test-run directory
stop stop RVBox through SCM and request a graceful guest shutdown
reset stop the guest if necessary, restore the declared baseline, and leave it off
@@ -29,6 +30,8 @@ Optional environment:
RVBOX_TEST_VBOX_HOST, RVBOX_TEST_VBOX_VM, RVBOX_TEST_VBOX_VM_UUID,
RVBOX_TEST_VBOX_SNAPSHOT, RVBOX_TEST_VBOX_SNAPSHOT_UUID,
RVBOX_TEST_GUEST_USER, RVBOX_TEST_GUEST_PASSWORD_FILE (overrides)
RVBOX_TEST_PROVISIONER_USER, RVBOX_TEST_PROVISIONER_PASSWORD_FILE
(required by install; a fixture-only full-token administrator)
RVBOX_TEST_HOST_STAGE_ROOT (default /home/cabbage/.local/state/rvbox-test-runs)
RVBOX_TEST_RUN_ROOT (default .test-runs/windows-vm)
EOF
@@ -77,7 +80,7 @@ while [ "$#" -gt 0 ]; do
esac
done
case $action in status|prepare|stage|run|collect|stop|reset|recover) ;; *) usage >&2; fail "unknown action $action" ;; esac
case $action in status|prepare|stage|install|run|collect|stop|reset|recover) ;; *) usage >&2; fail "unknown action $action" ;; esac
if [ "$action" != status ]; then
[ -n "$run_id" ] || fail "$action requires --run-id"
safe_id "$run_id"
@@ -97,10 +100,12 @@ if [ -n "$endpoint" ]; then safe_word endpoint "$endpoint"; fi
: "${RVBOX_TEST_VBOX_HOST:=helium-remote}"
: "${RVBOX_TEST_VBOX_VM:=rvbox-win10-test}"
: "${RVBOX_TEST_VBOX_VM_UUID:=6cdc114f-71e5-4167-a394-e922e14e6f5c}"
: "${RVBOX_TEST_VBOX_SNAPSHOT:=baseline-disk-first}"
: "${RVBOX_TEST_VBOX_SNAPSHOT_UUID:=9430a9a4-754a-4b22-beaa-8dfd90043f5b}"
: "${RVBOX_TEST_VBOX_SNAPSHOT:=baseline-clean}"
: "${RVBOX_TEST_VBOX_SNAPSHOT_UUID:=5e79176a-3e56-4c5d-bb61-a405a6dcdd59}"
: "${RVBOX_TEST_GUEST_USER:=rvboxtest}"
: "${RVBOX_TEST_GUEST_PASSWORD_FILE:=/home/cabbage/.local/share/rvbox-secrets/rvbox-win10-test.password}"
provisioner_user=${RVBOX_TEST_PROVISIONER_USER:-}
provisioner_password_file=${RVBOX_TEST_PROVISIONER_PASSWORD_FILE:-}
for name in RVBOX_TEST_VBOX_HOST RVBOX_TEST_VBOX_VM RVBOX_TEST_VBOX_VM_UUID \
RVBOX_TEST_VBOX_SNAPSHOT RVBOX_TEST_VBOX_SNAPSHOT_UUID \
@@ -115,6 +120,8 @@ safe_word RVBOX_TEST_VBOX_SNAPSHOT "$RVBOX_TEST_VBOX_SNAPSHOT"
safe_word RVBOX_TEST_VBOX_SNAPSHOT_UUID "$RVBOX_TEST_VBOX_SNAPSHOT_UUID"
safe_word RVBOX_TEST_GUEST_USER "$RVBOX_TEST_GUEST_USER"
safe_word RVBOX_TEST_GUEST_PASSWORD_FILE "$RVBOX_TEST_GUEST_PASSWORD_FILE"
if [ -n "$provisioner_user" ]; then safe_word RVBOX_TEST_PROVISIONER_USER "$provisioner_user"; fi
if [ -n "$provisioner_password_file" ]; then safe_word RVBOX_TEST_PROVISIONER_PASSWORD_FILE "$provisioner_password_file"; fi
host_stage_root=${RVBOX_TEST_HOST_STAGE_ROOT:-/home/cabbage/.local/state/rvbox-test-runs}
run_root=${RVBOX_TEST_RUN_ROOT:-$repo_root/.test-runs/windows-vm}
@@ -145,6 +152,8 @@ host_stage=$8
guest_root=$9
shift 9
endpoint=$1
provisioner_user=$2
provisioner_password_file=$3
[ "$endpoint" = - ] && endpoint=
fail() { printf '%s\n' "remote test-host: $*" >&2; exit 2; }
@@ -206,6 +215,41 @@ guest_run() {
printf '%s\n' "$output" | tr -d '\r' | grep -qx 'RVBOX_GUEST_OK'
}
provisioner_run() {
# The clean baseline deliberately has no RVBox service. Guest Control's
# normal test account has a filtered UAC token, so only the fixture-only
# full-token administrator may perform the first machine-wide install.
[ -n "$provisioner_user" ] || fail "install requires RVBOX_TEST_PROVISIONER_USER"
[ -n "$provisioner_password_file" ] || fail "install requires RVBOX_TEST_PROVISIONER_PASSWORD_FILE"
output=$(VBoxManage guestcontrol "$vm" --username "$provisioner_user" --passwordfile "$provisioner_password_file" \
run "$@" </dev/null 2>&1) || true
printf '%s\n' "$output"
printf '%s\n' "$output" | tr -d '\r' | grep -qx 'RVBOX_GUEST_OK'
}
assert_provisioner_elevated() {
# This fixture is en-US. Check the mandatory label before allowing any
# machine-wide mutation, so an accidentally filtered automation account
# fails closed instead of silently weakening the test contract.
provisioner_run --exe 'C:\\Windows\\System32\\cmd.exe' --wait-stdout --wait-stderr --unquoted-args -- \
/d /s /c 'whoami /groups | findstr /c:"High Mandatory Level" >NUL && echo RVBOX_GUEST_OK' >/dev/null || \
fail "fixture provisioner is not a full high-integrity administrator"
}
assert_clean_guest() {
# A missing service is the authoritative clean-baseline condition. The
# test service name is unique, so do not delete or alter any other service.
guest_run --exe 'C:\Windows\System32\cmd.exe' --wait-stdout --wait-stderr --unquoted-args -- \
/d /s /c 'sc.exe query RVBoxClient >NUL 2>&1 & if errorlevel 1060 (echo RVBOX_GUEST_OK) else exit /b 1' >/dev/null || \
fail "reset baseline is not clean: RVBoxClient is already installed"
}
assert_staged_guest() {
guest_run --exe 'C:\Windows\System32\cmd.exe' --wait-stdout --wait-stderr --unquoted-args -- \
/d /s /c "if exist \"$guest_root\\rvbox.exe\" if exist \"$guest_root\\client.toml\" echo RVBOX_GUEST_OK" >/dev/null || \
fail "staged guest bundle is missing rvbox.exe or client.toml"
}
wait_guest_additions() {
attempt=0
while [ "$attempt" -lt 60 ]; do
@@ -257,7 +301,8 @@ case "$action" in
step prepare-vm-started
wait_guest_additions
step prepare-guest-additions-ready
step prepare-bootstrap-complete
assert_clean_guest
step prepare-clean-baseline-verified
;;
probe-identity)
assert_identity
@@ -298,10 +343,29 @@ case "$action" in
VBoxManage guestcontrol "$vm" --username "$guest_user" --passwordfile "$password_file" \
copyto "$host_stage/ca.pem" "$guest_root\\ca.pem" </dev/null
;;
install)
assert_identity
require_lease
[ "$(state)" = running ] || fail "install requires a running prepared VM"
assert_clean_guest
assert_staged_guest
assert_provisioner_elevated
# Guest Control cannot reliably wait for GUI-subsystem rvbox.exe. It
# may report a non-zero wrapper result after the process has started,
# therefore SCM state is the completion proof for this exact install.
VBoxManage guestcontrol "$vm" --username "$provisioner_user" --passwordfile "$provisioner_password_file" \
run --exe "$guest_root\\rvbox.exe" --unquoted-args -- \
--install-service --config "$guest_root\\client.toml" </dev/null >/dev/null 2>&1 || true
wait_service RUNNING
step install-scm-service-running
printf 'service=RVBoxClient state=RUNNING install=clean-baseline\n'
;;
run)
assert_identity
require_lease
[ "$(state)" = running ] || fail "run requires a running prepared VM"
assert_staged_guest
assert_provisioner_elevated
if [ -n "$endpoint" ]; then
endpoint_host=${endpoint%:*}
endpoint_port=${endpoint##*:}
@@ -309,15 +373,15 @@ case "$action" in
-NoProfile -NonInteractive -Command "if (-not (Test-NetConnection -ComputerName '$endpoint_host' -Port $endpoint_port -InformationLevel Quiet)) { exit 1 }; Write-Output RVBOX_GUEST_OK" >/dev/null
fi
image="\\\"$guest_root\\rvbox.exe\\\" --service --config \\\"$guest_root\\client.toml\\\""
guest_run --exe 'C:\Windows\System32\cmd.exe' --wait-stdout --wait-stderr --unquoted-args -- \
provisioner_run --exe 'C:\Windows\System32\cmd.exe' --wait-stdout --wait-stderr --unquoted-args -- \
/d /s /c "sc.exe query RVBoxClient >NUL 2>&1 && echo RVBOX_GUEST_OK" >/dev/null || \
fail "RVBoxClient is not fixture-bootstrapped or the test token lacks SCM query access"
guest_run --exe 'C:\Windows\System32\cmd.exe' --wait-stdout --wait-stderr --unquoted-args -- \
fail "RVBoxClient is not installed; run install from the clean baseline first"
provisioner_run --exe 'C:\Windows\System32\cmd.exe' --wait-stdout --wait-stderr --unquoted-args -- \
/d /s /c "sc.exe config RVBoxClient binPath= \"$image\" start= demand >NUL 2>&1 && echo RVBOX_GUEST_OK" >/dev/null || \
fail "fixture service does not grant the test token SERVICE_CHANGE_CONFIG"
guest_run --exe 'C:\Windows\System32\cmd.exe' --wait-stdout --wait-stderr --unquoted-args -- \
fail "fixture provisioner could not change RVBoxClient configuration"
provisioner_run --exe 'C:\Windows\System32\cmd.exe' --wait-stdout --wait-stderr --unquoted-args -- \
/d /s /c '(sc.exe start RVBoxClient >NUL 2>&1 || sc.exe query RVBoxClient | findstr /c:"RUNNING" >NUL) && echo RVBOX_GUEST_OK' >/dev/null || \
fail "fixture service did not accept SCM start"
fail "fixture provisioner could not start RVBoxClient"
wait_service RUNNING
printf 'service=RVBoxClient state=RUNNING\n'
;;
@@ -382,7 +446,8 @@ REMOTE
"$1" "$RVBOX_TEST_VBOX_VM" "$RVBOX_TEST_VBOX_VM_UUID" \
"$RVBOX_TEST_VBOX_SNAPSHOT" "$RVBOX_TEST_VBOX_SNAPSHOT_UUID" \
"$RVBOX_TEST_GUEST_USER" "$RVBOX_TEST_GUEST_PASSWORD_FILE" \
"$host_stage" "$guest_root" "$remote_endpoint" </dev/null
"$host_stage" "$guest_root" "$remote_endpoint" \
"$provisioner_user" "$provisioner_password_file" </dev/null
}
case $action in
+1 -1
View File
@@ -7,7 +7,7 @@ param(
[ValidatePattern('^[a-z0-9][a-z0-9-]{0,63}$')]
[string] $RunId = '',
[string] $VmName = $(if ($env:RVBOX_WINDOWS_VM) { $env:RVBOX_WINDOWS_VM } elseif ($env:RVBOX_TEST_VBOX_VM) { $env:RVBOX_TEST_VBOX_VM } else { 'rvbox-win10-test' }),
[string] $Snapshot = $(if ($env:RVBOX_WINDOWS_BASELINE_SNAPSHOT) { $env:RVBOX_WINDOWS_BASELINE_SNAPSHOT } elseif ($env:RVBOX_TEST_VBOX_SNAPSHOT) { $env:RVBOX_TEST_VBOX_SNAPSHOT } else { 'baseline-disk-first' })
[string] $Snapshot = $(if ($env:RVBOX_WINDOWS_BASELINE_SNAPSHOT) { $env:RVBOX_WINDOWS_BASELINE_SNAPSHOT } elseif ($env:RVBOX_TEST_VBOX_SNAPSHOT) { $env:RVBOX_TEST_VBOX_SNAPSHOT } else { 'baseline-clean' })
)
$ErrorActionPreference = 'Stop'