a3s-box-runtime 3.1.0

MicroVM runtime engine — VM lifecycle, OCI images, attestation, networking
Documentation
use std::path::Path;
use std::time::{Duration, Instant};

use a3s_runtime::contract::{RuntimeInspection, RuntimeUnitState};
use a3s_runtime::RuntimeClient;

use super::cases::ResourceShape;
use super::fixture::BoxRuntimeConformanceFixture;
use super::{require, Result};

const CPU_MILLIS: u64 = 100;
const CPU_PERIOD_US: u64 = 100_000;
const CPU_QUOTA_US: u64 = CPU_MILLIS * (CPU_PERIOD_US / 1_000);
const MEMORY_BYTES: u64 = 128 * 1024 * 1024;
const PIDS: u32 = 32;

pub(super) async fn run(
    fixture: &BoxRuntimeConformanceFixture,
    client: &dyn RuntimeClient,
) -> Result<()> {
    let service = fixture.cases.apply(
        "resources-service",
        a3s_runtime::contract::RuntimeUnitClass::Service,
        "printf 'r17-resources-ready\\n'; exec sleep 3600",
        ResourceShape {
            cpu_millis: CPU_MILLIS,
            memory_bytes: MEMORY_BYTES,
            pids: PIDS,
            execution_timeout_ms: None,
        },
        a3s_runtime::contract::RestartPolicy::Never,
    );
    let running = client.apply(&service).await?;
    require(
        running.state == RuntimeUnitState::Running,
        "resource fixture Service did not reach running",
    )?;
    let record = fixture.record_for(&service.spec).await?;
    let config = &record
        .managed_execution
        .as_ref()
        .ok_or_else(|| super::protocol("resource fixture lost managed metadata"))?
        .request
        .config;
    require(
        config.resource_limits.cpu_quota == Some(CPU_QUOTA_US as i64)
            && config.resource_limits.cpu_period == Some(CPU_PERIOD_US),
        "CPU limits changed before provider launch",
    )?;
    require(
        config.resource_limits.sandbox_memory_limit_bytes == Some(MEMORY_BYTES)
            && config.resource_limits.memory_swap == Some(MEMORY_BYTES as i64),
        "memory limits changed before provider launch",
    )?;
    require(
        config.resource_limits.pids_limit == Some(u64::from(PIDS)),
        "PID limit changed before provider launch",
    )?;

    let cgroup = Path::new("/sys/fs/cgroup/a3s-box").join(&record.id);
    require(cgroup.is_dir(), "Sandbox cgroup was not created")?;
    require(
        read_trimmed(&cgroup.join("cpu.max"))? == format!("{CPU_QUOTA_US} {CPU_PERIOD_US}"),
        "cpu.max does not match the Runtime CPU request",
    )?;
    require(
        read_trimmed(&cgroup.join("memory.max"))? == MEMORY_BYTES.to_string(),
        "memory.max does not match the exact Runtime byte limit",
    )?;
    require(
        read_trimmed(&cgroup.join("pids.max"))? == PIDS.to_string(),
        "pids.max does not match the Runtime PID request",
    )?;

    let visible = client
        .exec(&fixture.cases.exec(
            "resources-visible-config",
            &service.spec,
            vec![
                "/bin/sh".into(),
                "-c".into(),
                "cat /sys/fs/cgroup/cpu.max; cat /sys/fs/cgroup/memory.max; cat /sys/fs/cgroup/pids.max"
                    .into(),
            ],
            5_000,
        ))
        .await?;
    require(
        visible
            .stdout
            .contains(&format!("{CPU_QUOTA_US} {CPU_PERIOD_US}"))
            && visible.stdout.contains(&MEMORY_BYTES.to_string())
            && visible.stdout.contains(&PIDS.to_string()),
        "workload did not observe its configured cgroup limits",
    )?;

    let throttled_before = counter(&cgroup.join("cpu.stat"), "nr_throttled")?;
    let cpu = client
        .exec(&fixture.cases.exec(
            "resources-cpu-behavior",
            &service.spec,
            vec![
                "/bin/sh".into(),
                "-c".into(),
                "timeout 1 sh -c 'trap \"exit 124\" TERM; while :; do :; done'".into(),
            ],
            5_000,
        ))
        .await?;
    require(
        cpu.exit_code == 124,
        format!(
            "CPU saturation oracle exited with {}; stdout={:?}; stderr={:?}",
            cpu.exit_code, cpu.stdout, cpu.stderr
        ),
    )?;
    let throttled_after = counter(&cgroup.join("cpu.stat"), "nr_throttled")?;
    require(
        throttled_after > throttled_before,
        "CPU workload was never throttled by the configured quota",
    )?;

    let pid_max_before = counter(&cgroup.join("pids.events"), "max")?;
    let _ = client
        .exec(&fixture.cases.exec(
            "resources-pids-behavior",
            &service.spec,
            vec![
                "/bin/sh".into(),
                "-c".into(),
                "i=0; while [ \"$i\" -lt 96 ]; do sleep 1 & i=$((i + 1)); done; wait".into(),
            ],
            15_000,
        ))
        .await?;
    let pid_max_after = counter(&cgroup.join("pids.events"), "max")?;
    require(
        pid_max_after > pid_max_before,
        "PID-heavy workload did not hit the configured pids.max",
    )?;

    let oom_before = counter(&cgroup.join("memory.events"), "oom_kill")?;
    let memory = client
        .exec(&fixture.cases.exec(
            "resources-memory-behavior",
            &service.spec,
            vec![
                "/bin/sh".into(),
                "-c".into(),
                "awk 'BEGIN { s=\"0123456789abcdef\"; for (i=0; i<25; i++) s=s s; print length(s) }'"
                    .into(),
            ],
            15_000,
        ))
        .await?;
    let oom_after = counter(&cgroup.join("memory.events"), "oom_kill")?;
    require(
        oom_after > oom_before && memory.exit_code != 0,
        "memory-heavy workload was not killed by the exact memory limit",
    )?;
    let RuntimeInspection::Found { observation, .. } =
        client.inspect(&service.spec.unit_id).await?
    else {
        return Err(super::protocol(
            "resource probes lost the long-running Service",
        ));
    };
    require(
        observation.state == RuntimeUnitState::Running,
        "resource-limit probes killed the Sandbox Service",
    )?;

    let timeout = fixture
        .cases
        .task("resources-execution-timeout", "exec sleep 3600", 400);
    let started = Instant::now();
    let timed_out = client.apply(&timeout).await?;
    require(
        timed_out.state == RuntimeUnitState::Failed
            && timed_out
                .failure
                .as_ref()
                .is_some_and(|failure| failure.code == "execution_timeout" && !failure.retryable)
            && started.elapsed() < Duration::from_secs(5),
        "Task execution timeout was not behaviorally enforced",
    )?;
    fixture
        .remove_unit(client, &timeout.spec, "resources-execution-timeout")
        .await?;
    fixture
        .remove_unit(client, &service.spec, "resources-service")
        .await
}

fn read_trimmed(path: &Path) -> Result<String> {
    std::fs::read_to_string(path)
        .map(|value| value.trim().to_string())
        .map_err(|error| super::external(&format!("read {}", path.display()), error))
}

fn counter(path: &Path, key: &str) -> Result<u64> {
    let value = read_trimmed(path)?;
    value
        .lines()
        .find_map(|line| {
            let mut fields = line.split_whitespace();
            (fields.next() == Some(key))
                .then(|| fields.next()?.parse::<u64>().ok())
                .flatten()
        })
        .ok_or_else(|| {
            super::protocol(format!(
                "cgroup counter {key:?} is missing from {}",
                path.display()
            ))
        })
}