v0.96.0 — R-50 island NIC: provision attaches the guest island net1

- LocalAPIConfig.island_bridge + island_guest_addr (+ IslandEnabled, Validate
  all-or-nothing + CIDR guard)
- buildBringUpConfig attaches static net1 (island) on provision + DR when set;
  absent otherwise (pre-R-50 byte-for-byte). Plumbed from cfg.LocalAPI at both
  RunBringUp sites. Endpoint already follows listen_addr (A0: no template change).
- healer stays eth0-only (A3 verify-only) — red-proof test locks the scoping
- example config + firewall example rewritten for the island; REUSE updated
- 3 non-hollow tests; full green. MinAgent unchanged.

Coupling: host-install island config requires agent >= 0.96.0 (vouch first).
This commit is contained in:
2026-07-25 14:16:23 +02:00
parent 36ed6594d4
commit dfd5d731ee
11 changed files with 223 additions and 68 deletions
+30
View File
@@ -165,6 +165,36 @@ func TestBuildBringUpConfig_ResourceCaps(t *testing.T) {
}
}
// R-50: with the island configured, bring-up attaches a static net1 on the island bridge; with it
// unset (or half-set), NO net1 is emitted — byte-for-byte the pre-R-50 config on non-island hosts.
// Pure-function check on buildBringUpConfig (the derivation that makes fresh installs F1-immune).
func TestBuildBringUpConfig_IslandNIC(t *testing.T) {
// island set → net1 present, exact shape, no hwaddr (PVE mints a fresh per-guest MAC)
island := buildBringUpConfig(BringUpSpec{
Mode: ModeProvision, IslandBridge: "vmbr9", IslandGuestAddr: "169.254.253.2/30",
}, scratchCfg())
if got, want := island["net1"], "name=eth1,bridge=vmbr9,ip=169.254.253.2/30"; got != want {
t.Errorf("island net1 mismatch:\n got %q\nwant %q", got, want)
}
// DR mode too — a restored customer guest must also reach the island-bound agent on the host.
dr := buildBringUpConfig(BringUpSpec{
Mode: ModeDRGuestLoss, KeepMAC: true, IslandBridge: "vmbr9", IslandGuestAddr: "169.254.253.2/30",
}, scratchCfg())
if _, ok := dr["net1"]; !ok {
t.Errorf("DR bring-up must also attach the island net1, got none")
}
// island unset → NO net1 key (non-island hosts unchanged; the pre-R-50 default)
none := buildBringUpConfig(BringUpSpec{Mode: ModeProvision}, scratchCfg())
if v, ok := none["net1"]; ok {
t.Errorf("net1 must be ABSENT when the island is not configured, got %q", v)
}
// half-configured (bridge only) → still no net1 (all-or-nothing; config.Validate rejects the config too)
half := buildBringUpConfig(BringUpSpec{Mode: ModeProvision, IslandBridge: "vmbr9"}, scratchCfg())
if v, ok := half["net1"]; ok {
t.Errorf("net1 must be ABSENT when only the bridge is set, got %q", v)
}
}
// Both restore sites allocate the guest INTO the felhom pool (SPIKE 3b): the provision bring-up
// threads spec.Pool, and the restore-test hardcodes DefaultPool — else a pool-scoped token 403s on
// the created guest's config/start/destroy. Asserts via the fakeAPI's captured RestoreLXCOptions.