fix(node-agent): reclaim capture port by PORT env, not image-tag regex
Burn test: only 3 of 8 Deltacast ports reached 'receiving'; the rest stuck 'connecting' forever. Root cause was NOT the board (all 8 SDI ports lock + feed framecache at 60fps — verified 8 live shm cursors). It was orphaned standby sidecars squatting host ports 7441-7445: new sidecars hit EADDRINUSE, got zero frames, and getStatus() reported 'connecting' forever. freeCapturePort() pre-filtered the container list by .Image regex, but after a wild-dragon-capture:latest rebuild the Docker list API degrades older containers' .Image to a bare image ID — so the tag regex silently SKIPPED the exact orphans holding the ports. Now we match by PORT env (survives rebuilds) and guard with the inspected Config.Image (which keeps the tag), so a port is always reclaimed before a new sidecar binds. This makes enable/disable 'just work' across image rebuilds.
This commit is contained in:
parent
e45de85512
commit
b508b203e3
1 changed files with 19 additions and 8 deletions
|
|
@ -733,18 +733,29 @@ async function freeCapturePort(capturePort) {
|
|||
const listRes = await dockerApi('GET', '/containers/json?all=1');
|
||||
if (listRes.status !== 200 || !Array.isArray(listRes.data)) return;
|
||||
for (const c of listRes.data) {
|
||||
const img = c.Image || '';
|
||||
if (!/wild-dragon-capture/.test(img)) continue;
|
||||
// Inspect to read the PORT env (list payload doesn't include env).
|
||||
// NOTE: do NOT pre-filter on c.Image here. After `wild-dragon-capture:latest`
|
||||
// is rebuilt, the Docker list API reports older containers' .Image as the
|
||||
// bare image ID (e.g. "226f9c953799") instead of the tag, so a regex on the
|
||||
// tag silently SKIPS those orphans — they keep holding the host port and the
|
||||
// replacement sidecar dies with EADDRINUSE ("connecting forever"). Identify
|
||||
// capture sidecars by their PORT env (+ inspected Config.Image) instead,
|
||||
// which survives a tag rebuild.
|
||||
try {
|
||||
const insp = await dockerApi('GET', `/containers/${c.Id}/json`);
|
||||
const cenv = (insp.status === 200 && insp.data?.Config?.Env) || [];
|
||||
if (insp.status !== 200) continue;
|
||||
const cfg = insp.data?.Config || {};
|
||||
const cenv = cfg.Env || [];
|
||||
const portEnv = cenv.find(e => e.startsWith('PORT='));
|
||||
const p = portEnv ? parseInt(portEnv.split('=')[1], 10) : NaN;
|
||||
if (p === capturePort) {
|
||||
console.log(`[sidecar] force-freeing capture port ${capturePort}: removing stale container ${c.Id.slice(0, 12)}`);
|
||||
await dockerApi('DELETE', `/containers/${c.Id}?force=true`).catch(() => {});
|
||||
}
|
||||
if (p !== capturePort) continue;
|
||||
// Config.Image (from inspect) preserves the original "wild-dragon-capture:..."
|
||||
// string even after a tag rebuild — use it as a sanity guard so we only ever
|
||||
// remove our own capture sidecars, never an unrelated host-net container that
|
||||
// happens to expose the same PORT env.
|
||||
const cfgImg = cfg.Image || '';
|
||||
if (!/wild-dragon-capture/.test(cfgImg)) continue;
|
||||
console.log(`[sidecar] force-freeing capture port ${capturePort}: removing stale container ${c.Id.slice(0, 12)} (image=${cfgImg})`);
|
||||
await dockerApi('DELETE', `/containers/${c.Id}?force=true`).catch(() => {});
|
||||
} catch (_) { /* container vanished mid-scan — fine */ }
|
||||
}
|
||||
} catch (e) {
|
||||
|
|
|
|||
Loading…
Reference in a new issue