a3s-box-runtime 3.1.0

MicroVM runtime engine — VM lifecycle, OCI images, attestation, networking
Documentation
use a3s_box_core::{
    ExecutionGeneration, ExecutionId, ExecutionManagerError, ExecutionManagerResult,
    ExecutionState, KillExecutionOptions, ReconcileOutcome,
};

use super::record::{lease_from_record, reservation_from_record};
use super::{
    BoxRecord, LocalExecutionHandle, LocalExecutionObservation, ManagedExecutionOperation,
    ManagedExecutionState,
};

pub(super) fn managed_state(record: &BoxRecord) -> ExecutionManagerResult<ManagedExecutionState> {
    record
        .managed_state()
        .map_err(|error| ExecutionManagerError::Internal(error.to_string()))?
        .ok_or_else(|| {
            ExecutionManagerError::Internal(format!("execution {} is not managed", record.id))
        })
}

pub(super) fn generation(
    record: &BoxRecord,
    execution_id: &ExecutionId,
) -> ExecutionManagerResult<ExecutionGeneration> {
    record
        .managed_execution
        .as_ref()
        .map(|metadata| metadata.generation)
        .ok_or_else(|| {
            ExecutionManagerError::Internal(format!(
                "execution {execution_id} has no managed generation"
            ))
        })
}

pub(super) fn require_generation(
    record: &BoxRecord,
    execution_id: &ExecutionId,
    expected: ExecutionGeneration,
) -> ExecutionManagerResult<()> {
    let actual = generation(record, execution_id)?;
    if actual == expected {
        Ok(())
    } else {
        Err(ExecutionManagerError::Conflict {
            execution_id: execution_id.clone(),
            message: format!(
                "expected generation {}, found {}",
                expected.get(),
                actual.get()
            ),
        })
    }
}

pub(super) fn state_conflict(
    record: &BoxRecord,
    execution_id: &ExecutionId,
    operation: &str,
) -> ExecutionManagerError {
    let state = managed_state(record)
        .map(|state| state.to_string())
        .unwrap_or_else(|error| error.to_string());
    ExecutionManagerError::Conflict {
        execution_id: execution_id.clone(),
        message: format!("cannot {operation} execution in state {state}"),
    }
}

pub(super) fn required_handle(
    observation: &LocalExecutionObservation,
    execution_id: &ExecutionId,
) -> ExecutionManagerResult<LocalExecutionHandle> {
    observation.handle.clone().ok_or_else(|| {
        ExecutionManagerError::Internal(format!(
            "backend returned no runtime evidence for {execution_id}"
        ))
    })
}

pub(super) fn pending_pause_policy(
    record: &BoxRecord,
    execution_id: &ExecutionId,
) -> ExecutionManagerResult<bool> {
    match record
        .managed_execution
        .as_ref()
        .and_then(|metadata| metadata.pending_operation.as_ref())
    {
        Some(ManagedExecutionOperation::Pause { keep_memory }) => Ok(*keep_memory),
        _ => Err(ExecutionManagerError::Internal(format!(
            "pausing execution {execution_id} has no persisted pause policy"
        ))),
    }
}

pub(super) fn pending_kill_options(
    record: &BoxRecord,
    execution_id: &ExecutionId,
) -> ExecutionManagerResult<KillExecutionOptions> {
    match record
        .managed_execution
        .as_ref()
        .and_then(|metadata| metadata.pending_operation.as_ref())
    {
        Some(ManagedExecutionOperation::Kill {
            signal,
            timeout_secs,
        }) => Ok(KillExecutionOptions {
            signal: *signal,
            timeout_secs: *timeout_secs,
        }),
        _ => Err(ExecutionManagerError::Internal(format!(
            "killing execution {execution_id} has no persisted termination policy"
        ))),
    }
}

pub(super) fn paused_with_memory(
    record: &BoxRecord,
    execution_id: &ExecutionId,
) -> ExecutionManagerResult<bool> {
    record
        .managed_execution
        .as_ref()
        .map(|metadata| metadata.paused_with_memory)
        .ok_or_else(|| {
            ExecutionManagerError::Internal(format!(
                "execution {execution_id} has no persisted pause mode"
            ))
        })
}

pub(super) fn pending_restart_source_state(
    record: &BoxRecord,
    execution_id: &ExecutionId,
) -> ExecutionManagerResult<ManagedExecutionState> {
    match record
        .managed_execution
        .as_ref()
        .and_then(|metadata| metadata.pending_operation.as_ref())
    {
        Some(ManagedExecutionOperation::Restart { source_state, .. }) => Ok(*source_state),
        _ => Err(ExecutionManagerError::Internal(format!(
            "restarting execution {execution_id} has no persisted restart source state"
        ))),
    }
}

pub(super) fn outcome_from_record(
    record: BoxRecord,
    state: ExecutionState,
) -> ExecutionManagerResult<ReconcileOutcome> {
    match state {
        ExecutionState::Created => Ok(ReconcileOutcome::Created(reservation_from_record(&record)?)),
        ExecutionState::Creating => Ok(ReconcileOutcome::Creating),
        ExecutionState::Running | ExecutionState::Paused => {
            Ok(ReconcileOutcome::Ready(lease_from_record(&record)?))
        }
        ExecutionState::Stopped | ExecutionState::Failed => Ok(ReconcileOutcome::Failed),
    }
}