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}