mirror of
https://github.com/openclaw/openclaw.git
synced 2026-08-25 11:55:47 -06:00
fix: doctor skips host services for isolated state (#115922)
* fix(doctor): isolate host service management * fix(doctor): clarify service isolation recovery * test(doctor): isolate service identity fixtures * test(daemon): keep lifecycle fixtures lint-clean * test(daemon): isolate install identity fixtures
This commit is contained in:
committed by
GitHub
parent
6ec3dbd92d
commit
383f8947c1
@@ -22,6 +22,12 @@ const serviceMock = vi.hoisted(() => ({
|
||||
readRuntime: vi.fn(async () => ({ status: "stopped" as const })),
|
||||
}));
|
||||
|
||||
vi.mock("../../config/paths.js", async () => {
|
||||
const actual =
|
||||
await vi.importActual<typeof import("../../config/paths.js")>("../../config/paths.js");
|
||||
return { ...actual, isDefaultInstallIdentity: () => true };
|
||||
});
|
||||
|
||||
vi.mock("../../daemon/service.js", () => ({
|
||||
resolveGatewayService: () => serviceMock,
|
||||
}));
|
||||
|
||||
@@ -13,6 +13,7 @@ const resolveNodeStartupTlsEnvironmentMock = vi.hoisted(() => vi.fn());
|
||||
const loadConfigMock = vi.hoisted(() => vi.fn());
|
||||
const readConfigFileSnapshotMock = vi.hoisted(() => vi.fn());
|
||||
const resolveGatewayPortMock = vi.hoisted(() => vi.fn(() => 18789));
|
||||
const isDefaultInstallIdentityMock = vi.hoisted(() => vi.fn(() => true));
|
||||
const replaceConfigFileMock = vi.hoisted(() => vi.fn());
|
||||
const resolveIsNixModeMock = vi.hoisted(() => vi.fn(() => false));
|
||||
const resolveSecretInputRefMock = vi.hoisted(() =>
|
||||
@@ -102,6 +103,7 @@ vi.mock("../../config/mutate.js", () => ({
|
||||
}));
|
||||
|
||||
vi.mock("../../config/paths.js", () => ({
|
||||
isDefaultInstallIdentity: isDefaultInstallIdentityMock,
|
||||
resolveGatewayPort: resolveGatewayPortMock,
|
||||
resolveIsNixMode: resolveIsNixModeMock,
|
||||
}));
|
||||
@@ -276,6 +278,7 @@ describe("runDaemonInstall", () => {
|
||||
resolveNodeStartupTlsEnvironmentMock.mockReset();
|
||||
readConfigFileSnapshotMock.mockReset();
|
||||
resolveGatewayPortMock.mockClear();
|
||||
isDefaultInstallIdentityMock.mockReturnValue(true);
|
||||
replaceConfigFileMock.mockReset();
|
||||
resolveIsNixModeMock.mockReset();
|
||||
resolveSecretInputRefMock.mockReset();
|
||||
@@ -362,6 +365,20 @@ describe("runDaemonInstall", () => {
|
||||
expect(installDaemonServiceAndEmitMock).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("blocks non-default install identities before inspecting host services", async () => {
|
||||
isDefaultInstallIdentityMock.mockReturnValue(false);
|
||||
|
||||
await runDaemonInstall({ json: true });
|
||||
|
||||
expect(actionState.failed[0]?.message).toContain(
|
||||
"service management skipped: non-default state dir or config path",
|
||||
);
|
||||
expect(readConfigFileSnapshotMock).not.toHaveBeenCalled();
|
||||
expect(service.isLoaded).not.toHaveBeenCalled();
|
||||
expect(service.readCommand).not.toHaveBeenCalled();
|
||||
expect(installDaemonServiceAndEmitMock).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("validates token SecretRef but does not serialize resolved token into service env", async () => {
|
||||
mockResolvedGatewayTokenSecretRef();
|
||||
|
||||
|
||||
@@ -25,10 +25,7 @@ import {
|
||||
isLoopbackHost,
|
||||
resolveGatewayBindHost,
|
||||
} from "../../gateway/net.js";
|
||||
import {
|
||||
formatExternalSupervisorActionRequired,
|
||||
isGatewayExternallySupervised,
|
||||
} from "../../infra/gateway-supervision.js";
|
||||
import { assertGatewayServiceMutationAllowed } from "../../infra/gateway-supervision.js";
|
||||
import {
|
||||
isDangerousHostEnvOverrideVarName,
|
||||
isDangerousHostEnvVarName,
|
||||
@@ -149,10 +146,10 @@ export async function runDaemonInstall(opts: DaemonInstallOptions) {
|
||||
if (failIfNixDaemonInstallMode(fail)) {
|
||||
return;
|
||||
}
|
||||
if (isGatewayExternallySupervised()) {
|
||||
fail(
|
||||
`Gateway install blocked: ${formatExternalSupervisorActionRequired("install or rewrite the gateway service")}`,
|
||||
);
|
||||
try {
|
||||
assertGatewayServiceMutationAllowed("install or rewrite the gateway service");
|
||||
} catch (error) {
|
||||
fail(`Gateway install blocked: ${String(error)}`);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -129,9 +129,10 @@ vi.mock("../../config/config.js", () => ({
|
||||
resolveGatewayPort: (cfg?: unknown, env?: unknown) => resolveGatewayPort(cfg, env),
|
||||
}));
|
||||
|
||||
vi.mock("../../config/paths.js", () => ({ isDefaultInstallIdentity: () => true }));
|
||||
|
||||
vi.mock("../../infra/gateway-processes.js", () => ({
|
||||
findVerifiedGatewayListenerPidsOnPortSync: (port: number) =>
|
||||
findVerifiedGatewayListenerPidsOnPortSync(port),
|
||||
findVerifiedGatewayListenerPidsOnPortSync,
|
||||
signalVerifiedGatewayPidSync: (pid: number, signal: "SIGTERM" | "SIGUSR1") =>
|
||||
signalVerifiedGatewayPidSync(pid, signal),
|
||||
formatGatewayPidList: (pids: number[]) => formatGatewayPidList(pids),
|
||||
|
||||
@@ -34,6 +34,7 @@ const resolveGatewayPortMock = vi.hoisted(() => vi.fn(() => 18789));
|
||||
const resolveOpenClawWrapperPathMock = vi.hoisted(() => vi.fn());
|
||||
const formatGatewayServiceStartRepairIssuesMock = vi.hoisted(() => vi.fn());
|
||||
const defaultRuntimeLogMock = vi.hoisted(() => vi.fn());
|
||||
const assertGatewayServiceMutationAllowedMock = vi.hoisted(() => vi.fn());
|
||||
|
||||
vi.mock("../../commands/daemon-install-helpers.js", () => ({
|
||||
buildGatewayInstallPlan: buildGatewayInstallPlanMock,
|
||||
@@ -64,6 +65,10 @@ vi.mock("../../daemon/service.js", () => ({
|
||||
formatGatewayServiceStartRepairIssues: formatGatewayServiceStartRepairIssuesMock,
|
||||
}));
|
||||
|
||||
vi.mock("../../infra/gateway-supervision.js", () => ({
|
||||
assertGatewayServiceMutationAllowed: assertGatewayServiceMutationAllowedMock,
|
||||
}));
|
||||
|
||||
vi.mock("../../runtime.js", () => ({
|
||||
defaultRuntime: { log: defaultRuntimeLogMock },
|
||||
}));
|
||||
@@ -87,6 +92,7 @@ describe("repairLoadedGatewayServiceForStart", () => {
|
||||
resolveOpenClawWrapperPathMock.mockReset();
|
||||
formatGatewayServiceStartRepairIssuesMock.mockReset();
|
||||
defaultRuntimeLogMock.mockClear();
|
||||
assertGatewayServiceMutationAllowedMock.mockReset();
|
||||
|
||||
resolveGatewayInstallTokenMock.mockResolvedValue({
|
||||
tokenRefConfigured: false,
|
||||
|
||||
@@ -12,6 +12,7 @@ import type {
|
||||
GatewayServiceState,
|
||||
} from "../../daemon/service.js";
|
||||
import { formatGatewayServiceStartRepairIssues } from "../../daemon/service.js";
|
||||
import { assertGatewayServiceMutationAllowed } from "../../infra/gateway-supervision.js";
|
||||
import { parseTcpPort, parseTcpPortFromArgs } from "../../infra/tcp-port.js";
|
||||
import { defaultRuntime } from "../../runtime.js";
|
||||
import { mergeInstallInvocationEnv } from "./install.js";
|
||||
@@ -48,6 +49,7 @@ export async function repairLoadedGatewayServiceForStart(
|
||||
warnings?: string[];
|
||||
loaded: boolean;
|
||||
}> {
|
||||
assertGatewayServiceMutationAllowed("repair the gateway service");
|
||||
const { snapshot: configSnapshot, writeOptions: configWriteOptions } =
|
||||
await readConfigFileSnapshotForWrite();
|
||||
const cfg = configSnapshot.valid ? configSnapshot.sourceConfig : configSnapshot.config;
|
||||
|
||||
Reference in New Issue
Block a user