diff --git a/crates/liboci-cli/README.md b/crates/liboci-cli/README.md index ea5809ae9..bd0b5c2f6 100644 --- a/crates/liboci-cli/README.md +++ b/crates/liboci-cli/README.md @@ -16,6 +16,7 @@ Interface](https://github.com/opencontainers/runtime-tools/blob/master/docs/comm | checkpoint | | | ✅ | ✅ | | | events | ✅ | | ✅ | | ✅ | | exec | ✅ | | ✅ | ✅ | ✅ | +| features | ✅ | | ✅ | | | | list | ✅ | | ✅ | ✅ | ✅ | | pause | ✅ | | ✅ | ✅ | ✅ | | ps | ✅ | | ✅ | ✅ | ✅ | diff --git a/crates/liboci-cli/src/checkpoint.rs b/crates/liboci-cli/src/checkpoint.rs index 3e8f72309..3ba62932e 100644 --- a/crates/liboci-cli/src/checkpoint.rs +++ b/crates/liboci-cli/src/checkpoint.rs @@ -2,29 +2,55 @@ use clap::Parser; use std::path::PathBuf; /// Checkpoint a running container +/// Reference: https://github.com/opencontainers/runc/blob/main/man/runc-checkpoint.8.md #[derive(Parser, Debug)] pub struct Checkpoint { - #[clap(value_parser = clap::builder::NonEmptyStringValueParser::new(), required = true)] - pub container_id: String, - /// Allow external unix sockets - #[clap(long)] - pub ext_unix_sk: bool, - /// Allow file locks - #[clap(long)] - pub file_locks: bool, /// Path for saving criu image files #[clap(long, default_value = "checkpoint")] pub image_path: PathBuf, + /// Path for saving work files and logs + #[clap(long)] + pub work_path: Option, + /// Path for previous criu image file in pre-dump + #[clap(long)] + pub parent_path: Option, /// Leave the process running after checkpointing #[clap(long)] pub leave_running: bool, + /// Allow open tcp connections + #[clap(long)] + pub tcp_established: bool, + /// Allow external unix sockets + #[clap(long)] + pub ext_unix_sk: bool, /// Allow shell jobs #[clap(long)] pub shell_job: bool, - /// Allow open tcp connections + /// Use lazy migration mechanism #[clap(long)] - pub tcp_established: bool, - /// Path for saving work files and logs + pub lazy_pages: bool, + /// Pass a file descriptor fd to criu #[clap(long)] - pub work_path: Option, + pub status_fd: Option, // TODO: Is u32 the right type? + /// Start a page server at the given URL + #[clap(long)] + pub page_server: Option, + /// Allow file locks + #[clap(long)] + pub file_locks: bool, + /// Do a pre-dump + #[clap(long)] + pub pre_dump: bool, + /// Cgroups mode + #[clap(long)] + pub manage_cgroups_mode: Option, + /// Checkpoint a namespace, but don't save its properties + #[clap(long)] + pub empty_ns: bool, + /// Enable auto-deduplication + #[clap(long)] + pub auto_dedup: bool, + + #[clap(value_parser = clap::builder::NonEmptyStringValueParser::new(), required = true)] + pub container_id: String, } diff --git a/crates/liboci-cli/src/create.rs b/crates/liboci-cli/src/create.rs index c83d56960..a67879220 100644 --- a/crates/liboci-cli/src/create.rs +++ b/crates/liboci-cli/src/create.rs @@ -3,22 +3,30 @@ use clap::Parser; use std::path::PathBuf; /// Create a container +/// Reference: https://github.com/opencontainers/runc/blob/main/man/runc-create.8.md #[derive(Parser, Debug)] pub struct Create { - /// File to write pid of the container created - // note that in the end, container is just another process - #[clap(short, long)] - pub pid_file: Option, - /// path to the bundle directory, containing config.json and root filesystem + /// Path to the bundle directory, containing config.json and root filesystem #[clap(short, long, default_value = ".")] pub bundle: PathBuf, /// Unix socket (file) path , which will receive file descriptor of the writing end of the pseudoterminal #[clap(short, long)] pub console_socket: Option, + /// File to write pid of the container created + // note that in the end, container is just another process + #[clap(short, long)] + pub pid_file: Option, + /// Do not use pivot rool to jail process inside rootfs + #[clap(long)] + pub no_pivot: bool, + /// Do not create a new session keyring for the container. + #[clap(long)] + pub no_new_keyring: bool, /// Pass N additional file descriptors to the container (stdio + $LISTEN_FDS + N in total) #[clap(long, default_value = "0")] pub preserve_fds: i32, - /// name of the container instance to be started + + /// Name of the container instance to be started #[clap(value_parser = clap::builder::NonEmptyStringValueParser::new(), required = true)] pub container_id: String, } diff --git a/crates/liboci-cli/src/exec.rs b/crates/liboci-cli/src/exec.rs index bc60b593e..8212d2196 100644 --- a/crates/liboci-cli/src/exec.rs +++ b/crates/liboci-cli/src/exec.rs @@ -4,40 +4,67 @@ use std::path::PathBuf; use clap::Parser; /// Execute a process within an existing container +/// Reference: https://github.com/opencontainers/runc/blob/main/man/runc-exec.8.md #[derive(Parser, Debug)] pub struct Exec { /// Unix socket (file) path , which will receive file descriptor of the writing end of the pseudoterminal #[clap(long)] pub console_socket: Option, - #[clap(short, long)] - pub tty: bool, #[clap(long)] /// Current working directory of the container pub cwd: Option, - #[clap(long)] - /// The file to which the pid of the container process should be written to - pub pid_file: Option, /// Environment variables that should be set in the container - #[clap(short, long, value_parser = parse_key_val::, number_of_values = 1)] + #[clap(short, long, value_parser = parse_env::, number_of_values = 1)] pub env: Vec<(String, String)>, - /// Prevent the process from gaining additional privileges - #[clap(long)] - pub no_new_privs: bool, + #[clap(short, long)] + pub tty: bool, + /// Run the command as a user + #[clap(short, long, value_parser = parse_user::)] + pub user: Option<(u32, Option)>, + /// Add additional group IDs. Can be specified multiple times + #[clap(long, short = 'g', number_of_values = 1)] + pub additional_gids: Vec, /// Path to process.json #[clap(short, long)] pub process: Option, /// Detach from the container process #[clap(short, long)] pub detach: bool, + #[clap(long)] + /// The file to which the pid of the container process should be written to + pub pid_file: Option, + /// Set the asm process label for the process commonly used with selinux + #[clap(long)] + pub process_label: Option, + /// Set the apparmor profile for the process + #[clap(long)] + pub apparmor: Option, + /// Prevent the process from gaining additional privileges + #[clap(long)] + pub no_new_privs: bool, + /// Add a capability to the bounding set for the process + #[clap(long, number_of_values = 1)] + pub cap: Vec, + /// Pass N additional file descriptors to the container + #[clap(long, default_value = "0")] + pub preserve_fds: i32, + /// Allow exec in a paused container + #[clap(long)] + pub ignore_paused: bool, + /// Execute a process in a sub-cgroup + #[clap(long)] + pub cgroup: Option, + /// Identifier of the container #[clap(value_parser = clap::builder::NonEmptyStringValueParser::new(), required = true)] pub container_id: String, + /// Command that should be executed in the container #[clap(required = false)] pub command: Vec, } -fn parse_key_val(s: &str) -> Result<(T, U), Box> +fn parse_env(s: &str) -> Result<(T, U), Box> where T: std::str::FromStr, T::Err: Error + Send + Sync + 'static, @@ -46,6 +73,20 @@ where { let pos = s .find('=') - .ok_or_else(|| format!("invalid KEY=value: no `=` found in `{s}`"))?; + .ok_or_else(|| format!("invalid VAR=value: no `=` found in `{s}`"))?; Ok((s[..pos].parse()?, s[pos + 1..].parse()?)) } + +fn parse_user(s: &str) -> Result<(T, Option), Box> +where + T: std::str::FromStr, + T::Err: Error + Send + Sync + 'static, + U: std::str::FromStr, + U::Err: Error + Send + Sync + 'static, +{ + if let Some(pos) = s.find(':') { + Ok((s[..pos].parse()?, Some(s[pos + 1..].parse()?))) + } else { + Ok((s.parse()?, None)) + } +} diff --git a/crates/liboci-cli/src/features.rs b/crates/liboci-cli/src/features.rs new file mode 100644 index 000000000..384a2953d --- /dev/null +++ b/crates/liboci-cli/src/features.rs @@ -0,0 +1,9 @@ +use clap::Parser; + +/// Return the features list for a container +/// This subcommand was introduced in runc by +/// https://github.com/opencontainers/runc/pull/3296 +/// It is documented here: +/// https://github.com/opencontainers/runtime-spec/blob/main/features-linux.md +#[derive(Parser, Debug)] +pub struct Features {} diff --git a/crates/liboci-cli/src/lib.rs b/crates/liboci-cli/src/lib.rs index 6576cfdf4..89c48a6d4 100644 --- a/crates/liboci-cli/src/lib.rs +++ b/crates/liboci-cli/src/lib.rs @@ -17,6 +17,7 @@ pub use {create::Create, delete::Delete, kill::Kill, start::Start, state::State} mod checkpoint; mod events; mod exec; +mod features; mod list; mod pause; mod ps; @@ -26,8 +27,8 @@ mod spec; mod update; pub use { - checkpoint::Checkpoint, events::Events, exec::Exec, list::List, pause::Pause, ps::Ps, - resume::Resume, run::Run, spec::Spec, update::Update, + checkpoint::Checkpoint, events::Events, exec::Exec, features::Features, list::List, + pause::Pause, ps::Ps, resume::Resume, run::Run, spec::Spec, update::Update, }; // Subcommands parsed by liboci-cli, based on the [OCI @@ -52,6 +53,7 @@ pub enum CommonCmd { Checkpointt(Checkpoint), Events(Events), Exec(Exec), + Features(Features), List(List), Pause(Pause), #[clap(allow_hyphen_values = true)] diff --git a/crates/liboci-cli/src/list.rs b/crates/liboci-cli/src/list.rs index 44a2ad2d1..acb49eb8a 100644 --- a/crates/liboci-cli/src/list.rs +++ b/crates/liboci-cli/src/list.rs @@ -2,4 +2,12 @@ use clap::Parser; /// List created containers #[derive(Parser, Debug)] -pub struct List {} +pub struct List { + /// Specify the format (default or table) + #[clap(long, default_value = "table")] + pub format: String, + + /// Only display container IDs + #[clap(long, short)] + pub quiet: bool, +} diff --git a/crates/liboci-cli/src/run.rs b/crates/liboci-cli/src/run.rs index e7a9d965f..8b7281b8e 100644 --- a/crates/liboci-cli/src/run.rs +++ b/crates/liboci-cli/src/run.rs @@ -4,19 +4,31 @@ use std::path::PathBuf; /// Create a container and immediately start it #[derive(Parser, Debug)] pub struct Run { - /// File to write pid of the container created - // note that in the end, container is just another process - #[clap(short, long)] - pub pid_file: Option, - /// path to the bundle directory, containing config.json and root filesystem + /// Path to the bundle directory, containing config.json and root filesystem #[clap(short, long, default_value = ".")] pub bundle: PathBuf, /// Unix socket (file) path , which will receive file descriptor of the writing end of the pseudoterminal #[clap(short, long)] pub console_socket: Option, + /// File to write pid of the container created + // note that in the end, container is just another process + #[clap(short, long)] + pub pid_file: Option, + /// Disable the use of the subreaper used to reap reparented processes + #[clap(long)] + pub no_subreaper: bool, + /// Do not use pivot root to jail process inside rootfs + #[clap(long)] + pub no_pivot: bool, + /// Do not create a new session keyring for the container. This will cause the container to inherit the calling processes session key. + #[clap(long)] + pub no_new_keyring: bool, /// Pass N additional file descriptors to the container (stdio + $LISTEN_FDS + N in total) #[clap(long, default_value = "0")] pub preserve_fds: i32, + // Keep container's state directory and cgroup + #[clap(long)] + pub keep: bool, /// name of the container instance to be started #[clap(value_parser = clap::builder::NonEmptyStringValueParser::new(), required = true)] pub container_id: String, diff --git a/crates/liboci-cli/src/spec.rs b/crates/liboci-cli/src/spec.rs index 6f685fcdd..d2f034f09 100644 --- a/crates/liboci-cli/src/spec.rs +++ b/crates/liboci-cli/src/spec.rs @@ -1,8 +1,13 @@ use clap::Parser; +use std::path::PathBuf; /// Command generates a config.json #[derive(Parser, Debug)] pub struct Spec { + /// Set path to the root of the bundle directory + #[clap(long, short)] + pub bundle: Option, + /// Generate a configuration for a rootless container #[clap(long)] pub rootless: bool, diff --git a/crates/liboci-cli/src/update.rs b/crates/liboci-cli/src/update.rs index b05f7b24a..eee356cb3 100644 --- a/crates/liboci-cli/src/update.rs +++ b/crates/liboci-cli/src/update.rs @@ -4,15 +4,67 @@ use std::path::PathBuf; /// Update running container resource constraints #[derive(Parser, Debug)] pub struct Update { - #[clap(value_parser = clap::builder::NonEmptyStringValueParser::new(), required = true)] - pub container_id: String, - /// Read the new resource limits from the given json file. Use - to read from stdin. /// If this option is used, all other options are ignored. #[clap(short, long)] pub resources: Option, + /// Set a new I/O weight + #[clap(long)] + pub blkio_weight: Option, + + /// Set CPU CFS period to be used for hardcapping (in microseconds) + #[clap(long)] + pub cpu_period: Option, + + /// Set CPU usage limit within a given period (in microseconds) + #[clap(long)] + pub cpu_quota: Option, + + /// Set CPU realtime period to be used for hardcapping (in microseconds) + #[clap(long)] + pub cpu_rt_period: Option, + + /// Set CPU realtime hardcap limit (in microseconds) + #[clap(long)] + pub cpu_rt_runtime: Option, + + /// Set CPU shares (relative weight vs. other containers) + #[clap(long)] + pub cpu_share: Option, + + /// Set CPU(s) to use. The list can contain commas and ranges. For example: 0-3,7 + #[clap(long)] + pub cpuset_cpus: Option, + + /// Set memory node(s) to use. The list format is the same as for --cpuset-cpus. + #[clap(long)] + pub cpuset_mems: Option, + + /// Set memory limit to num bytes. + #[clap(long)] + pub memory: Option, + + /// Set memory reservation (or soft limit) to num bytes. + #[clap(long)] + pub memory_reservation: Option, + + /// Set total memory + swap usage to num bytes. Use -1 to unset the limit (i.e. use unlimited swap). + #[clap(long)] + pub memory_swap: Option, + /// Set the maximum number of processes allowed in the container #[clap(long)] pub pids_limit: Option, + + /// Set the value for Intel RDT/CAT L3 cache schema. + #[clap(long)] + pub l3_cache_schema: Option, + + /// Set the Intel RDT/MBA memory bandwidth schema. + #[clap(long)] + pub mem_bw_schema: Option, + + #[clap(value_parser = clap::builder::NonEmptyStringValueParser::new(), required = true)] + pub container_id: String, } diff --git a/crates/youki/src/commands/features.rs b/crates/youki/src/commands/features.rs new file mode 100644 index 000000000..bf359bd0a --- /dev/null +++ b/crates/youki/src/commands/features.rs @@ -0,0 +1,8 @@ +//! Contains Functionality of `features` container command +use anyhow::Result; +use liboci_cli::Features; + +/// lists all existing containers +pub fn features(_: Features) -> Result<()> { + Ok(()) +} diff --git a/crates/youki/src/commands/mod.rs b/crates/youki/src/commands/mod.rs index 7cd3669ac..0c763c529 100644 --- a/crates/youki/src/commands/mod.rs +++ b/crates/youki/src/commands/mod.rs @@ -13,6 +13,7 @@ pub mod create; pub mod delete; pub mod events; pub mod exec; +pub mod features; pub mod info; pub mod kill; pub mod list; diff --git a/crates/youki/src/main.rs b/crates/youki/src/main.rs index bf82cb76c..6a92be8d0 100644 --- a/crates/youki/src/main.rs +++ b/crates/youki/src/main.rs @@ -48,9 +48,9 @@ struct Opts { enum SubCommand { // Standard and common commands handled by the liboci_cli crate #[clap(flatten)] - Standard(liboci_cli::StandardCmd), + Standard(Box), #[clap(flatten)] - Common(liboci_cli::CommonCmd), + Common(Box), // Youki specific extensions Info(info::Info), @@ -106,7 +106,7 @@ fn main() -> Result<()> { let systemd_cgroup = opts.global.systemd_cgroup; let cmd_result = match opts.subcmd { - SubCommand::Standard(cmd) => match cmd { + SubCommand::Standard(cmd) => match *cmd { StandardCmd::Create(create) => { commands::create::create(create, root_path, systemd_cgroup) } @@ -115,7 +115,7 @@ fn main() -> Result<()> { StandardCmd::Delete(delete) => commands::delete::delete(delete, root_path), StandardCmd::State(state) => commands::state::state(state, root_path), }, - SubCommand::Common(cmd) => match cmd { + SubCommand::Common(cmd) => match *cmd { CommonCmd::Checkpointt(checkpoint) => { commands::checkpoint::checkpoint(checkpoint, root_path) } @@ -127,6 +127,7 @@ fn main() -> Result<()> { std::process::exit(-1); } }, + CommonCmd::Features(features) => commands::features::features(features), CommonCmd::List(list) => commands::list::list(list, root_path), CommonCmd::Pause(pause) => commands::pause::pause(pause, root_path), CommonCmd::Ps(ps) => commands::ps::ps(ps, root_path),