From a46726427f48f4b686f3bc811e2b17b9ee1b32f5 Mon Sep 17 00:00:00 2001 From: Yashodhan Joshi Date: Wed, 26 May 2021 17:59:35 +0530 Subject: [PATCH 1/2] Add comments to main.rs Start draft of high-level documentation --- Cargo.toml | 1 + docs/doc-draft.md | 53 +++++++++++++++++++++++++++++++++++++++++++++++ src/main.rs | 28 +++++++++++++++++++++++++ 3 files changed, 82 insertions(+) create mode 100644 docs/doc-draft.md diff --git a/Cargo.toml b/Cargo.toml index eb8f2e50..af955cf3 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -3,6 +3,7 @@ name = "youki" version = "0.0.1" authors = ["utam0k "] edition = "2018" +description = "A container runtime written in Rust" [dependencies] clap = "3.0.0-beta.2" diff --git a/docs/doc-draft.md b/docs/doc-draft.md new file mode 100644 index 00000000..72eb37f2 --- /dev/null +++ b/docs/doc-draft.md @@ -0,0 +1,53 @@ +_This is a draft for a high level documentation of Youki. After finished this is intended to provide how control flow and high level functioning of Youki happens for development purposes._ + +## Some reference links + +These are references to various documentations and specifications, which can be useful to understand commands and constraints. + +- [OCI runtime specification] : The specification for a container runtime. Any OCI complaisant runtime must follow this. +- [runc man pages] : has information on various commandline options supported by runc, can be used to understand commands and their options. +- [cgroups man page](https://man7.org/linux/man-pages/man7/cgroups.7.html) : contains information about cgroups, their creation, deletion etc. + +--- + +## Control flow diagram + +This is diagram as given in #14, which is not actually how this works, but helpful to understand overall flow. Someone needs to check and correct. + +```mermaid +sequenceDiagram +participant U as User +participant D as Docker +participant YP as Youki(Parent Process) +participant YC as Youki(Child Process) +participant YI as Youki(Init Process) + + +U ->> D : $ docker run --rm -it --runtime youki $image +D ->> YP : youki create $container_id +YP ->> YC : fork(2) +YC ->> YC : create new namespace +YC ->> YI : fork(2) +YI ->> YI : Mount the device +YI -->> YP : ready message (Unix domain socket) +YP ->> D : exit $code +D ->> YP : $ youki start $container_id +YP -->> YI : start message (Unix domain socket) +YI ->> YI : run the commands in dockerfile +D ->> D : monitor pid written in pid file +D ->> U : exit $code + +``` + +--- + +## Control flow + +### main invocation + +On invoking Youki, main function parses args passed to it, which contains directory path to store container state (check runc . 8 . md in [runc man pages]), optional log path and log format string and a subcommand such as create, delete etc. + +From there it matches subcommand arg with possible subcommand and takes appropriate actions, such as creating a new container, deleting a container erc. + +[oci runtime specification]: https://github.com/opencontainers/runtime-spec/blob/master/runtime.md +[runc man pages]: (https://github.com/opencontainers/runc/blob/master/man/runc.8.md) diff --git a/src/main.rs b/src/main.rs index c9c05990..a26885df 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,3 +1,7 @@ +//! # Youki +//! Container Runtime written in Rust, inspired by [railcar](https://github.com/oracle/railcar) +//! This crate provides a container runtime which can be used by Docker to run containers. + use std::fs; use std::path::PathBuf; @@ -12,15 +16,20 @@ use youki::spec; use youki::start; use youki::{cgroups::Manager, command::linux::LinuxCommand}; +/// High-level commandline option definition +/// This takes global options as well as individual commands as specified in [OCI runtime-spec](https://github.com/opencontainers/runtime-spec/blob/master/runtime.md) +/// Also check [runc commandline documentation](https://github.com/opencontainers/runc/blob/master/man/runc.8.md) for more explanation #[derive(Clap, Debug)] #[clap(version = "1.0", author = "utam0k ")] struct Opts { + /// root directory to store container state #[clap(short, long, default_value = "/run/youki")] root: PathBuf, #[clap(short, long)] log: Option, #[clap(long)] log_format: Option, + /// command to actually manage container #[clap(subcommand)] subcmd: SubCommand, } @@ -40,6 +49,9 @@ pub struct Delete { pub struct StateArgs { pub container_id: String, } + +/// Subcommands accepted by Youki, confirming with [OCI runtime-spec](https://github.com/opencontainers/runtime-spec/blob/master/runtime.md) +/// Also for a short information, check [runc commandline documentation](https://github.com/opencontainers/runc/blob/master/man/runc.8.md) #[derive(Clap, Debug)] enum SubCommand { #[clap(version = "0.0.1", author = "utam0k ")] @@ -66,6 +78,8 @@ impl SubCommand { } } +/// This is the entry point in the container runtime. The binary is run by docker, +/// with various flags passed. This parses the flags, creates and manages appropriate resources. fn main() -> Result<()> { let opts = Opts::parse(); @@ -88,11 +102,17 @@ fn main() -> Result<()> { SubCommand::Create(create) => create.exec(root_path, LinuxCommand), SubCommand::Start(start) => start.exec(root_path), SubCommand::Kill(kill) => { + // resolves relative paths, symbolic links etc. and get complete path let root_path = fs::canonicalize(root_path)?; + // state of container is stored in a directory named as container id inside + // root directory given in commandline options let container_root = root_path.join(&kill.container_id); if !container_root.exists() { bail!("{} doesn't exists.", kill.container_id) } + + // load container state from json file, and check status of the container + // it might be possible that kill is invoked on a already stopped container etc. let container = Container::load(container_root)?.refresh_status()?; if container.can_kill() { let sig = signal::from_str(kill.signal.as_str())?; @@ -109,15 +129,23 @@ fn main() -> Result<()> { } } SubCommand::Delete(delete) => { + // state of container is stored in a directory named as container id inside + // root directory given in commandline options let container_root = root_path.join(&delete.container_id); if !container_root.exists() { bail!("{} doesn't exists.", delete.container_id) } + // load container state from json file, and check status of the container + // it might be possible that delete is invoked on a running container. let container = Container::load(container_root)?.refresh_status()?; if container.can_delete() { if container.root.exists() { + // remove the directory storing container state fs::remove_dir_all(&container.root)?; let spec = spec::Spec::load("config.json")?; + // remove the cgroup created for the container + // check https://man7.org/linux/man-pages/man7/cgroups.7.html + // creating and removing cgroups section for more information on cgroups let cmanager = Manager::new(spec.linux.unwrap().cgroups_path)?; cmanager.remove()?; } From d22daf7f2cc7ee8e3a92f1b00980dd66c6a383f2 Mon Sep 17 00:00:00 2001 From: Yashodhan Joshi Date: Thu, 27 May 2021 11:54:00 +0530 Subject: [PATCH 2/2] Changed 'Docker' to more generic 'high-level container runtime' --- src/main.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main.rs b/src/main.rs index a26885df..8b798044 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,6 +1,6 @@ //! # Youki //! Container Runtime written in Rust, inspired by [railcar](https://github.com/oracle/railcar) -//! This crate provides a container runtime which can be used by Docker to run containers. +//! This crate provides a container runtime which can be used by a high-level container runtime to run containers. use std::fs; use std::path::PathBuf; @@ -78,7 +78,7 @@ impl SubCommand { } } -/// This is the entry point in the container runtime. The binary is run by docker, +/// This is the entry point in the container runtime. The binary is run by a high-level container runtime, /// with various flags passed. This parses the flags, creates and manages appropriate resources. fn main() -> Result<()> { let opts = Opts::parse();