pub struct ControlPlane { /* private fields */ }Expand description
The control-plane client.
The transport, clock, entropy and adapters sit behind Arc, so a caller holding one across
tasks does not need a second credential chain.
Implementations§
Source§impl ControlPlane
impl ControlPlane
Sourcepub async fn describe_image(
&self,
identifier: &str,
) -> Result<Option<GetMicrovmImageResponseWire>, Error>
pub async fn describe_image( &self, identifier: &str, ) -> Result<Option<GetMicrovmImageResponseWire>, Error>
The image under identifier, or None when the service answers that there is none.
GetMicrovmImage’s 404 is an answer here rather than an error: “no image under this
name” is the state ensure_image builds from. Every other failure is an error as
usual, and throttles are retried.
Source§impl ControlPlane
impl ControlPlane
Sourcepub async fn create_image(
&self,
request: CreateImageRequest,
) -> Result<Image, Error>
pub async fn create_image( &self, request: CreateImageRequest, ) -> Result<Image, Error>
Creates an image, closing every local guard before the call.
Order matters and is the same order the Python client uses for the same reason: the
create call happens after the artifact upload, so a rejection the service raises
costs the caller the upload first. Everything checkable is checked here — and the
checks are Self::preflight, so an uploading caller can run the same list before
the upload and pay nothing for a request this client itself refuses.
The artifact is built here too, but not uploaded:
CreateImageRequest::code_artifact_uri is where the caller says they have already
put it, and ControlPlane::build_artifact_for produces the bytes to upload.
crate::sandbox::Sandbox::ensure_image is the path that uploads for itself.
Sourcepub fn preflight(&self, request: &CreateImageRequest) -> Result<(), Error>
pub fn preflight(&self, request: &CreateImageRequest) -> Result<(), Error>
Every local guard Self::create_image runs, callable before the artifact upload.
§One list of guards, reachable from before the upload
The guards below are pure functions of the request — no call, no credential — and
create_image runs them before its own wire call. But create_image runs after
the caller’s artifact upload, so from inside it a locally-refusable request has
already cost the caller the S3 PUT (issue #47). This method is the same list,
extracted so an uploading caller invokes it first and the list cannot drift between
call sites — create_image delegates here rather than keeping its own copy.
A caller who skips it loses nothing but the upload: create_image still refuses
before the wire.
Sourcepub fn build_artifact_for(
&self,
request: &CreateImageRequest,
) -> Result<Vec<u8>, Error>
pub fn build_artifact_for( &self, request: &CreateImageRequest, ) -> Result<Vec<u8>, Error>
The artifact bytes to upload to CreateImageRequest::code_artifact_uri.
Separate from ControlPlane::create_image because the upload is the caller’s — and
because the byte-scan guard (AC-2-3) needs to inspect these bytes without making a
control-plane call.
Sourcepub fn dockerfile_for(&self, request: &CreateImageRequest) -> String
pub fn dockerfile_for(&self, request: &CreateImageRequest) -> String
The Dockerfile request builds with: its own, or the default stanza for its base,
this plane’s port and its project’s ecosystem. The one fallback the artifact, its
content hash and an ensure request built from request all take.
Sourcepub async fn wait_for_image(
&self,
identifier: &str,
size: SizeClass,
opts: WaitOpts,
) -> Result<Image, Error>
pub async fn wait_for_image( &self, identifier: &str, size: SizeClass, opts: WaitOpts, ) -> Result<Image, Error>
Polls until the image is usable, distinguishing a stalled build from a slow one.
Returns the image as the service last described it. Raises
ErrorKind::BuildWedged for the replay signature (TRAP-2),
ErrorKind::Platform for a reported build failure, and ErrorKind::Timeout
when the deadline elapses.
Sourcepub async fn get_image_build(
&self,
identifier: &str,
version: &str,
build_id: &str,
) -> Result<GetImageBuildResponseWire, Error>
pub async fn get_image_build( &self, identifier: &str, version: &str, build_id: &str, ) -> Result<GetImageBuildResponseWire, Error>
GetMicrovmImageBuild: one build, with the snapshot sizes the listing does not carry.
§What this adds over the listing, and it is not marginal
snapshotBuild — the memory, code, and disk byte counts — is the only size any
operation in this model reports for anything the platform builds. GetMicrovmImage
carries none, GetMicrovm carries none, and both listings carry none, so a storage
estimate had nothing to multiply but the size class’s baseline. These three figures are
what the snapshot read, write, and storage line items actually bill on.
It also carries a stateReason at the same level the listing does, which is the level
that is populated on a real failure — the version’s is null (docs/PLATFORM.md).
build_id comes off ops::MicrovmImageBuildSummaryWire::build_id, which the listing
already parses; nothing else in the API mints one.
Sourcepub async fn get_image_version(
&self,
identifier: &str,
version: &str,
) -> Result<MicrovmImageVersionSummaryWire, Error>
pub async fn get_image_version( &self, identifier: &str, version: &str, ) -> Result<MicrovmImageVersionSummaryWire, Error>
GetMicrovmImageVersion: one version’s whole configuration, its state, and its
availability status.
§The only full config readback there is
The response echoes the creation request: baseImageArn with the baseImageVersion
that was actually used, the buildRoleArn, the codeArtifact URI, the egress
connectors, the CPU configurations, and resources — which is where a built image’s
size class lives, since GetMicrovm reports no memory figure at all. It is how a
caller who no longer holds the request finds out what an image was built with.
status is the member that matters for a retire: ACTIVE means RunMicrovm will
launch this version, INACTIVE means it refuses. See
Self::set_image_version_status.
One call rather than a filtered ListMicrovmImageVersions, and the difference is not
only cost: a listing can page, so “find version X” is a loop that may read the whole
history, while this addresses the version directly and answers
ResourceNotFoundException when it is gone.
Sourcepub async fn set_image_version_status(
&self,
identifier: &str,
version: &str,
status: VersionStatus,
) -> Result<MicrovmImageVersionSummaryWire, Error>
pub async fn set_image_version_status( &self, identifier: &str, version: &str, status: VersionStatus, ) -> Result<MicrovmImageVersionSummaryWire, Error>
UpdateMicrovmImageVersion: set a version ACTIVE or INACTIVE.
§The model’s only non-destructive retire
DeleteMicrovmImageVersion is the alternative and it is irreversible, cannot be
applied to an image’s last version at all, and destroys the readback a post-mortem
needs. INACTIVE does none of that: RunMicrovm refuses to launch the version,
already-running VMs keep running, and Self::get_image_version still answers
with the whole configuration. So the rollback for a bad build is two calls that both
preserve evidence — set the new version INACTIVE, re-pin the launch to the old one —
rather than a delete.
§Why the status is typed and the version is checked
ops::VersionStatus has two variants, so "INACTIVATE" is a compile error rather
than a ValidationException on the only member this request has. The version string is
checked against the Version shape locally for the reason
super::require_valid_version gives: this call is made at the moment someone is
rolling back, which is when a failure about the request rather than about the version
is most expensive.
Returns the readback, which carries the status the service now reports — so a caller confirms the change rather than assuming the 200 meant it took.
Sourcepub async fn managed_base_versions(
&self,
base_image_arn: &str,
) -> Result<Vec<ManagedMicrovmImageVersionWire>, Error>
pub async fn managed_base_versions( &self, base_image_arn: &str, ) -> Result<Vec<ManagedMicrovmImageVersionWire>, Error>
ListManagedMicrovmImageVersions, read to its last page: the versions of a managed
base image, newest first as the service orders them.
§What this is for
ops::CreateMicrovmImageWire::base_image_version is how a build stops floating on
whatever the service currently defaults to, and this is the only operation that says
what the legal values are. The default has already moved once: al2023-1 carried one
version in June and two by July ("0" and "1", measured 2026-08-16), so every build
made before this existed recorded no base version and is not reproducible.
§Every page, and an ARN rather than a name
Every page for the reason the other listings give: a version on page two is still a version, and a caller pinning “the newest” from a truncated page pins the wrong one.
The identifier must be the base’s full ARN — a bare al2023-1 answers
ValidationException: Invalid ARN format (measured 2026-08-16) — so that is checked
here rather than at the wire, because the service’s message names the value without
saying which member wanted an ARN or that
super::BaseImage::arn is what produces one.
Sourcepub async fn managed_base_images(
&self,
) -> Result<Vec<ManagedMicrovmImageSummaryWire>, Error>
pub async fn managed_base_images( &self, ) -> Result<Vec<ManagedMicrovmImageSummaryWire>, Error>
ListManagedMicrovmImages, read to its last page: the base images AWS publishes.
§Informational only, and that is a limitation rather than a choice
ManagedMicrovmImageSummary carries an ARN and two timestamps and nothing else —
no registry reference, no architecture, no working directory. A
super::BaseImage needs all three of ARN, Dockerfile FROM, and WORKDIR knowledge,
because require_matching_from compares a caller’s Dockerfile against the FROM and
require_workdir refuses inheritance when the base declares none. Neither guard has an
input here, and the registry ref is not derivable from the ARN — al2023-1 pairs with
public.ecr.aws/amazonlinux/amazonlinux:2023-minimal and nothing in the ARN says so.
So a discovered base cannot safely be built from, and this exists to answer one
question: has AWS published a base this client does not know about? microvm doctor
reports it. Measured 2026-08-16, the answer is one item, so hardcoding al2023-1
currently misses nothing.
Sourcepub async fn answers_listing(&self) -> Result<usize, Error>
pub async fn answers_listing(&self) -> Result<usize, Error>
One page of ListManagedMicrovmImages: whether the service answers a signed,
free, read-only request in this region (BIND-15’s service check), and how many managed
bases the page carried.
One operation, never the whole listing, so a preflight makes one AWS operation at most
(BIND-16); send_with_retry may repeat it on a throttle. A region without MicroVMs can
answer AccessDeniedException with a null message (TRAP-6), which this surfaces as the
error it is. A region AWS lists as planned can answer the listing instead
(ca-central-1, measured 2026-10-24).
Sourcepub async fn resolve_credentials(&self) -> Result<(), Error>
pub async fn resolve_credentials(&self) -> Result<(), Error>
Resolves the credentials this client signs with, sending nothing to the service
(BIND-15’s credentials check). See super::transport::Transport::resolve_credentials.
Sourcepub async fn list_images(&self) -> Result<Vec<MicrovmImageSummaryWire>, Error>
pub async fn list_images(&self) -> Result<Vec<MicrovmImageSummaryWire>, Error>
ListMicrovmImages, read to its last page.
§Why this is public where the private walkers are not
Self::find_image_by_name sends nameFilter and answers one image, which is what a
resolver wants and is a different question from “what is in this account”. The live tier
asks the second: tests/live_versions.rs walks the account looking for an image with a
version in a particular state, because which image that is is an account fact a test
must not hardcode. microvm ls deliberately does not use this — it reads the local
ledger, because the resources worth asking about are the ones a killed process never got
to report and no listing can attribute those back to a command.
Sourcepub async fn list_image_versions(
&self,
identifier: &str,
) -> Result<Vec<MicrovmImageVersionSummaryWire>, Error>
pub async fn list_image_versions( &self, identifier: &str, ) -> Result<Vec<MicrovmImageVersionSummaryWire>, Error>
ListMicrovmImageVersions, read to its last page.
A Result where the private Self::builds_of_version answers Option, and the
difference is the caller: the private one feeds a diagnosis, where a listing failure
must not become a wedge claim, and this one feeds a caller who asked about the versions
and needs to know if the answer is unavailable.
Sourcepub async fn list_image_builds(
&self,
identifier: &str,
version: &str,
) -> Result<Vec<MicrovmImageBuildSummaryWire>, Error>
pub async fn list_image_builds( &self, identifier: &str, version: &str, ) -> Result<Vec<MicrovmImageBuildSummaryWire>, Error>
ListMicrovmImageBuilds for one version, read to its last page.
The identifiers Self::get_image_build takes come from here: nothing else in the API
mints a buildId. See Self::list_image_versions on why this answers a Result where
the diagnosis path’s walker answers an Option.
Sourcepub async fn find_image_by_name(
&self,
name: &str,
) -> Result<Option<MicrovmImageSummaryWire>, Error>
pub async fn find_image_by_name( &self, name: &str, ) -> Result<Option<MicrovmImageSummaryWire>, Error>
The image with exactly name, or None when no page of the listing has one.
§nameFilter narrows, it does not answer
The model’s nameFilter is a server-side substring filter (“images whose name
contains the specified string”), so coding-agents also matches
coding-agents-old. The filter is sent — it keeps a large account’s listing to one
page in practice — and the exact-match comparison happens here, on the client,
because “contains” and “is” are different questions and only the second one is
safe to launch from.
§The listing paginates, and every page is read
An image on page two is still an image. A resolver that read only the first page would answer “no image named X” for a name that exists — the confident wrong answer, which then sends the caller to rebuild an image they already paid for.
Sourcepub async fn resolve_image_arn(&self, identifier: &str) -> Result<String, Error>
pub async fn resolve_image_arn(&self, identifier: &str) -> Result<String, Error>
The ARN for identifier: an ARN passes through untouched, a bare name is
resolved through the image listing by exact name.
§Why this exists
RunMicrovm.imageIdentifier takes an ARN, and a bare name sent verbatim is
answered with HTTP 400 “Malformed ARN” — after a credential resolution and a
signed call, and with a message that says nothing about names. Every consumer was
scripting this resolution with the AWS CLI (the coding-agents example did); this
is that script, once, with the pagination handled.
§An ARN costs zero calls
The passthrough is checked first, so a caller who already holds the ARN pays
nothing for the convenience existing. The prefix test is arn: exactly — the one
spelling every AWS ARN starts with, and a string the ImageName pattern
([a-zA-Z0-9-_]+) cannot produce, so no legal image name is mistaken for one.
Sourcepub fn artifact_content_hash_for(&self, request: &CreateImageRequest) -> String
pub fn artifact_content_hash_for(&self, request: &CreateImageRequest) -> String
The content hash of the artifact request would build, for content-addressed
image reuse.
Derives the Dockerfile exactly as ControlPlane::build_artifact_for does —
same default, same port — so the name a reuse check computes is the name a build
of the same request would carry. See artifact::artifact_content_hash for what
is hashed and why it is the inputs rather than the zip.
Sourcepub async fn delete_image(
&self,
identifier: &str,
attempts: u32,
backoff: Duration,
) -> bool
pub async fn delete_image( &self, identifier: &str, attempts: u32, backoff: Duration, ) -> bool
Deletes every version but the first, then the image, retrying.
§Why it retries
Not politeness: an image in CREATING refuses deletion and a VM still terminating
holds a reference. This loop is the difference between a clean account and a billed
leak.
§Why not backon, unlike transport::send_with_retry
Assessed for the dependency sweep (issue #91) and kept as a loop: the sleeps here
go through the injected [Clock] seam so tests drive them without real time, and
Clock::sleep returns a future borrowing &self — backon’s Sleeper needs a
future that owns its state, so bridging the seam costs an adapter bigger than the
five lines it would replace. The fixed interval is also the contract: teardown
polls a state that resolves on the service’s schedule, not a growing backoff.
§Why the first version is kept
The last remaining version cannot be deleted on its own, only together with the
image. Trying produces a ConflictException that reads like a permissions problem.
Returns true when the image was deleted, false when every attempt failed. It
does not raise, because the caller is a teardown path and the original failure is
the one worth reading.
§The identifier check is here rather than in Self::try_delete_image
Inside the attempt it would be re-decided on every retry, which for the default twenty
attempts is nineteen backoff sleeps over a value that cannot become valid. Before the loop
it costs one comparison and zero calls. The refusal is a false rather than an Err
because this function’s whole contract is that it does not raise into a teardown path —
and false from here is honest: no image was deleted.
Source§impl ControlPlane
impl ControlPlane
Sourcepub async fn run_microvm(
&self,
request: RunMicrovmRequest,
) -> Result<Microvm, Error>
pub async fn run_microvm( &self, request: RunMicrovmRequest, ) -> Result<Microvm, Error>
Launches a MicroVM.
TRAP-5 is already closed by RunHookPayload’s type; the duration range is checked
here, and the connectors are derived from intents (TRAP-4). The clientToken is
minted from a label (TRAP-1).
Sourcepub async fn wait_for_running(
&self,
id: &str,
opts: WaitOpts,
) -> Result<Microvm, Error>
pub async fn wait_for_running( &self, id: &str, opts: WaitOpts, ) -> Result<Microvm, Error>
Polls to RUNNING, failing fast on a terminal state (TRAP-8).
The fast failure is the whole value. A VM that reaches a terminal state before
RUNNING died during startup; polling through it wastes minutes and then reports a
connection error that hides the cause, and by then the VM is gone so stateReason is
the only evidence left.
Sourcepub async fn wait_for_launch(
&self,
id: &str,
opts: WaitOpts,
) -> Result<Microvm, Error>
pub async fn wait_for_launch( &self, id: &str, opts: WaitOpts, ) -> Result<Microvm, Error>
Polls a launch that may have adopted an existing VM through its client token.
A repeated clientToken makes RunMicrovm answer with the VM the first attempt
launched, and that VM may have idle-suspended since (#195). Self::wait_for_running
would read that SUSPENDED as a death during startup (TRAP-8) and report LaunchDied
for a healthy VM. Here SUSPENDING is waited through, SUSPENDED is resumed once, and
only the dead states fail fast. A genuine startup death still ends in a dead state,
so it still fails with stateReason.
Sourcepub async fn wait_for_state(
&self,
id: &str,
wanted: &[&str],
fail_on: &[&str],
opts: WaitOpts,
) -> Result<Microvm, Error>
pub async fn wait_for_state( &self, id: &str, wanted: &[&str], fail_on: &[&str], opts: WaitOpts, ) -> Result<Microvm, Error>
Polls until the VM reaches one of wanted, failing fast on any of fail_on.
§Why fail_on is a parameter rather than always the terminal set
Because suspend wants SUSPENDED and tolerates TERMINATED: a VM that dies while
suspending is a state to report, not an exception to raise out of the middle of a
teardown. The resume path passes the dead states instead, because a VM the idle
policy terminated during suspension never reaches RUNNING — and waiting only for
RUNNING there burns the full timeout and then reports “never reached RUNNING”, a
timeout message hiding a cause the service had already stated.
Sourcepub async fn get_microvm(&self, id: &str) -> Result<Microvm, Error>
pub async fn get_microvm(&self, id: &str) -> Result<Microvm, Error>
GetMicrovm.
microvmIdentifier is a URI parameter, so an empty one collapses the path onto the
listing and asks a different question — see super::require_valid_identifier. This is
the read every wait loop makes, so the check also covers
Self::wait_for_state and everything built on it.
Sourcepub async fn list_microvms(&self) -> Result<Vec<MicrovmItemWire>, Error>
pub async fn list_microvms(&self) -> Result<Vec<MicrovmItemWire>, Error>
ListMicrovms, read to its last page.
§Why every page
maxResults caps at 50, so a fleet above 50 VMs read from one page is a
confidently wrong answer rather than a missing one: the caller gets a list that
looks complete, and a VM absent from it is a VM nothing here will terminate. That is
the same argument ops::ListImagesResponseWire makes for the image listing, and a
fleet listing is the one a teardown reads.
Each VM is listed once (#280): a VM the service returns on two pages keeps the place it was first listed and the entry read last, the newer reading of its state.
Sourcepub async fn list_microvms_matching(
&self,
filter: &MicrovmFilter,
) -> Result<Vec<MicrovmItemWire>, Error>
pub async fn list_microvms_matching( &self, filter: &MicrovmFilter, ) -> Result<Vec<MicrovmItemWire>, Error>
Self::list_microvms, narrowed by the service’s image and version filters.
Sourcepub async fn list_microvm_summaries(
&self,
filter: &MicrovmFilter,
) -> Result<Vec<MicrovmSummaryWire>, Error>
pub async fn list_microvm_summaries( &self, filter: &MicrovmFilter, ) -> Result<Vec<MicrovmSummaryWire>, Error>
The paginated listing with optional started_at metadata. This adds metadata without
changing the exhaustively constructible item returned by the original listing methods.
Sourcepub async fn suspend(&self, id: &str) -> Result<(), Error>
pub async fn suspend(&self, id: &str) -> Result<(), Error>
SuspendMicrovm. Freezes the VM; the caller waits for SUSPENDED.
Sourcepub async fn resume(&self, id: &str) -> Result<(), Error>
pub async fn resume(&self, id: &str) -> Result<(), Error>
ResumeMicrovm. Thaws the VM; no token re-delivery and no re-bootstrap.
§A VM that ended before the resume is WindowClosed (STATE-12)
Nothing is checked against the suspended window before the call, because the service
decides whether a suspended VM can still resume. It terminates a VM the idle policy
suspended once suspendedDurationSeconds passes, and it doesn’t hold an explicit
SuspendMicrovm to that window: measured 2026-10-12 in us-east-1, an explicitly
suspended VM resumed 1283 s into a 60 s window (#597). A resume of a VM that has
terminated is refused with a ValidationException (“The MicroVM TerminateMicrovm
ended (measured 2026-10-12, us-east-1). So a refusal reads the VM’s state rather than
the message, and a VM that’s TERMINATING or TERMINATED is ErrorKind::WindowClosed
with its state and stateReason, carrying the refusal as its source. The error names
no cause, because the client doesn’t know which of the suspended window, the maximum
duration, or a terminate ended the VM; the stateReason is the service’s word on it.
Every other refusal comes back as the service gave it.
Sourcepub async fn terminate(&self, id: &str) -> Result<(), Error>
pub async fn terminate(&self, id: &str) -> Result<(), Error>
TerminateMicrovm.
The identifier check matters most on this one, and for a reason the others do not share:
the path is DELETE /microvms/{microvmIdentifier}, so an empty identifier is a DELETE
addressed at the collection. There is no operation on that path and the service
refuses it, but “send a delete at the fleet and rely on the service to refuse” is not a
thing to leave to the service.
Sourcepub async fn mint_auth_token(&self, id: &str) -> Result<ProxyToken, Error>
pub async fn mint_auth_token(&self, id: &str) -> Result<ProxyToken, Error>
Mints a proxy token for the agent port (TRAP-7, TRAP-9).
expirationInMinutes is capped at MAX_TOKEN_MINUTES here rather than passed
through, because over-asking is rejected and the rejection reads like a bad request
rather than like a ceiling. The refresh interval — half the ceiling — belongs to
the endpoint client that holds the token (T-W2-5), not here.
Scoped to the agent port alone, which is right for every request this client makes on
its own behalf and wrong for a caller reaching another port on the same VM. That
caller wants Self::mint_auth_token_for; see ops::PortSpecification for the
measurement that made the distinction necessary.
Sourcepub async fn mint_auth_token_for(
&self,
id: &str,
ports: &[PortSpecification],
) -> Result<ProxyToken, Error>
pub async fn mint_auth_token_for( &self, id: &str, ports: &[PortSpecification], ) -> Result<ProxyToken, Error>
Mints a proxy token scoped to ports, whatever they are.
The agent port is not added for you. A caller naming ports is answering the
question the token asks, and silently widening the scope would hand back a
credential broader than the one requested — the opposite of the mistake this exists
to fix, and a worse one, since it is invisible. A caller that needs both the agent
port and a workload port names both, which is what ProxyAuth does.
The returned token still records the agent port as its default header port, because that is the port a request carries when nobody names one.
Sourcepub async fn mint_shell_auth_token(&self, id: &str) -> Result<ProxyToken, Error>
pub async fn mint_shell_auth_token(&self, id: &str) -> Result<ProxyToken, Error>
Mints a shell token: the credential microvm shell opens the PTY WebSocket with.
CreateMicrovmShellAuthToken, which the VM must have been launched with
SHELL_INGRESS to answer — a VM launched without it gets a ValidationException
naming the connector, and RunMicrovmRequest::with_shell is how a launch requests
the measured pair. The request takes only the expiration; there is no
allowedPorts, because the shell is not a port (docs/PLATFORM.md, 2026-08-15).
§This is microvm shell’s method, not exec’s (TRAP-11)
One interactive session is not programmatic exec — no exec ids, no idempotency, no separated stdout/stderr, no exit codes — so nothing on the exec or session path calls this, and the lifecycle test asserts that absence. A caller holding the returned token has a real root PTY on the VM; treat it like the credential it is.
The expiration is pinned at MAX_TOKEN_MINUTES for the reason
Self::mint_auth_token gives: over-asking is rejected in a way that reads like
a bad request rather than like a ceiling. A shell session held past the ceiling
keeps its already-open WebSocket — the expiry gates the handshake, not an
established session.
Source§impl ControlPlane
impl ControlPlane
Sourcepub async fn sweep(
&self,
prefix: &str,
options: &SweepOptions,
) -> Result<SweepReport, Error>
pub async fn sweep( &self, prefix: &str, options: &SweepOptions, ) -> Result<SweepReport, Error>
Discover or clean up images whose names start with prefix, and their live VMs.
A nonempty image-name prefix is required. Keep entries are exact names or ARNs; their VMs are protected too. Destructive cleanup can stop every VM using a selected, non-kept image, including VMs owned by other runners. It cannot isolate a failed runner’s VMs when runners share an image. Keep shared images or use a separate image-name prefix for each job.
Cleanup waits for termination before deletion, then confirms image disappearance. Concurrent launches may keep an image in use; they are reported rather than repeatedly terminated. This is not an account-wide lock.
Source§impl ControlPlane
impl ControlPlane
Sourcepub fn from_ports(
transport: Arc<dyn Transport>,
region: Region,
clock: Arc<dyn Clock>,
entropy: Arc<dyn Entropy>,
adapters: Arc<dyn Adapters>,
) -> ControlPlane
pub fn from_ports( transport: Arc<dyn Transport>, region: Region, clock: Arc<dyn Clock>, entropy: Arc<dyn Entropy>, adapters: Arc<dyn Adapters>, ) -> ControlPlane
A client over caller-supplied ports, on the default agent port.
Every port is explicit, including the entropy. A plane over a scripted transport still mints client tokens, and a deterministic default would mint repeating ones (TRAP-1), so nothing here picks a source for the caller.
ControlPlane::new(region) and ControlPlane::with_transport(transport, region, clock)
are in microvms_core::prelude: they wire the production transport, clock, entropy and
adapters, which is I/O this constructor leaves to its caller. A test builds one with
testing::control_plane instead.
Sourcepub fn with_retry(self, retry: RetryPolicy) -> ControlPlane
pub fn with_retry(self, retry: RetryPolicy) -> ControlPlane
Uses a custom budget for throttles only; other retries retain their defaults.
Sourcepub fn with_entropy(self, entropy: Arc<dyn Entropy>) -> ControlPlane
pub fn with_entropy(self, entropy: Arc<dyn Entropy>) -> ControlPlane
Replaces the source client-token nonces, agent tokens and identity seeds are drawn from. The seam a test pins those values through.
Sourcepub fn with_port(self, port: u16) -> Result<ControlPlane, Error>
pub fn with_port(self, port: u16) -> Result<ControlPlane, Error>
Sets the agent port the hooks block and the proxy token name.
§Why this is fallible where every other builder method is not
Because port is the one value on this type that the model bounds, and 0 is a value a
caller reaches for on purpose. PortNumber and HooksPortInteger are both min: 1;
u16 closes the ceiling and nothing closed the floor, so before this returned a Result
the port set here landed in two places on the wire — hooks.port on every
CreateMicrovmImage built through the plane, and allowedPorts: [{"port": 0}] on every
token it minted (issue #24).
Both of those are guarded at the call itself as well (require_valid_port in
ControlPlane::create_image and in ControlPlane::mint_auth_token_for), so the
constraint would be closed without this. It is fallible anyway because the two refusals
arrive a long way from the mistake: the create one after an artifact upload, and the mint
one at the moment a session needs a credential. Refusing here means --port 0 fails
before a control plane is even usable, which is the difference between “that port is not
legal” and “your launch failed”.
Zero rather than the ceiling is what this actually catches, and it is not a typo case: 0 is what “let the kernel choose” means to a listener, so it is the value a caller passes when they have a socket they have not bound yet.
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> 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