diff --git a/hive-c0re/src/lifecycle.rs b/hive-c0re/src/lifecycle.rs index f8e4f1e..bdb0ffa 100644 --- a/hive-c0re/src/lifecycle.rs +++ b/hive-c0re/src/lifecycle.rs @@ -37,23 +37,30 @@ pub async fn spawn(name: &str, agent_flake: &str, agent_dir: &Path) -> Result<() validate(name)?; let container = container_name(name); run(&["create", &container, "--flake", agent_flake]).await?; - append_bind_flag(&container, agent_dir)?; + set_bind_flag(&container, agent_dir)?; run(&["start", &container]).await } /// `nixos-container` doesn't expose `--bind` on the CLI, but its start script /// expands `$EXTRA_NSPAWN_FLAGS` (from `/etc/nixos-containers/.conf`) -/// unquoted into the `systemd-nspawn` invocation. Append a `--bind` flag there. -fn append_bind_flag(container: &str, agent_dir: &Path) -> Result<()> { +/// unquoted into the `systemd-nspawn` invocation. Idempotently replace the +/// `EXTRA_NSPAWN_FLAGS` line with the bind we want. +fn set_bind_flag(container: &str, agent_dir: &Path) -> Result<()> { let path = format!("/etc/nixos-containers/{container}.conf"); - let line = format!( - "\nEXTRA_NSPAWN_FLAGS=\"--bind={}:{CONTAINER_RUNTIME_MOUNT}\"\n", + let original = std::fs::read_to_string(&path).with_context(|| format!("read {path}"))?; + let mut lines: Vec = original + .lines() + .filter(|line| !line.trim_start().starts_with("EXTRA_NSPAWN_FLAGS=")) + .map(str::to_owned) + .collect(); + lines.push(format!( + "EXTRA_NSPAWN_FLAGS=\"--bind={}:{CONTAINER_RUNTIME_MOUNT}\"", agent_dir.display() - ); - let mut content = std::fs::read_to_string(&path).with_context(|| format!("read {path}"))?; - content.push_str(&line); + )); + let mut content = lines.join("\n"); + content.push('\n'); std::fs::write(&path, content).with_context(|| format!("write {path}"))?; - tracing::info!(%path, "appended EXTRA_NSPAWN_FLAGS for bind mount"); + tracing::info!(%path, "set EXTRA_NSPAWN_FLAGS for bind mount"); Ok(()) } @@ -63,10 +70,14 @@ pub async fn kill(name: &str) -> Result<()> { run(&["stop", &container]).await } -pub async fn rebuild(name: &str, agent_flake: &str) -> Result<()> { +pub async fn rebuild(name: &str, agent_flake: &str, agent_dir: &Path) -> Result<()> { validate(name)?; let container = container_name(name); - run(&["update", &container, "--flake", agent_flake]).await + set_bind_flag(&container, agent_dir)?; + run(&["update", &container, "--flake", agent_flake]).await?; + // Restart so any nspawn-level changes (bind mounts, networking, etc.) apply. + run(&["stop", &container]).await?; + run(&["start", &container]).await } pub async fn list() -> Result> { diff --git a/hive-c0re/src/server.rs b/hive-c0re/src/server.rs index 131e1b4..487c92f 100644 --- a/hive-c0re/src/server.rs +++ b/hive-c0re/src/server.rs @@ -77,7 +77,8 @@ async fn dispatch(req: &HostRequest, agent_flake: &str, coord: &Coordinator) -> } HostRequest::Rebuild { name } => { tracing::info!(%name, "rebuild"); - lifecycle::rebuild(name, agent_flake).await?; + let agent_dir = Coordinator::agent_dir(name); + lifecycle::rebuild(name, agent_flake, &agent_dir).await?; HostResponse::success() } HostRequest::List => HostResponse::list(lifecycle::list().await?),