Skip to main content

orchestrator_runner/runner/
profile.rs

1use orchestrator_config::config::{
2    ExecutionFsMode, ExecutionNetworkMode, ExecutionProfileConfig, ExecutionProfileMode,
3};
4use std::path::{Path, PathBuf};
5
6#[derive(Debug, Clone)]
7/// Execution profile after resolving workspace-relative paths and inherited defaults.
8pub struct ResolvedExecutionProfile {
9    /// Name of the execution profile that produced this resolved view.
10    pub name: String,
11    /// Whether commands run on the host or inside a sandbox backend.
12    pub mode: ExecutionProfileMode,
13    /// Filesystem policy enforced by the execution backend.
14    pub fs_mode: ExecutionFsMode,
15    /// Paths that remain writable when sandboxing is enabled.
16    pub writable_paths: Vec<PathBuf>,
17    /// Network policy enforced by the execution backend.
18    pub network_mode: ExecutionNetworkMode,
19    /// Raw allowlist entries used when `network_mode=allowlist`.
20    pub network_allowlist: Vec<String>,
21    /// Optional memory limit in MiB.
22    pub max_memory_mb: Option<u64>,
23    /// Optional CPU time limit in seconds.
24    pub max_cpu_seconds: Option<u64>,
25    /// Optional maximum process count.
26    pub max_processes: Option<u64>,
27    /// Optional file-descriptor limit.
28    pub max_open_files: Option<u64>,
29}
30
31impl ResolvedExecutionProfile {
32    /// Returns the built-in host execution profile with no sandbox limits.
33    pub fn host() -> Self {
34        Self {
35            name: "host".to_string(),
36            mode: ExecutionProfileMode::Host,
37            fs_mode: ExecutionFsMode::Inherit,
38            writable_paths: Vec::new(),
39            network_mode: ExecutionNetworkMode::Inherit,
40            network_allowlist: Vec::new(),
41            max_memory_mb: None,
42            max_cpu_seconds: None,
43            max_processes: None,
44            max_open_files: None,
45        }
46    }
47
48    /// Resolves a configured execution profile against the workspace root.
49    pub fn from_config(
50        name: &str,
51        config: &ExecutionProfileConfig,
52        workspace_root: &Path,
53        always_writable: &[PathBuf],
54    ) -> Self {
55        let mut writable_paths = always_writable.to_vec();
56        writable_paths.extend(config.writable_paths.iter().map(|path| {
57            let raw = PathBuf::from(path);
58            if raw.is_absolute() {
59                raw
60            } else {
61                workspace_root.join(raw)
62            }
63        }));
64        Self {
65            name: name.to_string(),
66            mode: config.mode.clone(),
67            fs_mode: config.fs_mode.clone(),
68            writable_paths,
69            network_mode: config.network_mode.clone(),
70            network_allowlist: config.network_allowlist.clone(),
71            max_memory_mb: config.max_memory_mb,
72            max_cpu_seconds: config.max_cpu_seconds,
73            max_processes: config.max_processes,
74            max_open_files: config.max_open_files,
75        }
76    }
77}
78
79#[derive(Debug, Clone)]
80pub(crate) struct UnixResourceLimits {
81    pub(crate) max_memory_bytes: Option<u64>,
82    pub(crate) max_cpu_seconds: Option<u64>,
83    pub(crate) max_processes: Option<u64>,
84    pub(crate) max_open_files: Option<u64>,
85}
86
87impl UnixResourceLimits {
88    pub(crate) fn from_execution_profile(
89        execution_profile: &ResolvedExecutionProfile,
90    ) -> Option<Self> {
91        let limits = Self {
92            max_memory_bytes: execution_profile
93                .max_memory_mb
94                .map(|value| value.saturating_mul(1024 * 1024)),
95            max_cpu_seconds: execution_profile.max_cpu_seconds,
96            max_processes: execution_profile.max_processes,
97            max_open_files: execution_profile.max_open_files,
98        };
99        if limits.max_memory_bytes.is_none()
100            && limits.max_cpu_seconds.is_none()
101            && limits.max_processes.is_none()
102            && limits.max_open_files.is_none()
103        {
104            None
105        } else {
106            Some(limits)
107        }
108    }
109}