Skip to content
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
2 changes: 1 addition & 1 deletion src/json.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ use std::{

#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
pub struct Container {
dir: String,
pub dir: String,
pub state: State,
pub pid: Option<i32>,
}
Expand Down
47 changes: 26 additions & 21 deletions src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ use bento::{
bento_cli::{Cli, Commands},
env::Env,
json::{self, State},
runtime::{create, exec, kill_proc, start, stop},
runtime::{create, exec, kill_container, start, stop},
};
use clap::Parser;
use dotenv::dotenv;
Expand All @@ -27,27 +27,32 @@ fn main() -> Result<()> {
mount,
current_working_directory,
command,
}) => {
let cwd = match current_working_directory {
Some(c) => c,
None => &PathBuf::from("/"),
};
let mount_dir = match mount {
Some(m) => m,
None => &PathBuf::new(),
};
}) => match json::check_existing_container(name, &env.bento_containers_env_path) {
Some(_) => {
anyhow::bail!("Container already exists!");
}
None => {
let cwd = match current_working_directory {
Some(c) => c,
None => &PathBuf::from("/"),
};
let mount_dir = match mount {
Some(m) => m,
None => &PathBuf::new(),
};

match create(name, image, mount_dir, cwd, command, &env) {
Ok(_) => {
eprintln!("🍱 Bento Container {} finished", name)
}
Err(e) => return Err(anyhow!("Container not created.\n Error: {}.", e)),
};
}
match create(name, image, mount_dir, cwd, command, &env) {
Ok(_) => {
eprintln!("🍱 Bento Container {} finished", name)
}
Err(e) => return Err(anyhow!("Container not created.\n Error: {}.", e)),
};
}
},
Some(Commands::Start { name }) => {
match json::check_existing_container(name, &env.bento_containers_env_path) {
Some(_container) => {
if let Err(e) = start(name, &env) {
Some(container) => {
if let Err(e) = start(&container, name, &env) {
anyhow::bail!("Starting {} failed! Error: {}.", name, e);
}
}
Expand Down Expand Up @@ -81,7 +86,7 @@ fn main() -> Result<()> {
}
Some(Commands::Stop { name }) => {
match json::check_existing_container(name, &env.bento_containers_env_path) {
Some(container) => match stop(name, &container, &env) {
Some(container) => match stop(&container, name, &env) {
Ok(()) => eprintln!("Container {} stopped successfully", name),
Err(e) => eprintln!("{:?}", e),
},
Expand All @@ -92,7 +97,7 @@ fn main() -> Result<()> {
}
Some(Commands::Kill { name }) => {
match json::check_existing_container(name, &env.bento_containers_env_path) {
Some(container) => match kill_proc(&container) {
Some(container) => match kill_container(&container) {
Ok(()) => eprintln!("Container {} killed successfully", name),
Err(e) => eprintln!("{:?}", e),
},
Expand Down
199 changes: 134 additions & 65 deletions src/runtime.rs
Original file line number Diff line number Diff line change
Expand Up @@ -361,40 +361,43 @@ fn _clean_up(container_dir: &PathBuf) -> Result<()> {
Ok(())
}

pub fn start(name: &str, env: &Env) -> Result<()> {
let container_config_path = &env
.bento_containers_env_path
.join(name)
.join("bento_config.json");
pub fn start(container: &Container, name: &str, env: &Env) -> Result<()> {
if container.state == State::Created || container.state == State::Stopped {
let container_config_path = &env
.bento_containers_env_path
.join(name)
.join("bento_config.json");

let bento_config = get_bento_config(&container_config_path).with_context(|| {
format!(
"Container {} failed to load the bento_config.json at {}.",
let bento_config = get_bento_config(&container_config_path).with_context(|| {
format!(
"Container {} failed to load the bento_config.json at {}.",
name,
container_config_path.display()
)
})?;
unshare_user_namespace().with_context(|| {
format!(
"Container {} failed to go rootless by unsharing user namespace.",
name
)
})?;
unshare_mount_namespace()
.with_context(|| format!("Container {} failed to unshare mount namespace.", name))?;
if let Err(err) = mount_fs_overlay(&bento_config) {
unmount_and_clean_up(&bento_config)
.context("Failed to unmount and cleantup after failed start.")?;
return Err(anyhow!("Err: {}", err));
};
fork_into_namespaces(
&bento_config,
name,
container_config_path.display()
&env.bento_containers_env_path
.join("container_manifest.json"),
)
})?;
unshare_user_namespace().with_context(|| {
format!(
"Container {} failed to go rootless by unsharing user namespace.",
name
)
})?;
unshare_mount_namespace()
.with_context(|| format!("Container {} failed to unshare mount namespace.", name))?;
if let Err(err) = mount_fs_overlay(&bento_config) {
unmount_and_clean_up(&bento_config)
.context("Failed to unmount and cleantup after failed start.")?;
return Err(anyhow!("Err: {}", err));
};
fork_into_namespaces(
&bento_config,
name,
&env.bento_containers_env_path
.join("container_manifest.json"),
)
.with_context(|| format!("Container {} faild to fork the process.", name))?;

.with_context(|| format!("Container {} faild to fork the process.", name))?;
} else {
anyhow::bail!("Container failed to start! Check its status.");
}
Ok(())
}

Expand Down Expand Up @@ -485,50 +488,58 @@ fn apply_signal(pid: Pid, signal: Signal) -> Result<()> {
Ok(())
}

pub fn stop(name: &str, container: &Container, env: &Env) -> Result<()> {
if let Some(c_pid) = container.pid {
let pid = Pid::from_raw(c_pid);
match apply_signal(pid, Signal::SIGTERM) {
Ok(()) => {
for i in 1..=10 {
if let Some(c) =
json::check_existing_container(name, &env.bento_containers_env_path)
{
match c.state {
State::Stopped => return Ok(()),
_ => {
if i < 10 {
thread::sleep(time::Duration::from_secs(1));
} else {
apply_signal(pid, Signal::SIGKILL).with_context(|| {
format!("Failed to apply SIGKILL to pid {}", pid)
})?;
return Ok(());
pub fn stop(container: &Container, name: &str, env: &Env) -> Result<()> {
if container.state == State::Running {
if let Some(c_pid) = container.pid {
let pid = Pid::from_raw(c_pid);
match apply_signal(pid, Signal::SIGTERM) {
Ok(()) => {
for i in 1..=10 {
if let Some(c) =
json::check_existing_container(name, &env.bento_containers_env_path)
{
match c.state {
State::Stopped => return Ok(()),
_ => {
if i < 10 {
thread::sleep(time::Duration::from_secs(1));
} else {
apply_signal(pid, Signal::SIGKILL).with_context(|| {
format!("Failed to apply SIGKILL to pid {}", pid)
})?;
return Ok(());
}
}
}
}
}
thread::sleep(Duration::from_millis(200));
return Ok(());
}
thread::sleep(Duration::from_millis(200));
return Ok(());
Err(e) => return Err(e),
}
Err(e) => return Err(e),
}
} else {
return Err(anyhow!(ErrorKind::NotFound));
};
} else {
return Err(anyhow!(ErrorKind::NotFound));
};
anyhow::bail!("Container not currently running.");
}
}

pub fn kill_proc(container: &Container) -> Result<()> {
if let Some(c_pid) = container.pid {
let pid = Pid::from_raw(c_pid);
match apply_signal(pid, Signal::SIGKILL) {
Ok(()) => return Ok(()),
Err(e) => return Err(e),
}
pub fn kill_container(container: &Container) -> Result<()> {
if container.state == State::Running {
if let Some(c_pid) = container.pid {
let pid = Pid::from_raw(c_pid);
match apply_signal(pid, Signal::SIGKILL) {
Ok(()) => return Ok(()),
Err(e) => return Err(e),
}
} else {
return Err(anyhow!(ErrorKind::NotFound));
};
} else {
return Err(anyhow!(ErrorKind::NotFound));
};
anyhow::bail!("Container not currently running.");
}
}

pub fn exec(
Expand Down Expand Up @@ -763,4 +774,62 @@ mod tests {
assert_eq!(!containers_dir.exists(), true);
Ok(())
}
#[test]
fn container_limited_from_starting_by_state() {
let container: Container = Container {
dir: String::from("temp"),
state: State::Running,
pid: None,
};
let env = Env {
bento_dir: PathBuf::from("/test_dir"),
bento_image_env_path: PathBuf::from("/test_image_path"),
bento_containers_env_path: PathBuf::from("/test_container_path"),
};

let result = start(&container, &"container_name", &env);

assert!(result.is_err());

let error = result.unwrap_err();
assert_eq!(
error.to_string(),
"Container failed to start! Check its status."
);
}
#[test]
fn container_must_be_running_to_stop() {
let container: Container = Container {
dir: String::from("temp"),
state: State::Stopped,
pid: None,
};
let env = Env {
bento_dir: PathBuf::from("/test_dir"),
bento_image_env_path: PathBuf::from("/test_image_path"),
bento_containers_env_path: PathBuf::from("/test_container_path"),
};

let result = stop(&container, &"container_name", &env);

assert!(result.is_err());

let error = result.unwrap_err();
assert_eq!(error.to_string(), "Container not currently running.");
}
#[test]
fn container_must_be_running_to_kill() {
let container: Container = Container {
dir: String::from("temp"),
state: State::Stopped,
pid: None,
};

let result = kill_container(&container);

assert!(result.is_err());

let error = result.unwrap_err();
assert_eq!(error.to_string(), "Container not currently running.");
}
}