use std::sync::{Arc, Mutex};
use std::time::Duration;
use arcbox_vm_driver::NicSpec;
use arcbox_vm_driver::VmDriver;
use arcbox_vm_driver::net::{GuestNetwork, NetworkLease};
use async_trait::async_trait;
use super::actor::{Mailbox, WeakMailbox};
use super::effect::ReleaseScope;
use super::event::RestoreOrigin;
use super::tasks::{CaptureSpec, ComputerTasks, Drain, TaskFailure, TaskResult};
use crate::agent::{GuestAgent, GuestAgentFactory};
use crate::config::RuntimeConfig;
use crate::error::{ComputerError, Result};
use crate::lifecycle::runtime::ComputerRuntime;
use crate::sandbox::pool::SlotPool;
use crate::sandbox::record::ComputerProvisionOutcome;
use crate::sandbox::record::ComputerRecordStore;
use crate::sandbox::warm::WarmPublishTicket;
use crate::sandbox::{CheckpointInfo, ComputerId, NetworkAttachment};
use crate::snapshot::{SnapshotCatalog, SnapshotMeta};
use crate::snapshot_cow::CowManager;
mod boot;
mod restore;
mod teardown;
pub use boot::ActorSlot;
pub struct ComputerServices {
pub driver: Arc<dyn VmDriver>,
pub network: Arc<dyn GuestNetwork>,
pub agents: Arc<dyn GuestAgentFactory>,
pub config: Arc<RuntimeConfig>,
pub cow_manager: Arc<CowManager>,
pub records: Arc<ComputerRecordStore>,
pub snapshots: Arc<SnapshotCatalog>,
pub events: Arc<crate::sandbox::events::EventBus>,
pub pool: Arc<SlotPool>,
}
pub enum Launch {
Boot(Box<BootLaunch>),
Restore(Box<RestoreLaunch>),
Reinstated,
}
pub struct BootLaunch {
pub attachment: Option<NetworkAttachment>,
pub warm_publish: Option<WarmPublishTicket>,
}
pub struct RestoreLaunch {
pub snapshot_id: String,
pub snap_meta: SnapshotMeta,
pub lease: Option<NetworkLease>,
pub nic: Option<NicSpec>,
pub started: std::time::Instant,
}
pub struct ComputerFlows {
id: ComputerId,
computer: Arc<Mutex<ComputerRuntime>>,
services: Arc<ComputerServices>,
mailbox: WeakMailbox,
launch: Mutex<Launch>,
}
impl ComputerFlows {
pub fn new(
id: ComputerId,
computer: Arc<Mutex<ComputerRuntime>>,
services: Arc<ComputerServices>,
mailbox: &Mailbox,
launch: Launch,
) -> Self {
Self {
id,
computer,
services,
mailbox: mailbox.downgrade(),
launch: Mutex::new(launch),
}
}
fn agent(&self) -> Result<Arc<dyn GuestAgent>> {
let (handle, identity) = {
let computer = self.computer.lock().unwrap();
(computer.handle.clone(), computer.net_identity.clone())
};
let handle = handle.ok_or_else(|| {
ComputerError::Vsock(format!("computer {} has no running vm to reach", self.id))
})?;
self.services.agents.connect(handle, identity.as_ref())
}
fn mailbox(&self) -> Result<Mailbox> {
self.mailbox
.upgrade()
.ok_or_else(|| ComputerError::NotFound(self.id.clone()))
}
fn take_launch(&self) -> Launch {
std::mem::replace(&mut *self.launch.lock().unwrap(), Launch::Reinstated)
}
fn wrong_launch(&self, wanted: &str) -> TaskFailure {
TaskFailure::recoverable(ComputerError::Other(format!(
"computer {} was asked to {wanted} with no such launch",
self.id
)))
}
}
const fn warm_create(origin: RestoreOrigin) -> bool {
matches!(origin, RestoreOrigin::WarmCreate)
}
#[async_trait]
impl ComputerTasks for ComputerFlows {
async fn boot(
&self,
_warm: bool,
handed_off: tokio::sync::oneshot::Sender<()>,
) -> TaskResult<Arc<dyn GuestAgent>> {
self.boot_vm(handed_off).await
}
async fn gate(&self) -> TaskResult {
self.run_gate().await
}
async fn restore(
&self,
origin: RestoreOrigin,
) -> TaskResult<(Arc<dyn GuestAgent>, ComputerProvisionOutcome)> {
self.restore_vm(origin).await
}
async fn checkpoint(
&self,
hold: bool,
spec: Option<CaptureSpec>,
) -> TaskResult<CheckpointInfo> {
self.capture(hold, spec).await
}
async fn resume(&self) -> TaskResult<Arc<dyn GuestAgent>> {
self.resume_vm().await
}
async fn stop(&self, budget: Duration, drain: Drain) -> TaskResult {
self.stop_vm(budget, drain).await
}
async fn release(&self, scope: ReleaseScope) -> TaskResult {
self.release_scope(scope).await
}
async fn detach(&self) -> TaskResult {
self.detach_vm().await
}
fn adopted_agent(&self) -> Option<Arc<dyn GuestAgent>> {
self.agent().ok()
}
}