From 1ff167998450aa45dbecbeb819470c971b9e97ff Mon Sep 17 00:00:00 2001 From: Peter Steinberger Date: Fri, 27 Mar 2026 02:14:56 +0000 Subject: [PATCH] test: harden parallels windows smoke polling --- scripts/e2e/parallels-npm-update-smoke.sh | 85 +++++++++++++++++-- scripts/e2e/parallels-windows-smoke.sh | 99 ++++++++++++++++++++++- 2 files changed, 175 insertions(+), 9 deletions(-) diff --git a/scripts/e2e/parallels-npm-update-smoke.sh b/scripts/e2e/parallels-npm-update-smoke.sh index 063665ced98..204a7ac6a40 100755 --- a/scripts/e2e/parallels-npm-update-smoke.sh +++ b/scripts/e2e/parallels-npm-update-smoke.sh @@ -321,13 +321,59 @@ PY prlctl exec "$WINDOWS_VM" --current-user powershell.exe -NoProfile -ExecutionPolicy Bypass -EncodedCommand "$encoded" } +host_timeout_exec() { + local timeout_s="$1" + shift + HOST_TIMEOUT_S="$timeout_s" python3 - "$@" <<'PY' +import os +import subprocess +import sys + +timeout = int(os.environ["HOST_TIMEOUT_S"]) +args = sys.argv[1:] + +try: + completed = subprocess.run(args, stdout=subprocess.PIPE, stderr=subprocess.PIPE, timeout=timeout) +except subprocess.TimeoutExpired as exc: + if exc.stdout: + sys.stdout.buffer.write(exc.stdout) + if exc.stderr: + sys.stderr.buffer.write(exc.stderr) + sys.stderr.write(f"host timeout after {timeout}s\n") + raise SystemExit(124) + +if completed.stdout: + sys.stdout.buffer.write(completed.stdout) +if completed.stderr: + sys.stderr.buffer.write(completed.stderr) +raise SystemExit(completed.returncode) +PY +} + +guest_powershell_poll() { + local timeout_s="$1" + local script="$2" + local encoded + encoded="$( + SCRIPT_CONTENT="$script" python3 - <<'PY' +import base64 +import os + +script = "$ProgressPreference = 'SilentlyContinue'\n" + os.environ["SCRIPT_CONTENT"] +payload = script.encode("utf-16le") +print(base64.b64encode(payload).decode("ascii")) +PY + )" + host_timeout_exec "$timeout_s" prlctl exec "$WINDOWS_VM" --current-user powershell.exe -NoProfile -ExecutionPolicy Bypass -EncodedCommand "$encoded" +} + run_windows_script_via_log() { local script_url="$1" local tgz_url="$2" local head_short="$3" local session_id="$4" local runner_name log_name done_name done_status launcher_state - local start_seconds poll_deadline startup_checked + local start_seconds poll_deadline startup_checked poll_rc state_rc log_rc runner_name="openclaw-update-$RANDOM-$RANDOM.ps1" log_name="openclaw-update-$RANDOM-$RANDOM.log" done_name="openclaw-update-$RANDOM-$RANDOM.done" @@ -355,28 +401,55 @@ EOF )" while :; do + set +e done_status="$( - guest_powershell "\$done = Join-Path \$env:TEMP '$done_name'; if (Test-Path \$done) { (Get-Content \$done -Raw).Trim() }" + guest_powershell_poll 20 "\$done = Join-Path \$env:TEMP '$done_name'; if (Test-Path \$done) { (Get-Content \$done -Raw).Trim() }" )" + poll_rc=$? + set -e done_status="${done_status//$'\r'/}" + if [[ $poll_rc -ne 0 ]]; then + warn "windows update helper poll failed; retrying" + if (( SECONDS >= poll_deadline )); then + warn "windows update helper timed out while polling done file" + return 1 + fi + sleep 2 + continue + fi if [[ -n "$done_status" ]]; then - guest_powershell "\$log = Join-Path \$env:TEMP '$log_name'; if (Test-Path \$log) { Get-Content \$log }" + set +e + guest_powershell_poll 20 "\$log = Join-Path \$env:TEMP '$log_name'; if (Test-Path \$log) { Get-Content \$log }" + log_rc=$? + set -e + if [[ $log_rc -ne 0 ]]; then + warn "windows update helper log drain failed after completion" + fi [[ "$done_status" == "0" ]] return $? fi if [[ "$startup_checked" -eq 0 && $((SECONDS - start_seconds)) -ge 20 ]]; then + set +e launcher_state="$( - guest_powershell "\$runner = Join-Path \$env:TEMP '$runner_name'; \$log = Join-Path \$env:TEMP '$log_name'; \$done = Join-Path \$env:TEMP '$done_name'; 'runner=' + (Test-Path \$runner) + ' log=' + (Test-Path \$log) + ' done=' + (Test-Path \$done)" + guest_powershell_poll 20 "\$runner = Join-Path \$env:TEMP '$runner_name'; \$log = Join-Path \$env:TEMP '$log_name'; \$done = Join-Path \$env:TEMP '$done_name'; 'runner=' + (Test-Path \$runner) + ' log=' + (Test-Path \$log) + ' done=' + (Test-Path \$done)" )" + state_rc=$? + set -e launcher_state="${launcher_state//$'\r'/}" startup_checked=1 - if [[ "$launcher_state" == *"runner=False"* && "$launcher_state" == *"log=False"* && "$launcher_state" == *"done=False"* ]]; then + if [[ $state_rc -eq 0 && "$launcher_state" == *"runner=False"* && "$launcher_state" == *"log=False"* && "$launcher_state" == *"done=False"* ]]; then warn "windows update helper failed to materialize guest files" return 1 fi fi if (( SECONDS >= poll_deadline )); then - guest_powershell "\$log = Join-Path \$env:TEMP '$log_name'; if (Test-Path \$log) { Get-Content \$log }" + set +e + guest_powershell_poll 20 "\$log = Join-Path \$env:TEMP '$log_name'; if (Test-Path \$log) { Get-Content \$log }" + log_rc=$? + set -e + if [[ $log_rc -ne 0 ]]; then + warn "windows update helper log drain failed after timeout" + fi warn "windows update helper timed out waiting for done file" return 1 fi diff --git a/scripts/e2e/parallels-windows-smoke.sh b/scripts/e2e/parallels-windows-smoke.sh index 3fbdac5856a..917aade83b5 100644 --- a/scripts/e2e/parallels-windows-smoke.sh +++ b/scripts/e2e/parallels-windows-smoke.sh @@ -315,6 +315,35 @@ guest_exec() { prlctl exec "$VM_NAME" --current-user "$@" } +host_timeout_exec() { + local timeout_s="$1" + shift + HOST_TIMEOUT_S="$timeout_s" python3 - "$@" <<'PY' +import os +import subprocess +import sys + +timeout = int(os.environ["HOST_TIMEOUT_S"]) +args = sys.argv[1:] + +try: + completed = subprocess.run(args, stdout=subprocess.PIPE, stderr=subprocess.PIPE, timeout=timeout) +except subprocess.TimeoutExpired as exc: + if exc.stdout: + sys.stdout.buffer.write(exc.stdout) + if exc.stderr: + sys.stderr.buffer.write(exc.stderr) + sys.stderr.write(f"host timeout after {timeout}s\n") + raise SystemExit(124) + +if completed.stdout: + sys.stdout.buffer.write(completed.stdout) +if completed.stderr: + sys.stderr.buffer.write(completed.stderr) +raise SystemExit(completed.returncode) +PY +} + guest_powershell() { local script="$1" local encoded @@ -331,6 +360,23 @@ PY guest_exec powershell.exe -NoProfile -ExecutionPolicy Bypass -EncodedCommand "$encoded" } +guest_powershell_poll() { + local timeout_s="$1" + local script="$2" + local encoded + encoded="$( + SCRIPT_CONTENT="$script" python3 - <<'PY' +import base64 +import os + +script = "$ProgressPreference = 'SilentlyContinue'\n" + os.environ["SCRIPT_CONTENT"] +payload = script.encode("utf-16le") +print(base64.b64encode(payload).decode("ascii")) +PY + )" + host_timeout_exec "$timeout_s" prlctl exec "$VM_NAME" --current-user powershell.exe -NoProfile -ExecutionPolicy Bypass -EncodedCommand "$encoded" +} + guest_run_openclaw() { local env_name="${1:-}" local env_value="${2:-}" @@ -779,11 +825,15 @@ verify_version_contains() { } run_ref_onboard() { - local openai_key_q runner_name log_name done_name done_status + local openai_key_q runner_name log_name done_name done_status launcher_state + local poll_rc state_rc log_rc start_seconds poll_deadline startup_checked openai_key_q="$(ps_single_quote "$OPENAI_API_KEY_VALUE")" runner_name="openclaw-onboard-$RANDOM-$RANDOM.ps1" log_name="openclaw-onboard-$RANDOM-$RANDOM.log" done_name="openclaw-onboard-$RANDOM-$RANDOM.done" + start_seconds="$SECONDS" + poll_deadline=$((SECONDS + TIMEOUT_ONBOARD_S + 60)) + startup_checked=0 guest_powershell "$(cat <= poll_deadline )); then + warn "windows onboard helper timed out while polling done file" + return 1 + fi + sleep 2 + continue + fi if [[ -n "$done_status" ]]; then - guest_powershell "\$log = Join-Path \$env:TEMP '$log_name'; if (Test-Path \$log) { Get-Content \$log }" + set +e + guest_powershell_poll 20 "\$log = Join-Path \$env:TEMP '$log_name'; if (Test-Path \$log) { Get-Content \$log }" + log_rc=$? + set -e + if [[ $log_rc -ne 0 ]]; then + warn "windows onboard helper log drain failed after completion" + fi [[ "$done_status" == "0" ]] return $? fi + if [[ "$startup_checked" -eq 0 && $((SECONDS - start_seconds)) -ge 20 ]]; then + set +e + launcher_state="$( + guest_powershell_poll 20 "\$runner = Join-Path \$env:TEMP '$runner_name'; \$log = Join-Path \$env:TEMP '$log_name'; \$done = Join-Path \$env:TEMP '$done_name'; 'runner=' + (Test-Path \$runner) + ' log=' + (Test-Path \$log) + ' done=' + (Test-Path \$done)" + )" + state_rc=$? + set -e + launcher_state="${launcher_state//$'\r'/}" + startup_checked=1 + if [[ $state_rc -eq 0 && "$launcher_state" == *"runner=False"* && "$launcher_state" == *"log=False"* && "$launcher_state" == *"done=False"* ]]; then + warn "windows onboard helper failed to materialize guest files" + return 1 + fi + fi + if (( SECONDS >= poll_deadline )); then + set +e + guest_powershell_poll 20 "\$log = Join-Path \$env:TEMP '$log_name'; if (Test-Path \$log) { Get-Content \$log }" + log_rc=$? + set -e + if [[ $log_rc -ne 0 ]]; then + warn "windows onboard helper log drain failed after timeout" + fi + warn "windows onboard helper timed out waiting for done file" + return 1 + fi sleep 2 done }