actions: deny plants annotated denied/<id> tag
apply-commit denials now leave a git object behind: tag denied/<id> annotated with the operator's note (or empty body if they didn't supply one) at proposal/<id> inside the applied repo. rejected configs become first-class git history — git show denied/<id> in the manager's applied.git mount yields the tree the operator rejected plus the reason. helper event carries the tag for parity with deployed/failed. spawn denials fall through unannotated since they have no proposal commit. deny becomes async (single git plumbing call); dashboard + admin-socket callers grow .await.
This commit is contained in:
parent
df9da4d6e1
commit
6cf66e23dc
3 changed files with 34 additions and 4 deletions
|
|
@ -276,12 +276,42 @@ pub async fn destroy(coord: &Coordinator, name: &str, purge: bool) -> Result<()>
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn deny(coord: &Coordinator, id: i64, note: Option<&str>) -> Result<()> {
|
pub async fn deny(coord: &Coordinator, id: i64, note: Option<&str>) -> Result<()> {
|
||||||
let approval = coord.approvals.get(id)?;
|
let approval = coord.approvals.get(id)?;
|
||||||
coord.approvals.mark_denied(id, note)?;
|
coord.approvals.mark_denied(id, note)?;
|
||||||
tracing::info!(%id, note, "approval denied");
|
tracing::info!(%id, note, "approval denied");
|
||||||
|
let mut tag = None;
|
||||||
if let Some(a) = approval {
|
if let Some(a) = approval {
|
||||||
let sha = a.fetched_sha.clone();
|
let sha = a.fetched_sha.clone();
|
||||||
|
// ApplyCommit approvals leave a `denied/<id>` tag on the
|
||||||
|
// proposal commit so rejected configs are first-class git
|
||||||
|
// objects — `git show denied/<id>` in the manager's applied
|
||||||
|
// mount yields both the tree the operator rejected and (in
|
||||||
|
// the annotated body) the reason. Spawn approvals have no
|
||||||
|
// commit to tag, so they fall through unannotated.
|
||||||
|
if matches!(a.kind, ApprovalKind::ApplyCommit) {
|
||||||
|
let applied_dir = Coordinator::agent_applied_dir(&a.agent);
|
||||||
|
let proposal_ref = format!("refs/tags/proposal/{id}");
|
||||||
|
if lifecycle::git_rev_parse(&applied_dir, &proposal_ref)
|
||||||
|
.await
|
||||||
|
.is_ok()
|
||||||
|
{
|
||||||
|
let tag_name = format!("denied/{id}");
|
||||||
|
let body = note.unwrap_or("").to_owned();
|
||||||
|
if let Err(e) = lifecycle::git_tag_annotated(
|
||||||
|
&applied_dir,
|
||||||
|
&tag_name,
|
||||||
|
&proposal_ref,
|
||||||
|
&body,
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
{
|
||||||
|
tracing::warn!(%id, error = ?e, "plant denied tag failed");
|
||||||
|
} else {
|
||||||
|
tag = Some(tag_name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
coord.notify_manager(&HelperEvent::ApprovalResolved {
|
coord.notify_manager(&HelperEvent::ApprovalResolved {
|
||||||
id: a.id,
|
id: a.id,
|
||||||
agent: a.agent,
|
agent: a.agent,
|
||||||
|
|
@ -289,7 +319,7 @@ pub fn deny(coord: &Coordinator, id: i64, note: Option<&str>) -> Result<()> {
|
||||||
status: ApprovalStatus::Denied,
|
status: ApprovalStatus::Denied,
|
||||||
note: note.map(String::from),
|
note: note.map(String::from),
|
||||||
sha,
|
sha,
|
||||||
tag: None,
|
tag,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|
|
||||||
|
|
@ -460,7 +460,7 @@ async fn post_deny(
|
||||||
.as_deref()
|
.as_deref()
|
||||||
.map(str::trim)
|
.map(str::trim)
|
||||||
.filter(|s| !s.is_empty());
|
.filter(|s| !s.is_empty());
|
||||||
match actions::deny(&state.coord, id, note) {
|
match actions::deny(&state.coord, id, note).await {
|
||||||
Ok(()) => Redirect::to("/").into_response(),
|
Ok(()) => Redirect::to("/").into_response(),
|
||||||
Err(e) => error_response(&format!("deny {id} failed: {e:#}")),
|
Err(e) => error_response(&format!("deny {id} failed: {e:#}")),
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -146,7 +146,7 @@ async fn dispatch(req: &HostRequest, coord: Arc<Coordinator>) -> HostResponse {
|
||||||
HostResponse::success()
|
HostResponse::success()
|
||||||
}
|
}
|
||||||
HostRequest::Deny { id } => {
|
HostRequest::Deny { id } => {
|
||||||
actions::deny(&coord, *id, None)?;
|
actions::deny(&coord, *id, None).await?;
|
||||||
HostResponse::success()
|
HostResponse::success()
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue