mirror of
https://github.com/kevinveenbirkenbach/computer-playbook.git
synced 2026-11-06 10:59:59 +00:00
compose/cache.shared.override.yml attaches every worktree stack's
infinito service to the primary's network (infinito-nexus-core_default,
172.30.0.0/24) without an ipv4_address. The default network declared
no ip_range, so Docker IPAM handed out the lowest free address and the
worktree stacks took 172.30.0.2, .3, .8, .9 and, whenever the primary
container was down, .10, the primary's INFINITO_IP4. The primary's
`make compose-up` then failed with "Address already in use", and with
it `make quality-high` on main.
The default network now declares ip_range: ${INFINITO_IP_RANGE}, the
upper half of INFINITO_SUBNET. Every static address of the stack (.1,
.4 to .6, .10, .53) sits in the lower half, so a container without a
static address can no longer take one.
INFINITO_IP_RANGE is derived from the final INFINITO_SUBNET after
custom.env is applied, because a worktree pins its subnet there and
`make dotenv-force` regenerates without it in the environment. Existing
worktrees therefore get their own slot's range without a new pin.
A lint checks that every static IP in default.env and every
172.30.${slot}.N pin in scripts/system/worktree/up.sh lies below the
pool.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
41 lines
1.2 KiB
Python
41 lines
1.2 KiB
Python
"""INFINITO_IP_RANGE: the dynamic IPAM pool of the compose stack's bridge.
|
|
|
|
Docker hands every container that joins the bridge without an
|
|
``ipv4_address`` the lowest free address of the pool. Worktree stacks join the
|
|
primary's network as the cache network, so without a pool they took the
|
|
primary's static addresses below .128. The pool is the upper half of the final
|
|
``INFINITO_SUBNET``; every static address sits in the lower half.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import ipaddress
|
|
from typing import TYPE_CHECKING
|
|
|
|
if TYPE_CHECKING:
|
|
from utils.env.builder import EnvBuilder
|
|
|
|
KEY = "INFINITO_IP_RANGE"
|
|
SUBNET_KEY = "INFINITO_SUBNET"
|
|
COMMENT = "Dynamic IPAM pool of INFINITO_SUBNET (its upper half); every static IP of the stack sits below it."
|
|
|
|
|
|
def upper_half(subnet: str) -> str:
|
|
"""Return the upper half of ``subnet``.
|
|
|
|
Args:
|
|
subnet: an IPv4 CIDR such as ``172.30.0.0/24``.
|
|
|
|
Returns:
|
|
The CIDR of its upper half, e.g. ``172.30.0.128/25``.
|
|
"""
|
|
network = ipaddress.ip_network(subnet, strict=True)
|
|
return str(list(network.subnets(prefixlen_diff=1))[1])
|
|
|
|
|
|
def finalize(eb: EnvBuilder) -> None:
|
|
subnet = eb.get(SUBNET_KEY)
|
|
if not subnet:
|
|
return
|
|
eb.set(KEY, upper_half(subnet), comment=COMMENT)
|