pub struct ControlPlane { /* private fields */ }Expand description
Owns the runtime + registry + card-source ports and drives the deploy, undeploy, and recovery use-cases across them.
Implementations§
Source§impl ControlPlane
impl ControlPlane
Sourcepub fn new(
runtime: Arc<dyn AgentRuntime>,
registry: Arc<dyn AgentRegistry>,
cards: Arc<dyn CardSource>,
) -> Self
pub fn new( runtime: Arc<dyn AgentRuntime>, registry: Arc<dyn AgentRegistry>, cards: Arc<dyn CardSource>, ) -> Self
Assemble a control plane over concrete adapters.
cards reads a running agent’s published card; it is required rather
than optional because recover needs it to put adopted
agents back into discovery, and a control plane that silently skipped
that step would come back from a restart supervising agents nobody can
find.
The env allowlist is deny-all until set with
with_allowed_env.
Sourcepub fn with_allowed_env(self, allowed: EnvAllowlist) -> Self
pub fn with_allowed_env(self, allowed: EnvAllowlist) -> Self
Permit submitted configs to reference these host environment variables.
Give the runtime adapter the same allowlist at the composition edge: this one vets what the API accepts, the runtime’s decides what actually crosses into the agent. One policy value, two enforcement points — the runtime stays safe even when something other than this service drives it.
Sourcepub fn prepare(
&self,
raw_toml: &str,
) -> Result<PreparedDeploy, ControlPlaneError>
pub fn prepare( &self, raw_toml: &str, ) -> Result<PreparedDeploy, ControlPlaneError>
Vet and parse a submitted config, yielding the token deploy
requires.
The allowlist is checked against the raw text first. Parsing expands
${VAR} against this process’s environment and distinguishes a set from
an unset variable in its error, so a config naming a forbidden secret must
be rejected before it can be used to probe what secrets exist here.
Sourcepub async fn deploy(
&self,
prepared: PreparedDeploy,
config_path: PathBuf,
) -> Result<DeployedAgent, ControlPlaneError>
pub async fn deploy( &self, prepared: PreparedDeploy, config_path: PathBuf, ) -> Result<DeployedAgent, ControlPlaneError>
Deploy a prepared agent: provision + start it in the
runtime, then register its card so peers discover it. The runtime and
registry ids coincide (both the slug of the agent name).
The caller materializes the config on disk — at the path
prepared.id() told it to use — and passes that config_path (the file
the runtime reads). So the service orchestrates ports + pure config
without itself touching the filesystem (hex rule 9a), and the TOML is
parsed exactly once, during prepare.
Deploying onto a port another agent already holds is
PortInUse, not a started agent — see
check_port_free.
Sourcepub async fn recover(
&self,
) -> Result<Recovered<DeployedAgent>, ControlPlaneError>
pub async fn recover( &self, ) -> Result<Recovered<DeployedAgent>, ControlPlaneError>
Re-adopt the agents this platform was already running, and put the ones that answer back into discovery.
Call once at startup, before serving. Both halves of the control plane’s
knowledge are process state — the runtime’s instance table and the
registry’s cards — so without this a restarted control plane reports an
empty fleet while the agents are still up: GET /agents says nothing is
deployed, DELETE says NotFound, peers stop resolving each other by
skill, and a Terraform Read concludes the agents were destroyed
out-of-band and recreates them on top of the running ones.
Registration is attempted only for agents the runtime reports
Healthy — that state means the card probe
succeeded, so anything else would be a fetch known in advance to fail.
An agent that is adopted but not registered is still managed (it can be
stopped, and it will be registered by a later recovery once it answers);
it is simply not yet discoverable.
Idempotent: adoption re-inserts by id and registration upserts by card name, so calling this twice changes nothing.
Sourcepub async fn undeploy(&self, id: &AgentId) -> Result<(), ControlPlaneError>
pub async fn undeploy(&self, id: &AgentId) -> Result<(), ControlPlaneError>
Stop an agent in the runtime and deregister it. Idempotent on the registry side (a missing entry is not an error).
Sourcepub async fn status(
&self,
id: &AgentId,
) -> Result<RuntimeHealth, ControlPlaneError>
pub async fn status( &self, id: &AgentId, ) -> Result<RuntimeHealth, ControlPlaneError>
Report an agent’s current runtime health.
Sourcepub async fn logs(
&self,
id: &AgentId,
tail: Option<usize>,
) -> Result<Vec<String>, ControlPlaneError>
pub async fn logs( &self, id: &AgentId, tail: Option<usize>, ) -> Result<Vec<String>, ControlPlaneError>
An agent’s captured output, oldest line first, limited to the last tail
lines when given.
Health says an agent is Unhealthy; this is
where the reason is. Whether it can be answered at all depends on the
backend — see AgentRuntime::logs.
Sourcepub async fn list(
&self,
filter: ListFilter,
) -> Result<Vec<DeployedAgent>, ControlPlaneError>
pub async fn list( &self, filter: ListFilter, ) -> Result<Vec<DeployedAgent>, ControlPlaneError>
List deployed agents with their endpoint and health, ordered by id.
Sorted because a runtime’s own order is its map’s, i.e. arbitrary and
unstable between calls — which makes a2a ps shuffle between runs and
makes the API’s response undiffable for no reason.
Trait Implementations§
Auto Trait Implementations§
impl !RefUnwindSafe for ControlPlane
impl !UnwindSafe for ControlPlane
impl Freeze for ControlPlane
impl Send for ControlPlane
impl Sync for ControlPlane
impl Unpin for ControlPlane
impl UnsafeUnpin for ControlPlane
Blanket Implementations§
Source§impl<T> BorrowMut<T> for Twhere
T: ?Sized,
impl<T> BorrowMut<T> for Twhere
T: ?Sized,
Source§fn borrow_mut(&mut self) -> &mut T
fn borrow_mut(&mut self) -> &mut T
Source§impl<T> CloneToUninit for Twhere
T: Clone,
impl<T> CloneToUninit for Twhere
T: Clone,
Source§impl<T> Instrument for T
impl<T> Instrument for T
Source§fn instrument(self, span: Span) -> Instrumented<Self> ⓘ
fn instrument(self, span: Span) -> Instrumented<Self> ⓘ
Source§fn in_current_span(self) -> Instrumented<Self> ⓘ
fn in_current_span(self) -> Instrumented<Self> ⓘ
Source§impl<T> IntoEither for T
impl<T> IntoEither for T
Source§fn into_either(self, into_left: bool) -> Either<Self, Self> ⓘ
fn into_either(self, into_left: bool) -> Either<Self, Self> ⓘ
self into a Left variant of Either<Self, Self>
if into_left is true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read moreSource§fn into_either_with<F>(self, into_left: F) -> Either<Self, Self> ⓘ
fn into_either_with<F>(self, into_left: F) -> Either<Self, Self> ⓘ
self into a Left variant of Either<Self, Self>
if into_left(&self) returns true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read more