Skip to main content

libcontainer/workload/
mod.rs

1use std::collections::HashMap;
2use std::env;
3
4use oci_spec::runtime::Spec;
5
6pub mod default;
7
8pub static EMPTY: Vec<String> = Vec::new();
9
10#[derive(Debug, thiserror::Error)]
11pub enum ExecutorError {
12    #[error("invalid argument")]
13    InvalidArg,
14    #[error("failed to execute workload")]
15    Execution(#[from] Box<dyn std::error::Error + Send + Sync>),
16    #[error("{0}")]
17    Other(String),
18    #[error("{0} executor can't handle spec")]
19    CantHandle(&'static str),
20}
21
22#[derive(Debug, thiserror::Error)]
23pub enum ExecutorValidationError {
24    #[error("{0} executor can't handle spec")]
25    CantHandle(&'static str),
26    #[error("{0}")]
27    ArgValidationError(String),
28}
29
30#[derive(Debug, thiserror::Error)]
31pub enum ExecutorSetEnvsError {
32    #[error("failed to set envs")]
33    SetEnvs(#[from] Box<dyn std::error::Error + Send + Sync>),
34    #[error("{0}")]
35    Other(String),
36}
37
38// Here is an explanation about the complexity below regarding to
39// CloneBoxExecutor and Executor traits. This is one of the places rust actually
40// makes our life harder. The usecase for the executor is to allow users of
41// `libcontainer` to pass in a closure like function where the actual execution
42// of the container workload can be defined by user. To maximize the flexibility
43// for the users, we use trait object to allow users to pass in a closure like
44// objects, so the function can container any number of variables through the
45// structure. This is similar to the Fn family of traits that rust std lib has.
46// However, our usecase has a little bit more complexity than the Fn family of
47// traits. We require the struct implementing this Executor traits to be
48// cloneable, so we can pass the struct across fork/clone process boundary with
49// memory safety. We can't make the Executor trait to require Clone trait
50// because doing so will make the Executor trait not object safe. Part of the
51// reason is that without the CloneBoxExecutor trait, the default clone
52// implementation for Box<dyn trait> will first unwrap the box. However, the
53// `dyn trait` inside the box doesn't have a size, which violates the object
54// safety requirement for a trait. To work around this, we implement our own
55// CloneBoxExecutor trait, which is object safe.
56//
57// Note to future maintainers: if you find a better way to do this or Rust
58// introduced some new magical feature to simplify this logic, please consider
59// to refactor this part.
60
61pub trait CloneBoxExecutor {
62    fn clone_box(&self) -> Box<dyn Executor>;
63}
64
65pub trait Executor: CloneBoxExecutor {
66    /// Executes the workload
67    fn exec(&self, spec: &Spec) -> Result<(), ExecutorError>;
68
69    /// Validate if the spec can be executed by the executor. This step runs
70    /// after the container init process is created, entered into the correct
71    /// namespace and cgroups, and pivot_root into the rootfs. But this step
72    /// runs before waiting for the container start signal.
73    fn validate(&self, spec: &Spec) -> Result<(), ExecutorValidationError>;
74
75    /// Set environment variables for the container process to be executed.
76    /// This step runs after the container init process is created, entered
77    /// into the correct namespace and cgroups, and pivot_root into the rootfs.
78    /// But this step runs before waiting for the container start signal.
79    /// The host's environment variables are not cleared yet at this point.
80    /// They should be cleared explicitly if needed.
81    fn setup_envs(&self, envs: HashMap<String, String>) -> Result<(), ExecutorSetEnvsError> {
82        // The default implementation resets the process env based on the OCI spec.
83        // First, clear all host's envs.
84        env::vars().for_each(|(key, _value)| unsafe { env::remove_var(key) });
85
86        // Next, set envs based on the spec
87        envs.iter()
88            .for_each(|(key, value)| unsafe { env::set_var(key, value) });
89
90        Ok(())
91    }
92}
93
94impl<T> CloneBoxExecutor for T
95where
96    T: 'static + Executor + Clone,
97{
98    fn clone_box(&self) -> Box<dyn Executor> {
99        Box::new(self.clone())
100    }
101}
102
103impl Clone for Box<dyn Executor> {
104    fn clone(&self) -> Self {
105        self.clone_box()
106    }
107}