Skip to content

Commit 5577a1c

Browse files
VijitSingh97claude
andcommitted
feat(infra): turn a bridge-subnet collision into actionable guidance (#180)
The configurable subnet (network.subnet) gives an affected host the knob, but a non-expert who hits Docker's cryptic "Pool overlaps with other one on this address space" won't know to use it. `pithead up`/`apply`/`upgrade` now capture the compose-up failure and, when it's a subnet overlap, print the exact fix — set network.subnet to a free /24 (with an example) — instead of a raw Docker stack trace. Pure, unit-tested explain_subnet_collision helper. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
1 parent c3f2d5d commit 5577a1c

4 files changed

Lines changed: 45 additions & 8 deletions

File tree

CHANGELOG.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -161,7 +161,9 @@ per the process in [`docs/releasing.md`](docs/releasing.md).
161161
The structured fixed-IP layout is preserved (services keep their `.25``.31` octets), so the
162162
host-networked dashboard's bridge addressing and the #122 worker-SSRF CIDR guard still hold — only
163163
the `/24` base moves. A single `NETWORK_PREFIX` flows through every config path: compose
164-
interpolation, monerod/Tor container `envsubst`/`sed` at start, and the Tari config render.
164+
interpolation, monerod/Tor container `envsubst`/`sed` at start, and the Tari config render. And if
165+
a host *does* collide, `pithead up`/`apply` now catch Docker's overlap error and print the exact
166+
`network.subnet` fix (with an example) instead of a raw Docker stack trace.
165167
- Dashboard now records **every** P2Pool share instead of at most one per 30 s poll: it tracks the
166168
cumulative `shares_found` counter and records the per-poll delta as N distinct shares. A
167169
higher-hashrate or nano-sidechain node finding 2+ shares within one poll window no longer

docs/test-inventory.md

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ edit by hand** — re-run the target to refresh. See [Testing Strategy](testing-
55
how the tiers fit together._
66

77
**Totals:** 432 dashboard unit tests · 12 contract tests · 25 frontend
8-
tests · 28 `pithead` shell sections · 15 harness self-test sections ·
8+
tests · 29 `pithead` shell sections · 15 harness self-test sections ·
99
8 live config scenarios (15 axis values) · 6 mini-stack scenarios.
1010

1111
> Counts are **test functions / named cases** (parametrized pytest cases expand to more at
@@ -16,7 +16,7 @@ tests · 28 `pithead` shell sections · 15 harness self-test sections ·
1616
|---|---|---|
1717
| 1 — Unit | dashboard pytest | 432 |
1818
| 1 — Unit | frontend (node --test) | 25 |
19-
| 1 — Unit | `pithead` shell suite | 28 sections |
19+
| 1 — Unit | `pithead` shell suite | 29 sections |
2020
| 1 — Unit | compose interpolation + hardening (#90) | 1 |
2121
| 2 — Contract | fake-daemon clients | 12 |
2222
| 3 — Mini-stack | docker control-plane scenarios | 6 |
@@ -532,7 +532,7 @@ tests · 28 `pithead` shell sections · 15 harness self-test sections ·
532532
- heroKpis: mode colour follows the server mode_variant token
533533
- heroKpis: total is accent-coloured; blocks and tier carry no colour class
534534

535-
### `pithead` shell suite (tests/stack/run.sh) — 28 sections
535+
### `pithead` shell suite (tests/stack/run.sh) — 29 sections
536536
- unit: resolve_default
537537
- unit: assert_safe_dir
538538
- unit: is_public_ip classifier (#113)
@@ -541,6 +541,7 @@ tests · 28 `pithead` shell sections · 15 harness self-test sections ·
541541
- unit: docker_boot_enabled (#137)
542542
- unit: is_valid_host (#130)
543543
- unit: describe_change
544+
- unit: explain_subnet_collision (#180)
544545
- unit: env helpers
545546
- unit: export_build_provenance (Issue #58)
546547
- unit: node credential helpers
@@ -688,5 +689,5 @@ tests · 28 `pithead` shell sections · 15 harness self-test sections ·
688689

689690
---
690691

691-
_Grand total: **526** enumerated cases/sections across the four tiers (plus the live
692+
_Grand total: **527** enumerated cases/sections across the four tiers (plus the live
692693
lifecycle and fault-injection phases, which are exercised on a real server)._

pithead

Lines changed: 31 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -100,12 +100,38 @@ migrate_compose_project() {
100100
docker rm -f "${stale[@]}" >/dev/null 2>&1 || true
101101
}
102102

103+
# Turn Docker's cryptic bridge-subnet overlap error into an actionable fix (#180). $1 = compose
104+
# output. A no-op unless Docker rejected the subnet ("Pool overlaps with other one on this address
105+
# space"), which happens when the host already uses the stack's /24.
106+
explain_subnet_collision() {
107+
case "$1" in
108+
*"overlaps with other one"*|*"Pool overlaps"*)
109+
local sub; sub="$(env_get NETWORK_SUBNET 2>/dev/null || true)"; [ -n "$sub" ] || sub="172.28.0.0/24"
110+
warn "Docker refused the stack's bridge subnet ($sub): it overlaps a network already on this host."
111+
warn "Pick a free /24 and set it in ${CONFIG_FILE:-config.json} — e.g. \"network\": { \"subnet\": \"172.30.0.0/24\" } — then re-run '$0 apply' && '$0 up' (see docs/configuration.md, network.subnet)." ;;
112+
esac
113+
}
114+
115+
# Run `docker compose up` with live output; on failure, explain a bridge-subnet collision (#180) if
116+
# that's what Docker rejected. Returns compose's own exit code.
117+
compose_up_checked() {
118+
local tmp out rc
119+
tmp="$(mktemp)"
120+
docker compose up "$@" 2>&1 | tee "$tmp"
121+
rc=${PIPESTATUS[0]}
122+
out="$(cat "$tmp")"; rm -f "$tmp"
123+
[ "$rc" -ne 0 ] && explain_subnet_collision "$out"
124+
return "$rc"
125+
}
126+
103127
stack_up() {
104128
log "Starting stack..."
105129
warn_missing_data_dirs
106130
migrate_compose_project
107131
# Docker Compose automatically picks up COMPOSE_PROFILES from .env
108-
docker compose up -d
132+
if ! compose_up_checked -d; then
133+
error "Stack failed to start — see the error above."
134+
fi
109135
log "Stack started successfully!"
110136
announce_dashboard_url
111137
}
@@ -140,7 +166,9 @@ stack_upgrade() {
140166
generate_caddyfile
141167
log "Re-rendered generated config for the current release."
142168
migrate_compose_project
143-
docker compose up -d --build
169+
if ! compose_up_checked -d --build; then
170+
error "Upgrade failed during 'docker compose up' — see the error above."
171+
fi
144172
log "Stack upgraded."
145173
}
146174

@@ -2035,7 +2063,7 @@ apply() {
20352063
: > "$apply_marker"
20362064
# Compose recreates only the services whose resolved config changed; --remove-orphans
20372065
# drops monerod when a local→remote switch deactivates the local_node profile.
2038-
if ! docker compose up -d --remove-orphans; then
2066+
if ! compose_up_checked -d --remove-orphans; then
20392067
warn "Config files were updated but containers were NOT recreated ('docker compose up' failed)."
20402068
warn "Fix the cause shown above, then re-run '$0 apply' (it will retry the recreate) — or '$0 up'."
20412069
exit 1 # leave $apply_marker in place so the retry re-attempts the recreate

tests/stack/run.sh

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -132,6 +132,12 @@ assert_contains "tari mem is INFO" "$(run_sourced "$SANDBOX" describe_change T
132132
assert_contains "monero mem is INFO" "$(run_sourced "$SANDBOX" describe_change MONERO_MEM_LIMIT 4g 6g)" "INFO"
133133
assert_contains "monero mem recreate note" "$(run_sourced "$SANDBOX" describe_change MONERO_MEM_LIMIT 4g 6g)" "monerod container is recreated"
134134

135+
echo "== unit: explain_subnet_collision (#180) =="
136+
ov="$(run_sourced "$SANDBOX" explain_subnet_collision "invalid pool request: Pool overlaps with other one on this address space" 2>&1)"
137+
assert_contains "subnet overlap -> network.subnet hint" "$ov" "network"
138+
assert_contains "subnet overlap -> suggests a free /24" "$ov" "/24"
139+
assert_eq "non-overlap failure stays silent" "$(run_sourced "$SANDBOX" explain_subnet_collision "some other failure" 2>&1)" ""
140+
135141
echo "== unit: env helpers =="
136142
printf 'A=1\nB=two\nPROXY_AUTH_TOKEN=keep=me\n' > "$SANDBOX/old.env"
137143
printf 'A=1\nB=three\nC=4\nPROXY_AUTH_TOKEN=keep=me\n' > "$SANDBOX/new.env"

0 commit comments

Comments
 (0)