1
0
mirror of https://github.com/containers/youki synced 2024-09-18 09:51:58 +02:00

Add new setup_envs method for the Executor trait (#2820)

* Add set_envs to the Executor trait

Signed-off-by: Kotaro Inoue <k.musaino@gmail.com>

* Use set_envs in container_init_process

Signed-off-by: Kotaro Inoue <k.musaino@gmail.com>

* Fix linter issues

Signed-off-by: Kotaro Inoue <k.musaino@gmail.com>

* Add test of default set_envs implementation

Signed-off-by: Kotaro Inoue <k.musaino@gmail.com>

* Fix set_envs to return ExecuteSetEnvsError

Signed-off-by: Kotaro Inoue <k.musaino@gmail.com>

* Rename set_envs to setup_envs

Signed-off-by: Kotaro Inoue <k.musaino@gmail.com>

* Add detailed description for the setup_envs

Signed-off-by: Kotaro Inoue <k.musaino@gmail.com>

---------

Signed-off-by: Kotaro Inoue <k.musaino@gmail.com>
This commit is contained in:
Kotaro Inoue 2024-06-27 23:24:11 +09:00 committed by GitHub
parent 11ca60dc88
commit bdd5aabd0b
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 75 additions and 12 deletions

View File

@ -73,6 +73,8 @@ pub enum InitProcessError {
Workload(#[from] workload::ExecutorError),
#[error(transparent)]
WorkloadValidation(#[from] workload::ExecutorValidationError),
#[error(transparent)]
WorkloadSetEnvs(#[from] workload::ExecutorSetEnvsError),
#[error("invalid io priority class: {0}")]
IoPriorityClass(String),
#[error("call exec sched_setattr error: {0}")]
@ -558,18 +560,6 @@ pub fn container_init_process(
err
})?;
// add HOME into envs if not exists
if !envs.contains_key("HOME") {
if let Some(dir_home) = utils::get_user_home(proc.user().uid()) {
envs.insert("HOME".to_owned(), dir_home.to_string_lossy().to_string());
}
}
// Reset the process env based on oci spec.
env::vars().for_each(|(key, _value)| env::remove_var(key));
envs.iter()
.for_each(|(key, value)| env::set_var(key, value));
// Initialize seccomp profile right before we are ready to execute the
// payload so as few syscalls will happen between here and payload exec. The
// notify socket will still need network related syscalls.
@ -591,7 +581,15 @@ pub fn container_init_process(
tracing::warn!("seccomp not available, unable to set seccomp privileges!")
}
// add HOME into envs if not exists
if !envs.contains_key("HOME") {
if let Some(dir_home) = utils::get_user_home(proc.user().uid()) {
envs.insert("HOME".to_owned(), dir_home.to_string_lossy().to_string());
}
}
args.executor.validate(spec)?;
args.executor.setup_envs(envs)?;
// Notify main process that the init process is ready to execute the
// payload. Note, because we are already inside the pid namespace, the pid

View File

@ -138,6 +138,11 @@ fn is_executable(path: &Path) -> std::result::Result<bool, std::io::Error> {
#[cfg(test)]
mod tests {
use std::collections::HashMap;
use std::env;
use serial_test::serial;
use super::*;
#[test]
@ -177,4 +182,35 @@ mod tests {
assert!(!is_executable(&non_executable_path).unwrap());
assert!(!is_executable(directory_path).unwrap());
}
#[test]
#[serial]
fn test_executor_set_envs() {
// Store original environment variables to restore later
let original_envs: HashMap<String, String> = env::vars().collect();
// Test setting environment variables
{
let executor = get_executor();
let envs = HashMap::from([
("FOO".to_owned(), "hoge".to_owned()),
("BAR".to_owned(), "fuga".to_owned()),
("BAZ".to_owned(), "piyo".to_owned()),
]);
assert!(executor.setup_envs(envs).is_ok());
// Check if the environment variables are set correctly
let current_envs = std::env::vars().collect::<HashMap<String, String>>();
assert_eq!(current_envs.get("FOO").unwrap(), "hoge");
assert_eq!(current_envs.get("BAR").unwrap(), "fuga");
assert_eq!(current_envs.get("BAZ").unwrap(), "piyo");
// No other environment variables should be set
assert_eq!(current_envs.len(), 3);
}
// Restore original environment variables
original_envs.iter().for_each(|(key, value)| {
env::set_var(key, value);
});
}
}

View File

@ -1,3 +1,6 @@
use std::collections::HashMap;
use std::env;
use oci_spec::runtime::Spec;
pub mod default;
@ -24,6 +27,14 @@ pub enum ExecutorValidationError {
ArgValidationError(String),
}
#[derive(Debug, thiserror::Error)]
pub enum ExecutorSetEnvsError {
#[error("failed to set envs")]
SetEnvs(#[from] Box<dyn std::error::Error + Send + Sync>),
#[error("{0}")]
Other(String),
}
// Here is an explanation about the complexity below regarding to
// CloneBoxExecutor and Executor traits. This is one of the places rust actually
// makes our life harder. The usecase for the executor is to allow users of
@ -60,6 +71,24 @@ pub trait Executor: CloneBoxExecutor {
/// namespace and cgroups, and pivot_root into the rootfs. But this step
/// runs before waiting for the container start signal.
fn validate(&self, spec: &Spec) -> Result<(), ExecutorValidationError>;
/// Set environment variables for the container process to be executed.
/// This step runs after the container init process is created, entered
/// into the correct namespace and cgroups, and pivot_root into the rootfs.
/// But this step runs before waiting for the container start signal.
/// The host's environment variables are not cleared yet at this point.
/// They should be cleared explicitly if needed.
fn setup_envs(&self, envs: HashMap<String, String>) -> Result<(), ExecutorSetEnvsError> {
// The default implementation resets the process env based on the OCI spec.
// First, clear all host's envs.
env::vars().for_each(|(key, _value)| env::remove_var(key));
// Next, set envs based on the spec
envs.iter()
.for_each(|(key, value)| env::set_var(key, value));
Ok(())
}
}
impl<T> CloneBoxExecutor for T