diff --git a/crates/runc-shim/src/runc.rs b/crates/runc-shim/src/runc.rs index 0488897e..095ed574 100644 --- a/crates/runc-shim/src/runc.rs +++ b/crates/runc-shim/src/runc.rs @@ -47,7 +47,7 @@ use containerd_shim::{ use log::{debug, error}; use nix::{sys::signal::kill, unistd::Pid}; use oci_spec::runtime::{LinuxResources, Process}; -use runc::{Command, Runc, Spawner}; +use runc::{Command, Runc, RuncGlobalArgs, Spawner}; use tokio::{ fs::{remove_file, File, OpenOptions}, io::{AsyncBufReadExt, AsyncRead, AsyncReadExt, AsyncWrite, BufReader}, @@ -455,7 +455,14 @@ impl ProcessLifecycle for RuncExecLifecycle { async fn start(&self, p: &mut ExecProcess) -> containerd_shim::Result<()> { let bundle = self.bundle.to_string(); let pid_path = Path::new(&bundle).join(format!("{}.pid", &p.id)); + let log_path = Path::new(&bundle).join(format!("{}-exec.log", &p.id)); + let custom_global_args = RuncGlobalArgs { + log: Some(log_path.clone()), + ..Default::default() + }; + let mut exec_opts = runc::options::ExecOpts { + custom_args: custom_global_args, io: None, pid_file: Some(pid_path.to_owned()), console_socket: None, @@ -475,6 +482,7 @@ impl ProcessLifecycle for RuncExecLifecycle { .runtime .exec(&self.container_id, &self.spec, Some(&exec_opts)) .await; + let _ = tokio::fs::remove_file(log_path).await; if let Err(e) = exec_result { if let Some(s) = socket { s.clean().await; diff --git a/crates/runc/src/asynchronous/runc.rs b/crates/runc/src/asynchronous/runc.rs index 79b1fcd5..34ecc159 100644 --- a/crates/runc/src/asynchronous/runc.rs +++ b/crates/runc/src/asynchronous/runc.rs @@ -26,7 +26,7 @@ use crate::{ events, options::*, utils::{self, write_value_to_temp_file}, - Command, Response, Result, Runc, + Command, Response, Result, Runc, RuncGlobalArgs, }; // a macro tool to cleanup the file with name $filename, @@ -138,11 +138,15 @@ impl Runc { pub async fn exec(&self, id: &str, spec: &Process, opts: Option<&ExecOpts>) -> Result<()> { let f = write_value_to_temp_file(spec).await?; let mut args = vec!["exec".to_string(), "--process".to_string(), f.clone()]; + let mut custom_global_args = RuncGlobalArgs { + ..Default::default() + }; if let Some(opts) = opts { args.append(&mut tc!(opts.args(), &f)); + custom_global_args = opts.custom_args.clone(); } args.push(id.to_string()); - let mut cmd = self.command(&args)?; + let mut cmd = self.command_with_global_args(&args, custom_global_args)?; match opts { Some(ExecOpts { io: Some(io), .. }) => { tc!( diff --git a/crates/runc/src/lib.rs b/crates/runc/src/lib.rs index b80fac5b..55ca1e2d 100644 --- a/crates/runc/src/lib.rs +++ b/crates/runc/src/lib.rs @@ -65,6 +65,13 @@ pub mod utils; const JSON: &str = "json"; const TEXT: &str = "text"; +const DEBUG: &str = "--debug"; +const LOG: &str = "--log"; +const LOG_FORMAT: &str = "--log-format"; +const ROOT: &str = "--root"; +const ROOTLESS: &str = "--rootless"; +const SYSTEMD_CGROUP: &str = "--systemd-cgroup"; + pub type Result = std::result::Result; /// Response is for (pid, exit status, outputs). @@ -107,13 +114,88 @@ pub type Command = tokio::process::Command; #[derive(Debug, Clone)] pub struct Runc { command: PathBuf, - args: Vec, + global_args: RuncGlobalArgs, spawner: Arc, } +#[derive(Debug, Clone, Default)] +pub struct RuncGlobalArgs { + pub debug: Option, + pub log: Option, + pub log_format: Option, + pub root: Option, + pub systemd_cgroup: Option, + pub rootless: Option, +} + impl Runc { fn command(&self, args: &[String]) -> Result { - let args = [&self.args, args].concat(); + let custom_global_args = RuncGlobalArgs { + debug: None, + log: None, + log_format: None, + root: None, + systemd_cgroup: None, + rootless: None, + }; + self.command_with_global_args(args, custom_global_args) + } + + fn command_with_global_args( + &self, + args: &[String], + custom_global_args: RuncGlobalArgs, + ) -> Result { + let mut global_args_vec: Vec = Vec::new(); + if let Some(custom_debug) = custom_global_args.debug { + global_args_vec.push(DEBUG.to_string()); + global_args_vec.push(custom_debug.to_string()); + } else if let Some(debug) = self.global_args.debug { + global_args_vec.push(DEBUG.to_string()); + global_args_vec.push(debug.to_string()); + } + + if let Some(custom_log) = custom_global_args.log { + global_args_vec.push(LOG.to_string()); + global_args_vec.push(custom_log.to_string_lossy().to_string()); + } else if let Some(log) = &self.global_args.log { + global_args_vec.push(LOG.to_string()); + global_args_vec.push(log.to_string_lossy().to_string()); + } + + if let Some(custom_log_format) = custom_global_args.log_format { + global_args_vec.push(LOG_FORMAT.to_string()); + global_args_vec.push(custom_log_format); + } else if let Some(log_format) = &self.global_args.log_format { + global_args_vec.push(LOG_FORMAT.to_string()); + global_args_vec.push(log_format.to_string()); + } + + if let Some(custom_root) = custom_global_args.root { + global_args_vec.push(ROOT.to_string()); + global_args_vec.push(custom_root.to_string_lossy().to_string()); + } else if let Some(root) = &self.global_args.root { + global_args_vec.push(ROOT.to_string()); + global_args_vec.push(root.to_string_lossy().to_string()); + } + + if let Some(systemd_cgroup) = custom_global_args.systemd_cgroup { + global_args_vec.push(SYSTEMD_CGROUP.to_string()); + global_args_vec.push(systemd_cgroup.to_string()); + } else if let Some(systemd_cgroup) = self.global_args.systemd_cgroup { + global_args_vec.push(SYSTEMD_CGROUP.to_string()); + global_args_vec.push(systemd_cgroup.to_string()); + } + + if let Some(custom_rootless) = custom_global_args.rootless { + global_args_vec.push(ROOTLESS.to_string()); + global_args_vec.push(custom_rootless.to_string()); + } else if let Some(rootless) = self.global_args.rootless { + global_args_vec.push(ROOTLESS.to_string()); + global_args_vec.push(rootless.to_string()); + } + + let args = [global_args_vec, args.to_vec()].concat(); let mut cmd = Command::new(&self.command); // Default to piped stdio, and they may be override by command options. diff --git a/crates/runc/src/options.rs b/crates/runc/src/options.rs index 3c3b06fc..44e5d959 100644 --- a/crates/runc/src/options.rs +++ b/crates/runc/src/options.rs @@ -39,20 +39,12 @@ use std::{ time::Duration, }; -use crate::{error::Error, utils, DefaultExecutor, Io, LogFormat, Runc, Spawner}; +use crate::{error::Error, utils, DefaultExecutor, Io, LogFormat, Runc, RuncGlobalArgs, Spawner}; // constants for log format pub const JSON: &str = "json"; pub const TEXT: &str = "text"; -// constants for runc global flags -const DEBUG: &str = "--debug"; -const LOG: &str = "--log"; -const LOG_FORMAT: &str = "--log-format"; -const ROOT: &str = "--root"; -const ROOTLESS: &str = "--rootless"; -const SYSTEMD_CGROUP: &str = "--systemd-cgroup"; - // constants for runc-create/runc-exec flags const CONSOLE_SOCKET: &str = "--console-socket"; const DETACH: &str = "--detach"; @@ -79,7 +71,7 @@ pub trait Args { /// /// These options will be passed for all subsequent runc calls. /// See -#[derive(Debug, Default)] +#[derive(Debug, Default, Clone)] pub struct GlobalOpts { /// Override the name of the runc binary. If [`None`], `runc` is used. command: Option, @@ -203,7 +195,7 @@ impl GlobalOpts { self.args() } - fn output(&self) -> Result<(PathBuf, Vec), Error> { + fn output(&self) -> Result<(PathBuf, RuncGlobalArgs), Error> { let path = self .command .clone() @@ -211,40 +203,41 @@ impl GlobalOpts { let command = utils::binary_path(path).ok_or(Error::NotFound)?; - let mut args = Vec::new(); - + let mut global_args = RuncGlobalArgs { + debug: None, + log: None, + log_format: None, + root: None, + systemd_cgroup: None, + rootless: None, + }; // --root path : Set the root directory to store containers' state. if let Some(root) = &self.root { - args.push(ROOT.into()); - args.push(utils::abs_string(root)?); + global_args.root = Some(root.to_path_buf()); } // --debug : Enable debug logging. if self.debug { - args.push(DEBUG.into()); + global_args.debug = Some(self.debug); } // --log path : Set the log destination to path. The default is to log to stderr. if let Some(log_path) = &self.log { - args.push(LOG.into()); - args.push(utils::abs_string(log_path)?); + global_args.log = Some(log_path.to_path_buf()); } // --log-format text|json : Set the log format (default is text). - args.push(LOG_FORMAT.into()); - args.push(self.log_format.to_string()); + global_args.log_format = Some(self.log_format.to_string()); - // --systemd-cgroup : Enable systemd cgroup support. if self.systemd_cgroup { - args.push(SYSTEMD_CGROUP.into()); + global_args.systemd_cgroup = Some(true); } // --rootless true|false|auto : Enable or disable rootless mode. if let Some(mode) = self.rootless { - let arg = format!("{}={}", ROOTLESS, mode); - args.push(arg); + global_args.rootless = Some(mode); } - Ok((command, args)) + Ok((command, global_args)) } } @@ -252,7 +245,7 @@ impl Args for GlobalOpts { type Output = Result; fn args(&self) -> Self::Output { - let (command, args) = self.output()?; + let (command, global_args) = self.output()?; let executor = if let Some(exec) = self.executor.clone() { exec } else { @@ -260,7 +253,7 @@ impl Args for GlobalOpts { }; Ok(Runc { command, - args, + global_args, spawner: executor, }) } @@ -352,6 +345,7 @@ impl CreateOpts { /// Container execution options #[derive(Clone, Default)] pub struct ExecOpts { + pub custom_args: RuncGlobalArgs, pub io: Option>, /// Path to where a pid file should be created. pub pid_file: Option, @@ -596,15 +590,11 @@ mod tests { fn global_opts_test() { let cfg = GlobalOpts::default().command("true"); let runc = cfg.build().unwrap(); - let args = &runc.args; - assert_eq!(args.len(), 2); - assert!(args.contains(&LOG_FORMAT.to_string())); - assert!(args.contains(&TEXT.to_string())); - - let cfg = GlobalOpts::default().command("/bin/true"); - let runc = cfg.build().unwrap(); - assert_eq!(runc.args.len(), 2); + let global_args = &runc.global_args; + if let Some(log_format) = &global_args.log_format { + assert!(TEXT == log_format); + } let cfg = GlobalOpts::default() .command("true") .root("/tmp") @@ -614,16 +604,25 @@ mod tests { .systemd_cgroup(true) .rootless(true); let runc = cfg.build().unwrap(); - let args = &runc.args; - assert!(args.contains(&ROOT.to_string())); - assert!(args.contains(&DEBUG.to_string())); - assert!(args.contains(&"/tmp".to_string())); - assert!(args.contains(&LOG.to_string())); - assert!(args.contains(&"/tmp/runc.log".to_string())); - assert!(args.contains(&LOG_FORMAT.to_string())); - assert!(args.contains(&JSON.to_string())); - assert!(args.contains(&"--rootless=true".to_string())); - assert!(args.contains(&SYSTEMD_CGROUP.to_string())); - assert_eq!(args.len(), 9); + let global_args = &runc.global_args; + + if let Some(root) = &global_args.root { + assert!(root.to_string_lossy() == "/tmp"); + } + if let Some(debug) = global_args.debug { + assert!(debug); + } + if let Some(log) = &global_args.log { + assert!(log.to_string_lossy() == "/tmp/runc.log"); + } + if let Some(log_format) = &global_args.log_format { + assert!(log_format == "json"); + } + if let Some(root_less) = global_args.rootless { + assert!(root_less); + } + if let Some(systemd_cgroup) = global_args.systemd_cgroup { + assert!(systemd_cgroup); + } } }