Watch
0
0
Fork
You've already forked hyperhive
0
hyperhive/swarmctl/src/forge.rs
atlas d56d8f2b36 swarmctl: forge make-admin
Calls POST /api/forge/users/{name}/admin and prints what it found. It
fails with the controller's message when the user has not logged in via
SSO yet, and when the name is an agent's.

Refs #3782
2026-09-25 08:29:56 +02:00

66 lines
2.1 KiB
Rust

//! `swarmctl forge make-admin` — make an existing human forge account a site
//! admin, through the swarm-controller over the forge's admin API.
//!
//! Unlike the `agent` verbs this one waits: the controller answers with what
//! it found. Same round trip as [`crate::agent`], over the controller's unix
//! socket; the shape below mirrors the controller's own private
//! `MakeForgeAdminResponse` for the reason that module's doc comment gives.
use std::path::Path;
use anyhow::{Context as _, Result};
use serde::Deserialize;
use crate::agent::{parse_ident, post};
/// Success body of `POST /api/forge/users/{name}/admin`.
#[derive(Deserialize)]
struct MakeForgeAdminResponse {
change: AdminChange,
}
#[derive(Deserialize)]
#[serde(rename_all = "snake_case")]
enum AdminChange {
Promoted,
AlreadyAdmin,
}
/// Run `swarmctl forge make-admin`.
///
/// Synchronous for the same reason `agent create` is.
pub(crate) fn make_admin(socket: &Path, name: &str) -> Result<()> {
let name = parse_ident(name, "user name")?;
let rt = tokio::runtime::Builder::new_current_thread()
.enable_io()
.build()
.context("starting a tokio runtime for the controller request")?;
let resp: MakeForgeAdminResponse = rt.block_on(post(
socket,
&format!("/api/forge/users/{name}/admin"),
&serde_json::json!({}),
"forge-make-admin",
))?;
match resp.change {
AdminChange::Promoted => println!("forge: {name:?} is now a site admin"),
AdminChange::AlreadyAdmin => println!("forge: {name:?} was already a site admin"),
}
Ok(())
}
#[cfg(test)]
mod tests {
use super::{AdminChange, MakeForgeAdminResponse};
/// The controller's two answers, as it spells them.
#[test]
fn both_changes_decode() {
for (wire, want) in [("promoted", true), ("already_admin", false)] {
let resp: MakeForgeAdminResponse =
serde_json::from_str(&format!(r#"{{"change":"{wire}"}}"#)).expect("decodes");
assert_eq!(matches!(resp.change, AdminChange::Promoted), want);
}
}
}