feat(#1636): infra_admin capability — restart hive-ci/gateway/forge via restart tool
This commit is contained in:
parent
f0be0172d5
commit
f05031ebe3
6 changed files with 144 additions and 4 deletions
|
|
@ -391,7 +391,7 @@ async fn dispatch(req: &AgentRequest, agent: &str, coord: &Arc<Coordinator>) ->
|
|||
agent: target,
|
||||
} => handle_reminder_rollup(coord, agent, target.as_deref(), *since_secs),
|
||||
AgentRequest::Start { name } => handle_start_child(coord, agent, name).await,
|
||||
AgentRequest::Restart { name } => handle_restart_child(coord, agent, name),
|
||||
AgentRequest::Restart { name } => handle_restart_child(coord, agent, name).await,
|
||||
AgentRequest::Kill { name } => handle_kill_child(coord, agent, name).await,
|
||||
AgentRequest::Update { name } => handle_update_child(coord, agent, name),
|
||||
AgentRequest::ListDescendants => handle_list_descendants(agent).await,
|
||||
|
|
@ -515,7 +515,15 @@ async fn handle_start_child(coord: &Arc<Coordinator>, agent: &str, name: &str) -
|
|||
/// `Restart` — enqueue a restart for a direct-child container.
|
||||
/// Topology parenthood is the only authorisation criterion — no
|
||||
/// capability flag needed.
|
||||
fn handle_restart_child(coord: &Arc<Coordinator>, agent: &str, name: &str) -> AgentResponse {
|
||||
async fn handle_restart_child(coord: &Arc<Coordinator>, agent: &str, name: &str) -> AgentResponse {
|
||||
// Infra-container restart: an agent holding the `infra_admin`
|
||||
// capability can restart a hive infrastructure container (hive-ci /
|
||||
// hive-gateway / hive-forge) by passing its name to the same restart
|
||||
// tool. These names are never agent children, so this branch is
|
||||
// disjoint from the child-restart path below.
|
||||
if hive_sh4re::priv_proto::RESTARTABLE_INFRA_CONTAINERS.contains(&name) {
|
||||
return handle_restart_infra(agent, name).await;
|
||||
}
|
||||
if let Some(err) = require_child(agent, name, "restart") {
|
||||
return err;
|
||||
}
|
||||
|
|
@ -531,6 +539,29 @@ fn handle_restart_child(coord: &Arc<Coordinator>, agent: &str, name: &str) -> Ag
|
|||
AgentResponse::Ok
|
||||
}
|
||||
|
||||
/// Restart a hive infrastructure container on behalf of an agent that
|
||||
/// holds the `infra_admin` capability. The container name is already
|
||||
/// known to be in `RESTARTABLE_INFRA_CONTAINERS`; this gates on the
|
||||
/// capability and routes the systemctl restart through hive-priv (which
|
||||
/// re-validates the name root-side). Direct, not approval-gated.
|
||||
async fn handle_restart_infra(agent: &str, container: &str) -> AgentResponse {
|
||||
if !crate::capabilities::has_cap(agent, hive_sh4re::Capability::InfraAdmin) {
|
||||
tracing::warn!(%agent, %container, "agent: infra restart denied (no infra_admin capability)");
|
||||
return AgentResponse::Err {
|
||||
message: format!(
|
||||
"restarting infra container `{container}` requires the `infra_admin` capability"
|
||||
),
|
||||
};
|
||||
}
|
||||
tracing::info!(%agent, %container, "agent: restart infra container");
|
||||
match crate::priv_client::restart_infra_container(container).await {
|
||||
Ok(()) => AgentResponse::Ok,
|
||||
Err(e) => AgentResponse::Err {
|
||||
message: format!("{e:#}"),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
/// `Kill` — kill a direct-child container, unregister it, notify the
|
||||
/// manager.
|
||||
async fn handle_kill_child(coord: &Arc<Coordinator>, agent: &str, name: &str) -> AgentResponse {
|
||||
|
|
|
|||
|
|
@ -276,6 +276,18 @@ pub async fn restart_matrix_daemon(agent_name: &str) -> Result<()> {
|
|||
.await?)
|
||||
}
|
||||
|
||||
/// Restart a hive infrastructure container (hive-ci / hive-gateway /
|
||||
/// hive-forge) on the host via `systemctl restart
|
||||
/// container@<container>.service`. hive-priv re-validates `container`
|
||||
/// against its root-side allowlist; callers must already have checked
|
||||
/// the requesting agent holds the `infra_admin` capability.
|
||||
pub async fn restart_infra_container(container: &str) -> Result<()> {
|
||||
ok(call(&PrivRequest::RestartInfraContainer {
|
||||
container: container.to_owned(),
|
||||
})
|
||||
.await?)
|
||||
}
|
||||
|
||||
fn check(resp: PrivResponse) -> Result<(String, String)> {
|
||||
if resp.ok {
|
||||
Ok((resp.stdout, resp.stderr))
|
||||
|
|
|
|||
Loading…
Reference in a new issue