Watch
0
0
Fork
You've already forked hyperhive
0

add a per-agent nix builds mode, host or none, carried like privateUsers

This commit is contained in:
atlas 2026-11-03 00:30:55 +01:00 • committed by mara
commit 5485151ba6

View file

@ -16,15 +16,15 @@
# - a link planted at `root` in the template refuses the next start through
# the limits drop-in's `ExecStartPre=` check (whose build has a GC root),
# leaves the link's target untouched, and the start goes through once the
# link is gone.
#
# link is gone;
# - an agent whose config sets `services.hyperhive.agent.user.privateUsers`
# (hive-c0re sends the flag from the closure record; the test sends it)
# starts in its own
# user namespace with state it already had, owned by uid 1000 on disk: the
# data moves once to `dataOwner`, the agent user reads and writes it as
# itself, the other agent's conf (no flag sent) is untouched, and a second
# `WriteNspawnFlags` moves nothing.
# starts in its own user namespace with state it already had, owned by uid
# 1000 on disk: the data moves once to `dataOwner`, the agent user reads and
# writes it as itself, the other agent's conf (no flag sent) is untouched,
# and a second `WriteNspawnFlags` moves nothing;
# - an agent set to `services.hyperhive.agent.nixBuilds = "none"` has no
# nix-daemon socket and cannot build; a request naming no mode keeps that.
#
# The agent's journal forwarder itself (an OTLP push to the hive's collector) is
# not booted here; the module-eval suite covers its wiring.
@ -277,6 +277,9 @@ pkgs.testers.runNixOSTest {
assert host.succeed(f"cat {WORK}/kept.txt").strip() == "kept"
as_agent("hello")
as_agent("tree --version")
# The default mode: the host daemon's socket is there and answers.
assert "socket" in in_ct("ls /nix/var/nix/daemon-socket").split()
as_agent(f"nix-store --store daemon -q --hash {HELLO}")
with subtest("the switch ran once"):
salvages = [d for d in salvage_dirs() if d.startswith("salvaged-rootfs-")]
@ -335,10 +338,14 @@ pkgs.testers.runNixOSTest {
"binds": NS_BINDS,
"isolation": {"bridge": "${bridge}", "gateway_ip": "${gateway}"},
"private_users": True,
"nix_builds": "none",
}
priv_request(host, write)
ns_conf = host.succeed(f"cat /etc/nixos-containers/{NS}.conf")
assert "PRIVATE_USERS=pick" in ns_conf, ns_conf
assert "--tmpfs=/nix/var/nix/daemon-socket" in ns_conf, ns_conf
# The other agent's conf has no such flag: the control for the line above.
assert "daemon-socket" not in conf(), conf()
assert "--private-users-ownership=map" in ns_conf, ns_conf
# Every bind, the read-only config included, is mounted owneridmap.
assert ns_conf.count(":owneridmap") == len(NS_BINDS), ns_conf
@ -379,12 +386,27 @@ pkgs.testers.runNixOSTest {
host.succeed("stat -c %u:%g ${nsSocketDir}/web.sock | grep -x ${dataOwner}:${dataOwner}")
host.succeed(f"nixos-container run {NS} -- cat /proc/self/uid_map | grep -v ' 4294967295$'")
def no_daemon():
# The start script's bind is under the tmpfs: no socket in the
# directory, and a build cannot connect. The store path is still
# readable, and the first agent's directory (checked above) has the
# socket where this one has none.
host.fail(f"nixos-container run {NS} -- test -S /nix/var/nix/daemon-socket/socket")
assert as_ns("ls -A /nix/var/nix/daemon-socket") == ""
host.fail(f"nixos-container run {NS} -- runuser -l {NS_AGENT} -c 'nix-store --store daemon -q --hash {HELLO}'")
as_ns(f"test -r {HELLO}/bin/hello")
no_daemon()
# The move runs once: a file a host process leaves owned by 1000 after it
# is not chowned by the next request, and the agent restarts.
host.succeed(f"nixos-container stop {NS}")
host.succeed(f"echo late > {NS_DIR}/state/late.txt && chown 1000:1000 {NS_DIR}/state/late.txt")
priv_request(host, write)
# A request that names no nix-builds mode, as one with no readable
# record is, keeps the choice the last write made.
priv_request(host, {k: v for k, v in write.items() if k != "nix_builds"})
host.succeed(f"stat -c %u:%g {NS_DIR}/state/late.txt | grep -x 1000:1000")
assert "--tmpfs=/nix/var/nix/daemon-socket" in host.succeed(f"cat /etc/nixos-containers/{NS}.conf")
boot_ns()
no_daemon()
'';
}