103 lines
3.5 KiB
Rust
103 lines
3.5 KiB
Rust
//! Operations that are exposed through more than one surface (the host admin
|
|
//! socket *and* the dashboard's POST endpoints). Each function takes a
|
|
//! `&Coordinator` and the request parameters; callers stitch the response
|
|
//! shape they want (HTTP redirect vs JSON).
|
|
|
|
use anyhow::Result;
|
|
use hive_sh4re::{
|
|
ApprovalStatus, HelperEvent, MANAGER_AGENT, Message, SYSTEM_SENDER,
|
|
};
|
|
|
|
use crate::coordinator::Coordinator;
|
|
use crate::lifecycle;
|
|
|
|
/// Approve a pending request: read the agent.nix at the approval's commit from
|
|
/// the proposed repo, copy into the applied repo, commit there, and rebuild
|
|
/// the agent container. On failure marks the approval failed (with the error
|
|
/// note) and returns the error. Either way, an `ApprovalResolved` helper event
|
|
/// is pushed into the manager's inbox.
|
|
pub async fn approve(coord: &Coordinator, id: i64) -> Result<()> {
|
|
let approval = coord.approvals.mark_approved(id)?;
|
|
tracing::info!(%approval.id, %approval.agent, %approval.commit_ref, "approval: applying + rebuilding");
|
|
|
|
let agent_dir = coord.register_agent(&approval.agent)?;
|
|
let proposed_dir = Coordinator::agent_proposed_dir(&approval.agent);
|
|
let applied_dir = Coordinator::agent_applied_dir(&approval.agent);
|
|
let result: Result<()> = async {
|
|
lifecycle::apply_commit(&applied_dir, &proposed_dir, &approval.commit_ref).await?;
|
|
lifecycle::rebuild(
|
|
&approval.agent,
|
|
&coord.hyperhive_flake,
|
|
&agent_dir,
|
|
&applied_dir,
|
|
)
|
|
.await
|
|
}
|
|
.await;
|
|
match result {
|
|
Ok(()) => {
|
|
notify_manager(
|
|
coord,
|
|
&HelperEvent::ApprovalResolved {
|
|
id: approval.id,
|
|
agent: approval.agent.clone(),
|
|
commit_ref: approval.commit_ref.clone(),
|
|
status: ApprovalStatus::Approved,
|
|
note: None,
|
|
},
|
|
);
|
|
Ok(())
|
|
}
|
|
Err(e) => {
|
|
let note = format!("{e:#}");
|
|
let _ = coord.approvals.mark_failed(approval.id, ¬e);
|
|
notify_manager(
|
|
coord,
|
|
&HelperEvent::ApprovalResolved {
|
|
id: approval.id,
|
|
agent: approval.agent.clone(),
|
|
commit_ref: approval.commit_ref.clone(),
|
|
status: ApprovalStatus::Failed,
|
|
note: Some(note),
|
|
},
|
|
);
|
|
Err(e)
|
|
}
|
|
}
|
|
}
|
|
|
|
pub fn deny(coord: &Coordinator, id: i64) -> Result<()> {
|
|
let approval = coord.approvals.get(id)?;
|
|
coord.approvals.mark_denied(id)?;
|
|
tracing::info!(%id, "approval denied");
|
|
if let Some(a) = approval {
|
|
notify_manager(
|
|
coord,
|
|
&HelperEvent::ApprovalResolved {
|
|
id: a.id,
|
|
agent: a.agent,
|
|
commit_ref: a.commit_ref,
|
|
status: ApprovalStatus::Denied,
|
|
note: None,
|
|
},
|
|
);
|
|
}
|
|
Ok(())
|
|
}
|
|
|
|
fn notify_manager(coord: &Coordinator, event: &HelperEvent) {
|
|
let body = match serde_json::to_string(event) {
|
|
Ok(s) => s,
|
|
Err(e) => {
|
|
tracing::warn!(error = ?e, "failed to encode helper event");
|
|
return;
|
|
}
|
|
};
|
|
if let Err(e) = coord.broker.send(&Message {
|
|
from: SYSTEM_SENDER.to_owned(),
|
|
to: MANAGER_AGENT.to_owned(),
|
|
body,
|
|
}) {
|
|
tracing::warn!(error = ?e, "failed to push helper event to manager");
|
|
}
|
|
}
|