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

Implement exec command #138

Merged
merged 5 commits into from
Jul 17, 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
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
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍

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 {
utam0k marked this conversation as resolved.
Show resolved Hide resolved
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