read privateUsers from the closure record at start, and move the data owner option under hive-controller
This commit is contained in:
parent
1fd591d28b
commit
f5a609911c
2 changed files with 208 additions and 97 deletions
|
|
@ -451,6 +451,10 @@ async fn set_nspawn_flags(
|
|||
});
|
||||
}
|
||||
|
||||
// Whether the agent's config sets `privateUsers`, as the swarm builder
|
||||
// recorded it. Read from the record, never from the agent's nix.
|
||||
let private_users = private_users_of(&crate::attic::closure::record(agent_name).await);
|
||||
|
||||
// Web-socket subdir: bind-mount `/run/hive-agent/<name>/` into the
|
||||
// container so the harness can bind `web.sock` there and the host-side
|
||||
// gateway sees it. Subdir bind (not socket file) keeps the inode
|
||||
|
|
@ -460,7 +464,7 @@ async fn set_nspawn_flags(
|
|||
// and leaves an existing one alone; the container's activation hands it
|
||||
// to the agent user. Don't chown or chmod it here.
|
||||
let socket_dir = crate::agent_sockets::agent_dir_for(agent_name);
|
||||
crate::priv_client::ensure_agent_socket_dir(agent_name).await?;
|
||||
crate::priv_client::ensure_agent_socket_dir(agent_name, private_users).await?;
|
||||
binds.push(BindMount {
|
||||
host_path: socket_dir.to_string_lossy().into_owned(),
|
||||
container_path: socket_dir.to_string_lossy().into_owned(),
|
||||
|
|
@ -486,10 +490,26 @@ async fn set_nspawn_flags(
|
|||
isolation,
|
||||
&load_creds,
|
||||
super::hive_facts(hive),
|
||||
private_users,
|
||||
)
|
||||
.await
|
||||
}
|
||||
|
||||
/// 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.
|
||||
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),
|
||||
Err(e) => {
|
||||
tracing::warn!(error = %format!("{e:#}"), "closure record unreadable; the agent's user namespace is left as it is");
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// The binds every agent container gets, before its own state, config and
|
||||
/// role-specific dirs.
|
||||
fn base_binds(runtime_dir: &Path, claude_dir: &Path, claude_mount: String) -> Vec<BindMount> {
|
||||
|
|
@ -521,6 +541,40 @@ mod tests {
|
|||
};
|
||||
use crate::coordinator::HiveEnv;
|
||||
|
||||
fn record_with(private_users: bool) -> swarm_wire::agent_closures::AgentClosure {
|
||||
swarm_wire::agent_closures::AgentClosure {
|
||||
meta_rev: "a".repeat(40),
|
||||
store_path: "/nix/store/x".to_owned(),
|
||||
system: "x86_64-linux".to_owned(),
|
||||
built_unix: 1,
|
||||
cached: true,
|
||||
private_users,
|
||||
}
|
||||
}
|
||||
|
||||
/// 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.
|
||||
#[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)
|
||||
);
|
||||
assert_eq!(super::private_users_of(&Ok(None)), None);
|
||||
assert_eq!(super::private_users_of(&Err(anyhow::anyhow!("down"))), None);
|
||||
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));
|
||||
}
|
||||
|
||||
/// No agent container gets a cross-agent scratch dir; `/knowledge` is the
|
||||
/// control that the lookup finds a bind that is there.
|
||||
#[test]
|
||||
|
|
|
|||
|
|
@ -306,21 +306,24 @@ pub async fn write_nspawn_flags(
|
|||
isolation: NetworkIsolation,
|
||||
load_credentials: &[CredentialMount],
|
||||
hive_facts: HiveFacts,
|
||||
private_users: Option<bool>,
|
||||
) -> Result<()> {
|
||||
ok(call(&PrivRequest::WriteNspawnFlags {
|
||||
container: container.to_owned(),
|
||||
binds: binds.to_vec(),
|
||||
isolation,
|
||||
load_credentials: load_credentials.to_vec(),
|
||||
hive_facts,
|
||||
hive_facts: Box::new(hive_facts),
|
||||
private_users,
|
||||
})
|
||||
.await?)
|
||||
}
|
||||
|
||||
/// See [`PrivRequest::EnsureAgentSocketDir`].
|
||||
pub async fn ensure_agent_socket_dir(name: &str) -> Result<()> {
|
||||
pub async fn ensure_agent_socket_dir(name: &str, private_users: Option<bool>) -> Result<()> {
|
||||
ok(call(&PrivRequest::EnsureAgentSocketDir {
|
||||
name: name.to_owned(),
|
||||
private_users,
|
||||
})
|
||||
.await?)
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue