mirror of
https://github.com/containers/youki
synced 2024-09-21 11:22:14 +02:00
Merge branch 'main' into spec-to-crate
This commit is contained in:
commit
c9c0834185
@ -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
|
||||
|
@ -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
53
docs/doc-draft.md
Normal 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)
|
@ -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);
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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,
|
||||
|
30
src/main.rs
30
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 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()?;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user