-
Notifications
You must be signed in to change notification settings - Fork 3
Support -t (terminal) option. #17
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
78203e9
84556ba
e0834ed
690f77a
34f6255
96db5eb
015a89f
fd7c83e
79dd904
c807fd3
1755410
95f2418
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Large diffs are not rendered by default.
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -13,7 +13,7 @@ use clap::{ArgAction, Parser}; | |
| override_usage = "conmon [OPTIONS] -c <CID> --runtime <PATH>", | ||
| disable_version_flag = true | ||
| )] | ||
| #[derive(Default)] | ||
| #[derive(Default, Debug)] | ||
| pub struct Opts { | ||
| /// Conmon API version to use | ||
| #[arg(long = "api-version", value_parser = clap::value_parser!(i32))] | ||
|
|
@@ -56,7 +56,7 @@ pub struct Opts { | |
| pub exit_command: Option<PathBuf>, | ||
|
|
||
| /// Additional arg to pass to the exit command. Can be specified multiple times | ||
| #[arg(long = "exit-command-arg")] | ||
| #[arg(long = "exit-command-arg", allow_hyphen_values = true)] | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. |
||
| pub exit_args: Vec<String>, | ||
|
|
||
| /// Delay before invoking the exit command (in seconds) | ||
|
|
@@ -132,15 +132,15 @@ pub struct Opts { | |
| pub restore: Option<PathBuf>, | ||
|
|
||
| /// Additional arg to pass to the restore command. (DEPRECATED) | ||
| #[arg(long = "restore-arg", hide = true)] | ||
| #[arg(long = "restore-arg", hide = true, allow_hyphen_values = true)] | ||
| pub restore_args: Vec<String>, | ||
|
|
||
| /// Path to store runtime data for the container | ||
| #[arg(long = "runtime", short = 'r')] | ||
| pub runtime: Option<PathBuf>, | ||
|
|
||
| /// Additional arg to pass to the runtime. Can be specified multiple times | ||
| #[arg(long = "runtime-arg")] | ||
| #[arg(long = "runtime-arg", allow_hyphen_values = true)] | ||
| pub runtime_args: Vec<String>, | ||
|
|
||
| /// Additional opts to pass to the restore or exec command. Can be specified multiple times | ||
|
|
@@ -226,32 +226,34 @@ pub struct CommonCfg { | |
| pub runtime_opts: Vec<String>, | ||
| pub no_pivot: bool, | ||
| pub no_new_keyring: bool, | ||
| pub conmon_pidfile: Option<PathBuf>, | ||
| pub container_pidfile: PathBuf, | ||
| pub bundle: PathBuf, | ||
| pub full_attach: bool, | ||
| pub socket_dir_path: Option<PathBuf>, | ||
| pub stdin: bool, | ||
| pub leave_stdin_open: bool, | ||
| pub terminal: bool, | ||
|
Comment on lines
+229
to
+236
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Moving |
||
| } | ||
|
|
||
| #[derive(Debug, Default)] | ||
| pub struct CreateCfg { | ||
| pub common: CommonCfg, | ||
| pub bundle: PathBuf, | ||
| pub container_pidfile: PathBuf, | ||
| pub systemd_cgroup: bool, | ||
| pub conmon_pidfile: Option<PathBuf>, | ||
| } | ||
|
|
||
| #[derive(Debug, Default)] | ||
| pub struct ExecCfg { | ||
| pub common: CommonCfg, | ||
| pub exec_process_spec: PathBuf, | ||
| pub attach: bool, | ||
| pub container_pidfile: PathBuf, | ||
| } | ||
|
|
||
| #[derive(Debug, Default)] | ||
| pub struct RestoreCfg { | ||
| pub common: CommonCfg, | ||
| pub restore_path: PathBuf, | ||
| pub systemd_cgroup: bool, | ||
| pub container_pidfile: PathBuf, | ||
| pub bundle: PathBuf, | ||
| } | ||
|
|
||
| /// Try to detect "executable" bit. | ||
|
|
@@ -316,6 +318,18 @@ pub fn determine_cmd(mut opts: Opts) -> ConmonResult<Cmd> { | |
| )); | ||
| } | ||
|
|
||
| let cwd = std::env::current_dir() | ||
| .map_err(|e| ConmonError::new(format!("Failed to get working directory: {e}"), 1))?; | ||
|
|
||
| // container-pidfile defaults to "$cwd/pidfile-$cid" if none provided | ||
| let container_pidfile = opts | ||
| .container_pidfile | ||
| .take() | ||
| .unwrap_or_else(|| cwd.join(format!("pidfile-{}", cid))); | ||
|
|
||
| // bundle defaults to "$cwd" if none provided | ||
| let bundle = opts.bundle.take().unwrap_or_else(|| cwd.clone()); | ||
|
|
||
| let common = CommonCfg { | ||
| api_version, | ||
| cid, | ||
|
|
@@ -325,28 +339,22 @@ pub fn determine_cmd(mut opts: Opts) -> ConmonResult<Cmd> { | |
| runtime_opts: opts.runtime_opts, | ||
| no_pivot: opts.no_pivot, | ||
| no_new_keyring: opts.no_new_keyring, | ||
| conmon_pidfile: opts.conmon_pidfile, | ||
| container_pidfile, | ||
| bundle, | ||
| full_attach: opts.full_attach, | ||
| socket_dir_path: opts.socket_dir_path, | ||
| stdin: opts.stdin, | ||
| leave_stdin_open: opts.leave_stdin_open, | ||
| terminal: opts.terminal, | ||
| }; | ||
|
|
||
| let cwd = std::env::current_dir() | ||
| .map_err(|e| ConmonError::new(format!("Failed to get working directory: {e}"), 1))?; | ||
|
|
||
| // bundle defaults to "$cwd" if none provided | ||
| let bundle = opts.bundle.take().unwrap_or_else(|| cwd.clone()); | ||
|
|
||
| // container-pidfile defaults to "$cwd/pidfile-$cid" if none provided | ||
| let container_pidfile = opts | ||
| .container_pidfile | ||
| .take() | ||
| .unwrap_or_else(|| cwd.join(format!("pidfile-{}", common.cid))); | ||
|
|
||
| // decide which subcommand this flag combination means | ||
| if let Some(restore_path) = opts.restore.take() { | ||
| Ok(Cmd::Restore(RestoreCfg { | ||
| common, | ||
| restore_path, | ||
| systemd_cgroup: opts.systemd_cgroup, | ||
| container_pidfile, | ||
| bundle, | ||
| })) | ||
| } else if opts.exec { | ||
| let exec_process_spec = opts.exec_process_spec.take().ok_or_else(|| { | ||
|
|
@@ -359,15 +367,11 @@ pub fn determine_cmd(mut opts: Opts) -> ConmonResult<Cmd> { | |
| common, | ||
| exec_process_spec, | ||
| attach: opts.attach, | ||
| container_pidfile, | ||
| })) | ||
| } else { | ||
| Ok(Cmd::Create(CreateCfg { | ||
| common, | ||
| bundle, | ||
| container_pidfile, | ||
| systemd_cgroup: opts.systemd_cgroup, | ||
| conmon_pidfile: opts.conmon_pidfile, | ||
| })) | ||
| } | ||
| } | ||
|
|
@@ -616,9 +620,9 @@ mod tests { | |
| match cmd { | ||
| Cmd::Create(cfg) => { | ||
| // bundle defaults to cwd | ||
| assert_eq!(cfg.bundle, cwd); | ||
| assert_eq!(cfg.common.bundle, cwd); | ||
| // container-pidfile defaults to "$cwd/pidfile-$cid" | ||
| assert_eq!(cfg.container_pidfile, cwd.join("pidfile-abc")); | ||
| assert_eq!(cfg.common.container_pidfile, cwd.join("pidfile-abc")); | ||
| } | ||
| _ => panic!("expected Run"), | ||
| } | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,12 +1,9 @@ | ||
| use std::process::ExitCode; | ||
|
|
||
| use crate::cli::CreateCfg; | ||
| use crate::error::{ConmonError, ConmonResult}; | ||
| use crate::error::ConmonResult; | ||
| use crate::logging::plugin::LogPlugin; | ||
| use crate::parent_pipe::{get_pipe_fd_from_env, write_or_close_sync_fd}; | ||
| use crate::runtime::args::{RuntimeArgsGenerator, generate_runtime_args}; | ||
| use crate::runtime::run::{run_runtime, wait_for_runtime}; | ||
| use crate::runtime::stdio::{create_pipe, handle_stdio, read_pipe}; | ||
| use std::fs; | ||
| use std::process::Stdio; | ||
| use crate::runtime::args::RuntimeArgsGenerator; | ||
|
|
||
| pub struct Create { | ||
| cfg: CreateCfg, | ||
|
|
@@ -17,81 +14,36 @@ impl Create { | |
| Self { cfg } | ||
| } | ||
|
|
||
| // Helper function to read and return the container pid. | ||
| fn read_container_pid(&self) -> ConmonResult<i32> { | ||
| let contents = fs::read_to_string(self.cfg.container_pidfile.as_path())?; | ||
| let pid = contents.trim().parse::<i32>().map_err(|e| { | ||
| ConmonError::new( | ||
| format!( | ||
| "Invalid PID contents in {}: {} ({})", | ||
| self.cfg.container_pidfile.display(), | ||
| contents.trim(), | ||
| e | ||
| ), | ||
| 1, | ||
| ) | ||
| })?; | ||
| Ok(pid) | ||
| } | ||
|
|
||
| pub fn exec(&self, log_plugin: &mut dyn LogPlugin) -> ConmonResult<()> { | ||
| // Get the sync_pipe FD. It is used by the conmon caller to obtain the container_pid | ||
| // or the runtime error message later. | ||
| let sync_pipe_fd = get_pipe_fd_from_env("_OCI_SYNCPIPE")?; | ||
|
|
||
| // Generate the list of arguments for runtime. | ||
| let runtime_args = generate_runtime_args(&self.cfg.common, self)?; | ||
|
|
||
| // Generate pipes to handle stdio. | ||
| let (mainfd_stdout, workerfd_stdout) = create_pipe()?; | ||
| let (mainfd_stderr, workerfd_stderr) = create_pipe()?; | ||
|
|
||
| // Run the `runtime create` and store our PID after first fork to `conmon_pidfile. | ||
| let runtime_pid = run_runtime( | ||
| &runtime_args, | ||
| Stdio::null(), // TODO | ||
| Stdio::from(workerfd_stdout), | ||
| Stdio::from(workerfd_stderr), | ||
| )?; | ||
| if let Some(pidfile) = &self.cfg.conmon_pidfile { | ||
| std::fs::write(pidfile, runtime_pid.to_string())?; | ||
| } | ||
| pub fn exec(&self, log_plugin: &mut dyn LogPlugin) -> ConmonResult<ExitCode> { | ||
| // Start the `runtime create` session. | ||
| let mut runtime_session = crate::runtime::session::RuntimeSession::new(); | ||
| runtime_session.launch(&self.cfg.common, self, false)?; | ||
|
|
||
| // === | ||
| // Now, after the `run_runtime`, we are in the child process of our original process | ||
| // (See `run_runtime` code and description for more information). | ||
| // Now, after the `launch()`, we are in the child process of our original process, | ||
| // because we double-fork in the RuntimeProcess::spawn. | ||
| // (See `RuntimeProcess::spawn` code and description for more information). | ||
| // === | ||
|
|
||
| // Wait until the `runtime create` finishes. | ||
| let runtime_status = wait_for_runtime(runtime_pid)?; | ||
| if runtime_status != 0 { | ||
| // Pass the error to sync_pipe if there is one. | ||
| if let Some(fd) = sync_pipe_fd { | ||
| let err_bytes = read_pipe(&mainfd_stderr)?; | ||
| let err_str = String::from_utf8(err_bytes)?; | ||
| write_or_close_sync_fd(fd, -1, Some(&err_str), self.cfg.common.api_version, false)?; | ||
| } | ||
| return Err(ConmonError::new( | ||
| format!("Runtime exited with status: {runtime_status}"), | ||
| 1, | ||
| )); | ||
| // In case of `--terminal`, wait until runtime creates the console socket. | ||
| if self.cfg.common.terminal { | ||
| runtime_session.wait_for_terminal_creation()?; | ||
| } | ||
|
|
||
| // Pass the container_pid to sync_pipe if there is one. | ||
| if let Some(fd) = sync_pipe_fd { | ||
| let container_pid = self.read_container_pid()?; | ||
| write_or_close_sync_fd(fd, container_pid, None, self.cfg.common.api_version, false)?; | ||
| } | ||
| // Wait until the `runtime create` finishes and return an error in case it fails. | ||
| runtime_session.wait_for_success(self.cfg.common.api_version)?; | ||
|
|
||
| runtime_session.write_container_pid_file(&self.cfg.common)?; | ||
|
|
||
| // === | ||
| // Now we wait for an external application like podman to really start the container. | ||
| // and handle the containers stdio or its termination. | ||
| // === | ||
|
|
||
| // Handle the stdio. | ||
| handle_stdio(log_plugin, mainfd_stdout, mainfd_stderr)?; | ||
| // Run the eventloop to forward log messages to log plugin. | ||
| runtime_session.run_event_loop(log_plugin, self.cfg.common.leave_stdin_open)?; | ||
|
|
||
| Ok(()) | ||
| Ok(ExitCode::SUCCESS) | ||
| } | ||
| } | ||
|
Comment on lines
+17
to
48
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The refactoring of the |
||
|
|
||
|
|
@@ -107,9 +59,13 @@ impl RuntimeArgsGenerator for Create { | |
| argv.extend([ | ||
| "create".to_string(), | ||
| "--bundle".to_string(), | ||
| self.cfg.bundle.to_string_lossy().into_owned(), | ||
| self.cfg.common.bundle.to_string_lossy().into_owned(), | ||
| "--pid-file".to_string(), | ||
| self.cfg.container_pidfile.to_string_lossy().into_owned(), | ||
| self.cfg | ||
| .common | ||
| .container_pidfile | ||
| .to_string_lossy() | ||
| .into_owned(), | ||
| ]); | ||
| Ok(()) | ||
| } | ||
|
|
@@ -127,6 +83,8 @@ mod tests { | |
| runtime_opts: Vec<&str>, | ||
| no_pivot: bool, | ||
| no_new_keyring: bool, | ||
| pidfile: &str, | ||
| bundle: &str, | ||
| ) -> CommonCfg { | ||
| CommonCfg { | ||
| runtime: PathBuf::from("./runtime"), | ||
|
|
@@ -135,22 +93,16 @@ mod tests { | |
| runtime_opts: runtime_opts.into_iter().map(|s| s.to_string()).collect(), | ||
| no_pivot, | ||
| no_new_keyring, | ||
| container_pidfile: PathBuf::from(pidfile), | ||
| bundle: PathBuf::from(bundle), | ||
| ..Default::default() | ||
| } | ||
| } | ||
|
|
||
| fn mk_create_cfg( | ||
| systemd_cgroup: bool, | ||
| bundle: &str, | ||
| pidfile: &str, | ||
| common: CommonCfg, | ||
| ) -> CreateCfg { | ||
| fn mk_create_cfg(systemd_cgroup: bool, common: CommonCfg) -> CreateCfg { | ||
| CreateCfg { | ||
| systemd_cgroup, | ||
| bundle: PathBuf::from(bundle), | ||
| container_pidfile: PathBuf::from(pidfile), | ||
| common, | ||
| ..Default::default() | ||
| } | ||
| } | ||
|
|
||
|
|
@@ -162,12 +114,14 @@ mod tests { | |
| vec!["--optA", "X"], | ||
| false, | ||
| false, | ||
| "/tmp/pid-A", | ||
| "/tmp/bundle-A", | ||
| ); | ||
| let cfg = mk_create_cfg(true, "/tmp/bundle-A", "/tmp/pid-A", common); | ||
| let cfg = mk_create_cfg(true, common); | ||
| let create = Create::new(cfg); | ||
|
|
||
| let argv = | ||
| crate::runtime::args::generate_runtime_args(&create.cfg.common, &create).expect("ok"); | ||
| let argv = crate::runtime::args::generate_runtime_args(&create.cfg.common, &create, None) | ||
| .expect("ok"); | ||
|
|
||
| let expected: Vec<String> = vec![ | ||
| "./runtime".into(), | ||
|
|
@@ -188,12 +142,20 @@ mod tests { | |
|
|
||
| #[test] | ||
| fn generate_args_create_without_systemd_cgroup_with_generic_flags() { | ||
| let common = mk_common("cid456", vec![], vec!["--optB"], true, true); | ||
| let cfg = mk_create_cfg(false, "/tmp/bundle-B", "/tmp/pid-B", common); | ||
| let common = mk_common( | ||
| "cid456", | ||
| vec![], | ||
| vec!["--optB"], | ||
| true, | ||
| true, | ||
| "/tmp/pid-B", | ||
| "/tmp/bundle-B", | ||
| ); | ||
| let cfg = mk_create_cfg(false, common); | ||
| let create = Create::new(cfg); | ||
|
|
||
| let argv = | ||
| crate::runtime::args::generate_runtime_args(&create.cfg.common, &create).expect("ok"); | ||
| let argv = crate::runtime::args::generate_runtime_args(&create.cfg.common, &create, None) | ||
| .expect("ok"); | ||
|
|
||
| let expected: Vec<String> = vec![ | ||
| "./runtime".into(), | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Adding
Debugderive to theOptsstruct is a good practice, especially for command-line argument parsing. It significantly aids in debugging by allowing easy printing of the parsed options.