fix(docker): only heal on the ParseAddr poison signature, not any inspect error
prd-number-check / require-numbered-prds (pull_request) Successful in 5s
test / unit (pull_request) Successful in 50s
tracker-policy-pr / check-pr (pull_request) Successful in 6s
test / image-input-builds (pull_request) Successful in 59s
test / integration-docker (pull_request) Successful in 1m6s
test / coverage (pull_request) Successful in 42s
Update Quality Badges / update-badges (push) Successful in 49s
lint / lint (push) Successful in 1m2s
test / coverage (push) Successful in 24s
test / integration-docker (push) Successful in 1m3s
test / unit (push) Successful in 48s
test / image-input-builds (push) Successful in 2m44s
prd-number-check / require-numbered-prds (pull_request) Successful in 5s
test / unit (pull_request) Successful in 50s
tracker-policy-pr / check-pr (pull_request) Successful in 6s
test / image-input-builds (pull_request) Successful in 59s
test / integration-docker (pull_request) Successful in 1m6s
test / coverage (pull_request) Successful in 42s
Update Quality Badges / update-badges (push) Successful in 49s
lint / lint (push) Successful in 1m2s
test / coverage (push) Successful in 24s
test / integration-docker (push) Successful in 1m3s
test / unit (push) Successful in 48s
test / image-input-builds (push) Successful in 2m44s
Address review: the self-heal classified every non-absence `network inspect` failure as a poisoned network and force-removed the shared gateway. But inspect also fails on transient daemon/API errors, permission failures, timeouts, or a bad context — destroying a healthy gateway on that guess would tear the network out from under every live bottle. Now the destructive path runs only for the known poison signature (docker's `ParseAddr` error from the malformed `::1/64` IPv6 gateway). The absent case (`No such network`) still just creates; any other inspect failure raises a clear GatewayError without mutating shared state. Adds a regression test asserting a generic inspect error issues neither `docker rm --force` nor `docker network rm` and surfaces the error. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit was merged in pull request #522.
This commit is contained in:
@@ -144,19 +144,33 @@ class DockerGateway(Gateway):
|
||||
# by older releases. Replace it below.
|
||||
stale = True
|
||||
else:
|
||||
# inspect failed: either the network is absent (create it below) or
|
||||
# it exists but docker can't parse it. A daemon that default-enables
|
||||
# IPv6 attaches an fdd0::/64 subnet whose `::1/64` gateway trips
|
||||
# docker's own netip.ParseAddr in `network inspect`/`ls`, so the
|
||||
# command exits non-zero. A fixed release never *creates* such a
|
||||
# network, but one can survive on a shared host from an older or
|
||||
# concurrent launch — and `--ipv6=false` alone can't heal it, since
|
||||
# the create below only no-ops on "already exists". Force-replace it
|
||||
# so later reads (e.g. `_network_cidr` pinning a source IP) stop
|
||||
# failing. An absent network's inspect error is "No such network";
|
||||
# anything else means it exists but is poisoned.
|
||||
# inspect failed. Classify by stderr — do NOT assume "not absent"
|
||||
# implies "poisoned": a transient daemon/API error, permission
|
||||
# failure, timeout, or bad context also fails here, and destroying
|
||||
# the shared gateway on that guess would tear the network out from
|
||||
# under every live bottle.
|
||||
err = inspected.stderr.lower()
|
||||
stale = "no such network" not in err and "not found" not in err
|
||||
if "no such network" in err or "not found" in err:
|
||||
# Absent: nothing to replace — create it below.
|
||||
stale = False
|
||||
elif "parseaddr" in err:
|
||||
# Present but poisoned. A daemon that default-enables IPv6
|
||||
# attaches an fdd0::/64 subnet whose `::1/64` gateway trips
|
||||
# docker's own netip.ParseAddr in `network inspect`/`ls`, so the
|
||||
# command exits non-zero with that signature. A fixed release
|
||||
# never *creates* such a network, but one can survive on a
|
||||
# shared host from an older or concurrent launch — and
|
||||
# `--ipv6=false` alone can't heal it, since the create below only
|
||||
# no-ops on "already exists". Force-replace it so later reads
|
||||
# (e.g. `_network_cidr` pinning a source IP) stop failing.
|
||||
stale = True
|
||||
else:
|
||||
# Unrecognized failure: no evidence the network is malformed.
|
||||
# Surface it rather than mutate shared state on a guess.
|
||||
raise GatewayError(
|
||||
f"gateway network {self.network} could not be inspected: "
|
||||
f"{inspected.stderr.strip()}"
|
||||
)
|
||||
if stale:
|
||||
# Migrate the stale/poisoned network. Removing the fixed gateway is
|
||||
# safe here: this launch recreates it.
|
||||
|
||||
Reference in New Issue
Block a user