a3s_box_runtime/local_execution/
mod.rs1mod api;
4mod backend;
5mod create;
6mod lifecycle_lock;
7pub use lifecycle_lock::{
8 acquire_blocking as acquire_execution_lifecycle_lock, ExecutionLifecycleLock,
9};
10mod logs;
11mod operations;
12mod port;
13mod record;
14mod recovery;
15mod remove;
16mod resources;
17mod restart;
18#[cfg(unix)]
19mod session;
20#[cfg(not(unix))]
21mod session_unsupported;
22mod snapshot;
23mod store;
24mod support;
25#[cfg(feature = "vm")]
26mod vm_backend;
27#[cfg(feature = "vm")]
28mod vm_process;
29
30use std::path::PathBuf;
31use std::sync::Arc;
32
33use a3s_box_core::{
34 ExecutionGeneration, ExecutionId, ExecutionManagerError, ExecutionManagerResult,
35};
36
37pub use backend::{LocalExecutionBackend, LocalExecutionHandle, LocalExecutionObservation};
38use record::{build_managed_record, status_from_record};
39use store::RuntimeUpdate;
40#[cfg(feature = "vm")]
41pub use vm_backend::VmLocalExecutionBackend;
42
43use crate::{BoxRecord, ManagedExecutionOperation, ManagedExecutionState, ManagedExecutionStore};
44
45#[derive(Clone)]
47pub struct LocalExecutionManager {
48 store: ManagedExecutionStore,
49 home_dir: PathBuf,
50 backend: Arc<dyn LocalExecutionBackend>,
51}
52
53impl LocalExecutionManager {
54 pub fn new(
55 state_path: impl Into<PathBuf>,
56 home_dir: impl Into<PathBuf>,
57 backend: Arc<dyn LocalExecutionBackend>,
58 ) -> Self {
59 Self {
60 store: ManagedExecutionStore::new(state_path),
61 home_dir: home_dir.into(),
62 backend,
63 }
64 }
65
66 pub fn state_path(&self) -> &std::path::Path {
67 self.store.path()
68 }
69
70 pub(super) async fn require_running_record(
71 &self,
72 execution_id: &ExecutionId,
73 generation: ExecutionGeneration,
74 ) -> ExecutionManagerResult<BoxRecord> {
75 let record = self
76 .get(execution_id)
77 .await?
78 .ok_or_else(|| ExecutionManagerError::NotFound(execution_id.clone()))?;
79 support::require_generation(&record, execution_id, generation)?;
80 if support::managed_state(&record)? != ManagedExecutionState::Running {
81 return Err(ExecutionManagerError::Conflict {
82 execution_id: execution_id.clone(),
83 message: "execution is not running".to_string(),
84 });
85 }
86 if record.exec_socket_path.as_os_str().is_empty() {
87 return Err(ExecutionManagerError::Internal(format!(
88 "execution {execution_id} has no exec endpoint"
89 )));
90 }
91 #[cfg(target_os = "linux")]
92 {
93 let pid = record
94 .pid
95 .ok_or_else(|| ExecutionManagerError::NotFound(execution_id.clone()))?;
96 if !crate::process::is_process_alive_with_identity(pid, record.pid_start_time) {
97 return Err(ExecutionManagerError::NotFound(execution_id.clone()));
98 }
99 }
100 Ok(record)
101 }
102
103 #[cfg(feature = "vm")]
104 pub fn with_vm_backend(state_path: impl Into<PathBuf>, home_dir: impl Into<PathBuf>) -> Self {
105 let home_dir = home_dir.into();
106 Self::new(
107 state_path,
108 home_dir.clone(),
109 Arc::new(VmLocalExecutionBackend::new(home_dir)),
110 )
111 }
112}
113
114#[cfg(test)]
115mod tests;