extractUdpPort: handle Komodo's newline-string ports form
Real Komodo GetDeployment responses render config.ports as a single
newline-delimited string ("28963:28963/udp\n28963:28963/tcp\n"), not as
the array shapes we already handled. The walker only descended into
arrays and objects, so the string was skipped entirely and every Komodo
probe on a real deployment fell through to no-udp-in-response.
When the walker hits a value under a ports-keyed field that's a string,
split it on \r\n , ; and treat the resulting parts as a preferred string
array — the existing "host:container/udp" and "container/udp" regexes
handle each part.
Fixture pulled verbatim from the t5-nacht-1 response the fleet UI
surfaced. Also covers single-line, host-network (no host prefix), and
TCP-only-shouldn't-match cases.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
@@ -301,6 +301,18 @@ export function extractUdpPort(payload: unknown): number | null {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
for (const [key, val] of Object.entries(obj)) {
|
for (const [key, val] of Object.entries(obj)) {
|
||||||
|
// Komodo's GetDeployment renders `config.ports` as a single
|
||||||
|
// newline-delimited string ("28963:28963/udp\n28963:28963/tcp").
|
||||||
|
// Synthesize an array from it and treat as a preferred candidate.
|
||||||
|
if (PORT_ARRAY_KEYS.has(key) && typeof val === 'string' && val.trim()) {
|
||||||
|
const parts = val
|
||||||
|
.split(/[\r\n,;]+/)
|
||||||
|
.map((s) => s.trim())
|
||||||
|
.filter(Boolean);
|
||||||
|
if (parts.length > 0) {
|
||||||
|
candidates.push({ arr: parts, preferred: true });
|
||||||
|
}
|
||||||
|
}
|
||||||
walk(val, PORT_ARRAY_KEYS.has(key));
|
walk(val, PORT_ARRAY_KEYS.has(key));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -81,6 +81,32 @@ describe('extractUdpPort', () => {
|
|||||||
};
|
};
|
||||||
expect(extractUdpPort(payload)).toBe(28968);
|
expect(extractUdpPort(payload)).toBe(28968);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('splits Komodo`s newline-delimited string form of `config.ports`', () => {
|
||||||
|
// Verbatim shape from a real Komodo GetDeployment response — the
|
||||||
|
// container publishes UDP 28963 and TCP 28963 as a single string.
|
||||||
|
const payload = {
|
||||||
|
name: 't5-nacht-1',
|
||||||
|
config: {
|
||||||
|
image: { type: 'Image', params: { image: 'ghcr.io/ayymoss/plutainer:latest' } },
|
||||||
|
network: 'komodo_default',
|
||||||
|
ports: '28963:28963/udp\n28963:28963/tcp\n'
|
||||||
|
}
|
||||||
|
};
|
||||||
|
expect(extractUdpPort(payload)).toBe(28963);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('handles a single-line string-form ports value', () => {
|
||||||
|
expect(extractUdpPort({ config: { ports: '28969:28960/udp' } })).toBe(28969);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('handles a host-network string-form ports value (no host prefix)', () => {
|
||||||
|
expect(extractUdpPort({ config: { ports: '28970/udp\n' } })).toBe(28970);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('ignores TCP-only string-form ports entries', () => {
|
||||||
|
expect(extractUdpPort({ config: { ports: '80:80/tcp\n443:443/tcp' } })).toBeNull();
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('komodoResolvePortDetailed', () => {
|
describe('komodoResolvePortDetailed', () => {
|
||||||
|
|||||||
Reference in New Issue
Block a user