take the agent-create and forge-token responses from swarm-controller-api
swarmctl decoded POST /api/agents and POST /api/agents/{name}/forge-token
into hand-written copies of the controller's private response structs.
Both now live in swarm-controller-api, so a field renamed on one side is a
compile error on the other. Wire format and OpenAPI schema are unchanged.
The request side stays mirrored: CreateAgentRequest carries inference
settings and an ArchetypeRef (swarm-queue-client types) and Permission
(hive-sh4re), and the permissions body is a list of Permission. Where
those types live is #5471's decision.
Files:
- swarm-controller-api/src/agents.rs, lib.rs: the two responses, with
round-trip tests whose literals come from the tests that move here.
- swarm-controller/src/main.rs: imports them; the struct definitions and
create_agent_warning_tests (now in the api crate) go.
- swarmctl/src/agent.rs: imports them; the mirrors and their decode tests
go, and the module doc says only the request is mirrored now.
- swarmctl/src/permissions.rs: its doc pointed at agent.rs for why it
mirrors; that reason no longer covers Permissions, so it gives its own.
- swarmctl/README.md, swarm-controller-api/README.md, CLAUDE.md: which
bodies are shared and which are mirrored.
Part of #5472
This commit is contained in:
parent
d4714072a9
commit
98501d2d88
8 changed files with 110 additions and 118 deletions
|
|
@ -39,6 +39,7 @@ use backon::{ExponentialBuilder, Retryable as _};
|
|||
use hive_jobq_wire::GraphWire as _;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use swarm_authelia_bridge_sock::BridgeResponse;
|
||||
use swarm_controller_api::agents::{CreateAgentResponse, MintAgentForgeTokenResponse};
|
||||
use swarm_controller_api::forge::{ForgeAdminChange, MakeForgeAdminResponse};
|
||||
use utoipa::{OpenApi, ToSchema};
|
||||
use utoipa_axum::{router::OpenApiRouter, routes};
|
||||
|
|
@ -1816,28 +1817,6 @@ struct CreateAgentRequest {
|
|||
archetype: Option<swarm_queue_client::agent_archetype::ArchetypeRef>,
|
||||
}
|
||||
|
||||
/// Where the queued job landed — a caller polls `/api/jobq/graph` (or
|
||||
/// `?states=`) with this id to watch it settle, same as every other job
|
||||
/// kind this daemon will ever queue.
|
||||
#[derive(Clone, Debug, Serialize, ToSchema)]
|
||||
struct CreateAgentResponse {
|
||||
node_id: u64,
|
||||
/// Name collisions that were **allowed through**, phrased for the
|
||||
/// operator who just chose the name.
|
||||
///
|
||||
/// The creation still happened — this is the "loud warning now, refuse
|
||||
/// after a grace period" step, so a caller that ignores this field gets
|
||||
/// exactly today's behaviour. It rides on the response rather than
|
||||
/// living only in the daemon's log because the person who can still fix
|
||||
/// the name in one keystroke is the one holding this response, and they
|
||||
/// are not reading the journal.
|
||||
///
|
||||
/// Empty (and omitted from the JSON) in the normal case, so a client
|
||||
/// that never looks at it is not handed an empty array to reason about.
|
||||
#[serde(default, skip_serializing_if = "Vec::is_empty")]
|
||||
warnings: Vec<String>,
|
||||
}
|
||||
|
||||
/// Queue the whole agent-creation job graph for `name` — two independent
|
||||
/// roots, `CreateIdentity` and `CreateForgeUser`, since an authelia
|
||||
/// subject and a forge account need nothing from each other. `CreateRepo`
|
||||
|
|
@ -2706,13 +2685,6 @@ async fn get_agent_config_pr(
|
|||
Ok(Json(cache.get(&name)))
|
||||
}
|
||||
|
||||
/// Success body of `POST /api/agents/{name}/forge-token`.
|
||||
#[derive(Clone, Debug, Serialize, ToSchema)]
|
||||
struct MintAgentForgeTokenResponse {
|
||||
/// The queued node, so a caller can follow it in the job view.
|
||||
node_id: u64,
|
||||
}
|
||||
|
||||
/// Insert one job per agent: `CreateForgeUser`, then `MintAgentForgeToken`
|
||||
/// `after_ok` it — the same edge [`declare_agent_job`] uses. Returns the mint
|
||||
/// nodes' ids.
|
||||
|
|
@ -6601,36 +6573,6 @@ mod tests {
|
|||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod create_agent_warning_tests {
|
||||
use super::CreateAgentResponse;
|
||||
|
||||
/// The normal case must not grow a field. A client that has never
|
||||
/// heard of warnings should see the response it always saw — that is
|
||||
/// what makes "warn now, refuse later" a non-breaking first step.
|
||||
#[test]
|
||||
fn no_warnings_are_omitted_from_the_json_entirely() {
|
||||
let json = serde_json::to_string(&CreateAgentResponse {
|
||||
node_id: 7,
|
||||
warnings: vec![],
|
||||
})
|
||||
.unwrap();
|
||||
assert_eq!(json, r#"{"node_id":7}"#);
|
||||
}
|
||||
|
||||
/// Presence arm — without it, a `skip_serializing_if` that dropped the
|
||||
/// field unconditionally would pass the test above.
|
||||
#[test]
|
||||
fn warnings_are_serialized_when_present() {
|
||||
let json = serde_json::to_string(&CreateAgentResponse {
|
||||
node_id: 7,
|
||||
warnings: vec!["name is reserved".to_owned()],
|
||||
})
|
||||
.unwrap();
|
||||
assert_eq!(json, r#"{"node_id":7,"warnings":["name is reserved"]}"#);
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod mint_identity_tests {
|
||||
use std::sync::{Arc, Mutex};
|
||||
|
|
|
|||
Loading…
Reference in a new issue