1
0
Fork 0
mirror of https://github.com/containers/youki synced 2024-05-22 23:46:08 +02:00

Merge branch 'main' into spec-to-crate

This commit is contained in:
ferrell-code 2021-05-27 20:57:44 -04:00 committed by GitHub
commit c9c0834185
Signed by: GitHub
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 116 additions and 24 deletions

View File

@ -3,7 +3,9 @@ FROM mcr.microsoft.com/vscode/devcontainers/rust:1
WORKDIR /workspaces
RUN curl -fsSL get.docker.com -o get-docker.sh && sh get-docker.sh && rm get-docker.sh
RUN rustup component add rust-src clippy
RUN curl -L https://github.com/rust-analyzer/rust-analyzer/releases/latest/download/rust-analyzer-linux -o /usr/local/cargo/bin/rust-analyzer && chmod +x /usr/local/cargo/bin/rust-analyzer
RUN curl -L https://github.com/rust-analyzer/rust-analyzer/releases/latest/download/rust-analyzer-x86_64-unknown-linux-gnu.gz \
-o /usr/local/cargo/bin/rust-analyzer.gz && gzip -d /usr/local/cargo/bin/rust-analyzer.gz \
&& chmod +x /usr/local/cargo/bin/rust-analyzer
RUN wget https://dl.google.com/go/go1.13.5.linux-amd64.tar.gz && tar -C /usr/local -xzf go1.13.5.linux-amd64.tar.gz
RUN mkdir /workspaces/go
ENV PATH $PATH:/usr/local/go/bin

View File

@ -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"

53
docs/doc-draft.md Normal file
View File

@ -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)

View File

@ -140,7 +140,7 @@ mod tests {
fn setup(testname: &str, throttle_type: &str) -> (PathBuf, PathBuf) {
let tmp = create_temp_dir(testname).expect("create temp directory for test");
let throttle_file = set_fixture(&tmp, throttle_type, "")
.expect(&format!("set fixture for {}", throttle_type));
.unwrap_or_else(|_| panic!("set fixture for {}", throttle_type));
(tmp, throttle_file)
}
@ -182,7 +182,7 @@ mod tests {
Blkio::apply(&test_root, &blkio).expect("apply blkio");
let content = fs::read_to_string(throttle)
.expect(&format!("read {} content", CGROUP_BLKIO_THROTTLE_READ_BPS));
.unwrap_or_else(|_| panic!("read {} content", CGROUP_BLKIO_THROTTLE_READ_BPS));
assert_eq!("8:0 102400", content);
}
@ -202,7 +202,7 @@ mod tests {
Blkio::apply(&test_root, &blkio).expect("apply blkio");
let content = fs::read_to_string(throttle)
.expect(&format!("read {} content", CGROUP_BLKIO_THROTTLE_WRITE_BPS));
.unwrap_or_else(|_| panic!("read {} content", CGROUP_BLKIO_THROTTLE_WRITE_BPS));
assert_eq!("8:0 102400", content);
}
@ -222,7 +222,7 @@ mod tests {
Blkio::apply(&test_root, &blkio).expect("apply blkio");
let content = fs::read_to_string(throttle)
.expect(&format!("read {} content", CGROUP_BLKIO_THROTTLE_READ_IOPS));
.unwrap_or_else(|_| panic!("read {} content", CGROUP_BLKIO_THROTTLE_READ_IOPS));
assert_eq!("8:0 102400", content);
}
@ -243,10 +243,8 @@ mod tests {
.build();
Blkio::apply(&test_root, &blkio).expect("apply blkio");
let content = fs::read_to_string(throttle).expect(&format!(
"read {} content",
CGROUP_BLKIO_THROTTLE_WRITE_IOPS
));
let content = fs::read_to_string(throttle)
.unwrap_or_else(|_| panic!("read {} content", CGROUP_BLKIO_THROTTLE_WRITE_IOPS));
assert_eq!("8:0 102400", content);
}

View File

@ -147,6 +147,9 @@ impl Memory {
}
fn set_memory(val: i64, cgroup_root: &Path) -> Result<()> {
if val == 0 {
return Ok(());
}
let path = cgroup_root.join(CGROUP_MEMORY_LIMIT);
match Self::set(val, &path) {
@ -158,16 +161,16 @@ impl Memory {
Errno::EBUSY => {
let usage = Self::get_memory_usage(cgroup_root)?;
let max_usage = Self::get_memory_max_usage(cgroup_root)?;
Err(anyhow!(
bail!(
"unable to set memory limit to {} (current usage: {}, peak usage: {})",
val,
usage,
max_usage,
))
)
}
_ => Err(anyhow!(e)),
_ => bail!(e),
},
None => Err(anyhow!(e)),
None => bail!(e),
}
}
}
@ -225,12 +228,8 @@ impl Memory {
}
}
None => match resource.swap {
Some(swap) => {
Self::set_memory_and_swap(0, swap, false, cgroup_root)?;
}
None => {
Self::set_memory_and_swap(0, 0, false, cgroup_root)?;
}
Some(swap) => Self::set_memory_and_swap(0, swap, false, cgroup_root)?,
None => Self::set_memory_and_swap(0, 0, false, cgroup_root)?,
},
}
Ok(())
@ -271,6 +270,19 @@ mod tests {
assert_eq!(limit.to_string(), content)
}
#[test]
fn pass_set_memory_if_limit_is_zero() {
let sample_val = "1024";
let limit = 0;
let tmp = create_temp_dir("pass_set_memory_if_limit_is_zero")
.expect("create temp directory for test");
set_fixture(&tmp, CGROUP_MEMORY_LIMIT, sample_val).expect("Set fixure for memory limit");
Memory::set_memory(limit, &tmp).expect("Set memory limit");
let content =
std::fs::read_to_string(tmp.join(CGROUP_MEMORY_LIMIT)).expect("Read to string");
assert_eq!(content, sample_val)
}
#[test]
fn test_set_swap() {
let limit = 512;

View File

@ -90,11 +90,7 @@ mod tests {
priority: 2,
},
];
let priorities_string = priorities
.clone()
.iter()
.map(|p| p.to_string())
.collect::<String>();
let priorities_string = priorities.iter().map(|p| p.to_string()).collect::<String>();
let network = LinuxNetwork {
class_id: None,
priorities,

View File

@ -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;
@ -11,15 +15,20 @@ use youki::signal;
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,
}
@ -39,6 +48,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>")]
@ -65,6 +77,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();
@ -87,11 +101,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())?;
@ -108,15 +128,25 @@ 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 = oci_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()?;
}