Watch
0
0
Fork
You've already forked hyperhive
0

bind a container apply to the closure realised for the agent, and record the deployed pins on drift

hive-priv applies only the path its last RealiseClosure returned for that agent. On drift the swap and create record the pins of the commit the closure was built from.
This commit is contained in:
atlas 2026-10-29 22:36:58 +01:00
commit 6b800600e2

View file

@ -7,7 +7,7 @@
//! and stream progress to the caller as [`PrivEvent::Line`]s when asked to.
use anyhow::{Context as _, Result, bail};
use hive_priv_sock::{PrivEvent, PrivStream, PrivStreamLine, valid_closure_path};
use hive_priv_sock::{PrivEvent, PrivStream, PrivStreamLine};
use tokio::io::{AsyncWriteExt, BufReader};
use tokio::net::unix::OwnedWriteHalf;
use tokio::process::Command;
@ -71,17 +71,8 @@ pub(crate) async fn destroy_container(name: &str) -> Result<(String, String)> {
container_run(&["destroy", &container_system_name(name)]).await
}
/// `toplevel` as a store path, or the error saying it is not one. The path
/// crosses the socket from hive-c0re, which is not root.
fn toplevel_to_apply(toplevel: &str) -> Result<&str> {
if !valid_closure_path(toplevel) {
bail!("{toplevel:?} is not a store path");
}
Ok(toplevel)
}
/// Shared body for `CreateContainer` / `UpdateContainer`: validate the name
/// and the closure, and apply it (streaming line events to `writer` when
/// and that `toplevel` is the closure realised for the agent, and apply it (streaming line events to `writer` when
/// `stream` is set). Nothing is built here: `toplevel` is a closure already in
/// the store, which `RealiseClosure` put there.
///
@ -102,7 +93,7 @@ pub(crate) async fn container_toplevel_action(
writer: &mut OwnedWriteHalf,
) -> Result<(String, String)> {
validate_agent_name(name)?;
let toplevel = toplevel_to_apply(toplevel)?;
let toplevel = crate::closure::applicable_for(name, toplevel)?;
let system_name = container_system_name(name);
if verb == "update" {
return swap_container_profile(&system_name, toplevel, stream, writer).await;
@ -567,34 +558,9 @@ async fn container_run_streaming(
#[cfg(test)]
mod tests {
use super::{BoundedRun, run_bounded, toplevel_to_apply};
use super::{BoundedRun, run_bounded};
use crate::shared::test_support::scratch;
const TOPLEVEL: &str = "/nix/store/0123456789abcdfghijklmnpqrsvwxyz-nixos-system-atlas";
/// A store path passes through unchanged; anything else, whatever the
/// request says, never reaches `nix-env` or `nixos-container`.
#[test]
fn only_a_store_path_is_applied() {
assert_eq!(toplevel_to_apply(TOPLEVEL).unwrap(), TOPLEVEL);
for bad in [
"",
"main",
"../../x",
"/etc/passwd",
"/nix/store/x",
&format!("{TOPLEVEL}/bin"),
&format!("{TOPLEVEL};id"),
&"c".repeat(40),
] {
let err = toplevel_to_apply(bad).unwrap_err();
assert!(
format!("{err:#}").contains("not a store path"),
"{bad}: {err:#}"
);
}
}
/// Whether `pid` is gone, a zombie (`Z`), or being reaped (`X`): none of
/// these runs again, and a process passes through them in that order,
/// so once this is true it stays true.