Watch
0
0
Fork
You've already forked hyperhive
0

swarm-builder: an agent option whose eval failed is left out of the reply, not read as its default

This commit is contained in:
atlas 2026-11-06 10:03:18 +01:00 • committed by mara
commit 5d2b0c9a54

View file

@ -518,13 +518,14 @@ fn socket_bind(agent_name: &str, socket_dir: &Path, private_users: Option<bool>)
}
/// What hive-priv is told about the agent's user namespace: the record's flag,
/// or `None` (leave the agent as it is) when there is no record or it cannot be
/// read, so a swarm outage never turns a namespaced agent off or on.
/// or `None` (leave the agent as it is) when there is no record, it cannot be
/// read, or it carries no flag because the builder could not read the option,
/// so neither a swarm outage nor a failed eval turns a namespaced agent off or on.
fn private_users_of(
record: &Result<Option<swarm_wire::agent_closures::AgentClosure>>,
) -> Option<bool> {
match record {
Ok(record) => record.as_ref().map(|r| r.private_users),
Ok(record) => record.as_ref().and_then(|r| r.private_users),
Err(e) => {
tracing::warn!(error = %format!("{e:#}"), "closure record unreadable; the agent's user namespace is left as it is");
None
@ -533,13 +534,15 @@ fn private_users_of(
}
/// What hive-priv is told about the agent's nix builds: the record's mode, or
/// `None` (leave the agent as the last write set it) when there is no record or
/// it cannot be read, so a swarm outage never turns an agent's host builds on.
/// `None` (leave the agent as the last write set it) when there is no record,
/// it cannot be read, or it carries no mode because the builder could not read
/// the option, so neither a swarm outage nor a failed eval turns an agent's
/// host builds on.
fn nix_builds_of(
record: &Result<Option<swarm_wire::agent_closures::AgentClosure>>,
) -> Option<NixBuilds> {
match record {
Ok(record) => record.as_ref().map(|r| r.nix_builds),
Ok(record) => record.as_ref().and_then(|r| r.nix_builds),
Err(e) => {
tracing::warn!(error = %format!("{e:#}"), "closure record unreadable; the agent's nix builds are left as they are");
None
@ -579,13 +582,13 @@ mod tests {
use crate::coordinator::HiveEnv;
use hive_types::NixBuilds;
fn record_with(private_users: bool) -> swarm_wire::agent_closures::AgentClosure {
record_with_builds(private_users, NixBuilds::Host)
fn record_with(private_users: Option<bool>) -> swarm_wire::agent_closures::AgentClosure {
record_with_builds(private_users, Some(NixBuilds::Host))
}
fn record_with_builds(
private_users: bool,
nix_builds: NixBuilds,
private_users: Option<bool>,
nix_builds: Option<NixBuilds>,
) -> swarm_wire::agent_closures::AgentClosure {
swarm_wire::agent_closures::AgentClosure {
meta_rev: "a".repeat(40),
@ -613,48 +616,58 @@ mod tests {
assert!(!b.read_only);
}
/// The flag in the record is what hive-priv is told, both ways. No record
/// and an unreadable one say nothing, so a swarm outage changes no agent's
/// namespace; a record from before the field reads false.
/// The flag in the record is what hive-priv is told, both ways. No record,
/// an unreadable one, and a record whose builder could not read the option
/// (or that predates the field) say nothing, so neither a swarm outage nor
/// a failed eval changes an agent's namespace.
#[test]
fn the_records_flag_is_what_the_start_path_passes() {
assert_eq!(
super::private_users_of(&Ok(Some(record_with(true)))),
Some(true)
);
assert_eq!(
super::private_users_of(&Ok(Some(record_with(false)))),
Some(false)
);
for flag in [true, false] {
assert_eq!(
super::private_users_of(&Ok(Some(record_with(Some(flag))))),
Some(flag)
);
}
assert_eq!(super::private_users_of(&Ok(None)), None);
assert_eq!(super::private_users_of(&Err(anyhow::anyhow!("down"))), None);
assert_eq!(
super::private_users_of(&Ok(Some(record_with(None)))),
None,
"an unread flag keeps the last write"
);
let old = serde_json::json!({
"meta_rev": "a", "store_path": "/nix/store/x",
"system": "s", "built_unix": 1,
});
let old = serde_json::from_value(old).expect("decodes");
assert_eq!(super::private_users_of(&Ok(Some(old))), Some(false));
assert_eq!(super::private_users_of(&Ok(Some(old))), None);
}
/// The record's mode is what hive-priv is told, both ways. No record and an
/// unreadable one say nothing, so a swarm outage changes no agent; a record
/// from before the field reads as host builds.
/// The record's mode is what hive-priv is told, both ways. No record, an
/// unreadable one, and a record whose builder could not read the option (or
/// that predates the field) say nothing, so neither a swarm outage nor a
/// failed eval changes an agent.
#[test]
fn the_records_nix_builds_is_what_the_start_path_passes() {
for mode in [NixBuilds::Host, NixBuilds::Disabled] {
assert_eq!(
super::nix_builds_of(&Ok(Some(record_with_builds(false, mode)))),
super::nix_builds_of(&Ok(Some(record_with_builds(None, Some(mode))))),
Some(mode)
);
}
assert_eq!(super::nix_builds_of(&Ok(None)), None);
assert_eq!(super::nix_builds_of(&Err(anyhow::anyhow!("down"))), None);
assert_eq!(
super::nix_builds_of(&Ok(Some(record_with_builds(None, None)))),
None,
"an unread mode keeps the last write"
);
let old = serde_json::json!({
"meta_rev": "a", "store_path": "/nix/store/x",
"system": "s", "built_unix": 1,
});
let old = serde_json::from_value(old).expect("decodes");
assert_eq!(super::nix_builds_of(&Ok(Some(old))), Some(NixBuilds::Host));
assert_eq!(super::nix_builds_of(&Ok(Some(old))), None);
// A mode a newer swarm wrote, which this build does not know, decodes
// and builds nothing.
let newer = serde_json::json!({