fix(drift): stop flagging declared external networks as drift (#1402)

The spatial drift engine reported a service attached to a declared
external network (top-level networks: { foo: { external: true } }) as
"attached to a network not declared in compose", marking the stack
permanently drifted. runtimeResourceName project-prefixed every declared
network to <project>_<key>, but Docker never prefixes an external
network: it references a pre-existing network by its real name. The
phantom <project>_<key> never matched the runtime name, so the
attachment fell through to a foreign-network finding.

Resolve external networks to their real name (the key, or a name:
override) without the project prefix, so the raw declared adapter agrees
with the rendered model the Network Inspector already used. Add unit,
adapter, and engine-level regression tests, and extend the adapter
equivalence test to cover an external network with no name override.
This commit is contained in:
Anso
2026-06-21 17:58:00 -04:00
committed by GitHub
parent b9314eb67f
commit b611f41872
3 changed files with 55 additions and 4 deletions
+8 -4
View File
@@ -24,9 +24,13 @@ export function isLoopback(ip: string): boolean {
}
/** Resolved runtime name of a top-level network/volume: a `name:` override wins,
* otherwise compose prefixes the project (`<project>_<key>`). */
export function runtimeResourceName(projectName: string, key: string, declaredName: string | undefined): string {
return declaredName && declaredName !== key ? declaredName : `${projectName}_${key}`;
* otherwise compose prefixes the project (`<project>_<key>`). An external
* resource is never project-prefixed: it references a pre-existing network/volume
* by its real name (the key, or a `name:` override), so prefixing it would invent
* a `<project>_<key>` that no runtime resource matches and read as foreign drift. */
export function runtimeResourceName(projectName: string, key: string, declaredName: string | undefined, external = false): string {
if (declaredName && declaredName !== key) return declaredName;
return external ? key : `${projectName}_${key}`;
}
/** Extract host port numbers referenced by free-text access URLs, for the
@@ -67,7 +71,7 @@ export function fromEffectiveModel(m: EffectiveModel): NormalizedNetworkModel {
export function fromDeclaredCompose(m: DeclaredCompose, projectName: string): NormalizedNetworkModel {
const networks: NormalizedNetworkModel['networks'] = {};
for (const [key, res] of Object.entries(m.networks)) {
networks[key] = { runtimeName: runtimeResourceName(projectName, key, res.name), external: res.external };
networks[key] = { runtimeName: runtimeResourceName(projectName, key, res.name, res.external), external: res.external };
}
return {
projectName,