fix(firecracker): status() defers unverifiable nft, like the preflight
`status()` hard-failed when it couldn't confirm the nft table, but listing nftables usually needs root — so an unprivileged `backend status` reported "not ready" even with the pool fully up, making it useless as a launch gate (and skipping the firecracker integration test on a set-up host). Base readiness on what the launch preflight actually hard-requires: the TAP pool present (unprivileged, authoritative) + no range overlap. Report the nft table state (present / unverified / not-confirmable-unprivileged) but don't let it flip readiness — the post-boot isolation probe is the authoritative isolation check, same as the preflight's deferral. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01WBMWTEtQdJ4W5UrWuLHCck
This commit is contained in:
@@ -74,6 +74,49 @@ class TestNetpoolRenderers(unittest.TestCase):
|
||||
self.assertIn("firecracker-netpool.sh up", out)
|
||||
|
||||
|
||||
class TestFirecrackerStatus(unittest.TestCase):
|
||||
"""`status()` gates launch: ready == TAP pool present + no overlap.
|
||||
An nft table that can't be confirmed unprivileged is reported, not
|
||||
treated as not-ready (the post-boot probe is authoritative)."""
|
||||
|
||||
def _run(self):
|
||||
import contextlib
|
||||
import io
|
||||
from bot_bottle.backend.firecracker import setup as fc_setup
|
||||
buf = io.StringIO()
|
||||
with contextlib.redirect_stderr(buf):
|
||||
rc = fc_setup.status()
|
||||
return rc, buf.getvalue()
|
||||
|
||||
def test_ready_when_taps_present_even_if_nft_unverifiable(self):
|
||||
from bot_bottle.backend.firecracker import setup as fc_setup
|
||||
with patch.object(fc_setup.netpool, "missing_taps", return_value=[]), \
|
||||
patch.object(fc_setup.netpool, "overlapping_routes", return_value=[]), \
|
||||
patch.object(fc_setup.shutil, "which", return_value=None):
|
||||
rc, out = self._run()
|
||||
self.assertEqual(0, rc)
|
||||
self.assertIn("unverified", out)
|
||||
|
||||
def test_not_ready_when_taps_missing(self):
|
||||
from bot_bottle.backend.firecracker import setup as fc_setup
|
||||
with patch.object(fc_setup.netpool, "missing_taps", return_value=["bbfc0"]), \
|
||||
patch.object(fc_setup.netpool, "overlapping_routes", return_value=[]), \
|
||||
patch.object(fc_setup.shutil, "which", return_value=None):
|
||||
rc, _ = self._run()
|
||||
self.assertEqual(1, rc)
|
||||
|
||||
def test_not_ready_on_range_overlap(self):
|
||||
from bot_bottle.backend.firecracker import netpool
|
||||
from bot_bottle.backend.firecracker import setup as fc_setup
|
||||
conflict = netpool.RouteConflict(dst="10.243.0.0/24", dev="eth0")
|
||||
with patch.object(fc_setup.netpool, "missing_taps", return_value=[]), \
|
||||
patch.object(fc_setup.netpool, "overlapping_routes", return_value=[conflict]), \
|
||||
patch.object(fc_setup.shutil, "which", return_value=None):
|
||||
rc, out = self._run()
|
||||
self.assertEqual(1, rc)
|
||||
self.assertIn("CLASHES", out)
|
||||
|
||||
|
||||
class TestNetpoolOverlap(unittest.TestCase):
|
||||
"""`overlapping_routes()` flags a pool base that collides with an
|
||||
existing host route (Tailscale CGNAT peer, docker/libvirt bridge,
|
||||
|
||||
Reference in New Issue
Block a user