mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-18 11:24:47 +00:00
fix(gateway): preserve restart drain for active runs
Fixes https://github.com/openclaw/openclaw/issues/65485
This commit is contained in:
@@ -16,6 +16,8 @@ const loadConfig = vi.fn<() => OpenClawConfig>(() => ({
|
||||
},
|
||||
},
|
||||
}));
|
||||
const writeGatewayRestartIntentSync = vi.fn();
|
||||
const clearGatewayRestartIntentSync = vi.fn();
|
||||
|
||||
vi.mock("../../config/config.js", () => ({
|
||||
loadConfig: () => loadConfig(),
|
||||
@@ -26,6 +28,11 @@ vi.mock("../../runtime.js", () => ({
|
||||
defaultRuntime,
|
||||
}));
|
||||
|
||||
vi.mock("../../infra/restart.js", () => ({
|
||||
clearGatewayRestartIntentSync: () => clearGatewayRestartIntentSync(),
|
||||
writeGatewayRestartIntentSync: (opts: unknown) => writeGatewayRestartIntentSync(opts),
|
||||
}));
|
||||
|
||||
let runServiceRestart: typeof import("./lifecycle-core.js").runServiceRestart;
|
||||
let runServiceStart: typeof import("./lifecycle-core.js").runServiceStart;
|
||||
let runServiceStop: typeof import("./lifecycle-core.js").runServiceStop;
|
||||
@@ -92,6 +99,8 @@ describe("runServiceRestart token drift", () => {
|
||||
},
|
||||
});
|
||||
resetLifecycleServiceMocks();
|
||||
writeGatewayRestartIntentSync.mockClear();
|
||||
clearGatewayRestartIntentSync.mockClear();
|
||||
service.readCommand.mockResolvedValue({
|
||||
programArguments: [],
|
||||
environment: { OPENCLAW_GATEWAY_TOKEN: "service-token" },
|
||||
@@ -182,6 +191,7 @@ describe("runServiceRestart token drift", () => {
|
||||
|
||||
expect(loadConfig).not.toHaveBeenCalled();
|
||||
expect(service.readCommand).not.toHaveBeenCalled();
|
||||
expect(writeGatewayRestartIntentSync).not.toHaveBeenCalled();
|
||||
const payload = readJsonLog<{ warnings?: string[] }>();
|
||||
expect(payload.warnings).toBeUndefined();
|
||||
});
|
||||
@@ -305,6 +315,27 @@ describe("runServiceRestart token drift", () => {
|
||||
expect(payload.message).toBe("restart scheduled, gateway will restart momentarily");
|
||||
});
|
||||
|
||||
it("writes a restart intent before service-manager restart", async () => {
|
||||
service.readRuntime.mockResolvedValue({ status: "running", pid: 1234 });
|
||||
|
||||
await runServiceRestart(createServiceRunArgs());
|
||||
|
||||
expect(writeGatewayRestartIntentSync).toHaveBeenCalledWith({ targetPid: 1234 });
|
||||
expect(clearGatewayRestartIntentSync).not.toHaveBeenCalled();
|
||||
expect(service.restart).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
it("clears restart intent when service-manager restart fails before signaling", async () => {
|
||||
service.readRuntime.mockResolvedValue({ status: "running", pid: 1234 });
|
||||
writeGatewayRestartIntentSync.mockReturnValueOnce(true);
|
||||
service.restart.mockRejectedValueOnce(new Error("launchctl failed before signaling"));
|
||||
|
||||
await expect(runServiceRestart(createServiceRunArgs())).rejects.toThrow("__exit__:1");
|
||||
|
||||
expect(writeGatewayRestartIntentSync).toHaveBeenCalledWith({ targetPid: 1234 });
|
||||
expect(clearGatewayRestartIntentSync).toHaveBeenCalledOnce();
|
||||
});
|
||||
|
||||
it("emits scheduled when service start routes through a scheduled restart", async () => {
|
||||
service.restart.mockResolvedValue({ outcome: "scheduled" });
|
||||
|
||||
|
||||
@@ -9,6 +9,10 @@ import type { GatewayService } from "../../daemon/service.js";
|
||||
import { renderSystemdUnavailableHints } from "../../daemon/systemd-hints.js";
|
||||
import { isSystemdUserServiceAvailable } from "../../daemon/systemd.js";
|
||||
import { isGatewaySecretRefUnavailableError } from "../../gateway/credentials.js";
|
||||
import {
|
||||
clearGatewayRestartIntentSync,
|
||||
writeGatewayRestartIntentSync,
|
||||
} from "../../infra/restart.js";
|
||||
import { isWSL } from "../../infra/wsl.js";
|
||||
import { defaultRuntime } from "../../runtime.js";
|
||||
import { resolveGatewayTokenForDriftCheck } from "./gateway-token-drift.js";
|
||||
@@ -458,7 +462,21 @@ export async function runServiceRestart(params: {
|
||||
try {
|
||||
let restartResult: GatewayServiceRestartResult = { outcome: "completed" };
|
||||
if (loaded) {
|
||||
restartResult = await params.service.restart({ env: process.env, stdout });
|
||||
let wroteRestartIntent = false;
|
||||
if (params.serviceNoun === "Gateway") {
|
||||
const runtime = await params.service.readRuntime(process.env).catch(() => null);
|
||||
wroteRestartIntent = writeGatewayRestartIntentSync({
|
||||
targetPid: runtime?.pid,
|
||||
});
|
||||
}
|
||||
try {
|
||||
restartResult = await params.service.restart({ env: process.env, stdout });
|
||||
} catch (err) {
|
||||
if (wroteRestartIntent) {
|
||||
clearGatewayRestartIntentSync();
|
||||
}
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
let restartStatus = describeGatewayServiceRestart(params.serviceNoun, restartResult);
|
||||
if (restartStatus.scheduled) {
|
||||
|
||||
Reference in New Issue
Block a user