Skip to content
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

Add comments to main.rs #38

Merged
merged 2 commits into from
May 27, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ name = "youki"
version = "0.0.1"
authors = ["utam0k <k0ma@utam0k.jp>"]
edition = "2018"
description = "A container runtime written in Rust"

[dependencies]
clap = "3.0.0-beta.2"
Expand Down
53 changes: 53 additions & 0 deletions docs/doc-draft.md
Original file line number Diff line number Diff line change
@@ -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)
28 changes: 28 additions & 0 deletions src/main.rs
Original file line number Diff line number Diff line change
@@ -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 a high-level container runtime to run containers.

use std::fs;
use std::path::PathBuf;

Expand All @@ -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 <k0ma@utam0k.jp>")]
struct Opts {
/// root directory to store container state
#[clap(short, long, default_value = "/run/youki")]
root: PathBuf,
#[clap(short, long)]
log: Option<PathBuf>,
#[clap(long)]
log_format: Option<String>,
/// command to actually manage container
#[clap(subcommand)]
subcmd: SubCommand,
}
Expand All @@ -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 <k0ma@utam0k.jp>")]
Expand All @@ -66,6 +78,8 @@ impl SubCommand {
}
}

/// 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();

Expand All @@ -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())?;
Expand All @@ -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()?;
}
Expand Down