diff --git a/server/src/domain/monitors/monitor.type.ts b/server/src/domain/monitors/monitor.type.ts index bacaedb47..d5519af0e 100644 --- a/server/src/domain/monitors/monitor.type.ts +++ b/server/src/domain/monitors/monitor.type.ts @@ -6,6 +6,7 @@ import http from "node:http"; import { HardwareStats } from "@/domain/checks/check.type.js"; import { MonitorStats } from "@/domain/monitor-stats/monitor-stats.type.js"; import { DockerLogPage } from "@/domain/docker/docker-log.type.js"; +import { isDockerSocketUrl } from "@/utils/dockerHost.js"; export const HttpStatusCodes = [ ...Object.keys(http.STATUS_CODES).map(Number), @@ -67,10 +68,9 @@ export const EgressAttributableTypes = [ ] as const satisfies readonly MonitorType[]; export type EgressAttributableType = (typeof EgressAttributableTypes)[number]; -// A Docker daemon reached over a unix socket is local IPC and cannot fail through egress; one reached over -// TCP or TLS is as remote as any other target. The URL, not the type, settles it. -const isLocalDockerSocket = (monitor: Pick): boolean => - monitor.type === "docker" && (monitor.url?.trim().startsWith("unix://") ?? false); +// A Docker daemon reached over a unix socket (`unix:///path` or a bare absolute path) is local IPC and cannot +// fail through egress; one reached over TCP or TLS is as remote as any other target. The URL, not the type, settles it. +const isLocalDockerSocket = (monitor: Pick): boolean => monitor.type === "docker" && isDockerSocketUrl(monitor.url); export const isEgressAttributable = (monitor: Pick): boolean => EgressAttributableTypes.some((t) => t === monitor.type) && !isLocalDockerSocket(monitor); diff --git a/server/test/unit/services/checkProducer.test.ts b/server/test/unit/services/checkProducer.test.ts index 85763bfa9..460f54ee0 100644 --- a/server/test/unit/services/checkProducer.test.ts +++ b/server/test/unit/services/checkProducer.test.ts @@ -291,19 +291,21 @@ describe("CheckProducer", () => { }); it("never consults egress for a docker daemon on a local socket", async () => { - const check: Record = { id: "check-1" }; - const { producer, defaults } = createProducer({ - networkService: { requestStatus: jest.fn().mockResolvedValue({ ...failingStatus, type: "docker" }) }, - checkService: { toCheck: jest.fn().mockReturnValue(check) }, - egressService: { assessAfterFailure: jest.fn().mockResolvedValue("degraded") }, - }); + for (const url of ["unix:///var/run/docker.sock", "/var/run/docker.sock"]) { + const check: Record = { id: "check-1" }; + const { producer, defaults } = createProducer({ + networkService: { requestStatus: jest.fn().mockResolvedValue({ ...failingStatus, type: "docker" }) }, + checkService: { toCheck: jest.fn().mockReturnValue(check) }, + egressService: { assessAfterFailure: jest.fn().mockResolvedValue("degraded") }, + }); - await producer.produce(makeMonitor({ type: "docker", url: "unix:///var/run/docker.sock" })); + await producer.produce(makeMonitor({ type: "docker", url })); - // Local IPC never leaves the instance, so it cannot fail through egress. - expect(defaults.egressService.assessAfterFailure).not.toHaveBeenCalled(); - expect(check).not.toHaveProperty("egressStatus"); - expect(defaults.buffer.addToBuffer).toHaveBeenCalledWith(check); + // Local IPC never leaves the instance, so it cannot fail through egress. + expect(defaults.egressService.assessAfterFailure).not.toHaveBeenCalled(); + expect(check).not.toHaveProperty("egressStatus"); + expect(defaults.buffer.addToBuffer).toHaveBeenCalledWith(check); + } }); it("consults egress for a hardware check and for a docker daemon reached over TCP", async () => {