swarm-queue-client: one agent-token spelling, and no hive in AgentCredential
`swarm_queue_client::agent_token::format_agent_token` / `parse_agent_token` are the spelling an agent presents its own queue secret in, `swarm-agent.<agent>.<secret>`, and the one the auth-callout responder reads back. The prefix is what separates it from an OIDC access token, which may itself contain `.`. Parsing distinguishes "not an agent token" (no prefix) from "a malformed one"; the error names the problem and never the value. The module is store-free, so the agent formats its token without linking the secret-store client. `swarm_secret_client::queue::AgentCredential` loses `hive`: an agent's identity is not tied to a hive, and nothing reads the field. Objects already in the store carry it and still decode, since unknown fields are ignored; a test parses one. The controller stops writing it. With the credential no longer naming a hive, and the agent's policy naming none since #4762, nothing in the mint consumes one. `hive` goes from `mint_and_verify`, from the `MintAgentIdentity` node, and from `POST /api/agents/{name}/identity`, which now takes no body and no longer checks a hive against the roster; a caller that still sends one is not refused, the body is ignored. `swarmctl agent mint-identity` loses `--hive`, so passing it is now a usage error.
This commit is contained in:
parent
6170e74a31
commit
fc97c237dc
10 changed files with 234 additions and 201 deletions
|
|
@ -51,13 +51,6 @@ struct CreateAgentResponse {
|
|||
warnings: Vec<String>,
|
||||
}
|
||||
|
||||
/// Body of `POST /api/agents/{name}/identity`. Mirrors the controller's own
|
||||
/// `MintAgentIdentityRequest` — see this module's doc comment.
|
||||
#[derive(Serialize)]
|
||||
struct MintIdentityRequest<'a> {
|
||||
hive: &'a str,
|
||||
}
|
||||
|
||||
/// Success body of `POST /api/agents/{name}/identity`.
|
||||
#[derive(Deserialize)]
|
||||
struct MintIdentityResponse {
|
||||
|
|
@ -117,11 +110,10 @@ pub(crate) fn parse_ident(value: &str, what: &str) -> Result<String> {
|
|||
///
|
||||
/// Synchronous for the same reason [`create`] is, and built on the same
|
||||
/// round trip.
|
||||
pub(crate) fn mint_identity(socket: &Path, name: &str, hive: &str) -> Result<()> {
|
||||
pub(crate) fn mint_identity(socket: &Path, name: &str) -> Result<()> {
|
||||
// Client-side first, so a typo is a local error rather than a 400 the
|
||||
// operator waits for. The controller validates both again.
|
||||
// operator waits for. The controller validates it again.
|
||||
let name = parse_ident(name, "agent name")?;
|
||||
let hive = parse_ident(hive, "hive")?;
|
||||
|
||||
let rt = tokio::runtime::Builder::new_current_thread()
|
||||
.enable_io()
|
||||
|
|
@ -130,15 +122,15 @@ pub(crate) fn mint_identity(socket: &Path, name: &str, hive: &str) -> Result<()>
|
|||
let resp: MintIdentityResponse = rt.block_on(post(
|
||||
socket,
|
||||
&format!("/api/agents/{name}/identity"),
|
||||
&MintIdentityRequest { hive: &hive },
|
||||
&serde_json::json!({}),
|
||||
"mint-identity",
|
||||
))?;
|
||||
|
||||
println!("queued: job node {}", resp.node_id);
|
||||
println!(
|
||||
"agent {name:?} will have its identity re-minted on hive {hive:?} once the job graph \
|
||||
runs; `swarmctl` does not wait for it. An existing queue secret is kept as it is; the \
|
||||
store certificate is re-minted and the agent picks the new one up on its next boot"
|
||||
"agent {name:?} will have its identity re-minted once the job graph runs; `swarmctl` \
|
||||
does not wait for it. An existing queue secret is kept as it is; the store \
|
||||
certificate is re-minted and the agent picks the new one up on its next boot"
|
||||
);
|
||||
Ok(())
|
||||
}
|
||||
|
|
|
|||
|
|
@ -242,16 +242,6 @@ struct AgentMintForgeTokenArgs {
|
|||
struct AgentMintIdentityArgs {
|
||||
/// Name of an agent that already exists.
|
||||
name: String,
|
||||
/// The hive that agent runs on.
|
||||
///
|
||||
/// Required, and deliberately not defaulted: the credentials this mints
|
||||
/// name a hive, and neither this CLI nor the controller keeps a roster of
|
||||
/// which agent is on which hive. Naming the wrong one gives the agent an
|
||||
/// identity scoped to a hive it doesn't run on. The controller checks
|
||||
/// the value against the swarm's hive roster and names the known hives if
|
||||
/// it misses.
|
||||
#[arg(long, value_name = "HIVE")]
|
||||
hive: String,
|
||||
/// swarm-controller's unix socket.
|
||||
///
|
||||
/// Supplied by the nix module that installs this binary, from the same
|
||||
|
|
@ -357,7 +347,7 @@ fn main() -> Result<()> {
|
|||
command: AgentVerb::MintIdentity(args),
|
||||
} => {
|
||||
let socket = path_from(args.controller_socket, "SWARM_CONTROLLER_SOCKET")?;
|
||||
agent::mint_identity(&socket, &args.name, &args.hive)
|
||||
agent::mint_identity(&socket, &args.name)
|
||||
}
|
||||
// Same socket-resolution reasoning as `Create` above.
|
||||
Verb::Agent {
|
||||
|
|
@ -732,20 +722,10 @@ mod tests {
|
|||
assert!(args.controller_socket.is_none());
|
||||
}
|
||||
|
||||
/// The backfill verb takes the same two names as `create`, and `--hive`
|
||||
/// is required on it for the same reason: it is an address nobody can
|
||||
/// infer.
|
||||
#[test]
|
||||
fn the_backfill_verb_takes_an_agent_and_a_hive() {
|
||||
let cli = Cli::try_parse_from([
|
||||
"swarmctl",
|
||||
"agent",
|
||||
"mint-identity",
|
||||
"scribe",
|
||||
"--hive",
|
||||
"alpha",
|
||||
])
|
||||
.expect("the minimal form parses");
|
||||
fn the_backfill_verb_takes_an_agent_and_no_hive() {
|
||||
let cli = Cli::try_parse_from(["swarmctl", "agent", "mint-identity", "scribe"])
|
||||
.expect("the minimal form parses");
|
||||
let Verb::Agent {
|
||||
command: AgentVerb::MintIdentity(args),
|
||||
} = cli.command
|
||||
|
|
@ -753,12 +733,18 @@ mod tests {
|
|||
panic!("expected `agent mint-identity`");
|
||||
};
|
||||
assert_eq!(args.name, "scribe");
|
||||
assert_eq!(args.hive, "alpha");
|
||||
assert!(args.controller_socket.is_none());
|
||||
|
||||
assert!(
|
||||
Cli::try_parse_from(["swarmctl", "agent", "mint-identity", "scribe"]).is_err(),
|
||||
"an omitted hive must not be defaulted"
|
||||
Cli::try_parse_from([
|
||||
"swarmctl",
|
||||
"agent",
|
||||
"mint-identity",
|
||||
"scribe",
|
||||
"--hive",
|
||||
"a"
|
||||
])
|
||||
.is_err(),
|
||||
"the identity has no hive, so the verb must not take one"
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue