fix: handle missing systemctl in containers (#26089) (#26699)

* Daemon: handle missing systemctl in containers

* Daemon: harden missing-systemctl detection

* Daemon tests: cover systemctl spawn failure path

* Changelog: note container systemctl service-check fix

* Update CHANGELOG.md

* Daemon: fail closed on unknown systemctl is-enabled errors

* Daemon tests: cover is-enabled unknown-error path

---------

Co-authored-by: Vincent Koc <vincentkoc@ieee.org>
This commit is contained in:
Sahil Satralkar
2026-03-02 11:18:06 +05:30
committed by GitHub
parent 5d78fcf1b5
commit cda119b052
3 changed files with 96 additions and 7 deletions

View File

@@ -42,6 +42,56 @@ describe("systemd availability", () => {
});
});
describe("isSystemdServiceEnabled", () => {
beforeEach(() => {
execFileMock.mockClear();
});
it("returns false when systemctl is not present", async () => {
const { isSystemdServiceEnabled } = await import("./systemd.js");
execFileMock.mockImplementation((_cmd, _args, _opts, cb) => {
const err = new Error("spawn systemctl EACCES") as Error & { code?: string };
err.code = "EACCES";
cb(err, "", "");
});
const result = await isSystemdServiceEnabled({ env: {} });
expect(result).toBe(false);
});
it("calls systemctl is-enabled when systemctl is present", async () => {
const { isSystemdServiceEnabled } = await import("./systemd.js");
execFileMock.mockImplementationOnce((_cmd, args, _opts, cb) => {
expect(args).toEqual(["--user", "is-enabled", "openclaw-gateway.service"]);
cb(null, "enabled", "");
});
const result = await isSystemdServiceEnabled({ env: {} });
expect(result).toBe(true);
});
it("returns false when systemctl reports disabled", async () => {
const { isSystemdServiceEnabled } = await import("./systemd.js");
execFileMock.mockImplementationOnce((_cmd, _args, _opts, cb) => {
const err = new Error("disabled") as Error & { code?: number };
err.code = 1;
cb(err, "disabled", "");
});
const result = await isSystemdServiceEnabled({ env: {} });
expect(result).toBe(false);
});
it("throws when systemctl is-enabled fails for non-state errors", async () => {
const { isSystemdServiceEnabled } = await import("./systemd.js");
execFileMock.mockImplementationOnce((_cmd, _args, _opts, cb) => {
const err = new Error("Failed to connect to bus") as Error & { code?: number };
err.code = 1;
cb(err, "", "Failed to connect to bus");
});
await expect(isSystemdServiceEnabled({ env: {} })).rejects.toThrow(
"systemctl is-enabled unavailable: Failed to connect to bus",
);
});
});
describe("systemd runtime parsing", () => {
it("parses active state details", () => {
const output = [