feat(enroll): opt-in IPvlan per-agent for Wi-Fi-bridged VMs

Wi-Fi APs bind one MAC per associated station, so VirtualBox/VMware
guests bridged over Wi-Fi rotate the VM's DHCP lease when Docker's
macvlan starts emitting container-MAC frames through the vNIC. Adds a
`use_ipvlan` toggle on the Agent Enrollment tab (mirrors the updater
daemon checkbox): flips the flag on SwarmHost, bakes `ipvlan=true` into
the agent's decnet.ini, and `_worker_config` forces ipvlan=True on the
per-host shard at dispatch. Safe no-op on wired/bare-metal agents.
This commit is contained in:
2026-04-19 17:57:45 -04:00
parent 6d7567b6bb
commit 5df995fda1
6 changed files with 109 additions and 6 deletions

View File

@@ -78,6 +78,42 @@ async def test_bundle_urls_use_master_host_not_request_base(client, auth_token):
assert "testserver" not in sh
@pytest.mark.anyio
async def test_use_ipvlan_opt_in_persists_and_bakes_into_ini(client, auth_token):
"""use_ipvlan=True must persist on the SwarmHost row AND bake `ipvlan = true`
into the agent's decnet.ini so locally-initiated deploys also use IPvlan."""
from decnet.web.dependencies import repo
resp = await _post(client, auth_token, agent_name="ipv-node", use_ipvlan=True)
assert resp.status_code == 201
host_uuid = resp.json()["host_uuid"]
token = resp.json()["token"]
row = await repo.get_swarm_host_by_uuid(host_uuid)
assert row["use_ipvlan"] is True
tgz = await client.get(f"/api/v1/swarm/enroll-bundle/{token}.tgz")
assert tgz.status_code == 200
with tarfile.open(fileobj=io.BytesIO(tgz.content), mode="r:gz") as tar:
ini = tar.extractfile("etc/decnet/decnet.ini").read().decode()
assert "ipvlan = true" in ini
@pytest.mark.anyio
async def test_use_ipvlan_default_false(client, auth_token):
from decnet.web.dependencies import repo
resp = await _post(client, auth_token, agent_name="macv-node")
assert resp.status_code == 201
row = await repo.get_swarm_host_by_uuid(resp.json()["host_uuid"])
assert row["use_ipvlan"] is False
tgz = await client.get(f"/api/v1/swarm/enroll-bundle/{resp.json()['token']}.tgz")
with tarfile.open(fileobj=io.BytesIO(tgz.content), mode="r:gz") as tar:
ini = tar.extractfile("etc/decnet/decnet.ini").read().decode()
assert "ipvlan = false" in ini
@pytest.mark.anyio
async def test_duplicate_agent_name_409(client, auth_token):
r1 = await _post(client, auth_token, agent_name="dup-node")