microvms-app 1.3.0

The MicroVMs client's use cases, written against ports: the control plane, sandboxes, sessions, agents
Documentation
// SPDX-License-Identifier: Apache-2.0
//! `preflight`: whether a harness can launch here, checked before it queues work (#223).
//!
//! Three checks, in order, each a [`Check`] the CLI's `doctor` renders too (it shares
//! [`region_check`] and [`credentials_check`] and prints the same lines):
//!
//! 1. **region**: the region resolves, from the caller or from `$AWS_REGION` /
//!    `$AWS_DEFAULT_REGION` ([`Region::from_env`]). Fatal when it does not resolve. Advisory
//!    when it is outside [`crate::region::MEASURED_REGIONS`]: AWS adds regions faster than a
//!    constant is re-read.
//! 2. **credentials**: the default credential chain resolves credentials
//!    ([`ControlPlane::resolve_credentials`]). No AWS API call; the chain may read an SSO
//!    cache, run a credential process, or ask the instance metadata service.
//! 3. **service**: one `ListManagedMicrovmImages` page in that region
//!    ([`ControlPlane::answers_listing`]): the endpoint resolves, the signature and the IAM
//!    permission are accepted, and the region answers the listing. A region without
//!    MicroVMs can answer `AccessDeniedException` with a null message (TRAP-6). A pass does
//!    not prove the region runs MicroVMs: measured 2026-10-24, `ca-central-1`, which AWS
//!    lists as planned, answered the listing.
//!
//! A check whose precondition failed is reported as not run ([`Check::ran`] false), fatal, and
//! makes no call. [`PreflightReport::ok`] is true exactly when no fatal check failed or was
//! skipped. The Stateright model in `crates/model/src/preflight.rs` specifies this (BIND-15, BIND-16).
//!
//! # Nothing billable, and what is not checked
//!
//! The one AWS operation is a free read (`send_with_retry` may repeat it on a throttle); no
//! call creates, changes, or bills anything. The Python harvester's check that boto3 knows the
//! `lambda-microvms` service has no counterpart: this client is Rust and speaks the service
//! model it was built against ([`crate::constants::MODEL_API_VERSION`]), so the listing is the
//! equivalent evidence that the endpoint accepts that API version. A harness that also calls
//! the service through boto3 checks its own boto3. Preflight does not check the execution or
//! build role, the S3 artifact bucket, image quotas, or VPC connectors: each needs a call this
//! module does not make, and `doctor` reports the infrastructure it can see.

use std::future::Future;

pub use microvms_domain::preflight::*;

use crate::control::ControlPlane;
use crate::error::Error;
use crate::region::Region;

/// The credentials line, from a control plane that may not have been built.
pub async fn credentials_check(plane: &Result<ControlPlane, Error>, region: &Region) -> Check {
    let failure = match plane {
        Ok(plane) => plane
            .resolve_credentials()
            .await
            .err()
            .map(|e| e.to_string()),
        Err(error) => Some(error.to_string()),
    };
    match failure {
        None => Check::pass(
            "credentials",
            format!("the default chain resolved credentials for {region}"),
        ),
        Some(detail) => Check::fail(
            "credentials",
            detail,
            "`aws sso login`, or set AWS_PROFILE / AWS_ACCESS_KEY_ID",
        ),
    }
}

/// The service line: one free, read-only listing page.
pub async fn service_check(plane: &ControlPlane) -> Check {
    let region = plane.region();
    match plane.answers_listing().await {
        Ok(bases) => Check::pass(
            "service",
            format!(
                "ListManagedMicrovmImages answered in {region} ({bases} managed base(s)); API \
                 version {}",
                crate::constants::MODEL_API_VERSION
            ),
        ),
        Err(error) => Check::fail(
            "service",
            format!("ListManagedMicrovmImages in {region} failed: {error}"),
            "an AccessDeniedException with no message is also what a region without MicroVMs \
             answers; otherwise allow lambda:ListManagedMicrovmImages",
        ),
    }
}

/// `preflight` (`microvms_core::preflight::preflight`) over a resolved region and a control-plane constructor, for a caller (and a
/// test) that supplies the transport. `connect` is not called when no region resolved.
pub async fn preflight_with<F, Fut>(resolved: Result<Region, Error>, connect: F) -> PreflightReport
where
    F: FnOnce(Region) -> Fut,
    Fut: Future<Output = Result<ControlPlane, Error>>,
{
    let region_line = region_check(&resolved);
    let Ok(region) = resolved else {
        return PreflightReport {
            region: None,
            checks: vec![
                region_line,
                Check::not_run("credentials", "no region resolved"),
                Check::not_run("service", "no region resolved"),
            ],
        };
    };
    let plane = connect(region.clone()).await;
    let credentials = credentials_check(&plane, &region).await;
    let service = match (&plane, credentials.ok) {
        (Ok(plane), true) => service_check(plane).await,
        _ => Check::not_run("service", "credentials did not resolve"),
    };
    PreflightReport {
        region: Some(region),
        checks: vec![region_line, credentials, service],
    }
}

#[cfg(test)]
mod tests {
    use std::sync::{Arc, Mutex};

    use super::*;
    use crate::control::fake::{Answer, FakeControlPlane, TestClock};
    use crate::control::transport::{Call, Reply, Transport};
    use crate::error::ErrorKind;

    /// A recorder whose credential chain fails.
    struct NoCredentials(Mutex<Vec<String>>);

    impl Transport for NoCredentials {
        fn send(
            &self,
            call: Call,
        ) -> std::pin::Pin<Box<dyn Future<Output = Result<Reply, Error>> + Send + '_>> {
            self.0
                .lock()
                .expect("not poisoned")
                .push(call.operation.into());
            Box::pin(async { panic!("no call may follow a failed credential chain") })
        }

        fn resolve_credentials(
            &self,
        ) -> std::pin::Pin<Box<dyn Future<Output = Result<(), Error>> + Send + '_>> {
            Box::pin(async { Err(Error::new(ErrorKind::Credentials, "nothing in the chain")) })
        }
    }

    fn plane(transport: Arc<dyn Transport>, region: Region) -> ControlPlane {
        crate::testing::control_plane(transport, region, Arc::new(TestClock::new()))
    }

    /// **BIND-15, the model's table**: `ok` is true exactly when no line blocks, and an
    /// unlisted region's advisory line does not.
    #[tokio::test]
    async fn the_outcome_is_the_models_table() {
        for (region, listing, ok) in [
            (Region::US_EAST_1, Answer::ok(r#"{"items": []}"#), true),
            (
                "us-west-1".parse().expect("a region name"),
                Answer::ok(r#"{"items": []}"#),
                true,
            ),
            (
                "us-west-1".parse().expect("a region name"),
                Answer::failure(403, "AccessDeniedException"),
                false,
            ),
        ] {
            let recorder = Arc::new(FakeControlPlane::new());
            recorder.answer("ListManagedMicrovmImages", listing);
            let transport = Arc::clone(&recorder) as Arc<dyn Transport>;
            let report = preflight_with(
                Ok(region.clone()),
                |r| async move { Ok(plane(transport, r)) },
            )
            .await;
            assert_eq!(report.ok(), ok, "{region}: {report:#?}");
            assert_eq!(
                recorder.operations(),
                ["ListManagedMicrovmImages"],
                "BIND-16: one free read"
            );
            assert_eq!(report.checks.len(), 3);
        }
    }

    #[tokio::test]
    async fn service_failure_names_the_iam_action_namespace() {
        let recorder = Arc::new(FakeControlPlane::new());
        recorder.answer(
            "ListManagedMicrovmImages",
            Answer::failure(403, "AccessDeniedException"),
        );
        let check = service_check(&plane(recorder, Region::US_EAST_1)).await;
        assert!(!check.ok && check.ran && check.fatal);
        assert!(
            check
                .remedy
                .contains("allow lambda:ListManagedMicrovmImages")
        );
        assert!(!check.remedy.contains("lambda-microvms:"));
    }

    /// **BIND-16: no call after the credentials fail**, and the service line says it did not
    /// run rather than passing or vanishing.
    ///
    /// **Falsification** — 2026-09-24. Run `service_check` whatever the credentials line says
    /// and `NoCredentials::send` panics; restored.
    #[tokio::test]
    async fn failed_credentials_stop_the_preflight_before_any_call() {
        let transport = Arc::new(NoCredentials(Mutex::new(Vec::new())));
        let shared = Arc::clone(&transport) as Arc<dyn Transport>;
        let report = preflight_with(
            Ok(Region::US_EAST_1),
            |r| async move { Ok(plane(shared, r)) },
        )
        .await;
        assert!(!report.ok());
        let credentials = report.check("credentials").expect("reported");
        assert!(!credentials.ok && credentials.ran && credentials.fatal);
        let service = report.check("service").expect("reported");
        assert!(!service.ok && !service.ran && service.fatal, "{service:?}");
        assert!(transport.0.lock().expect("not poisoned").is_empty());
    }

    /// **BIND-16: no region, no control plane, no call.**
    #[tokio::test]
    async fn an_unresolved_region_builds_no_control_plane() {
        let refused =
            Region::from_env(&|name| (name == "AWS_REGION").then(|| "EU-CENTRAL-1".into()));
        let report = preflight_with(refused, |_| async {
            panic!("no control plane may be built without a region")
        })
        .await;
        assert!(!report.ok());
        assert_eq!(report.region, None);
        assert!(
            report
                .check("region")
                .is_some_and(|check| check.fatal && !check.ok)
        );
        for name in ["credentials", "service"] {
            assert!(report.check(name).is_some_and(|check| !check.ran), "{name}");
        }
    }

    /// A control plane that could not be built is a failed credentials line, and the service
    /// is not asked.
    #[tokio::test]
    async fn a_control_plane_that_fails_to_build_is_a_credentials_failure() {
        let report = preflight_with(Ok(Region::US_EAST_1), |_| async {
            Err(Error::new(ErrorKind::Credentials, "no provider"))
        })
        .await;
        assert!(!report.ok());
        assert!(
            report
                .check("credentials")
                .is_some_and(|check| check.ran && !check.ok)
        );
        assert!(report.check("service").is_some_and(|check| !check.ran));
    }

    /// The region line's three cases, which `doctor` shares.
    #[test]
    fn the_region_line_is_pass_advisory_or_fatal() {
        assert!(region_check(&Ok(Region::US_EAST_1)).ok);
        let unlisted = region_check(&Ok("us-west-1".parse().expect("a region name")));
        assert!(!unlisted.ok && !unlisted.fatal);
        let refused = region_check(&Err(Error::invalid_arg("nope")));
        assert!(!refused.ok && refused.fatal);
    }
}