feat(#2363): DHCP pool for bridge service containers
Add a DHCP pool to the gateway's dnsmasq so bridge-attached service containers (hive-ci and future equivalents) get their addresses from a proper DHCP server instead of a brittle static derivation. gateway (hive-gateway.nix): - Add IPv4 arithmetic helpers (ipToInt, intToIp, pow2) to compute the DHCP pool range at nix eval time from bridgeIp + bridgePrefixLength. - Reserve the last dhcpPoolSize (14) usable host addresses as the DHCP pool (e.g. .241-.254 on a /24 with 10.42.0.0 network). - Add dhcp-range and dhcp-leasefile to the dnsmasq settings block. The pool is active whenever services.hyperhive.network.enable is true. hive-ci (hive-ci.nix): - Remove the ciBridgeIp / ciBridgeOctets static derivation and the brittle top-of-/24 comment block. - Switch networking.interfaces.eth0 to useDHCP = true so hive-ci gets its address from the gateway DHCP pool. lifecycle (mod.rs, tests.rs): - Add DHCP_POOL_SIZE = 14 constant (must stay in sync with dhcpPoolSize in hive-gateway.nix). - Remap agents whose FNV-1a hash falls in the DHCP pool into the agent-only window [2, dhcp_start - 1]. Only the rare agent whose name hashes into the pool is affected; all others keep their IPs. - Update and extend tests: agent range is now .2-.240 on /24; add agent_network_ip_never_in_dhcp_pool covering 18 agent names.
This commit is contained in:
parent
16f69ca890
commit
a3796890f5
4 changed files with 114 additions and 36 deletions
|
|
@ -39,17 +39,54 @@ async fn setup_proposed_seeds_flake_nix() {
|
|||
|
||||
#[test]
|
||||
fn agent_network_ip_is_in_subnet() {
|
||||
// Default subnet 10.42.0.0/24 — agents get .2 to .254.
|
||||
// Default subnet 10.42.0.0/24 — agents get .2 to .240 (last 14 are DHCP pool).
|
||||
let ip = agent_network_ip("alice", "10.42.0.0/24").expect("should produce an IP");
|
||||
let octets: Vec<u8> = ip.split('.').map(|o| o.parse().unwrap()).collect();
|
||||
assert_eq!(&octets[..3], &[10, 42, 0], "wrong /24 prefix");
|
||||
// usable=253, dhcp_start=241 → agent range is .2-.240
|
||||
assert!(
|
||||
octets[3] >= 2 && octets[3] <= 254,
|
||||
"host byte {}",
|
||||
octets[3] >= 2 && octets[3] <= 240,
|
||||
"host byte {} — expected in agent-only range [2,240]",
|
||||
octets[3]
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn agent_network_ip_never_in_dhcp_pool() {
|
||||
// No agent should be assigned an address in the DHCP pool
|
||||
// (.241-.254 on a /24 with DHCP_POOL_SIZE=14).
|
||||
let subnet = "10.42.0.0/24";
|
||||
let names = [
|
||||
"alice",
|
||||
"bob",
|
||||
"carol",
|
||||
"damocles",
|
||||
"iris",
|
||||
"argus",
|
||||
"atlas",
|
||||
"ruth",
|
||||
"dmatrix",
|
||||
"bitburner",
|
||||
"lexis",
|
||||
"sock",
|
||||
"triage",
|
||||
"janet",
|
||||
"eve",
|
||||
"frank",
|
||||
"grace",
|
||||
"heidi",
|
||||
];
|
||||
for name in names {
|
||||
let ip = agent_network_ip(name, subnet).unwrap_or_else(|| panic!("{name} returned None"));
|
||||
let last: u8 = ip.rsplit('.').next().unwrap().parse().unwrap();
|
||||
// dhcp_start_offset = usable(253) + 2 - DHCP_POOL_SIZE(14) = 241
|
||||
assert!(
|
||||
last < 241,
|
||||
"{name} got .{last} — inside the DHCP pool [.241-.254]"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn agent_network_ip_stable() {
|
||||
// Same name + subnet must always produce the same IP.
|
||||
|
|
@ -121,10 +158,10 @@ fn agent_network_ip_normalizes_bridge_ip_subnet() {
|
|||
from_bridge, from_canonical,
|
||||
"bridge-IP and canonical-network form should normalize to the same result"
|
||||
);
|
||||
// Result must still be in .2-.254.
|
||||
// Result must be in .2-.240 (DHCP pool .241-.254 is excluded).
|
||||
let ip = from_bridge.unwrap();
|
||||
let last: u8 = ip.rsplit('.').next().unwrap().parse().unwrap();
|
||||
assert!((2..=254).contains(&last), "host byte {last}");
|
||||
assert!((2..=240).contains(&last), "host byte {last}");
|
||||
}
|
||||
|
||||
/// `setup_proposed` is idempotent: calling it on an existing repo is a
|
||||
|
|
|
|||
Loading…
Reference in a new issue