From 251181255be7369c5285bb9a1dfa178d880fdd65 Mon Sep 17 00:00:00 2001 From: anti Date: Sun, 10 May 2026 00:50:41 -0400 Subject: [PATCH] fix(network): reuse existing decnet_lan when active deckies are connected MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Docker refuses network removal (403) when containers hold endpoints. The old IPAM-drift path tried to disconnect+remove even with live containers — disconnect silently failed, remove raised APIError. Since DECNET assigns IPs explicitly in compose (never via Docker's auto-assign pool), an ip_range mismatch on an existing same-driver network is harmless. Bail out early and attach to the existing network whenever Containers is non-empty. --- decnet/network.py | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/decnet/network.py b/decnet/network.py index 5eaa7474..c52d187c 100644 --- a/decnet/network.py +++ b/decnet/network.py @@ -155,7 +155,9 @@ def _ensure_network( # Same driver — but if the IPAM pool drifted (different subnet, # gateway, or ip-range than this deploy asks for), reusing it # hands out addresses from the old pool and we race the real LAN. - # Compare and rebuild on mismatch. + # Compare and rebuild on mismatch — but only when no containers + # are attached. With active endpoints Docker refuses the remove + # with 403; just attach to the existing network instead. pools = (net.attrs.get("IPAM") or {}).get("Config") or [] cur = pools[0] if pools else {} if ( @@ -164,8 +166,15 @@ def _ensure_network( and cur.get("IPRange") == ip_range ): return # right driver AND matching pool, leave it alone - # Driver mismatch OR IPAM drift — tear it down. Disconnect any live - # containers first so `remove()` doesn't refuse with ErrNetworkInUse. + if net.attrs.get("Containers"): + # Active endpoints — can't safely rebuild. Attach to the + # existing network; IPAM drift on ip_range only affects + # Docker's auto-assign pool, which DECNET doesn't use + # (IPs are always set explicitly in the compose file). + return + # Driver mismatch OR empty-endpoint IPAM drift — tear it down. + # Disconnect any live containers first so `remove()` doesn't + # refuse with ErrNetworkInUse. for cid in (net.attrs.get("Containers") or {}): try: net.disconnect(cid, force=True)