Skip to content

Commit aa828d8

Browse files
committed
🐛 fix(self-update): prefer the bind-mounted socket over TCP for the helper
resolveHelperDockerConnection checked the watcher's TCP modem first, so a self/infrastructure update would route the helper through a Docker socket proxy even when the target container has the socket bind-mounted. For an infrastructure update (dd.update.mode=infrastructure) that proxy is the container being replaced — stopping it would sever the helper mid-update. The helper now resolves a bind-mounted socket first and only falls back to TCP when the target container has no socket bind. This keeps infrastructure updates bypassing the proxy while still supporting socket-less TCP setups.
1 parent cbe815a commit aa828d8

2 files changed

Lines changed: 35 additions & 9 deletions

File tree

app/triggers/providers/docker/SelfUpdateTransitionShared.test.ts

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -275,15 +275,33 @@ describe('resolveHelperDockerConnection', () => {
275275
};
276276
}
277277

278-
test('returns tcp mode when modem.host is a non-empty string', () => {
278+
test('returns tcp mode when modem.host is a non-empty string and no socket bind is present', () => {
279279
const deps = makeDeps();
280280
const result = resolveHelperDockerConnection(
281281
deps,
282282
{ createContainer: vi.fn(), modem: { host: 'docker-host', port: 2376, protocol: 'https' } },
283283
undefined,
284284
);
285285
expect(result).toEqual({ mode: 'tcp', host: 'docker-host', port: 2376, protocol: 'https' });
286-
expect(deps.findDockerSocketBind).not.toHaveBeenCalled();
286+
// findDockerSocketBind is called first (socket-first precedence); it returns undefined
287+
// here, so the code falls through to TCP.
288+
expect(deps.findDockerSocketBind).toHaveBeenCalledWith(undefined);
289+
});
290+
291+
test('infrastructure-update guard: socket bind takes precedence over modem.host', () => {
292+
// When the target container has the Docker socket bind-mounted AND the watcher also
293+
// has a TCP modem.host (e.g. Drydock talks to sockguard over TCP), the helper must
294+
// use the direct socket path — not the TCP connection that runs through the proxy
295+
// being replaced. This is the infrastructure update mode invariant.
296+
const deps = makeDeps('/var/run/docker.sock');
297+
const spec = createCurrentContainerSpec();
298+
const result = resolveHelperDockerConnection(
299+
deps,
300+
{ createContainer: vi.fn(), modem: { host: 'sockguard-host', port: 2375, protocol: 'http' } },
301+
spec,
302+
);
303+
expect(result).toEqual({ mode: 'socket', socketPath: '/var/run/docker.sock' });
304+
expect(deps.findDockerSocketBind).toHaveBeenCalledWith(spec);
287305
});
288306

289307
test('defaults port to 2375 and protocol to http when not provided', () => {

app/triggers/providers/docker/SelfUpdateTransitionShared.ts

Lines changed: 15 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,18 @@ function resolveHelperDockerConnection(
7575
dockerApi: SelfUpdateDockerApi,
7676
currentContainerSpec: SelfUpdateContainerSpec | undefined,
7777
): HelperDockerConnection {
78+
// Socket-bind-first: when the target container has the Docker socket bind-mounted,
79+
// the helper MUST use that direct socket path. Using the TCP connection (which may
80+
// run through a proxy) would sever the helper the moment that proxy is stopped and
81+
// replaced — exactly the scenario in infrastructure update mode (dd.update.mode=infrastructure),
82+
// where the socket proxy itself is the container being updated. TCP is only the
83+
// fallback for deployments where Drydock reaches Docker purely over a remote TCP host
84+
// and has no local socket bind.
85+
const socketPath = dependencies.findDockerSocketBind(currentContainerSpec);
86+
if (socketPath) {
87+
return { mode: 'socket', socketPath };
88+
}
89+
7890
const modemHost = dockerApi.modem?.host;
7991
if (typeof modemHost === 'string' && modemHost.length > 0) {
8092
return {
@@ -85,13 +97,9 @@ function resolveHelperDockerConnection(
8597
};
8698
}
8799

88-
const socketPath = dependencies.findDockerSocketBind(currentContainerSpec);
89-
if (!socketPath) {
90-
throw new Error(
91-
'Self-update requires the Docker socket to be bind-mounted (e.g. /var/run/docker.sock:/var/run/docker.sock), or the watcher must be configured with a TCP Docker host',
92-
);
93-
}
94-
return { mode: 'socket', socketPath };
100+
throw new Error(
101+
'Self-update requires the Docker socket to be bind-mounted (e.g. /var/run/docker.sock:/var/run/docker.sock), or the watcher must be configured with a TCP Docker host',
102+
);
95103
}
96104

97105
async function executeSelfUpdateTransition(

0 commit comments

Comments
 (0)