943ba03b76
- self-restart: a server in our unit's cgroup (/agenthub.service, system or user) restarts via `systemctl restart --no-block` (sudo -n for the system unit), our launchd job by exiting into KeepAlive. The detached restart-helper replacement held the port while Restart=always started the unit every 3 s (v2.7.2 on the S3 box: 240+ restarts, MainPID 0). - run-as.mayHaveTmux: only ENOENT means "no tmux". A dev server in server mode without CAP_DAC_READ_SEARCH got EACCES on a member's 0700 .agenthub/tmux, dropped the member from the list and ended their runs within seconds — the real cause of `401 not a live pane's token`. - tmux: a member's list is re-asked every 5 s, not 1 s (one sudo + three auth.log lines each). - POST /api/lanes/:id/start: 409 ENOCWD for a folder that no longer exists (the pane started in the account's home otherwise). - Settings: Remote sources only for staff (a developer got a 403). - scripts/test-server.js drops every inherited AGENTHUB_* (pane env). Tests: self-restart.test.js, server-s3-panes (mayHaveTmux), lanes-api (ENOCWD); touched suites 771 + client pages 191, tsc -b.
89 lines
2.9 KiB
JavaScript
89 lines
2.9 KiB
JavaScript
/**
|
|
* @file self-restart.test.js
|
|
* @description After an update, a server our service unit runs is restarted
|
|
* by the unit, never by a detached replacement: v2.7.2 on the S3 system unit
|
|
* left the replacement holding the port while `Restart=always` started the
|
|
* unit every 3 s, each start exiting "already running".
|
|
* @author Nguyễn Ngọc Trí Vĩ <vinnt@smartgift.vn>
|
|
*/
|
|
|
|
const { describe, it } = require("node:test");
|
|
const assert = require("node:assert/strict");
|
|
const { scheduleRestart, owningUnit } = require("../lib/self-restart");
|
|
|
|
function deps(unit, restartAnswer = "restarted") {
|
|
const calls = { restart: [], kill: 0, spawn: 0 };
|
|
return {
|
|
calls,
|
|
unit,
|
|
svc: () => ({
|
|
restartUserService: (name) => {
|
|
calls.restart.push(name);
|
|
return restartAnswer;
|
|
},
|
|
}),
|
|
kill: () => calls.kill++,
|
|
spawnHelper: () => {
|
|
calls.spawn++;
|
|
return { unref() {} };
|
|
},
|
|
};
|
|
}
|
|
|
|
describe("self-restart", () => {
|
|
it("tells which unit owns this process, from its cgroup or launchd label", () => {
|
|
const linux = (text) => owningUnit({ platform: "linux", cgroup: () => text });
|
|
assert.equal(linux("0::/system.slice/agenthub.service\n"), "systemd");
|
|
assert.equal(
|
|
linux("0::/user.slice/user-1001.slice/user@1001.service/app.slice/agenthub.service\n"),
|
|
"systemd"
|
|
);
|
|
assert.equal(linux("0::/user.slice/user-1001.slice/session-3.scope\n"), null);
|
|
assert.equal(linux("0::/system.slice/agenthub.service.d/x\n"), null);
|
|
assert.equal(
|
|
owningUnit({
|
|
platform: "linux",
|
|
cgroup: () => {
|
|
throw new Error("no /proc");
|
|
},
|
|
}),
|
|
null
|
|
);
|
|
assert.equal(
|
|
owningUnit({ platform: "darwin", env: { XPC_SERVICE_NAME: "io.smartgift.agenthub" } }),
|
|
"launchd"
|
|
);
|
|
assert.equal(owningUnit({ platform: "darwin", env: {} }), null);
|
|
});
|
|
|
|
it("asks systemd to restart its unit and starts no replacement", () => {
|
|
const d = deps("systemd");
|
|
assert.equal(scheduleRestart("/r", d), "service");
|
|
assert.deepEqual(d.calls.restart, ["agenthub"]);
|
|
assert.equal(d.calls.spawn, 0);
|
|
assert.equal(d.calls.kill, 0, "systemd's restart job stops this process");
|
|
});
|
|
|
|
it("exits for Restart=always when systemd cannot be asked — still no replacement", () => {
|
|
const d = deps("systemd", "failed");
|
|
assert.equal(scheduleRestart("/r", d), "exit");
|
|
assert.equal(d.calls.kill, 1);
|
|
assert.equal(d.calls.spawn, 0);
|
|
});
|
|
|
|
it("exits under launchd, whose KeepAlive starts the new code", () => {
|
|
const d = deps("launchd");
|
|
assert.equal(scheduleRestart("/r", d), "exit");
|
|
assert.equal(d.calls.kill, 1);
|
|
assert.equal(d.calls.spawn, 0);
|
|
assert.deepEqual(d.calls.restart, []);
|
|
});
|
|
|
|
it("hands over to the detached helper when no unit owns the server", () => {
|
|
const d = deps(null);
|
|
assert.equal(scheduleRestart("/r", d), "helper");
|
|
assert.equal(d.calls.spawn, 1);
|
|
assert.equal(d.calls.kill, 1);
|
|
});
|
|
});
|