diff --git a/scripts/e2e/bundled-plugin-install-uninstall-docker.sh b/scripts/e2e/bundled-plugin-install-uninstall-docker.sh index 395c37dd9b3e..8daf3e8f024f 100755 --- a/scripts/e2e/bundled-plugin-install-uninstall-docker.sh +++ b/scripts/e2e/bundled-plugin-install-uninstall-docker.sh @@ -4,12 +4,62 @@ set -euo pipefail ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)" source "$ROOT_DIR/scripts/lib/docker-e2e-image.sh" IMAGE_NAME="$(docker_e2e_resolve_image "openclaw-bundled-plugin-install-uninstall-e2e" OPENCLAW_BUNDLED_PLUGIN_INSTALL_UNINSTALL_E2E_IMAGE)" +LIST_TIMEOUT_MS="$( + docker_e2e_read_positive_int_env OPENCLAW_BUNDLED_PLUGIN_LIST_TIMEOUT_MS 30000 +)" +LIST_MAX_BUFFER_BYTES="$( + docker_e2e_read_positive_int_env OPENCLAW_BUNDLED_PLUGIN_LIST_MAX_BUFFER_BYTES 4194304 +)" +RUNTIME_PORT_BASE="$(docker_e2e_read_tcp_port_env OPENCLAW_BUNDLED_PLUGIN_RUNTIME_PORT_BASE 19000)" +RUNTIME_OUTPUT_CHARS="$( + docker_e2e_read_positive_int_env OPENCLAW_BUNDLED_PLUGIN_RUNTIME_OUTPUT_CHARS 1048576 +)" +RUNTIME_LOG_SCAN_BYTES="$( + docker_e2e_read_positive_int_env OPENCLAW_BUNDLED_PLUGIN_RUNTIME_LOG_SCAN_BYTES 262144 +)" +RUNTIME_GATEWAY_LOG_BYTES="$( + docker_e2e_read_positive_int_env OPENCLAW_BUNDLED_PLUGIN_RUNTIME_GATEWAY_LOG_BYTES 16777216 +)" +RUNTIME_READY_MS="$( + docker_e2e_read_positive_int_env OPENCLAW_BUNDLED_PLUGIN_RUNTIME_READY_MS 900000 +)" +RUNTIME_RPC_MS="$(docker_e2e_read_positive_int_env OPENCLAW_BUNDLED_PLUGIN_RUNTIME_RPC_MS 60000)" +RUNTIME_RPC_READY_MS="$( + docker_e2e_read_positive_int_env OPENCLAW_BUNDLED_PLUGIN_RUNTIME_RPC_READY_MS 210000 +)" +RUNTIME_WATCHDOG_MS="$( + docker_e2e_read_positive_int_env OPENCLAW_BUNDLED_PLUGIN_RUNTIME_WATCHDOG_MS 1000 +)" +RUNTIME_COMMAND_MS="$( + docker_e2e_read_positive_int_env OPENCLAW_BUNDLED_PLUGIN_RUNTIME_COMMAND_MS 120000 +)" +RUNTIME_HTTP_MS="$(docker_e2e_read_positive_int_env OPENCLAW_BUNDLED_PLUGIN_RUNTIME_HTTP_MS 5000)" +RUNTIME_TEARDOWN_GRACE_MS="$( + docker_e2e_read_positive_int_env OPENCLAW_BUNDLED_PLUGIN_RUNTIME_TEARDOWN_GRACE_MS 10000 +)" +RUNTIME_TEARDOWN_KILL_GRACE_MS="$( + docker_e2e_read_positive_int_env OPENCLAW_BUNDLED_PLUGIN_RUNTIME_TEARDOWN_KILL_GRACE_MS 1000 +)" docker_e2e_build_or_reuse "$IMAGE_NAME" bundled-plugin-install-uninstall OPENCLAW_TEST_STATE_SCRIPT_B64="$(docker_e2e_test_state_shell_b64 bundled-plugin-install-uninstall empty)" DOCKER_ENV_ARGS=( -e COREPACK_ENABLE_DOWNLOAD_PROMPT=0 + -e "OPENCLAW_BUNDLED_PLUGIN_LIST_TIMEOUT_MS=$LIST_TIMEOUT_MS" + -e "OPENCLAW_BUNDLED_PLUGIN_LIST_MAX_BUFFER_BYTES=$LIST_MAX_BUFFER_BYTES" + -e "OPENCLAW_BUNDLED_PLUGIN_RUNTIME_PORT_BASE=$RUNTIME_PORT_BASE" + -e "OPENCLAW_BUNDLED_PLUGIN_RUNTIME_OUTPUT_CHARS=$RUNTIME_OUTPUT_CHARS" + -e "OPENCLAW_BUNDLED_PLUGIN_RUNTIME_LOG_SCAN_BYTES=$RUNTIME_LOG_SCAN_BYTES" + -e "OPENCLAW_BUNDLED_PLUGIN_RUNTIME_GATEWAY_LOG_BYTES=$RUNTIME_GATEWAY_LOG_BYTES" + -e "OPENCLAW_BUNDLED_PLUGIN_RUNTIME_READY_MS=$RUNTIME_READY_MS" + -e "OPENCLAW_BUNDLED_PLUGIN_RUNTIME_RPC_MS=$RUNTIME_RPC_MS" + -e "OPENCLAW_BUNDLED_PLUGIN_RUNTIME_RPC_READY_MS=$RUNTIME_RPC_READY_MS" + -e "OPENCLAW_BUNDLED_PLUGIN_RUNTIME_WATCHDOG_MS=$RUNTIME_WATCHDOG_MS" + -e "OPENCLAW_BUNDLED_PLUGIN_RUNTIME_COMMAND_MS=$RUNTIME_COMMAND_MS" + -e "OPENCLAW_BUNDLED_PLUGIN_RUNTIME_HTTP_MS=$RUNTIME_HTTP_MS" + -e "OPENCLAW_BUNDLED_PLUGIN_RUNTIME_TEARDOWN_GRACE_MS=$RUNTIME_TEARDOWN_GRACE_MS" + -e "OPENCLAW_BUNDLED_PLUGIN_RUNTIME_TEARDOWN_KILL_GRACE_MS=$RUNTIME_TEARDOWN_KILL_GRACE_MS" -e "OPENCLAW_TEST_STATE_SCRIPT_B64=$OPENCLAW_TEST_STATE_SCRIPT_B64" ) for env_name in \ @@ -17,21 +67,7 @@ for env_name in \ OPENCLAW_BUNDLED_PLUGIN_SWEEP_INDEX \ OPENCLAW_BUNDLED_PLUGIN_SWEEP_IDS \ OPENCLAW_BUNDLED_PLUGIN_SWEEP_COMMAND_TIMEOUT \ - OPENCLAW_BUNDLED_PLUGIN_LIST_TIMEOUT_MS \ - OPENCLAW_BUNDLED_PLUGIN_LIST_MAX_BUFFER_BYTES \ OPENCLAW_BUNDLED_PLUGIN_RUNTIME_SMOKE \ - OPENCLAW_BUNDLED_PLUGIN_RUNTIME_PORT_BASE \ - OPENCLAW_BUNDLED_PLUGIN_RUNTIME_OUTPUT_CHARS \ - OPENCLAW_BUNDLED_PLUGIN_RUNTIME_LOG_SCAN_BYTES \ - OPENCLAW_BUNDLED_PLUGIN_RUNTIME_GATEWAY_LOG_BYTES \ - OPENCLAW_BUNDLED_PLUGIN_RUNTIME_READY_MS \ - OPENCLAW_BUNDLED_PLUGIN_RUNTIME_RPC_MS \ - OPENCLAW_BUNDLED_PLUGIN_RUNTIME_RPC_READY_MS \ - OPENCLAW_BUNDLED_PLUGIN_RUNTIME_COMMAND_MS \ - OPENCLAW_BUNDLED_PLUGIN_RUNTIME_HTTP_MS \ - OPENCLAW_BUNDLED_PLUGIN_RUNTIME_TEARDOWN_GRACE_MS \ - OPENCLAW_BUNDLED_PLUGIN_RUNTIME_TEARDOWN_KILL_GRACE_MS \ - OPENCLAW_BUNDLED_PLUGIN_RUNTIME_WATCHDOG_MS \ OPENCLAW_BUNDLED_PLUGIN_TTS_LIVE_PROVIDER \ OPENCLAW_PLUGIN_LIFECYCLE_TRACE \ OPENAI_API_KEY; do diff --git a/scripts/e2e/upgrade-survivor-docker.sh b/scripts/e2e/upgrade-survivor-docker.sh index 302502337029..ec5ccab5aaae 100755 --- a/scripts/e2e/upgrade-survivor-docker.sh +++ b/scripts/e2e/upgrade-survivor-docker.sh @@ -18,11 +18,33 @@ UPDATE_RESTART_MODE="${OPENCLAW_UPGRADE_SURVIVOR_UPDATE_RESTART_MODE:-manual}" COMMAND_TIMEOUT="${OPENCLAW_UPGRADE_SURVIVOR_COMMAND_TIMEOUT:-900s}" START_BUDGET_SECONDS="$(openclaw_e2e_read_positive_int_env OPENCLAW_UPGRADE_SURVIVOR_START_BUDGET_SECONDS 90)" STATUS_BUDGET_SECONDS="$(openclaw_e2e_read_positive_int_env OPENCLAW_UPGRADE_SURVIVOR_STATUS_BUDGET_SECONDS 30)" +PROBE_TIMEOUT_MS="$(openclaw_e2e_read_nonnegative_int_env OPENCLAW_UPGRADE_SURVIVOR_PROBE_TIMEOUT_MS 60000)" +PROBE_ATTEMPT_TIMEOUT_MS="$( + openclaw_e2e_read_positive_int_env OPENCLAW_UPGRADE_SURVIVOR_PROBE_ATTEMPT_TIMEOUT_MS 5000 +)" +PROBE_MAX_BODY_BYTES="$( + openclaw_e2e_read_positive_int_env OPENCLAW_UPGRADE_SURVIVOR_PROBE_MAX_BODY_BYTES 1048576 +)" LANE_ARTIFACT_SUFFIX="${OPENCLAW_DOCKER_ALL_LANE_NAME:-default}" LANE_ARTIFACT_SUFFIX="${LANE_ARTIFACT_SUFFIX//[^A-Za-z0-9_.-]/_}" ARTIFACT_DIR="${OPENCLAW_UPGRADE_SURVIVOR_ARTIFACT_DIR:-$ROOT_DIR/.artifacts/upgrade-survivor/$LANE_ARTIFACT_SUFFIX}" ROOT_MANAGED_VPS="${OPENCLAW_UPGRADE_SURVIVOR_ROOT_MANAGED_VPS:-0}" DOCKER_RUN_USER_ARGS=() +PROBE_ENV_ARGS=( + -e OPENCLAW_UPGRADE_SURVIVOR_PROBE_TIMEOUT_MS="$PROBE_TIMEOUT_MS" + -e OPENCLAW_UPGRADE_SURVIVOR_PROBE_ATTEMPT_TIMEOUT_MS="$PROBE_ATTEMPT_TIMEOUT_MS" + -e OPENCLAW_UPGRADE_SURVIVOR_PROBE_MAX_BODY_BYTES="$PROBE_MAX_BODY_BYTES" +) +if [ -n "${OPENCLAW_UPGRADE_SURVIVOR_READYZ_ALLOW_FAILING:-}" ]; then + PROBE_ENV_ARGS+=( + -e OPENCLAW_UPGRADE_SURVIVOR_READYZ_ALLOW_FAILING="$OPENCLAW_UPGRADE_SURVIVOR_READYZ_ALLOW_FAILING" + ) +fi +if [ -n "${OPENCLAW_UPGRADE_SURVIVOR_READYZ_ALLOW_DEGRADED:-}" ]; then + PROBE_ENV_ARGS+=( + -e OPENCLAW_UPGRADE_SURVIVOR_READYZ_ALLOW_DEGRADED="$OPENCLAW_UPGRADE_SURVIVOR_READYZ_ALLOW_DEGRADED" + ) +fi cleanup_outer() { docker_e2e_cleanup_package_tgz "${PACKAGE_TGZ:-}" } @@ -112,6 +134,7 @@ if [ "${OPENCLAW_UPGRADE_SURVIVOR_PUBLISHED_BASELINE:-0}" = "1" ]; then -e OPENCLAW_UPGRADE_SURVIVOR_SUMMARY_JSON=/tmp/openclaw-upgrade-survivor-artifacts/summary.json \ -e OPENCLAW_UPGRADE_SURVIVOR_START_BUDGET_SECONDS="$START_BUDGET_SECONDS" \ -e OPENCLAW_UPGRADE_SURVIVOR_STATUS_BUDGET_SECONDS="$STATUS_BUDGET_SECONDS" \ + "${PROBE_ENV_ARGS[@]}" \ -v "$ARTIFACT_DIR:/tmp/openclaw-upgrade-survivor-artifacts" \ "${DOCKER_E2E_PACKAGE_ARGS[@]}" \ "${DOCKER_RUN_USER_ARGS[@]}" \ @@ -139,6 +162,7 @@ docker_e2e_run_with_harness \ -e OPENCLAW_UPGRADE_SURVIVOR_COMMAND_TIMEOUT="$COMMAND_TIMEOUT" \ -e OPENCLAW_UPGRADE_SURVIVOR_START_BUDGET_SECONDS="$START_BUDGET_SECONDS" \ -e OPENCLAW_UPGRADE_SURVIVOR_STATUS_BUDGET_SECONDS="$STATUS_BUDGET_SECONDS" \ + "${PROBE_ENV_ARGS[@]}" \ -v "$ARTIFACT_DIR:/tmp/openclaw-upgrade-survivor-artifacts" \ "${DOCKER_E2E_PACKAGE_ARGS[@]}" \ "${DOCKER_RUN_USER_ARGS[@]}" \ @@ -371,11 +395,20 @@ node scripts/e2e/lib/upgrade-survivor/probe-gateway.mjs \ --path /healthz \ --expect live \ --out /tmp/openclaw-upgrade-survivor-healthz.json -node scripts/e2e/lib/upgrade-survivor/probe-gateway.mjs \ - --base-url "http://127.0.0.1:$PORT" \ - --path /readyz \ - --expect ready \ - --out /tmp/openclaw-upgrade-survivor-readyz.json + +readyz_probe_args=( + --base-url "http://127.0.0.1:$PORT" + --path /readyz + --expect ready +) +if [ -n "${OPENCLAW_UPGRADE_SURVIVOR_READYZ_ALLOW_FAILING:-}" ]; then + readyz_probe_args+=(--allow-failing "$OPENCLAW_UPGRADE_SURVIVOR_READYZ_ALLOW_FAILING") +fi +if [ "${OPENCLAW_UPGRADE_SURVIVOR_READYZ_ALLOW_DEGRADED:-}" = "1" ]; then + readyz_probe_args+=(--allow-degraded-ready) +fi +readyz_probe_args+=(--out /tmp/openclaw-upgrade-survivor-readyz.json) +node scripts/e2e/lib/upgrade-survivor/probe-gateway.mjs "${readyz_probe_args[@]}" echo "Checking gateway RPC status..." status_start="$(node -e "process.stdout.write(String(Date.now()))")" diff --git a/test/scripts/docker-build-helper.test.ts b/test/scripts/docker-build-helper.test.ts index d58ce5a1e759..0e62dafa4a2b 100644 --- a/test/scripts/docker-build-helper.test.ts +++ b/test/scripts/docker-build-helper.test.ts @@ -2261,6 +2261,32 @@ fi expect(runner).toContain( 'openclaw gateway --port "$PORT" --bind loopback --allow-unconfigured', ); + expect(runner).toContain( + 'PROBE_TIMEOUT_MS="$(openclaw_e2e_read_nonnegative_int_env OPENCLAW_UPGRADE_SURVIVOR_PROBE_TIMEOUT_MS 60000)"', + ); + expect(runner).toContain( + "openclaw_e2e_read_positive_int_env OPENCLAW_UPGRADE_SURVIVOR_PROBE_ATTEMPT_TIMEOUT_MS 5000", + ); + expect(runner).toContain( + "openclaw_e2e_read_positive_int_env OPENCLAW_UPGRADE_SURVIVOR_PROBE_MAX_BODY_BYTES 1048576", + ); + expect(runner).toContain('-e OPENCLAW_UPGRADE_SURVIVOR_PROBE_TIMEOUT_MS="$PROBE_TIMEOUT_MS"'); + expect(runner).toContain( + '-e OPENCLAW_UPGRADE_SURVIVOR_PROBE_ATTEMPT_TIMEOUT_MS="$PROBE_ATTEMPT_TIMEOUT_MS"', + ); + expect(runner).toContain( + '-e OPENCLAW_UPGRADE_SURVIVOR_PROBE_MAX_BODY_BYTES="$PROBE_MAX_BODY_BYTES"', + ); + expect(runner).toContain("readyz_probe_args=("); + expect(runner).toContain( + 'readyz_probe_args+=(--allow-failing "$OPENCLAW_UPGRADE_SURVIVOR_READYZ_ALLOW_FAILING")', + ); + expect(runner).toContain("readyz_probe_args+=(--allow-degraded-ready)"); + expect(runner).toContain( + 'node scripts/e2e/lib/upgrade-survivor/probe-gateway.mjs "${readyz_probe_args[@]}"', + ); + expect(runner).toContain("OPENCLAW_UPGRADE_SURVIVOR_READYZ_ALLOW_FAILING"); + expect(runner).toContain("OPENCLAW_UPGRADE_SURVIVOR_READYZ_ALLOW_DEGRADED"); expect(publishedRunner).toContain( 'COMMAND_TIMEOUT="${OPENCLAW_UPGRADE_SURVIVOR_COMMAND_TIMEOUT:-900s}"', @@ -2316,6 +2342,9 @@ fi it.each([ ["start budget", "OPENCLAW_UPGRADE_SURVIVOR_START_BUDGET_SECONDS", "90s"], ["status budget", "OPENCLAW_UPGRADE_SURVIVOR_STATUS_BUDGET_SECONDS", "30s"], + ["probe timeout", "OPENCLAW_UPGRADE_SURVIVOR_PROBE_TIMEOUT_MS", "soon"], + ["probe attempt timeout", "OPENCLAW_UPGRADE_SURVIVOR_PROBE_ATTEMPT_TIMEOUT_MS", "0"], + ["probe body cap", "OPENCLAW_UPGRADE_SURVIVOR_PROBE_MAX_BODY_BYTES", "64bytes"], ])("rejects invalid upgrade survivor Docker %s before Docker setup", (_label, envName, value) => { const result = spawnSync("bash", [UPGRADE_SURVIVOR_DOCKER_E2E_PATH], { encoding: "utf8", @@ -3863,6 +3892,30 @@ output="$(cat "$sampler_log")" expect(probe + runtimeSmoke, `${envName} consumed by probe/runtime smoke`).toContain(envName); } expect(runner).toContain("OPENCLAW_PLUGIN_LIFECYCLE_TRACE"); + for (const [envName, fallback] of [ + ["OPENCLAW_BUNDLED_PLUGIN_LIST_TIMEOUT_MS", "30000"], + ["OPENCLAW_BUNDLED_PLUGIN_LIST_MAX_BUFFER_BYTES", "4194304"], + ["OPENCLAW_BUNDLED_PLUGIN_RUNTIME_OUTPUT_CHARS", "1048576"], + ["OPENCLAW_BUNDLED_PLUGIN_RUNTIME_LOG_SCAN_BYTES", "262144"], + ["OPENCLAW_BUNDLED_PLUGIN_RUNTIME_GATEWAY_LOG_BYTES", "16777216"], + ["OPENCLAW_BUNDLED_PLUGIN_RUNTIME_READY_MS", "900000"], + ["OPENCLAW_BUNDLED_PLUGIN_RUNTIME_RPC_MS", "60000"], + ["OPENCLAW_BUNDLED_PLUGIN_RUNTIME_RPC_READY_MS", "210000"], + ["OPENCLAW_BUNDLED_PLUGIN_RUNTIME_WATCHDOG_MS", "1000"], + ["OPENCLAW_BUNDLED_PLUGIN_RUNTIME_COMMAND_MS", "120000"], + ["OPENCLAW_BUNDLED_PLUGIN_RUNTIME_HTTP_MS", "5000"], + ["OPENCLAW_BUNDLED_PLUGIN_RUNTIME_TEARDOWN_GRACE_MS", "10000"], + ["OPENCLAW_BUNDLED_PLUGIN_RUNTIME_TEARDOWN_KILL_GRACE_MS", "1000"], + ] as const) { + expect(runner, `${envName} host validation`).toContain( + `docker_e2e_read_positive_int_env ${envName} ${fallback}`, + ); + expect(runner, `${envName} Docker forwarding`).toContain(`-e "${envName}=`); + } + expect(runner).toContain( + "docker_e2e_read_tcp_port_env OPENCLAW_BUNDLED_PLUGIN_RUNTIME_PORT_BASE 19000", + ); + expect(runner).toContain('-e "OPENCLAW_BUNDLED_PLUGIN_RUNTIME_PORT_BASE=$RUNTIME_PORT_BASE"'); expect(runner).toContain("scripts/e2e/lib/bundled-plugin-install-uninstall/sweep.sh"); expect(runner).toContain('tee "$RUN_LOG"'); expect(runner).not.toContain('cat "$RUN_LOG"'); @@ -3883,6 +3936,30 @@ output="$(cat "$sampler_log")" expect(sweep).toContain("assert-uninstalled"); }); + it.each([ + ["list timeout", "OPENCLAW_BUNDLED_PLUGIN_LIST_TIMEOUT_MS", "100ms"], + ["runtime port base", "OPENCLAW_BUNDLED_PLUGIN_RUNTIME_PORT_BASE", "99999"], + ["runtime log scan", "OPENCLAW_BUNDLED_PLUGIN_RUNTIME_LOG_SCAN_BYTES", "64bytes"], + ["runtime command timeout", "OPENCLAW_BUNDLED_PLUGIN_RUNTIME_COMMAND_MS", "soon"], + ["runtime teardown grace", "OPENCLAW_BUNDLED_PLUGIN_RUNTIME_TEARDOWN_GRACE_MS", "0"], + ])( + "rejects invalid bundled plugin Docker %s values before Docker setup", + (_label, envName, value) => { + const result = spawnSync("bash", [BUNDLED_PLUGIN_INSTALL_UNINSTALL_E2E_PATH], { + encoding: "utf8", + env: { + ...process.env, + OPENCLAW_SKIP_DOCKER_BUILD: "1", + [envName]: value, + }, + }); + + expect(result.status).toBe(2); + expect(result.stderr).toContain(`invalid ${envName}: ${value}`); + expect(result.stderr).not.toContain("Docker image not found"); + }, + ); + it("passes installer tag env to bash, not curl", () => { const runner = readFileSync(INSTALL_E2E_RUNNER_PATH, "utf8");