swarm-controller: create every agent's subagent stream
swarm-controller now creates `term-sub-<agent>` for every agent a hive is declared to run, at start and every minute after, with the config `swarm_queue_client::subagent_term::open_or_create` spells (subjects `$SWARM.term.<agent>.sub.>`, max_age 24h). An existing stream is opened as it is, as the controller does for its other streams and buckets, under the `$JS.API.STREAM.CREATE.*` grant it already holds. The agent no longer creates the stream: its token is granted publish on `$SWARM.term.<agent>.sub.>` and no `$JS.API.STREAM.CREATE|INFO` subject, and the subagent daemon only publishes. A `CREATE` carries the stream's config in its payload, which no subject grant narrows, so the agent could otherwise pick the stream's subjects and limits.
This commit is contained in:
parent
d6f94e5247
commit
318f67cda9
13 changed files with 135 additions and 102 deletions
|
|
@ -363,16 +363,12 @@ mod tests {
|
|||
"$KV.agent-icons.{agent}",
|
||||
"--agent-token-publish-subject",
|
||||
"$SWARM.term.{agent}.sub.>",
|
||||
"--agent-token-publish-subject",
|
||||
"$JS.API.STREAM.CREATE.term-sub-{agent}",
|
||||
"--agent-token-publish-subject",
|
||||
"$JS.API.STREAM.INFO.term-sub-{agent}",
|
||||
"--store-cert-role",
|
||||
"swarm-nats-auth",
|
||||
])
|
||||
.expect("the unit's own argument vector must parse");
|
||||
assert_eq!(args.agent_client_suffix, "-agent");
|
||||
assert_eq!(args.agent_token_publish_subjects.len(), 6);
|
||||
assert_eq!(args.agent_token_publish_subjects.len(), 4);
|
||||
}
|
||||
|
||||
/// The control for the case above: an ordinary value parses through the
|
||||
|
|
|
|||
|
|
@ -1219,34 +1219,32 @@ mod tests {
|
|||
);
|
||||
}
|
||||
|
||||
/// The subagent templates as `swarm-nats.nix` spells them expand to the
|
||||
/// subjects and stream name the subagent daemon uses, and to `CREATE` and
|
||||
/// `INFO` on that one stream only.
|
||||
/// The subagent template as `swarm-nats.nix` spells it expands to the
|
||||
/// subjects the subagent daemon publishes on, publish only. The stream is
|
||||
/// the controller's to create, which its own grant covers.
|
||||
#[test]
|
||||
fn an_agents_subagent_grant_is_its_own_stream_and_nothing_wider() {
|
||||
fn an_agents_subagent_grant_is_publish_on_its_own_family_only() {
|
||||
use swarm_queue_client::subagent_term::{stream_name, stream_subjects, subject};
|
||||
|
||||
let p = policy_with_agent_subject()
|
||||
.with_agent_token_subjects(vec![
|
||||
"$SWARM.term.{agent}.sub.>".to_owned(),
|
||||
"$JS.API.STREAM.CREATE.term-sub-{agent}".to_owned(),
|
||||
"$JS.API.STREAM.INFO.term-sub-{agent}".to_owned(),
|
||||
])
|
||||
.expect("per-agent templates are valid");
|
||||
.with_agent_token_subjects(vec!["$SWARM.term.{agent}.sub.>".to_owned()])
|
||||
.expect("a per-agent template is valid");
|
||||
let g = p.agent_token_permissions("atlas").expect("configured");
|
||||
assert_eq!(
|
||||
g.publish,
|
||||
vec![
|
||||
stream_subjects("atlas"),
|
||||
format!("$JS.API.STREAM.CREATE.{}", stream_name("atlas")),
|
||||
format!("$JS.API.STREAM.INFO.{}", stream_name("atlas")),
|
||||
]
|
||||
);
|
||||
assert_eq!(g.publish, vec![stream_subjects("atlas")]);
|
||||
assert!(subject("atlas", "scout").starts_with(g.publish[0].trim_end_matches('>')));
|
||||
let argus = p.agent_token_permissions("argus").expect("configured");
|
||||
assert_eq!(argus.publish, vec![stream_subjects("argus")]);
|
||||
assert_ne!(g.publish, argus.publish);
|
||||
|
||||
let stream = stream_name("atlas");
|
||||
assert!(
|
||||
g.publish.iter().all(|s| !argus.publish.contains(s)),
|
||||
"atlas and argus share a subject: {g:?} {argus:?}"
|
||||
!stream.contains('.'),
|
||||
"{stream} is one token, so `*` covers it"
|
||||
);
|
||||
assert!(
|
||||
policy()
|
||||
.reader_subjects()
|
||||
.contains(&"$JS.API.STREAM.CREATE.*".to_owned())
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue