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

Make container commands more suitable for use as a library #314

Merged
merged 10 commits into from
Sep 24, 2021
Merged
21 changes: 3 additions & 18 deletions src/commands/create.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,23 +33,6 @@ pub struct Create {
// it is running, it is just another process, and has attributes such as pid, file descriptors, etc.
// associated with it like any other process.
impl Create {
/// instant Create Command
pub fn new(
container_id: String,
pid_file: Option<PathBuf>,
bundle: PathBuf,
console_socket: Option<PathBuf>,
preserve_fds: i32,
) -> Self {
Self {
pid_file,
bundle,
console_socket,
container_id,
preserve_fds,
}
}
/// Starts a new container process
pub fn exec(&self, root_path: PathBuf, systemd_cgroup: bool) -> Result<()> {
let syscall = create_syscall();
ContainerBuilder::new(self.container_id.clone(), syscall.as_ref())
Expand All @@ -59,6 +42,8 @@ impl Create {
.with_preserved_fds(self.preserve_fds)
.as_init(&self.bundle)
.with_systemd(systemd_cgroup)
.build()
.build()?;

Ok(())
}
}
85 changes: 8 additions & 77 deletions src/commands/delete.rs
Original file line number Diff line number Diff line change
@@ -1,15 +1,7 @@
use std::fs;
use std::path::PathBuf;

use anyhow::{bail, Context, Result};
use crate::commands::load_container;
use anyhow::{Context, Result};
use clap::Clap;
use nix::sys::signal::Signal;

use crate::container::{Container, ContainerStatus};
use crate::hooks;
use crate::utils;
use cgroups;
use nix::sys::signal as nix_signal;
use std::path::PathBuf;

#[derive(Clap, Debug)]
pub struct Delete {
Expand All @@ -21,72 +13,11 @@ pub struct Delete {
}

impl Delete {
/// instant Delete Command
///
/// This method is provided for those using `youki` as a library to enable the use of the
/// Delete command to remove containers.
pub fn new(container_id: String, force: bool) -> Self {
Self {
container_id,
force,
}
}

pub fn exec(&self, root_path: PathBuf, systemd_cgroup: bool) -> Result<()> {
pub fn exec(&self, root_path: PathBuf) -> Result<()> {
log::debug!("start deleting {}", self.container_id);
// 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(&self.container_id);
if !container_root.exists() {
bail!("{} doesn't exist.", self.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.
log::debug!("load the container from {:?}", container_root);
let mut container = Container::load(container_root)?.refresh_status()?;
if container.can_kill() && self.force {
let sig = Signal::SIGKILL;
log::debug!("kill signal {} to {}", sig, container.pid().unwrap());
nix_signal::kill(container.pid().unwrap(), sig)?;
container = container.update_status(ContainerStatus::Stopped);
container.save()?;
}
log::debug!("container status: {:?}", container.status());
if container.can_delete() {
if container.root.exists() {
let config_absolute_path = container.root.join("config.json");
log::debug!("load spec from {:?}", config_absolute_path);
let spec = oci_spec::runtime::Spec::load(config_absolute_path)?;
log::debug!("spec: {:?}", spec);

// remove the directory storing container state
log::debug!("remove dir {:?}", container.root);
fs::remove_dir_all(&container.root)?;

let cgroups_path = utils::get_cgroup_path(
&spec.linux.context("no linux in spec")?.cgroups_path,
container.id(),
);

// 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 =
cgroups::common::create_cgroup_manager(cgroups_path, systemd_cgroup)?;
cmanager.remove()?;

if let Some(hooks) = spec.hooks.as_ref() {
hooks::run_hooks(hooks.poststop.as_ref(), Some(&container))
.with_context(|| "Failed to run post stop hooks")?;
}
}
std::process::exit(0)
} else {
bail!(
"{} could not be deleted because it was {:?}",
container.id(),
container.status()
)
}
let mut container = load_container(root_path, self.container_id.as_str())?;
container
.delete(self.force)
.with_context(|| format!("failed to delete container {}", self.container_id))
}
}
49 changes: 7 additions & 42 deletions src/commands/events.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,9 @@
use crate::utils;
use cgroups::common;
use clap::Clap;
use std::{path::PathBuf, thread, time::Duration};
use std::path::PathBuf;

use anyhow::{bail, Context, Result};
use anyhow::{Context, Result};

use crate::container::{Container, ContainerStatus};
use crate::commands::load_container;

#[derive(Clap, Debug)]
pub struct Events {
Expand All @@ -22,42 +20,9 @@ pub struct Events {

impl Events {
pub fn exec(&self, root_path: PathBuf) -> Result<()> {
let container_dir = root_path.join(&self.container_id);
if !container_dir.exists() {
log::debug!("{:?}", container_dir);
bail!("{} doesn't exist.", self.container_id)
}

let container = Container::load(container_dir)?.refresh_status()?;
if !container.state.status.eq(&ContainerStatus::Running) {
bail!("{} is not in running state", self.container_id);
}

let cgroups_path = utils::get_cgroup_path(
&container
.spec()?
.linux
.context("no linux in spec")?
.cgroups_path,
&self.container_id,
);
let use_systemd = container
.systemd()
.context("Could not determine cgroup manager")?;

let cgroup_manager = common::create_cgroup_manager(cgroups_path, use_systemd)?;
match self.stats {
true => {
let stats = cgroup_manager.stats()?;
println!("{}", serde_json::to_string_pretty(&stats)?);
}
false => loop {
let stats = cgroup_manager.stats()?;
println!("{}", serde_json::to_string_pretty(&stats)?);
thread::sleep(Duration::from_secs(self.interval as u64));
},
}

Ok(())
let mut container = load_container(root_path, &self.container_id)?;
container
.events(self.interval, self.stats)
.with_context(|| format!("failed to get events from container {}", self.container_id))
}
}
41 changes: 9 additions & 32 deletions src/commands/kill.rs
Original file line number Diff line number Diff line change
@@ -1,14 +1,10 @@
//! Contains functionality of kill container command
use std::{fs, path::PathBuf};
use std::path::PathBuf;

use anyhow::{bail, Result};
use anyhow::{Context, Result};
use clap::Clap;
use nix::sys::signal as nix_signal;

use crate::{
container::{Container, ContainerStatus},
signal::ToSignal,
};
use crate::{commands::load_container, signal::ToSignal};

#[derive(Clap, Debug)]
pub struct Kill {
Expand All @@ -19,30 +15,11 @@ pub struct Kill {

impl Kill {
pub fn exec(&self, root_path: PathBuf) -> Result<()> {
// 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(&self.container_id);
if !container_root.exists() {
bail!("{} doesn't exist.", self.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 = self.signal.to_signal()?;
log::debug!("kill signal {} to {}", sig, container.pid().unwrap());
nix_signal::kill(container.pid().unwrap(), sig)?;
container.update_status(ContainerStatus::Stopped).save()?;
std::process::exit(0)
} else {
bail!(
"{} could not be killed because it was {:?}",
container.id(),
container.status()
)
}
let mut container = load_container(root_path, &self.container_id)?;
let signal = self
.signal
.to_signal()
.with_context(|| format!("signal {} is unknown", self.signal))?;
container.kill(signal)
}
}
2 changes: 1 addition & 1 deletion src/commands/list.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ impl List {
continue;
}

let container = Container::load(container_dir)?.refresh_status()?;
let container = Container::load(container_dir)?;
let pid = if let Some(pid) = container.pid() {
pid.to_string()
} else {
Expand Down
19 changes: 19 additions & 0 deletions src/commands/mod.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
use anyhow::{bail, Context, Result};
use std::{fs, path::Path};

use crate::container::Container;

pub mod create;
pub mod delete;
pub mod events;
Expand All @@ -12,3 +17,17 @@ pub mod run;
pub mod spec_json;
pub mod start;
pub mod state;

fn load_container<P: AsRef<Path>>(root_path: P, container_id: &str) -> Result<Container> {
// resolves relative paths, symbolic links etc. and get complete path
let root_path = fs::canonicalize(&root_path)
.with_context(|| format!("failed to canonicalize {}", root_path.as_ref().display()))?;
// the state of the container is stored in a directory named after the container id
let container_root = root_path.join(container_id);
if !container_root.exists() {
bail!("{} does not exist.", container_id)
}

Container::load(container_root)
.with_context(|| format!("could not load state for container {}", container_id))
}
50 changes: 7 additions & 43 deletions src/commands/pause.rs
Original file line number Diff line number Diff line change
@@ -1,16 +1,10 @@
//! Contains functionality of pause container command
use std::fs::canonicalize;
use crate::commands::load_container;
use std::path::PathBuf;

use anyhow::{bail, Context, Result};
use anyhow::{Context, Result};
use clap::Clap;

use crate::container::Container;
use crate::container::ContainerStatus;
use crate::utils;
use cgroups;
use cgroups::common::FreezerState;

/// Structure to implement pause command
#[derive(Clap, Debug)]
pub struct Pause {
Expand All @@ -24,41 +18,11 @@ pub struct Pause {
// https://man7.org/linux/man-pages/man7/cgroups.7.html
// https://www.kernel.org/doc/Documentation/cgroup-v1/freezer-subsystem.txt
impl Pause {
/// Suspend the running container
pub fn exec(&self, root_path: PathBuf, systemd_cgroup: bool) -> Result<()> {
pub fn exec(&self, root_path: PathBuf) -> Result<()> {
log::debug!("start pausing container {}", self.container_id);
let root_path = canonicalize(root_path)?;
let container_root = root_path.join(&self.container_id);
if !container_root.exists() {
bail!("{} doesn't exist.", self.container_id)
}

// populate data in a container structure from its file
let container = Container::load(container_root)?.refresh_status()?;
// check if a container is pauseable :
// for example, a stopped container cannot be paused
if !container.can_pause() {
bail!(
"{} could not be paused because it was {:?}",
self.container_id,
container.status()
);
}

let spec = container.spec()?;
let cgroups_path = utils::get_cgroup_path(
&spec.linux.context("no linux in spec")?.cgroups_path,
&self.container_id,
);
// create cgroup manager structure from the config at the path
let cmanager = cgroups::common::create_cgroup_manager(cgroups_path, systemd_cgroup)?;
// freeze the container
cmanager.freeze(FreezerState::Frozen)?;

log::debug!("saving paused status");
container.update_status(ContainerStatus::Paused).save()?;

log::debug!("container {} paused", self.container_id);
Ok(())
let mut container = load_container(root_path, &self.container_id)?;
container
.pause()
.with_context(|| format!("failed to pause container {}", self.container_id))
}
}
2 changes: 1 addition & 1 deletion src/commands/ps.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ impl Ps {
if !container_root.exists() {
bail!("{} doesn't exist.", self.container_id)
}
let container = Container::load(container_root)?.refresh_status()?;
let container = Container::load(container_root)?;
if container.root.exists() {
let config_absolute_path = container.root.join("config.json");
log::debug!("load spec from {:?}", config_absolute_path);
Expand Down
Loading