microsandbox_core/
error.rs

1use microsandbox_utils::MicrosandboxUtilsError;
2use sqlx::migrate::MigrateError;
3use std::{
4    error::Error,
5    fmt::{self, Display},
6    path::{PathBuf, StripPrefixError},
7    time::SystemTimeError,
8};
9use thiserror::Error;
10
11use crate::oci::DockerRegistryResponseError;
12
13//--------------------------------------------------------------------------------------------------
14// Types
15//--------------------------------------------------------------------------------------------------
16
17/// The result of a microsandbox-related operation.
18pub type MicrosandboxResult<T> = Result<T, MicrosandboxError>;
19
20/// An error that occurred during a file system operation.
21#[derive(pretty_error_debug::Debug, Error)]
22pub enum MicrosandboxError {
23    /// An I/O error.
24    #[error("io error: {0}")]
25    Io(#[from] std::io::Error),
26
27    /// An error that can represent any error.
28    #[error(transparent)]
29    Custom(#[from] AnyError),
30
31    /// An error that occurred during an OCI distribution operation.
32    #[error("oci distribution error: {0}")]
33    OciDistribution(#[from] anyhow::Error),
34
35    /// An error that occurred during an HTTP request.
36    #[error("http request error: {0}")]
37    HttpRequest(#[from] reqwest::Error),
38
39    /// An error that occurred during an HTTP middleware operation.
40    #[error("http middleware error: {0}")]
41    HttpMiddleware(#[from] reqwest_middleware::Error),
42
43    /// An error that occurred during a database operation.
44    #[error("database error: {0}")]
45    Database(#[from] sqlx::Error),
46
47    /// An error that occurred when a manifest was not found.
48    #[error("manifest not found")]
49    ManifestNotFound,
50
51    /// An error that occurred when a join handle returned an error.
52    #[error("join error: {0}")]
53    JoinError(#[from] tokio::task::JoinError),
54
55    /// An error that occurred when an unsupported image hash algorithm was used.
56    #[error("unsupported image hash algorithm: {0}")]
57    UnsupportedImageHashAlgorithm(String),
58
59    /// An error that occurred when an image layer download failed.
60    #[error("image layer download failed: {0}")]
61    ImageLayerDownloadFailed(String),
62
63    /// An error that occurred when an invalid path pair was used.
64    #[error("invalid path pair: {0}")]
65    InvalidPathPair(String),
66
67    /// An error that occurred when an invalid port pair was used.
68    #[error("invalid port pair: {0}")]
69    InvalidPortPair(String),
70
71    /// An error that occurred when an invalid environment variable pair was used.
72    #[error("invalid environment variable pair: {0}")]
73    InvalidEnvPair(String),
74
75    /// An error that occurred when an invalid MicroVm configuration was used.
76    #[error("invalid MicroVm configuration: {0}")]
77    InvalidMicroVMConfig(InvalidMicroVMConfigError),
78
79    /// An error that occurred when an invalid resource limit format was used.
80    #[error("invalid resource limit format: {0}")]
81    InvalidRLimitFormat(String),
82
83    /// An error that occurred when an invalid resource limit value was used.
84    #[error("invalid resource limit value: {0}")]
85    InvalidRLimitValue(String),
86
87    /// An error that occurred when an invalid resource limit resource was used.
88    #[error("invalid resource limit resource: {0}")]
89    InvalidRLimitResource(String),
90
91    /// An error that occurred when a Serde JSON error occurred.
92    #[error("serde json error: {0}")]
93    SerdeJson(#[from] serde_json::Error),
94
95    /// An error that occurred when a Serde YAML error occurred.
96    #[error("serde yaml error: {0}")]
97    SerdeYaml(#[from] serde_yaml::Error),
98
99    /// An error that occurred when a TOML error occurred.
100    #[error("toml error: {0}")]
101    Toml(#[from] toml::de::Error),
102
103    /// An error that occurred when a configuration validation error occurred.
104    #[error("configuration validation error: {0}")]
105    ConfigValidation(String),
106
107    /// An error that occurred when a configuration validation error occurred.
108    #[error("configuration validation errors: {0:?}")]
109    ConfigValidationErrors(Vec<String>),
110
111    /// An error that occurs when trying to access group resources for a service that has no group
112    #[error("service '{0}' belongs to no group")]
113    ServiceBelongsToNoGroup(String),
114
115    /// An error that occurs when trying to access group resources for a service that belongs to a
116    /// different group.
117    #[error("service '{0}' belongs to wrong group: '{1}'")]
118    ServiceBelongsToWrongGroup(String, String),
119
120    /// An error that occurred when failed to get shutdown eventfd
121    #[error("failed to get shutdown eventfd: {0}")]
122    FailedToGetShutdownEventFd(i32),
123
124    /// An error that occurred when failed to write to shutdown eventfd
125    #[error("failed to write to shutdown eventfd: {0}")]
126    FailedToShutdown(String),
127
128    /// An error that occurred when failed to start VM
129    #[error("failed to start VM: {0}")]
130    FailedToStartVM(i32),
131
132    /// An error that occurred when a path does not exist
133    #[error("path does not exist: {0}")]
134    PathNotFound(String),
135
136    /// An error that occurred when a rootfs path does not exist
137    #[error("rootfs path does not exist: {0}")]
138    RootFsPathNotFound(String),
139
140    /// An error that occurred when the supervisor binary was not found
141    #[error("supervisor binary not found: {0}")]
142    SupervisorBinaryNotFound(String),
143
144    /// An error that occurred when failed to start VM
145    #[error("failed to start VM: {0}")]
146    StartVmFailed(i32),
147
148    /// An error that occurred when waiting for a process to exit
149    #[error("process wait error: {0}")]
150    ProcessWaitError(String),
151
152    /// An error that occurred running the supervisor.
153    #[error("supervisor error: {0}")]
154    SupervisorError(String),
155
156    /// An error that occurred when failed to kill process
157    #[error("failed to kill process: {0}")]
158    ProcessKillError(String),
159
160    /// An error that occurred when merging configurations
161    #[error("configuration merge error: {0}")]
162    ConfigMerge(String),
163
164    /// An error that occurred when no more IP addresses are available for assignment
165    #[error("no available IP addresses in the pool")]
166    NoAvailableIPs,
167
168    /// An error that occurred during a walkdir operation
169    #[error("walkdir error: {0}")]
170    WalkDir(#[from] walkdir::Error),
171
172    /// An error that occurred when stripping a path prefix
173    #[error("strip prefix error: {0}")]
174    StripPrefix(#[from] StripPrefixError),
175
176    /// An error that occurred during a nix operation
177    #[error("nix error: {0}")]
178    NixError(#[from] nix::Error),
179
180    /// An error that occurred when converting system time
181    #[error("system time error: {0}")]
182    SystemTime(#[from] SystemTimeError),
183
184    /// An error that occurred during layer extraction.
185    /// This typically happens when the join handle for the blocking task fails.
186    #[error("layer extraction error: {0}")]
187    LayerExtraction(String),
188
189    /// An error that occurred during layer handling operations like opening files or unpacking archives.
190    /// Contains both the underlying IO error and the path to the layer being processed.
191    #[error("layer handling error: {source}")]
192    LayerHandling {
193        /// The underlying IO error that occurred
194        source: std::io::Error,
195        /// The path to the layer being processed when the error occurred
196        layer: String,
197    },
198
199    /// An error that occurred when a configuration file was not found
200    #[error("configuration file not found: {0}")]
201    ConfigNotFound(String),
202
203    /// Error when a service's rootfs directory is not found
204    #[error("Service rootfs not found: {0}")]
205    RootfsNotFound(String),
206
207    /// An error that occurred when parsing an image reference
208    #[error("invalid image reference: {0}")]
209    ImageReferenceError(String),
210
211    /// An error that occurred when trying to remove running services
212    #[error("Cannot remove running services: {0}")]
213    ServiceStillRunning(String),
214
215    /// An error that occurred when invalid command line arguments were provided
216    #[error("{0}")]
217    InvalidArgument(String),
218
219    /// An error that occurred when validating paths
220    #[error("path validation error: {0}")]
221    PathValidation(String),
222
223    /// An error that occurred when the microsandbox config file was not found
224    #[error("microsandbox config file not found at: {0}")]
225    MicrosandboxConfigNotFound(String),
226
227    /// An error that occurred when failed to parse configuration file
228    #[error("failed to parse configuration file: {0}")]
229    ConfigParseError(String),
230
231    /// An error that occurred when a log file was not found
232    #[error("log not found: {0}")]
233    LogNotFound(String),
234
235    /// An error that occurred when a pager error occurred
236    #[error("pager error: {0}")]
237    PagerError(String),
238
239    /// An error from microsandbox-utils
240    #[error("microsandbox-utils error: {0}")]
241    MicrosandboxUtilsError(#[from] MicrosandboxUtilsError),
242
243    /// An error that occurred when a migration error occurred
244    #[error("migration error: {0}")]
245    MigrationError(#[from] MigrateError),
246
247    /// An error that occurred when a Docker registry response error occurred
248    #[error("docker registry response error: {0}")]
249    DockerRegistryResponseError(#[from] DockerRegistryResponseError),
250
251    /// An error that occurred when parsing an image reference selector with an invalid format
252    #[error("invalid image reference    selector format: {0}")]
253    InvalidReferenceSelectorFormat(String),
254
255    /// An error that occurred when parsing an invalid digest in an image reference selector
256    #[error("invalid image reference selector digest: {0}")]
257    InvalidReferenceSelectorDigest(String),
258
259    /// An error that occurred when a feature is not yet implemented
260    #[error("feature not yet implemented: {0}")]
261    NotImplemented(String),
262
263    /// An error that occurred when a sandbox was not found in the configuration
264    #[error("cannot find sandbox: '{0}' in '{1}'")]
265    SandboxNotFoundInConfig(String, PathBuf),
266
267    /// An error that occurs when an invalid log level is used.
268    #[error("invalid log level: {0}")]
269    InvalidLogLevel(u8),
270
271    /// Empty path segment
272    #[error("empty path segment")]
273    EmptyPathSegment,
274
275    /// Invalid path component (e.g. ".", "..", "/")
276    #[error("invalid path component: {0}")]
277    InvalidPathComponent(String),
278
279    /// Script not found in sandbox configuration
280    #[error("script '{0}' not found in sandbox configuration '{1}'")]
281    ScriptNotFoundInSandbox(String, String),
282
283    /// An error that occurred running the sandbox server.
284    #[error("sandbox server error: {0}")]
285    SandboxServerError(String),
286
287    /// An error that occurred when an invalid network scope was used.
288    #[error("invalid network scope: {0}")]
289    InvalidNetworkScope(String),
290
291    /// An error that occurred when a start script or exec command or shell is missing.
292    #[error("missing start script or exec command or shell")]
293    MissingStartOrExecOrShell,
294
295    /// An error that occurred when trying to install a script with the same name as an existing command.
296    #[error("command already exists: {0}")]
297    CommandExists(String),
298
299    /// An error that occurred when a command was not found.
300    #[error("command not found: {0}")]
301    CommandNotFound(String),
302}
303
304/// An error that occurred when an invalid MicroVm configuration was used.
305#[derive(Debug, Error)]
306pub enum InvalidMicroVMConfigError {
307    /// The root path does not exist.
308    #[error("root path does not exist: {0}")]
309    RootPathDoesNotExist(String),
310
311    /// A host path that should be mounted does not exist.
312    #[error("host path does not exist: {0}")]
313    HostPathDoesNotExist(String),
314
315    /// The number of vCPUs is zero.
316    #[error("number of vCPUs is zero")]
317    NumVCPUsIsZero,
318
319    /// The amount of memory is zero.
320    #[error("amount of memory is zero")]
321    MemoryIsZero,
322
323    /// The command line contains invalid characters. Only printable ASCII characters (space through tilde) are allowed.
324    #[error("command line contains invalid characters (only ASCII characters between space and tilde are allowed): {0}")]
325    InvalidCommandLineString(String),
326
327    /// An error that occurs when conflicting guest paths are detected.
328    #[error("Conflicting guest paths: '{0}' and '{1}' overlap")]
329    ConflictingGuestPaths(String, String),
330}
331
332/// An error that can represent any error.
333#[derive(Debug)]
334pub struct AnyError {
335    error: anyhow::Error,
336}
337
338//--------------------------------------------------------------------------------------------------
339// Methods
340//--------------------------------------------------------------------------------------------------
341
342impl MicrosandboxError {
343    /// Creates a new `Err` result.
344    pub fn custom(error: impl Into<anyhow::Error>) -> MicrosandboxError {
345        MicrosandboxError::Custom(AnyError {
346            error: error.into(),
347        })
348    }
349}
350
351impl AnyError {
352    /// Downcasts the error to a `T`.
353    pub fn downcast<T>(&self) -> Option<&T>
354    where
355        T: Display + fmt::Debug + Send + Sync + 'static,
356    {
357        self.error.downcast_ref::<T>()
358    }
359}
360
361//--------------------------------------------------------------------------------------------------
362// Functions
363//--------------------------------------------------------------------------------------------------
364
365/// Creates an `Ok` `MicrosandboxResult`.
366#[allow(non_snake_case)]
367pub fn Ok<T>(value: T) -> MicrosandboxResult<T> {
368    Result::Ok(value)
369}
370
371//--------------------------------------------------------------------------------------------------
372// Trait Implementations
373//--------------------------------------------------------------------------------------------------
374
375impl PartialEq for AnyError {
376    fn eq(&self, other: &Self) -> bool {
377        self.error.to_string() == other.error.to_string()
378    }
379}
380
381impl Display for AnyError {
382    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
383        write!(f, "{}", self.error)
384    }
385}
386
387impl Error for AnyError {}