mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-10 04:32:44 +00:00
refactor(daemon): share systemd service action flow
This commit is contained in:
@@ -11,7 +11,9 @@ import { parseSystemdExecStart } from "./systemd-unit.js";
|
|||||||
import {
|
import {
|
||||||
isSystemdUserServiceAvailable,
|
isSystemdUserServiceAvailable,
|
||||||
parseSystemdShow,
|
parseSystemdShow,
|
||||||
|
restartSystemdService,
|
||||||
resolveSystemdUserUnitPath,
|
resolveSystemdUserUnitPath,
|
||||||
|
stopSystemdService,
|
||||||
} from "./systemd.js";
|
} from "./systemd.js";
|
||||||
|
|
||||||
describe("systemd availability", () => {
|
describe("systemd availability", () => {
|
||||||
@@ -151,3 +153,58 @@ describe("parseSystemdExecStart", () => {
|
|||||||
]);
|
]);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe("systemd service control", () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
execFileMock.mockReset();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("stops the resolved user unit", async () => {
|
||||||
|
execFileMock
|
||||||
|
.mockImplementationOnce((_cmd, _args, _opts, cb) => cb(null, "", ""))
|
||||||
|
.mockImplementationOnce((_cmd, args, _opts, cb) => {
|
||||||
|
expect(args).toEqual(["--user", "stop", "openclaw-gateway.service"]);
|
||||||
|
cb(null, "", "");
|
||||||
|
});
|
||||||
|
const write = vi.fn();
|
||||||
|
const stdout = { write } as unknown as NodeJS.WritableStream;
|
||||||
|
|
||||||
|
await stopSystemdService({ stdout, env: {} });
|
||||||
|
|
||||||
|
expect(write).toHaveBeenCalledTimes(1);
|
||||||
|
expect(String(write.mock.calls[0]?.[0])).toContain("Stopped systemd service");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("restarts a profile-specific user unit", async () => {
|
||||||
|
execFileMock
|
||||||
|
.mockImplementationOnce((_cmd, _args, _opts, cb) => cb(null, "", ""))
|
||||||
|
.mockImplementationOnce((_cmd, args, _opts, cb) => {
|
||||||
|
expect(args).toEqual(["--user", "restart", "openclaw-gateway-work.service"]);
|
||||||
|
cb(null, "", "");
|
||||||
|
});
|
||||||
|
const write = vi.fn();
|
||||||
|
const stdout = { write } as unknown as NodeJS.WritableStream;
|
||||||
|
|
||||||
|
await restartSystemdService({ stdout, env: { OPENCLAW_PROFILE: "work" } });
|
||||||
|
|
||||||
|
expect(write).toHaveBeenCalledTimes(1);
|
||||||
|
expect(String(write.mock.calls[0]?.[0])).toContain("Restarted systemd service");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("surfaces stop failures with systemctl detail", async () => {
|
||||||
|
execFileMock
|
||||||
|
.mockImplementationOnce((_cmd, _args, _opts, cb) => cb(null, "", ""))
|
||||||
|
.mockImplementationOnce((_cmd, _args, _opts, cb) => {
|
||||||
|
const err = new Error("stop failed") as Error & { code?: number };
|
||||||
|
err.code = 1;
|
||||||
|
cb(err, "", "permission denied");
|
||||||
|
});
|
||||||
|
|
||||||
|
await expect(
|
||||||
|
stopSystemdService({
|
||||||
|
stdout: { write: vi.fn() } as unknown as NodeJS.WritableStream,
|
||||||
|
env: {},
|
||||||
|
}),
|
||||||
|
).rejects.toThrow("systemctl stop failed: permission denied");
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|||||||
@@ -253,6 +253,22 @@ export async function uninstallSystemdService({
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function runSystemdServiceAction(params: {
|
||||||
|
stdout: NodeJS.WritableStream;
|
||||||
|
env?: Record<string, string | undefined>;
|
||||||
|
action: "stop" | "restart";
|
||||||
|
label: string;
|
||||||
|
}) {
|
||||||
|
await assertSystemdAvailable();
|
||||||
|
const serviceName = resolveSystemdServiceName(params.env ?? {});
|
||||||
|
const unitName = `${serviceName}.service`;
|
||||||
|
const res = await execSystemctl(["--user", params.action, unitName]);
|
||||||
|
if (res.code !== 0) {
|
||||||
|
throw new Error(`systemctl ${params.action} failed: ${res.stderr || res.stdout}`.trim());
|
||||||
|
}
|
||||||
|
params.stdout.write(`${formatLine(params.label, unitName)}\n`);
|
||||||
|
}
|
||||||
|
|
||||||
export async function stopSystemdService({
|
export async function stopSystemdService({
|
||||||
stdout,
|
stdout,
|
||||||
env,
|
env,
|
||||||
@@ -260,14 +276,12 @@ export async function stopSystemdService({
|
|||||||
stdout: NodeJS.WritableStream;
|
stdout: NodeJS.WritableStream;
|
||||||
env?: Record<string, string | undefined>;
|
env?: Record<string, string | undefined>;
|
||||||
}): Promise<void> {
|
}): Promise<void> {
|
||||||
await assertSystemdAvailable();
|
await runSystemdServiceAction({
|
||||||
const serviceName = resolveSystemdServiceName(env ?? {});
|
stdout,
|
||||||
const unitName = `${serviceName}.service`;
|
env,
|
||||||
const res = await execSystemctl(["--user", "stop", unitName]);
|
action: "stop",
|
||||||
if (res.code !== 0) {
|
label: "Stopped systemd service",
|
||||||
throw new Error(`systemctl stop failed: ${res.stderr || res.stdout}`.trim());
|
});
|
||||||
}
|
|
||||||
stdout.write(`${formatLine("Stopped systemd service", unitName)}\n`);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function restartSystemdService({
|
export async function restartSystemdService({
|
||||||
@@ -277,14 +291,12 @@ export async function restartSystemdService({
|
|||||||
stdout: NodeJS.WritableStream;
|
stdout: NodeJS.WritableStream;
|
||||||
env?: Record<string, string | undefined>;
|
env?: Record<string, string | undefined>;
|
||||||
}): Promise<void> {
|
}): Promise<void> {
|
||||||
await assertSystemdAvailable();
|
await runSystemdServiceAction({
|
||||||
const serviceName = resolveSystemdServiceName(env ?? {});
|
stdout,
|
||||||
const unitName = `${serviceName}.service`;
|
env,
|
||||||
const res = await execSystemctl(["--user", "restart", unitName]);
|
action: "restart",
|
||||||
if (res.code !== 0) {
|
label: "Restarted systemd service",
|
||||||
throw new Error(`systemctl restart failed: ${res.stderr || res.stdout}`.trim());
|
});
|
||||||
}
|
|
||||||
stdout.write(`${formatLine("Restarted systemd service", unitName)}\n`);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function isSystemdServiceEnabled(args: {
|
export async function isSystemdServiceEnabled(args: {
|
||||||
|
|||||||
Reference in New Issue
Block a user