mirror of
https://github.com/openclaw/openclaw.git
synced 2026-08-07 02:22:46 +00:00
fix(rpc): resolve taskkill from system32 in script probes
This commit is contained in:
@@ -8,6 +8,7 @@ import path from "node:path";
|
||||
import process from "node:process";
|
||||
import { setTimeout as delay } from "node:timers/promises";
|
||||
import { fileURLToPath, pathToFileURL } from "node:url";
|
||||
import { resolveWindowsTaskkillPath } from "../lib/windows-taskkill.mjs";
|
||||
|
||||
const PLUGIN_SPEC =
|
||||
process.env.OPENCLAW_KITCHEN_SINK_NPM_SPEC || "npm:@openclaw/kitchen-sink@latest";
|
||||
@@ -575,11 +576,12 @@ function commandProcessTreeIsAlive(child) {
|
||||
}
|
||||
|
||||
function signalWindowsProcessTree(pid, signal, runTaskkill = childProcess.spawnSync) {
|
||||
const taskkillPath = resolveWindowsTaskkillPath();
|
||||
const args = ["/PID", String(pid), "/T"];
|
||||
if (signal === "SIGKILL") {
|
||||
args.push("/F");
|
||||
}
|
||||
const result = runTaskkill("taskkill", args, { stdio: "ignore" });
|
||||
const result = runTaskkill(taskkillPath, args, { stdio: "ignore" });
|
||||
return !result?.error && result?.status === 0;
|
||||
}
|
||||
|
||||
|
||||
@@ -10,6 +10,7 @@ import path from "node:path";
|
||||
import { performance } from "node:perf_hooks";
|
||||
import { fileURLToPath, pathToFileURL } from "node:url";
|
||||
import { readBoundedResponseText } from "./lib/bounded-response.mjs";
|
||||
import { resolveWindowsTaskkillPath } from "./lib/windows-taskkill.mjs";
|
||||
|
||||
const DEFAULT_METHODS = ["health", "config.get"];
|
||||
const DEFAULT_ITERATIONS = 10;
|
||||
@@ -301,16 +302,17 @@ export function signalGatewayProcess(
|
||||
}
|
||||
}
|
||||
if (platform === "win32" && typeof child.pid === "number") {
|
||||
const taskkillPath = resolveWindowsTaskkillPath();
|
||||
const args = ["/PID", String(child.pid), "/T"];
|
||||
if (signal === "SIGKILL") {
|
||||
args.push("/F");
|
||||
}
|
||||
const result = runTaskkill("taskkill", args, { stdio: "ignore" });
|
||||
const result = runTaskkill(taskkillPath, args, { stdio: "ignore" });
|
||||
if (!result?.error && result?.status === 0) {
|
||||
return true;
|
||||
}
|
||||
if (signal !== "SIGKILL") {
|
||||
const forceResult = runTaskkill("taskkill", [...args, "/F"], { stdio: "ignore" });
|
||||
const forceResult = runTaskkill(taskkillPath, [...args, "/F"], { stdio: "ignore" });
|
||||
if (!forceResult?.error && forceResult?.status === 0) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -62,10 +62,15 @@ import {
|
||||
validateCliArgs,
|
||||
waitForGatewayReady,
|
||||
} from "../../scripts/e2e/kitchen-sink-rpc-walk.mjs";
|
||||
import { resolveWindowsTaskkillPath } from "../../scripts/lib/windows-taskkill.mjs";
|
||||
import { cleanupTempDirs, makeTempDir } from "../helpers/temp-dir.js";
|
||||
|
||||
const posixIt = process.platform === "win32" ? it.skip : it;
|
||||
|
||||
function expectedTaskkillPath(): string {
|
||||
return resolveWindowsTaskkillPath();
|
||||
}
|
||||
|
||||
afterEach(() => {
|
||||
vi.restoreAllMocks();
|
||||
vi.useRealTimers();
|
||||
@@ -296,9 +301,14 @@ describe("kitchen-sink RPC gateway teardown", () => {
|
||||
runTaskkill,
|
||||
}),
|
||||
).toBe(true);
|
||||
expect(runTaskkill).toHaveBeenNthCalledWith(1, "taskkill", ["/PID", "12345", "/T"], {
|
||||
stdio: "ignore",
|
||||
});
|
||||
expect(runTaskkill).toHaveBeenNthCalledWith(
|
||||
1,
|
||||
expectedTaskkillPath(),
|
||||
["/PID", "12345", "/T"],
|
||||
{
|
||||
stdio: "ignore",
|
||||
},
|
||||
);
|
||||
|
||||
expect(
|
||||
signalGateway(child, "SIGKILL", killProcess, {
|
||||
@@ -306,9 +316,14 @@ describe("kitchen-sink RPC gateway teardown", () => {
|
||||
runTaskkill,
|
||||
}),
|
||||
).toBe(true);
|
||||
expect(runTaskkill).toHaveBeenNthCalledWith(2, "taskkill", ["/PID", "12345", "/T", "/F"], {
|
||||
stdio: "ignore",
|
||||
});
|
||||
expect(runTaskkill).toHaveBeenNthCalledWith(
|
||||
2,
|
||||
expectedTaskkillPath(),
|
||||
["/PID", "12345", "/T", "/F"],
|
||||
{
|
||||
stdio: "ignore",
|
||||
},
|
||||
);
|
||||
expect(killProcess).not.toHaveBeenCalled();
|
||||
expect(child.kill).not.toHaveBeenCalled();
|
||||
});
|
||||
@@ -331,12 +346,22 @@ describe("kitchen-sink RPC gateway teardown", () => {
|
||||
}),
|
||||
).toBe(true);
|
||||
|
||||
expect(runTaskkill).toHaveBeenNthCalledWith(1, "taskkill", ["/PID", "12345", "/T"], {
|
||||
stdio: "ignore",
|
||||
});
|
||||
expect(runTaskkill).toHaveBeenNthCalledWith(2, "taskkill", ["/PID", "12345", "/T", "/F"], {
|
||||
stdio: "ignore",
|
||||
});
|
||||
expect(runTaskkill).toHaveBeenNthCalledWith(
|
||||
1,
|
||||
expectedTaskkillPath(),
|
||||
["/PID", "12345", "/T"],
|
||||
{
|
||||
stdio: "ignore",
|
||||
},
|
||||
);
|
||||
expect(runTaskkill).toHaveBeenNthCalledWith(
|
||||
2,
|
||||
expectedTaskkillPath(),
|
||||
["/PID", "12345", "/T", "/F"],
|
||||
{
|
||||
stdio: "ignore",
|
||||
},
|
||||
);
|
||||
expect(killProcess).not.toHaveBeenCalled();
|
||||
expect(child.kill).not.toHaveBeenCalled();
|
||||
});
|
||||
@@ -702,17 +727,27 @@ setInterval(() => {}, 1000);
|
||||
platform: "win32",
|
||||
runTaskkill,
|
||||
});
|
||||
expect(runTaskkill).toHaveBeenNthCalledWith(1, "taskkill", ["/PID", "12345", "/T"], {
|
||||
stdio: "ignore",
|
||||
});
|
||||
expect(runTaskkill).toHaveBeenNthCalledWith(
|
||||
1,
|
||||
expectedTaskkillPath(),
|
||||
["/PID", "12345", "/T"],
|
||||
{
|
||||
stdio: "ignore",
|
||||
},
|
||||
);
|
||||
|
||||
signalProcessGroup(child, "SIGKILL", {
|
||||
platform: "win32",
|
||||
runTaskkill,
|
||||
});
|
||||
expect(runTaskkill).toHaveBeenNthCalledWith(2, "taskkill", ["/PID", "12345", "/T", "/F"], {
|
||||
stdio: "ignore",
|
||||
});
|
||||
expect(runTaskkill).toHaveBeenNthCalledWith(
|
||||
2,
|
||||
expectedTaskkillPath(),
|
||||
["/PID", "12345", "/T", "/F"],
|
||||
{
|
||||
stdio: "ignore",
|
||||
},
|
||||
);
|
||||
expect(child.kill).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
@@ -731,12 +766,22 @@ setInterval(() => {}, 1000);
|
||||
runTaskkill,
|
||||
});
|
||||
|
||||
expect(runTaskkill).toHaveBeenNthCalledWith(1, "taskkill", ["/PID", "12345", "/T"], {
|
||||
stdio: "ignore",
|
||||
});
|
||||
expect(runTaskkill).toHaveBeenNthCalledWith(2, "taskkill", ["/PID", "12345", "/T", "/F"], {
|
||||
stdio: "ignore",
|
||||
});
|
||||
expect(runTaskkill).toHaveBeenNthCalledWith(
|
||||
1,
|
||||
expectedTaskkillPath(),
|
||||
["/PID", "12345", "/T"],
|
||||
{
|
||||
stdio: "ignore",
|
||||
},
|
||||
);
|
||||
expect(runTaskkill).toHaveBeenNthCalledWith(
|
||||
2,
|
||||
expectedTaskkillPath(),
|
||||
["/PID", "12345", "/T", "/F"],
|
||||
{
|
||||
stdio: "ignore",
|
||||
},
|
||||
);
|
||||
expect(child.kill).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
import { spawnSync } from "node:child_process";
|
||||
import { EventEmitter } from "node:events";
|
||||
import { describe, expect, it, vi } from "vitest";
|
||||
import { resolveWindowsTaskkillPath } from "../../scripts/lib/windows-taskkill.mjs";
|
||||
import {
|
||||
assertRpcSmokeResponse,
|
||||
cleanupTempRoot,
|
||||
@@ -16,6 +17,31 @@ import {
|
||||
waitForGatewayReady,
|
||||
} from "../../scripts/measure-rpc-rtt.mjs";
|
||||
|
||||
function expectedTaskkillPath(): string {
|
||||
return resolveWindowsTaskkillPath();
|
||||
}
|
||||
|
||||
function restoreEnvValue(key: string, value: string | undefined): void {
|
||||
if (value === undefined) {
|
||||
delete process.env[key];
|
||||
return;
|
||||
}
|
||||
process.env[key] = value;
|
||||
}
|
||||
|
||||
function withDefaultWindowsSystemRoot(run: () => void): void {
|
||||
const originalSystemRoot = process.env.SystemRoot;
|
||||
const originalWindir = process.env.WINDIR;
|
||||
try {
|
||||
process.env.SystemRoot = "C:\\Windows";
|
||||
delete process.env.WINDIR;
|
||||
run();
|
||||
} finally {
|
||||
restoreEnvValue("SystemRoot", originalSystemRoot);
|
||||
restoreEnvValue("WINDIR", originalWindir);
|
||||
}
|
||||
}
|
||||
|
||||
class FakeWebSocket extends EventEmitter {
|
||||
static OPEN = 1;
|
||||
static instances: FakeWebSocket[] = [];
|
||||
@@ -324,7 +350,7 @@ describe("scripts/measure-rpc-rtt.mjs", () => {
|
||||
expect(signalGatewayProcess(child, "SIGTERM", kill, { runTaskkill })).toBe(true);
|
||||
|
||||
if (process.platform === "win32") {
|
||||
expect(runTaskkill).toHaveBeenCalledWith("taskkill", ["/PID", "12345", "/T"], {
|
||||
expect(runTaskkill).toHaveBeenCalledWith(expectedTaskkillPath(), ["/PID", "12345", "/T"], {
|
||||
stdio: "ignore",
|
||||
});
|
||||
expect(child.kill).not.toHaveBeenCalled();
|
||||
@@ -335,66 +361,90 @@ describe("scripts/measure-rpc-rtt.mjs", () => {
|
||||
});
|
||||
|
||||
it("signals Windows gateway process trees with taskkill", () => {
|
||||
const child = Object.assign(new EventEmitter(), {
|
||||
exitCode: null,
|
||||
kill: vi.fn(),
|
||||
pid: 12345,
|
||||
signalCode: null,
|
||||
});
|
||||
const kill = vi.fn(() => true);
|
||||
const runTaskkill = vi.fn(() => ({ error: undefined, status: 0 }));
|
||||
withDefaultWindowsSystemRoot(() => {
|
||||
const child = Object.assign(new EventEmitter(), {
|
||||
exitCode: null,
|
||||
kill: vi.fn(),
|
||||
pid: 12345,
|
||||
signalCode: null,
|
||||
});
|
||||
const kill = vi.fn(() => true);
|
||||
const runTaskkill = vi.fn(() => ({ error: undefined, status: 0 }));
|
||||
|
||||
expect(
|
||||
signalGatewayProcess(child, "SIGTERM", kill, {
|
||||
platform: "win32",
|
||||
runTaskkill,
|
||||
}),
|
||||
).toBe(true);
|
||||
expect(runTaskkill).toHaveBeenNthCalledWith(1, "taskkill", ["/PID", "12345", "/T"], {
|
||||
stdio: "ignore",
|
||||
});
|
||||
expect(
|
||||
signalGatewayProcess(child, "SIGTERM", kill, {
|
||||
platform: "win32",
|
||||
runTaskkill,
|
||||
}),
|
||||
).toBe(true);
|
||||
expect(runTaskkill).toHaveBeenNthCalledWith(
|
||||
1,
|
||||
expectedTaskkillPath(),
|
||||
["/PID", "12345", "/T"],
|
||||
{
|
||||
stdio: "ignore",
|
||||
},
|
||||
);
|
||||
|
||||
expect(
|
||||
signalGatewayProcess(child, "SIGKILL", kill, {
|
||||
platform: "win32",
|
||||
runTaskkill,
|
||||
}),
|
||||
).toBe(true);
|
||||
expect(runTaskkill).toHaveBeenNthCalledWith(2, "taskkill", ["/PID", "12345", "/T", "/F"], {
|
||||
stdio: "ignore",
|
||||
expect(
|
||||
signalGatewayProcess(child, "SIGKILL", kill, {
|
||||
platform: "win32",
|
||||
runTaskkill,
|
||||
}),
|
||||
).toBe(true);
|
||||
expect(runTaskkill).toHaveBeenNthCalledWith(
|
||||
2,
|
||||
expectedTaskkillPath(),
|
||||
["/PID", "12345", "/T", "/F"],
|
||||
{
|
||||
stdio: "ignore",
|
||||
},
|
||||
);
|
||||
expect(kill).not.toHaveBeenCalled();
|
||||
expect(child.kill).not.toHaveBeenCalled();
|
||||
});
|
||||
expect(kill).not.toHaveBeenCalled();
|
||||
expect(child.kill).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("force-kills Windows gateway process trees when graceful taskkill fails", () => {
|
||||
const child = Object.assign(new EventEmitter(), {
|
||||
exitCode: null,
|
||||
kill: vi.fn(),
|
||||
pid: 12345,
|
||||
signalCode: null,
|
||||
});
|
||||
const kill = vi.fn(() => true);
|
||||
const runTaskkill = vi
|
||||
.fn()
|
||||
.mockReturnValueOnce({ error: undefined, status: 1 })
|
||||
.mockReturnValueOnce({ error: undefined, status: 0 });
|
||||
withDefaultWindowsSystemRoot(() => {
|
||||
const child = Object.assign(new EventEmitter(), {
|
||||
exitCode: null,
|
||||
kill: vi.fn(),
|
||||
pid: 12345,
|
||||
signalCode: null,
|
||||
});
|
||||
const kill = vi.fn(() => true);
|
||||
const runTaskkill = vi
|
||||
.fn()
|
||||
.mockReturnValueOnce({ error: undefined, status: 1 })
|
||||
.mockReturnValueOnce({ error: undefined, status: 0 });
|
||||
|
||||
expect(
|
||||
signalGatewayProcess(child, "SIGTERM", kill, {
|
||||
platform: "win32",
|
||||
runTaskkill,
|
||||
}),
|
||||
).toBe(true);
|
||||
expect(
|
||||
signalGatewayProcess(child, "SIGTERM", kill, {
|
||||
platform: "win32",
|
||||
runTaskkill,
|
||||
}),
|
||||
).toBe(true);
|
||||
|
||||
expect(runTaskkill).toHaveBeenNthCalledWith(1, "taskkill", ["/PID", "12345", "/T"], {
|
||||
stdio: "ignore",
|
||||
expect(runTaskkill).toHaveBeenNthCalledWith(
|
||||
1,
|
||||
expectedTaskkillPath(),
|
||||
["/PID", "12345", "/T"],
|
||||
{
|
||||
stdio: "ignore",
|
||||
},
|
||||
);
|
||||
expect(runTaskkill).toHaveBeenNthCalledWith(
|
||||
2,
|
||||
expectedTaskkillPath(),
|
||||
["/PID", "12345", "/T", "/F"],
|
||||
{
|
||||
stdio: "ignore",
|
||||
},
|
||||
);
|
||||
expect(kill).not.toHaveBeenCalled();
|
||||
expect(child.kill).not.toHaveBeenCalled();
|
||||
});
|
||||
expect(runTaskkill).toHaveBeenNthCalledWith(2, "taskkill", ["/PID", "12345", "/T", "/F"], {
|
||||
stdio: "ignore",
|
||||
});
|
||||
expect(kill).not.toHaveBeenCalled();
|
||||
expect(child.kill).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("treats missing gateway process groups as already exited", () => {
|
||||
@@ -442,12 +492,22 @@ describe("scripts/measure-rpc-rtt.mjs", () => {
|
||||
await stopGateway(child, { killGraceMs: 1, killProcess: kill, runTaskkill });
|
||||
|
||||
if (process.platform === "win32") {
|
||||
expect(runTaskkill).toHaveBeenNthCalledWith(1, "taskkill", ["/PID", "12346", "/T"], {
|
||||
stdio: "ignore",
|
||||
});
|
||||
expect(runTaskkill).toHaveBeenNthCalledWith(2, "taskkill", ["/PID", "12346", "/T", "/F"], {
|
||||
stdio: "ignore",
|
||||
});
|
||||
expect(runTaskkill).toHaveBeenNthCalledWith(
|
||||
1,
|
||||
expectedTaskkillPath(),
|
||||
["/PID", "12346", "/T"],
|
||||
{
|
||||
stdio: "ignore",
|
||||
},
|
||||
);
|
||||
expect(runTaskkill).toHaveBeenNthCalledWith(
|
||||
2,
|
||||
expectedTaskkillPath(),
|
||||
["/PID", "12346", "/T", "/F"],
|
||||
{
|
||||
stdio: "ignore",
|
||||
},
|
||||
);
|
||||
expect(child.kill).not.toHaveBeenCalled();
|
||||
} else {
|
||||
expect(kill).toHaveBeenNthCalledWith(1, -12346, 0);
|
||||
@@ -489,12 +549,22 @@ describe("scripts/measure-rpc-rtt.mjs", () => {
|
||||
});
|
||||
|
||||
if (process.platform === "win32") {
|
||||
expect(runTaskkill).toHaveBeenNthCalledWith(1, "taskkill", ["/PID", "12350", "/T"], {
|
||||
stdio: "ignore",
|
||||
});
|
||||
expect(runTaskkill).toHaveBeenNthCalledWith(2, "taskkill", ["/PID", "12350", "/T", "/F"], {
|
||||
stdio: "ignore",
|
||||
});
|
||||
expect(runTaskkill).toHaveBeenNthCalledWith(
|
||||
1,
|
||||
expectedTaskkillPath(),
|
||||
["/PID", "12350", "/T"],
|
||||
{
|
||||
stdio: "ignore",
|
||||
},
|
||||
);
|
||||
expect(runTaskkill).toHaveBeenNthCalledWith(
|
||||
2,
|
||||
expectedTaskkillPath(),
|
||||
["/PID", "12350", "/T", "/F"],
|
||||
{
|
||||
stdio: "ignore",
|
||||
},
|
||||
);
|
||||
expect(child.kill).not.toHaveBeenCalled();
|
||||
} else {
|
||||
expect(kill).toHaveBeenCalledWith(-12350, "SIGKILL");
|
||||
@@ -549,7 +619,7 @@ describe("scripts/measure-rpc-rtt.mjs", () => {
|
||||
processLike.emit("SIGTERM");
|
||||
|
||||
if (process.platform === "win32") {
|
||||
expect(runTaskkill).toHaveBeenCalledWith("taskkill", ["/PID", "12348", "/T"], {
|
||||
expect(runTaskkill).toHaveBeenCalledWith(expectedTaskkillPath(), ["/PID", "12348", "/T"], {
|
||||
stdio: "ignore",
|
||||
});
|
||||
expect(child.kill).not.toHaveBeenCalled();
|
||||
@@ -595,7 +665,7 @@ describe("scripts/measure-rpc-rtt.mjs", () => {
|
||||
processLike.emit("exit");
|
||||
|
||||
if (process.platform === "win32") {
|
||||
expect(runTaskkill).toHaveBeenCalledWith("taskkill", ["/PID", "12349", "/T"], {
|
||||
expect(runTaskkill).toHaveBeenCalledWith(expectedTaskkillPath(), ["/PID", "12349", "/T"], {
|
||||
stdio: "ignore",
|
||||
});
|
||||
expect(child.kill).not.toHaveBeenCalled();
|
||||
|
||||
Reference in New Issue
Block a user