lifecycle: bind /meta RO into manager

set_nspawn_flags now adds a third manager-only bind alongside
/agents (RW) and /applied (RO): --bind-ro=/var/lib/hyperhive/meta
:/meta. manager can git log /meta to see every deploy across the
swarm and cat /meta/flake.lock to introspect which sha each agent
is currently pinned at. defensive create_dir_all on the host
side so a cold start with no agents (meta repo not yet seeded)
doesn't trip systemd-nspawn's missing-bind-source check before
the migration plants the dir.
This commit is contained in:
müde 2026-05-16 00:24:39 +02:00
parent 92822efe16
commit 3d14ddeb7d

View file

@ -592,6 +592,10 @@ const HOST_AGENTS_ROOT: &str = "/var/lib/hyperhive/agents";
/// `APPLIED_STATE_ROOT` in coordinator.rs. /// `APPLIED_STATE_ROOT` in coordinator.rs.
const HOST_APPLIED_ROOT: &str = "/var/lib/hyperhive/applied"; const HOST_APPLIED_ROOT: &str = "/var/lib/hyperhive/applied";
/// On-host meta repo root, mirrored RO into the manager. Matches
/// `meta::meta_dir()` but duplicated here so lifecycle stays a leaf.
const HOST_META_ROOT: &str = "/var/lib/hyperhive/meta";
fn set_nspawn_flags( fn set_nspawn_flags(
container: &str, container: &str,
runtime_dir: &Path, runtime_dir: &Path,
@ -607,6 +611,14 @@ fn set_nspawn_flags(
notes = notes_dir.display(), notes = notes_dir.display(),
); );
if container == MANAGER_NAME { if container == MANAGER_NAME {
use std::fmt::Write as _;
// systemd-nspawn refuses to start a container whose bind
// source doesn't exist. The meta repo is created by the
// startup migration, but make sure the directory is there
// before the manager comes up in case set_nspawn_flags fires
// first (e.g. cold start with no agents).
std::fs::create_dir_all(HOST_META_ROOT)
.with_context(|| format!("create {HOST_META_ROOT}"))?;
// Manager edits sub-agent proposed/ repos and its own. RW so it can // Manager edits sub-agent proposed/ repos and its own. RW so it can
// git-commit. Sub-agents see only their own /run/hive socket and // git-commit. Sub-agents see only their own /run/hive socket and
// /root/.claude (no /agents or /applied). // /root/.claude (no /agents or /applied).
@ -617,7 +629,11 @@ fn set_nspawn_flags(
// denied tags into its proposed clones and diff against // denied tags into its proposed clones and diff against
// what's actually deployed. RO bind makes destructive git // what's actually deployed. RO bind makes destructive git
// plumbing inside the container unable to corrupt applied. // plumbing inside the container unable to corrupt applied.
use std::fmt::Write as _; //
// /meta is a third RO mount exposing the system-wide deploy
// flake (`git log /meta --oneline` shows every deploy across
// every agent; `cat /meta/flake.lock` resolves which sha each
// agent is pinned at right now).
let _ = write!( let _ = write!(
binds, binds,
" --bind={HOST_AGENTS_ROOT}:{CONTAINER_MANAGER_AGENTS_MOUNT}", " --bind={HOST_AGENTS_ROOT}:{CONTAINER_MANAGER_AGENTS_MOUNT}",
@ -626,6 +642,11 @@ fn set_nspawn_flags(
binds, binds,
" --bind-ro={HOST_APPLIED_ROOT}:{CONTAINER_MANAGER_APPLIED_MOUNT}", " --bind-ro={HOST_APPLIED_ROOT}:{CONTAINER_MANAGER_APPLIED_MOUNT}",
); );
let _ = write!(
binds,
" --bind-ro={HOST_META_ROOT}:{mount}",
mount = crate::meta::CONTAINER_MANAGER_META_MOUNT,
);
} }
let bind_flag = format!("EXTRA_NSPAWN_FLAGS=\"{binds}\""); let bind_flag = format!("EXTRA_NSPAWN_FLAGS=\"{binds}\"");
let mut lines: Vec<String> = original let mut lines: Vec<String> = original