fix: expose asks for too much, and the fake legacy box had no claude user
Run 18: 74/2. grok passes (reading the installer worked), migrate's
retire passes. Two left, both mine:
1. box expose — read the Incus proxy docs instead of guessing again.
Proxy IS supported on VMs, but NAT mode only (correct already), and
crucially it does NOT need a static address: with no static IP, Incus
reads the box's address off the bridge's neighbour table and keeps
the NAT rules in step. My first cut pinned the lease with a device
override anyway — unnecessary, and almost certainly the step that was
failing before the proxy was ever added. Dropped it. Ask for less.
The drill also stopped throwing incus's reason away: it swallowed
stderr, then RE-RAN the command and printed only the last line ('box:
expose failed') — box's own words, never incus's. It now captures the
first attempt and prints all of it.
2. 'legacy box never came up' was unfixable by any timeout. wait_box
uses 'box exec', which for a legacy-tagged box resolves the user to
'claude' — and the drill's synthetic legacy box was a BARE image with
no claude user, so sudo -u claude could never answer, on a box that
was perfectly healthy (every migration check against it passed). The
fake legacy box now creates a claude user, like a real pre-0.4.0 box
had.
This commit is contained in:
parent
8729c3e522
commit
e4b546cd29
2 changed files with 22 additions and 19 deletions
26
bin/box
26
bin/box
|
|
@ -1005,23 +1005,17 @@ cmd_expose() {
|
||||||
incus network acl rule add box-isolate ingress action=allow \
|
incus network acl rule add box-isolate ingress action=allow \
|
||||||
"destination=$ip/32" "destination_port=$port" protocol=tcp >/dev/null 2>&1 || true
|
"destination=$ip/32" "destination_port=$port" protocol=tcp >/dev/null 2>&1 || true
|
||||||
|
|
||||||
# A VM's proxy device needs NAT mode — the plain userspace forkproxy is a
|
# A VM's proxy device must be NAT mode — Incus supports proxy on containers in
|
||||||
# container thing. NAT mode DNATs host:port → instance:port in netfilter, and
|
# both modes, but on VMs "NAT mode only" (the userspace forkproxy is a
|
||||||
# it REQUIRES the instance's NIC to hold a static address, so pin the lease
|
# container thing). NAT mode DNATs host:port → instance:port in netfilter, and
|
||||||
# the box already has. Pinning also fixes the restart caveat: the exposure
|
# it needs the host to be the instance's gateway, which boxnet makes true.
|
||||||
# survives a reboot instead of pointing at a lease the box no longer owns.
|
#
|
||||||
|
# It does NOT need a static address: with no static IP, Incus reads the box's
|
||||||
|
# current address off the bridge's neighbour table and keeps the NAT rules in
|
||||||
|
# step. The first cut of this pinned the lease with a device override anyway —
|
||||||
|
# unnecessary, and it is what made expose fail before it ever reached the
|
||||||
|
# proxy. Ask for less; the docs said so.
|
||||||
local err; err="$(mktemp)"
|
local err; err="$(mktemp)"
|
||||||
if ! incus config device override "$inst" eth0 "ipv4.address=$ip" >/dev/null 2>"$err"; then
|
|
||||||
# Already overridden (a second exposure on the same box) is fine.
|
|
||||||
grep -qi 'already' "$err" || {
|
|
||||||
echo "box: could not pin the box's address (needed for a VM proxy):" >&2
|
|
||||||
sed 's/^/ /' "$err" >&2; rm -f "$err"
|
|
||||||
incus network acl rule remove box-isolate ingress action=allow \
|
|
||||||
"destination=$ip/32" "destination_port=$port" protocol=tcp >/dev/null 2>&1
|
|
||||||
die "expose failed"
|
|
||||||
}
|
|
||||||
fi
|
|
||||||
|
|
||||||
if incus config device add "$inst" "$dev" proxy \
|
if incus config device add "$inst" "$dev" proxy \
|
||||||
"listen=tcp:127.0.0.1:$hport" "connect=tcp:$ip:$port" \
|
"listen=tcp:127.0.0.1:$hport" "connect=tcp:$ip:$port" \
|
||||||
bind=host nat=true >/dev/null 2>"$err"; then
|
bind=host nat=true >/dev/null 2>"$err"; then
|
||||||
|
|
|
||||||
|
|
@ -714,7 +714,8 @@ if incus file push "$srv" archive/tmp/srv.js >/dev/null 2>&1; then
|
||||||
# nothing holds its stdout (trap 2/3). The listener outlives the exec.
|
# nothing holds its stdout (trap 2/3). The listener outlives the exec.
|
||||||
timeout -k 5 20 incus exec archive -- sh -c 'setsid node /tmp/srv.js >/tmp/srv.log 2>&1 </dev/null &' </dev/null
|
timeout -k 5 20 incus exec archive -- sh -c 'setsid node /tmp/srv.js >/tmp/srv.log 2>&1 </dev/null &' </dev/null
|
||||||
sleep 3
|
sleep 3
|
||||||
if box expose archive "$EP" "$EHP" >/dev/null 2>&1; then
|
xlog="$(mktemp)"
|
||||||
|
if box expose archive "$EP" "$EHP" >"$xlog" 2>&1; then
|
||||||
ok "box expose archive $EP $EHP — the device was added"
|
ok "box expose archive $EP $EHP — the device was added"
|
||||||
box expose archive --list 2>/dev/null | grep -q "$EP" \
|
box expose archive --list 2>/dev/null | grep -q "$EP" \
|
||||||
&& ok "expose --list shows the open door" || no "expose --list does not show the exposure"
|
&& ok "expose --list shows the open door" || no "expose --list does not show the exposure"
|
||||||
|
|
@ -741,9 +742,12 @@ if incus file push "$srv" archive/tmp/srv.js >/dev/null 2>&1; then
|
||||||
&& no "the host still reaches the box after --remove — the door did not shut" \
|
&& no "the host still reaches the box after --remove — the door did not shut" \
|
||||||
|| ok "after --remove, 127.0.0.1:$EHP is dead — the door shut"
|
|| ok "after --remove, 127.0.0.1:$EHP is dead — the door shut"
|
||||||
else
|
else
|
||||||
no "box expose failed to add the device — tail: $(box expose archive "$EP" "$EHP" 2>&1 | tail -1)"
|
no "box expose failed to add the device"
|
||||||
|
inf "what box and incus actually said:"
|
||||||
|
sed 's/^/ /' "$xlog" 2>/dev/null
|
||||||
rm -f "$srv" 2>/dev/null
|
rm -f "$srv" 2>/dev/null
|
||||||
fi
|
fi
|
||||||
|
rm -f "$xlog" 2>/dev/null
|
||||||
timeout -k 5 15 incus exec archive -- pkill -f srv.js </dev/null >/dev/null 2>&1
|
timeout -k 5 15 incus exec archive -- pkill -f srv.js </dev/null >/dev/null 2>&1
|
||||||
else
|
else
|
||||||
rm -f "$srv"
|
rm -f "$srv"
|
||||||
|
|
@ -807,9 +811,14 @@ else
|
||||||
# A minimal legacy box: no template payload, just boots and networks on the
|
# A minimal legacy box: no template payload, just boots and networks on the
|
||||||
# old stack, wearing the old tag. This is what migrate has to move.
|
# old stack, wearing the old tag. This is what migrate has to move.
|
||||||
printf '\n minting a faithful legacy box on the old stack…\n'
|
printf '\n minting a faithful legacy box on the old stack…\n'
|
||||||
|
# The legacy box must carry a 'claude' user, because that is what a real
|
||||||
|
# pre-0.4.0 box had — and box_user() maps the legacy tag to it. Without the
|
||||||
|
# user, 'box exec' (sudo -u claude) can never answer and wait_box fails
|
||||||
|
# forever on a box that is perfectly healthy. Run 17/18 lost a FAIL to this.
|
||||||
if mint_legacy=$(incus launch images:debian/13/cloud legacybox --profile claude-dev \
|
if mint_legacy=$(incus launch images:debian/13/cloud legacybox --profile claude-dev \
|
||||||
--config user.claudebox=1 --vm --device root,size=20GiB \
|
--config user.claudebox=1 --vm --device root,size=20GiB \
|
||||||
--config security.secureboot=false 2>&1); then
|
--config security.secureboot=false \
|
||||||
|
--config cloud-init.user-data="$(printf '#cloud-config\nusers:\n - name: claude\n shell: /bin/bash\n sudo: "ALL=(ALL) NOPASSWD:ALL"\n lock_passwd: true\n')" 2>&1); then
|
||||||
wait_box legacybox && ok "legacy box up on the old stack (claudenet, user.claudebox=1)" \
|
wait_box legacybox && ok "legacy box up on the old stack (claudenet, user.claudebox=1)" \
|
||||||
|| no "legacy box never came up — cannot drill migration"
|
|| no "legacy box never came up — cannot drill migration"
|
||||||
box list 2>/dev/null | grep -q '^legacybox' \
|
box list 2>/dev/null | grep -q '^legacybox' \
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue