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