Skip to content

Commit

Permalink
Merge pull request #138 from Furisto/exec
Browse files Browse the repository at this point in the history
Implement exec command
  • Loading branch information
utam0k authored Jul 17, 2021
2 parents 005bb5a + 84bc49c commit 0181ee1
Show file tree
Hide file tree
Showing 19 changed files with 554 additions and 77 deletions.
10 changes: 10 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ oci_spec = { version = "0.1.0", path = "./oci_spec" }
systemd = { version = "0.8", default-features = false }
dbus = "0.9.2"
tabwriter = "1"
fastrand = "1.4.1"

[dev-dependencies]
oci_spec = { version = "0.1.0", path = "./oci_spec", features = ["proptests"] }
Expand Down
19 changes: 19 additions & 0 deletions oci_spec/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
use nix::sys::stat::SFlag;
use std::collections::HashMap;
use std::convert::TryFrom;

use std::fs::File;
use std::path::{Path, PathBuf};

Expand Down Expand Up @@ -475,6 +477,23 @@ pub enum LinuxNamespaceType {
Network = 0x40000000,
}

impl TryFrom<&str> for LinuxNamespaceType {
type Error = anyhow::Error;

fn try_from(namespace: &str) -> Result<Self, Self::Error> {
match namespace {
"mnt" => Ok(LinuxNamespaceType::Mount),
"cgroup" => Ok(LinuxNamespaceType::Cgroup),
"uts" => Ok(LinuxNamespaceType::Uts),
"ipc" => Ok(LinuxNamespaceType::Ipc),
"user" => Ok(LinuxNamespaceType::User),
"pid" => Ok(LinuxNamespaceType::Pid),
"net" => Ok(LinuxNamespaceType::Network),
_ => bail!("unknown namespace {}, could not convert", namespace),
}
}
}

#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct LinuxNamespace {
#[serde(rename = "type")]
Expand Down
12 changes: 7 additions & 5 deletions src/container/builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,16 @@ use std::path::PathBuf;

use super::{init_builder::InitContainerBuilder, tenant_builder::TenantContainerBuilder};
pub struct ContainerBuilder {
/// Id of the container
pub(super) container_id: String,

/// Root directory for container state
pub(super) root_path: PathBuf,

/// Interface to operating system primitives
pub(super) syscall: LinuxSyscall,

/// File which will be used to communicate the pid of the
/// container process to the higher level runtime
pub(super) pid_file: Option<PathBuf>,

/// Socket to communicate the file descriptor of the ptty
pub(super) console_socket: Option<PathBuf>,
}

Expand Down Expand Up @@ -60,7 +62,7 @@ impl ContainerBuilder {
///
/// ContainerBuilder::new("74f1a4cb3801".to_owned())
/// .as_tenant()
/// .with_container_command(vec!["sleep".to_owned(), "9001".to_owned()])
/// .with_container_args(vec!["sleep".to_owned(), "9001".to_owned()])
/// .build();
/// ```
#[allow(clippy::wrong_self_convention)]
Expand Down
48 changes: 36 additions & 12 deletions src/container/builder_impl.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use std::path::PathBuf;

use anyhow::Result;
use anyhow::{Context, Result};
use nix::{
sched,
unistd::{Gid, Uid},
Expand All @@ -21,25 +21,39 @@ use crate::{
use super::{Container, ContainerStatus};

pub(super) struct ContainerBuilderImpl {
/// Flag indicating if an init or a tenant container should be created
pub init: bool,
/// Interface to operating system primitives
pub syscall: LinuxSyscall,
/// Flag indicating if systemd should be used for cgroup management
pub use_systemd: bool,
/// Id of the container
pub container_id: String,
pub root_path: PathBuf,
/// Directory where the state of the container will be stored
pub container_dir: PathBuf,
/// OCI complient runtime spec
pub spec: Spec,
/// Root filesystem of the container
pub rootfs: PathBuf,
/// File which will be used to communicate the pid of the
/// container process to the higher level runtime
pub pid_file: Option<PathBuf>,
/// Socket to communicate the file descriptor of the ptty
pub console_socket: Option<FileDescriptor>,
/// Options for rootless containers
pub rootless: Option<Rootless>,
/// Socket to communicate container start
pub notify_socket: NotifyListener,
/// Container state
pub container: Option<Container>,
}

impl ContainerBuilderImpl {
pub(super) fn create(&mut self) -> Result<()> {
if let Process::Parent(_) = self.run_container()? {
std::process::exit(0);
if self.init {
std::process::exit(0);
}
}

Ok(())
Expand All @@ -56,6 +70,7 @@ impl ContainerBuilderImpl {

// first fork, which creates process, which will later create actual container process
match fork::fork_first(
self.init,
&self.pid_file,
&self.rootless,
linux,
Expand All @@ -68,12 +83,18 @@ impl ContainerBuilderImpl {
Process::Child(child) => {
// set limits and namespaces to the process
for rlimit in self.spec.process.rlimits.iter() {
self.syscall.set_rlimit(rlimit)?
self.syscall
.set_rlimit(rlimit)
.context("failed to set rlimit")?;
}
self.syscall.set_id(Uid::from_raw(0), Gid::from_raw(0))?;
self.syscall
.set_id(Uid::from_raw(0), Gid::from_raw(0))
.context("failed to become root")?;

let without = sched::CloneFlags::CLONE_NEWUSER;
namespaces.apply_unshare(without)?;
namespaces
.apply_unshare(without)
.context("could not unshare namespaces")?;

// set up tty if specified
if let Some(csocketfd) = &self.console_socket {
Expand All @@ -89,12 +110,15 @@ impl ContainerBuilderImpl {
// This is actually the child process after fork
Process::Init(mut init) => {
// prepare process
setup_init_process(
&self.spec,
&self.syscall,
self.rootfs.clone(),
&namespaces,
)?;
if self.init {
setup_init_process(
&self.spec,
&self.syscall,
self.rootfs.clone(),
&namespaces,
)?;
}

init.ready()?;
self.notify_socket.wait_for_container_start()?;
// actually run the command / program to be run in container
Expand Down
21 changes: 17 additions & 4 deletions src/container/container.rs
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,10 @@ impl Container {
self.state.status.can_delete()
}

pub fn can_exec(&self) -> bool {
self.state.status == ContainerStatus::Running
}

pub fn pid(&self) -> Option<Pid> {
self.state.pid.map(Pid::from_raw)
}
Expand Down Expand Up @@ -129,6 +133,19 @@ impl Container {
None
}

pub fn bundle(&self) -> String {
self.state.bundle.clone()
}

pub fn set_systemd(mut self, should_use: bool) -> Self {
self.state.use_systemd = Some(should_use);
self
}

pub fn systemd(&self) -> Option<bool> {
self.state.use_systemd
}

pub fn update_status(&self, status: ContainerStatus) -> Self {
let created = match (status, self.state.created) {
(ContainerStatus::Created, None) => Some(Utc::now()),
Expand All @@ -152,8 +169,4 @@ impl Container {
root: container_root,
})
}

pub fn bundle(&self) -> String {
self.state.bundle.clone()
}
}
18 changes: 13 additions & 5 deletions src/container/init_builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,10 @@ use std::{
path::{Path, PathBuf},
};

use crate::{notify_socket::NotifyListener, rootless, tty, utils};
use crate::{
notify_socket::{NotifyListener, NOTIFY_FILE},
rootless, tty, utils,
};

use super::{
builder::ContainerBuilder, builder_impl::ContainerBuilderImpl, Container, ContainerStatus,
Expand Down Expand Up @@ -43,16 +46,22 @@ impl InitContainerBuilder {
let spec = self.load_and_safeguard_spec(&container_dir)?;

unistd::chdir(&*container_dir)?;
let container_state = self.create_container_state(&container_dir)?;
let container_state = self
.create_container_state(&container_dir)?
.set_systemd(self.use_systemd);

let notify_socket: NotifyListener = NotifyListener::new(&container_dir)?;
let notify_socket: NotifyListener = NotifyListener::new(NOTIFY_FILE)?;
// convert path of root file system of the container to absolute path
let rootfs = fs::canonicalize(&spec.root.path)?;

// if socket file path is given in commandline options,
// get file descriptors of console socket
let csocketfd = if let Some(console_socket) = &self.base.console_socket {
Some(tty::setup_console_socket(&container_dir, console_socket)?)
Some(tty::setup_console_socket(
&container_dir,
console_socket,
"console-socket",
)?)
} else {
None
};
Expand All @@ -63,7 +72,6 @@ impl InitContainerBuilder {
init: true,
syscall: self.base.syscall,
container_id: self.base.container_id,
root_path: self.base.root_path,
pid_file: self.base.pid_file,
console_socket: csocketfd,
use_systemd: self.use_systemd,
Expand Down
6 changes: 5 additions & 1 deletion src/container/mod.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
//! Container management
/// This crate is responsible for the creation of containers. It provides a builder that can
/// be used to configure and create containers. We distinguish between an init container for which
/// namespaces and cgroups will be created (usually) and a tenant container process that will move
/// into the existing namespaces and cgroups of the initial container process (e.g. used to implement
/// the exec command).
pub mod builder;
mod builder_impl;
#[allow(clippy::module_inception)]
Expand Down
11 changes: 8 additions & 3 deletions src/container/state.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,14 @@ use std::fmt::Display;
use std::fs;
use std::{fs::File, path::Path};

use anyhow::Result;
use anyhow::{Context, Result};
use chrono::{DateTime, Utc};
use serde::{Deserialize, Serialize};

const STATE_FILE_PATH: &str = "state.json";

/// Indicates status of the container
#[derive(Serialize, Deserialize, Debug, Copy, Clone)]
#[derive(Serialize, Deserialize, Debug, Copy, Clone, PartialEq, Eq)]
#[serde(rename_all = "camelCase")]
pub enum ContainerStatus {
// The container is being created
Expand Down Expand Up @@ -78,6 +78,8 @@ pub struct State {
// User that created the container
#[serde(skip_serializing_if = "Option::is_none")]
pub creator: Option<u32>,
// Specifies if systemd should be used to manage cgroups
pub use_systemd: Option<bool>,
}

impl State {
Expand All @@ -96,6 +98,7 @@ impl State {
annotations: HashMap::default(),
created: None,
creator: None,
use_systemd: None,
}
}

Expand All @@ -115,7 +118,9 @@ impl State {

pub fn load(container_root: &Path) -> Result<Self> {
let state_file_path = container_root.join(STATE_FILE_PATH);
let file = File::open(state_file_path)?;
let file = File::open(&state_file_path).with_context(|| {
format!("failed to open container state file {:?}", state_file_path)
})?;
let state: Self = serde_json::from_reader(&file)?;
Ok(state)
}
Expand Down
Loading

0 comments on commit 0181ee1

Please sign in to comment.