share the forge and schedule wire types through swarm-controller-api
swarmctl hand-mirrored the controller's forge make-admin response and the /api/schedules bodies. Both now import them from a new serde-only crate, swarm-controller-api, the way hivectl and hive-c0re share hive-host-sock. ToSchema sits behind its schema feature, which only the controller turns on. The wire format is unchanged: round-trip tests in the new crate pin each type against the JSON the controller and swarmctl produced, and the controller's OpenAPI document for these routes is byte-identical. Files: swarm-controller-api/ is the new crate; swarm-controller and swarmctl (src, Cargo.toml, Cargo.lock, root Cargo.toml) switch to it; CLAUDE.md lists the crate and which swarmctl shapes are still local mirrors; swarmctl/README.md says the forge and schedule bodies come from the crate, so none of its lines still call them mirrored. Part of #5472.
This commit is contained in:
parent
22b9c570ba
commit
be92bc0835
17 changed files with 529 additions and 377 deletions
|
|
@ -3,28 +3,14 @@
|
|||
//!
|
||||
//! 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.
|
||||
//! socket.
|
||||
|
||||
use anyhow::{Context as _, Result};
|
||||
use serde::Deserialize;
|
||||
use swarm_controller_api::forge::{ForgeAdminChange, MakeForgeAdminResponse};
|
||||
|
||||
use crate::agent::{parse_ident, post};
|
||||
use crate::remote::Target;
|
||||
|
||||
/// 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.
|
||||
|
|
@ -43,23 +29,8 @@ pub(crate) fn make_admin(target: &Target, name: &str) -> Result<()> {
|
|||
))?;
|
||||
|
||||
match resp.change {
|
||||
AdminChange::Promoted => println!("forge: {name:?} is now a site admin"),
|
||||
AdminChange::AlreadyAdmin => println!("forge: {name:?} was already a site admin"),
|
||||
ForgeAdminChange::Promoted => println!("forge: {name:?} is now a site admin"),
|
||||
ForgeAdminChange::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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue