From 34f832fe566fc8d1245e2f6083073938db323326 Mon Sep 17 00:00:00 2001 From: Furisto <24721048+Furisto@users.noreply.github.com> Date: Mon, 20 Sep 2021 22:13:04 +0200 Subject: [PATCH 01/10] Add delete command to container --- src/commands/delete.rs | 74 +++---------------------------- src/container/container_delete.rs | 67 ++++++++++++++++++++++++++++ src/container/mod.rs | 1 + src/main.rs | 2 +- 4 files changed, 76 insertions(+), 68 deletions(-) create mode 100644 src/container/container_delete.rs diff --git a/src/commands/delete.rs b/src/commands/delete.rs index a0591b210..2cc6fa5af 100644 --- a/src/commands/delete.rs +++ b/src/commands/delete.rs @@ -1,15 +1,7 @@ -use std::fs; -use std::path::PathBuf; - +use crate::container::Container; use anyhow::{bail, 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 { @@ -21,18 +13,7 @@ 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 @@ -43,50 +24,9 @@ impl Delete { // 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 = Container::load(container_root)?; + container + .delete(self.force) + .with_context(|| format!("failed to delete container {}", self.container_id)) } } diff --git a/src/container/container_delete.rs b/src/container/container_delete.rs new file mode 100644 index 000000000..75619cd2b --- /dev/null +++ b/src/container/container_delete.rs @@ -0,0 +1,67 @@ +use super::{Container, ContainerStatus}; +use crate::hooks; +use crate::utils; +use anyhow::{bail, Context, Result}; +use cgroups; +use nix::sys::signal; +use oci_spec::runtime::Spec; +use std::fs; + +impl Container { + pub fn delete(&mut self, force: bool) -> Result<()> { + self.refresh_status() + .with_context(|| format!("failed to refresh status of container {}", self.state.id))?; + if self.can_kill() && force { + let sig = signal::Signal::SIGKILL; + log::debug!("kill signal {} to {}", sig, self.pid().unwrap()); + signal::kill(self.pid().unwrap(), sig)?; + self.update_status(ContainerStatus::Stopped); + self.save()?; + } + log::debug!("container status: {:?}", self.status()); + if self.can_delete() { + if self.root.exists() { + let config_absolute_path = self.root.join("config.json"); + log::debug!("load spec from {:?}", config_absolute_path); + let spec = Spec::load(config_absolute_path)?; + log::debug!("spec: {:?}", spec); + + // remove the directory storing container state + log::debug!("remove dir {:?}", self.root); + fs::remove_dir_all(&self.root).with_context(|| { + format!("failed to remove container dir {}", self.root.display()) + })?; + + let cgroups_path = utils::get_cgroup_path( + &spec.linux.context("no linux in spec")?.cgroups_path, + self.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 use_systemd = self + .state + .use_systemd + .context("container state does not contain cgroup manager")?; + let cmanager = cgroups::common::create_cgroup_manager(&cgroups_path, use_systemd) + .with_context(|| format!("failed to create cgroup manager"))?; + cmanager.remove().with_context(|| { + format!("failed to remove cgroup {}", cgroups_path.display()) + })?; + + if let Some(hooks) = spec.hooks.as_ref() { + hooks::run_hooks(hooks.poststop.as_ref(), Some(self)) + .with_context(|| "failed to run post stop hooks")?; + } + } + std::process::exit(0) + } else { + bail!( + "{} could not be deleted because it was {:?}", + self.id(), + self.status() + ) + } + } +} diff --git a/src/container/mod.rs b/src/container/mod.rs index 04e21de25..bfdd185c3 100644 --- a/src/container/mod.rs +++ b/src/container/mod.rs @@ -8,6 +8,7 @@ pub mod builder; mod builder_impl; #[allow(clippy::module_inception)] mod container; +pub mod container_delete; pub mod init_builder; pub mod state; pub mod tenant_builder; diff --git a/src/main.rs b/src/main.rs index 64fdb47a2..a82a946c8 100644 --- a/src/main.rs +++ b/src/main.rs @@ -101,7 +101,7 @@ fn main() -> Result<()> { SubCommand::Run(run) => run.exec(root_path, systemd_cgroup), SubCommand::Exec(exec) => exec.exec(root_path), SubCommand::Kill(kill) => kill.exec(root_path), - SubCommand::Delete(delete) => delete.exec(root_path, systemd_cgroup), + SubCommand::Delete(delete) => delete.exec(root_path), SubCommand::State(state) => state.exec(root_path), SubCommand::Info(info) => info.exec(), SubCommand::List(list) => list.exec(root_path), From 9b02987449624b1d6898e3cb5824d3c8d0743e82 Mon Sep 17 00:00:00 2001 From: Furisto <24721048+Furisto@users.noreply.github.com> Date: Mon, 20 Sep 2021 22:26:35 +0200 Subject: [PATCH 02/10] Add kill command to container --- src/commands/kill.rs | 26 +++++++------------------- src/container/container_kill.rs | 20 ++++++++++++++++++++ src/container/mod.rs | 1 + 3 files changed, 28 insertions(+), 19 deletions(-) create mode 100644 src/container/container_kill.rs diff --git a/src/commands/kill.rs b/src/commands/kill.rs index 3dd88a420..47e2adba8 100644 --- a/src/commands/kill.rs +++ b/src/commands/kill.rs @@ -1,14 +1,10 @@ //! Contains functionality of kill container command use std::{fs, path::PathBuf}; -use anyhow::{bail, Result}; +use anyhow::{bail, Context, Result}; use clap::Clap; -use nix::sys::signal as nix_signal; -use crate::{ - container::{Container, ContainerStatus}, - signal::ToSignal, -}; +use crate::{container::Container, signal::ToSignal}; #[derive(Clap, Debug)] pub struct Kill { @@ -31,18 +27,10 @@ impl Kill { // 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 signal = self + .signal + .to_signal() + .with_context(|| format!("signal {} is unknown", self.signal))?; + container.kill(signal) } } diff --git a/src/container/container_kill.rs b/src/container/container_kill.rs new file mode 100644 index 000000000..a8d261320 --- /dev/null +++ b/src/container/container_kill.rs @@ -0,0 +1,20 @@ +use super::{Container, ContainerStatus}; +use anyhow::{bail, Result}; +use nix::sys::signal::{self, Signal}; + +impl Container { + pub fn kill(&self, signal: Signal) -> Result<()> { + if self.can_kill() { + log::debug!("kill signal {} to {}", signal, self.pid().unwrap()); + signal::kill(self.pid().unwrap(), signal)?; + self.update_status(ContainerStatus::Stopped).save()?; + std::process::exit(0) + } else { + bail!( + "{} could not be killed because it was {:?}", + self.id(), + self.status() + ) + } + } +} diff --git a/src/container/mod.rs b/src/container/mod.rs index bfdd185c3..d7d642cf0 100644 --- a/src/container/mod.rs +++ b/src/container/mod.rs @@ -9,6 +9,7 @@ mod builder_impl; #[allow(clippy::module_inception)] mod container; pub mod container_delete; +pub mod container_kill; pub mod init_builder; pub mod state; pub mod tenant_builder; From 9d1363dbcf8b1fc0a7989e88b9ac23f510f72357 Mon Sep 17 00:00:00 2001 From: Furisto <24721048+Furisto@users.noreply.github.com> Date: Mon, 20 Sep 2021 23:00:46 +0200 Subject: [PATCH 03/10] Add pause command to container --- src/commands/pause.rs | 40 ++++---------------------------- src/container/container_pause.rs | 38 ++++++++++++++++++++++++++++++ src/container/mod.rs | 1 + src/main.rs | 2 +- 4 files changed, 45 insertions(+), 36 deletions(-) create mode 100644 src/container/container_pause.rs diff --git a/src/commands/pause.rs b/src/commands/pause.rs index 51d1d48a9..2db2a8fbd 100644 --- a/src/commands/pause.rs +++ b/src/commands/pause.rs @@ -1,16 +1,11 @@ //! Contains functionality of pause container command +use crate::container::Container; use std::fs::canonicalize; use std::path::PathBuf; -use anyhow::{bail, Context, Result}; +use anyhow::{bail, 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 { @@ -24,8 +19,7 @@ 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); @@ -34,31 +28,7 @@ impl Pause { } // 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 = Container::load(container_root)?; + container.pause() } } diff --git a/src/container/container_pause.rs b/src/container/container_pause.rs new file mode 100644 index 000000000..aee802ee5 --- /dev/null +++ b/src/container/container_pause.rs @@ -0,0 +1,38 @@ +use crate::utils; + +use super::{Container, ContainerStatus}; +use anyhow::{bail, Context, Result}; +use cgroups::common::FreezerState; + +impl Container { + pub fn pause(&mut self) -> Result<()> { + self.refresh_status() + .context("failed to refresh container status")?; + + if !self.can_pause() { + bail!( + "{} could not be paused because it was {:?}", + self.id(), + self.status() + ); + } + + let spec = self.spec()?; + let cgroups_path = utils::get_cgroup_path( + &spec.linux.context("no linux in spec")?.cgroups_path, + &self.id(), + ); + + let use_systemd = self + .systemd() + .context("container state does not contain cgroup manager")?; + let cmanager = cgroups::common::create_cgroup_manager(cgroups_path, use_systemd)?; + cmanager.freeze(FreezerState::Frozen)?; + + log::debug!("saving paused status"); + self.update_status(ContainerStatus::Paused).save()?; + + log::debug!("container {} paused", self.id()); + Ok(()) + } +} diff --git a/src/container/mod.rs b/src/container/mod.rs index d7d642cf0..e53c665cb 100644 --- a/src/container/mod.rs +++ b/src/container/mod.rs @@ -10,6 +10,7 @@ mod builder_impl; mod container; pub mod container_delete; pub mod container_kill; +pub mod container_pause; pub mod init_builder; pub mod state; pub mod tenant_builder; diff --git a/src/main.rs b/src/main.rs index a82a946c8..f8c7cecb2 100644 --- a/src/main.rs +++ b/src/main.rs @@ -106,7 +106,7 @@ fn main() -> Result<()> { SubCommand::Info(info) => info.exec(), SubCommand::List(list) => list.exec(root_path), SubCommand::Spec(spec) => spec.exec(), - SubCommand::Pause(pause) => pause.exec(root_path, systemd_cgroup), + SubCommand::Pause(pause) => pause.exec(root_path), SubCommand::Resume(resume) => resume.exec(root_path, systemd_cgroup), SubCommand::Events(events) => events.exec(root_path), SubCommand::Ps(ps) => ps.exec(root_path), From 5108a92a3065f6ea15d4ca61f04a1b825d4a4152 Mon Sep 17 00:00:00 2001 From: Furisto <24721048+Furisto@users.noreply.github.com> Date: Mon, 20 Sep 2021 23:22:45 +0200 Subject: [PATCH 04/10] Add resume command to container --- src/commands/resume.rs | 36 ++++---------------------- src/container/container_resume.rs | 42 +++++++++++++++++++++++++++++++ src/container/mod.rs | 1 + src/main.rs | 2 +- 4 files changed, 49 insertions(+), 32 deletions(-) create mode 100644 src/container/container_resume.rs diff --git a/src/commands/resume.rs b/src/commands/resume.rs index 4a3fa7186..447b1b22e 100644 --- a/src/commands/resume.rs +++ b/src/commands/resume.rs @@ -6,10 +6,6 @@ use anyhow::{bail, 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 resume command #[derive(Clap, Debug)] @@ -24,7 +20,7 @@ pub struct Resume { // https://man7.org/linux/man-pages/man7/cgroups.7.html // https://www.kernel.org/doc/Documentation/cgroup-v1/freezer-subsystem.txt impl Resume { - pub fn exec(&self, root_path: PathBuf, systemd_cgroup: bool) -> Result<()> { + pub fn exec(&self, root_path: PathBuf) -> Result<()> { log::debug!("start resuming container {}", self.container_id); let root_path = canonicalize(root_path)?; let container_root = root_path.join(&self.container_id); @@ -32,31 +28,9 @@ impl Resume { bail!("{} doesn't exist.", self.container_id) } - let container = Container::load(container_root)?.refresh_status()?; - // check if container can be resumed : - // for example, a running process cannot be resumed - if !container.can_resume() { - bail!( - "{} could not be resumed 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)?; - // resume the frozen container - cmanager.freeze(FreezerState::Thawed)?; - - log::debug!("saving running status"); - container.update_status(ContainerStatus::Running).save()?; - - log::debug!("container {} resumed", self.container_id); - Ok(()) + let mut container = Container::load(container_root)?; + container + .resume() + .with_context(|| format!("failed to resume container {}", self.container_id)) } } diff --git a/src/container/container_resume.rs b/src/container/container_resume.rs new file mode 100644 index 000000000..b99a0db9c --- /dev/null +++ b/src/container/container_resume.rs @@ -0,0 +1,42 @@ +use crate::utils; + +use super::{Container, ContainerStatus}; + +use anyhow::{bail, Context, Result}; +use cgroups::common::FreezerState; + +impl Container { + pub fn resume(&mut self) -> Result<()> { + self.refresh_status() + .with_context(|| format!("failed to refresh status of container"))?; + // check if container can be resumed : + // for example, a running process cannot be resumed + if !self.can_resume() { + bail!( + "{} could not be resumed because it was {:?}", + self.id(), + self.status() + ); + } + + let spec = self.spec()?; + let cgroups_path = utils::get_cgroup_path( + &spec.linux.context("no linux in spec")?.cgroups_path, + &self.id(), + ); + + // create cgroup manager structure from the config at the path + let use_systemd = self + .systemd() + .context("container state does not contain cgroup manager")?; + let cmanager = cgroups::common::create_cgroup_manager(cgroups_path, use_systemd)?; + // resume the frozen container + cmanager.freeze(FreezerState::Thawed)?; + + log::debug!("saving running status"); + self.update_status(ContainerStatus::Running).save()?; + + log::debug!("container {} resumed", self.id()); + Ok(()) + } +} diff --git a/src/container/mod.rs b/src/container/mod.rs index e53c665cb..7cc86eb9f 100644 --- a/src/container/mod.rs +++ b/src/container/mod.rs @@ -11,6 +11,7 @@ mod container; pub mod container_delete; pub mod container_kill; pub mod container_pause; +pub mod container_resume; pub mod init_builder; pub mod state; pub mod tenant_builder; diff --git a/src/main.rs b/src/main.rs index f8c7cecb2..b6f8ded93 100644 --- a/src/main.rs +++ b/src/main.rs @@ -107,7 +107,7 @@ fn main() -> Result<()> { SubCommand::List(list) => list.exec(root_path), SubCommand::Spec(spec) => spec.exec(), SubCommand::Pause(pause) => pause.exec(root_path), - SubCommand::Resume(resume) => resume.exec(root_path, systemd_cgroup), + SubCommand::Resume(resume) => resume.exec(root_path), SubCommand::Events(events) => events.exec(root_path), SubCommand::Ps(ps) => ps.exec(root_path), } From b3f642aad98ce54ef88b11a194e5ea8a91ca5db9 Mon Sep 17 00:00:00 2001 From: Furisto <24721048+Furisto@users.noreply.github.com> Date: Mon, 20 Sep 2021 23:36:42 +0200 Subject: [PATCH 05/10] Add events command to container --- src/commands/events.rs | 41 +++++-------------------------- src/container/container_events.rs | 39 +++++++++++++++++++++++++++++ src/container/mod.rs | 1 + 3 files changed, 46 insertions(+), 35 deletions(-) create mode 100644 src/container/container_events.rs diff --git a/src/commands/events.rs b/src/commands/events.rs index 80555393d..e3ef90edb 100644 --- a/src/commands/events.rs +++ b/src/commands/events.rs @@ -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 crate::container::{Container, ContainerStatus}; +use crate::container::Container; #[derive(Clap, Debug)] pub struct Events { @@ -28,36 +26,9 @@ impl Events { 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 = Container::load(container_dir)?; + container + .events(self.interval, self.stats) + .with_context(|| format!("failed to get events from container {}", self.container_id)) } } diff --git a/src/container/container_events.rs b/src/container/container_events.rs new file mode 100644 index 000000000..7b0c323a0 --- /dev/null +++ b/src/container/container_events.rs @@ -0,0 +1,39 @@ +use std::{thread, time::Duration}; + +use crate::utils; + +use super::{Container, ContainerStatus}; +use anyhow::{bail, Context, Result}; + +impl Container { + pub fn events(&mut self, interval: u32, stats: bool) -> Result<()> { + self.refresh_status() + .context("failed to refresh container status")?; + if !self.state.status.eq(&ContainerStatus::Running) { + bail!("{} is not in running state", self.id()); + } + + let cgroups_path = utils::get_cgroup_path( + &self.spec()?.linux.context("no linux in spec")?.cgroups_path, + &self.id(), + ); + let use_systemd = self + .systemd() + .context("Could not determine cgroup manager")?; + + let cgroup_manager = cgroups::common::create_cgroup_manager(cgroups_path, use_systemd)?; + match 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(interval as u64)); + }, + } + + Ok(()) + } +} diff --git a/src/container/mod.rs b/src/container/mod.rs index 7cc86eb9f..84671b118 100644 --- a/src/container/mod.rs +++ b/src/container/mod.rs @@ -9,6 +9,7 @@ mod builder_impl; #[allow(clippy::module_inception)] mod container; pub mod container_delete; +pub mod container_events; pub mod container_kill; pub mod container_pause; pub mod container_resume; From feacf8ef60f1d77f1a3304a20ce7de25fd6b0194 Mon Sep 17 00:00:00 2001 From: Furisto <24721048+Furisto@users.noreply.github.com> Date: Tue, 21 Sep 2021 21:37:12 +0200 Subject: [PATCH 06/10] Add start command to container --- src/commands/start.rs | 45 +++------------------------ src/container/container_start.rs | 52 ++++++++++++++++++++++++++++++++ src/container/mod.rs | 1 + 3 files changed, 58 insertions(+), 40 deletions(-) create mode 100644 src/container/container_start.rs diff --git a/src/commands/start.rs b/src/commands/start.rs index 75f393fa9..1756a272a 100644 --- a/src/commands/start.rs +++ b/src/commands/start.rs @@ -4,11 +4,8 @@ use std::path::PathBuf; use anyhow::{bail, Context, Result}; use clap::Clap; -use nix::unistd; -use crate::container::{Container, ContainerStatus}; -use crate::hooks; -use crate::notify_socket::{NotifySocket, NOTIFY_FILE}; +use crate::container::Container; #[derive(Clap, Debug)] pub struct Start { @@ -26,41 +23,9 @@ impl Start { if !container_root.exists() { bail!("{} doesn't exist.", self.container_id) } - let container = Container::load(container_root)?.refresh_status()?; - if !container.can_start() { - let err_msg = format!( - "{} could not be started because it was {:?}", - container.id(), - container.status() - ); - log::error!("{}", err_msg); - bail!(err_msg); - } - - let spec_path = container.root.join("config.json"); - let spec = oci_spec::runtime::Spec::load(spec_path).context("failed to load spec")?; - if let Some(hooks) = spec.hooks.as_ref() { - // While prestart is marked as deprecated in the OCI spec, the docker and integration test still - // uses it. - #[allow(deprecated)] - hooks::run_hooks(hooks.prestart.as_ref(), Some(&container)) - .with_context(|| "Failed to run pre start hooks")?; - } - - unistd::chdir(container.root.as_os_str())?; - - let mut notify_socket = NotifySocket::new(&container.root.join(NOTIFY_FILE)); - notify_socket.notify_container_start()?; - let container = container.update_status(ContainerStatus::Running); - container.save()?; - - // Run post start hooks. It runs after the container process is started. - // It is called in the Runtime Namespace. - if let Some(hooks) = spec.hooks.as_ref() { - hooks::run_hooks(hooks.poststart.as_ref(), Some(&container)) - .with_context(|| "Failed to run post start hooks")?; - } - - Ok(()) + let mut container = Container::load(container_root)?.refresh_status()?; + container + .start() + .with_context(|| format!("failed to start container {}", self.container_id)) } } diff --git a/src/container/container_start.rs b/src/container/container_start.rs new file mode 100644 index 000000000..5a4593322 --- /dev/null +++ b/src/container/container_start.rs @@ -0,0 +1,52 @@ +use crate::{ + hooks, + notify_socket::{NotifySocket, NOTIFY_FILE}, +}; + +use super::{Container, ContainerStatus}; +use anyhow::{bail, Context, Result}; +use nix::unistd; + +impl Container { + pub fn start(&mut self) -> Result<()> { + self.refresh_status() + .context("failed to refresh container status")?; + + if !self.can_start() { + let err_msg = format!( + "{} could not be started because it was {:?}", + self.id(), + self.status() + ); + log::error!("{}", err_msg); + bail!(err_msg); + } + + let spec = self + .spec() + .with_context(|| format!("failed to load runtime spec for container {}", self.id()))?; + if let Some(hooks) = spec.hooks.as_ref() { + // While prestart is marked as deprecated in the OCI spec, the docker and integration test still + // uses it. + #[allow(deprecated)] + hooks::run_hooks(hooks.prestart.as_ref(), Some(self)) + .with_context(|| "failed to run pre start hooks")?; + } + + unistd::chdir(self.root.as_os_str())?; + + let mut notify_socket = NotifySocket::new(&self.root.join(NOTIFY_FILE)); + notify_socket.notify_container_start()?; + self.update_status(ContainerStatus::Running); + self.save()?; + + // Run post start hooks. It runs after the container process is started. + // It is called in the runtime namespace. + if let Some(hooks) = spec.hooks.as_ref() { + hooks::run_hooks(hooks.poststart.as_ref(), Some(self)) + .with_context(|| "failed to run post start hooks")?; + } + + Ok(()) + } +} diff --git a/src/container/mod.rs b/src/container/mod.rs index 84671b118..d6ab211a1 100644 --- a/src/container/mod.rs +++ b/src/container/mod.rs @@ -13,6 +13,7 @@ pub mod container_events; pub mod container_kill; pub mod container_pause; pub mod container_resume; +pub mod container_start; pub mod init_builder; pub mod state; pub mod tenant_builder; From 933480600271759e99195e16a2e8085ef0aeec06 Mon Sep 17 00:00:00 2001 From: Furisto <24721048+Furisto@users.noreply.github.com> Date: Tue, 21 Sep 2021 23:03:12 +0200 Subject: [PATCH 07/10] Consolidate duplicated code --- src/commands/create.rs | 21 +++------------------ src/commands/delete.rs | 15 +++------------ src/commands/events.rs | 12 +++--------- src/commands/kill.rs | 19 ++++--------------- src/commands/mod.rs | 19 +++++++++++++++++++ src/commands/pause.rs | 18 ++++++------------ src/commands/resume.rs | 13 +++---------- src/commands/run.rs | 29 +++++++++++++++-------------- src/commands/start.rs | 14 +++----------- src/container/container_delete.rs | 12 +++++------- src/container/container_kill.rs | 6 ++++-- src/container/container_resume.rs | 2 +- src/container/init_builder.rs | 8 ++++---- 13 files changed, 73 insertions(+), 115 deletions(-) diff --git a/src/commands/create.rs b/src/commands/create.rs index da1f927a8..3fe7b3de6 100644 --- a/src/commands/create.rs +++ b/src/commands/create.rs @@ -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, - bundle: PathBuf, - console_socket: Option, - 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()) @@ -59,6 +42,8 @@ impl Create { .with_preserved_fds(self.preserve_fds) .as_init(&self.bundle) .with_systemd(systemd_cgroup) - .build() + .build()?; + + Ok(()) } } diff --git a/src/commands/delete.rs b/src/commands/delete.rs index 2cc6fa5af..48d029c26 100644 --- a/src/commands/delete.rs +++ b/src/commands/delete.rs @@ -1,5 +1,5 @@ -use crate::container::Container; -use anyhow::{bail, Context, Result}; +use crate::commands::load_container; +use anyhow::{Context, Result}; use clap::Clap; use std::path::PathBuf; @@ -15,16 +15,7 @@ pub struct Delete { impl Delete { 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)?; + 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)) diff --git a/src/commands/events.rs b/src/commands/events.rs index e3ef90edb..7e8687b32 100644 --- a/src/commands/events.rs +++ b/src/commands/events.rs @@ -1,9 +1,9 @@ use clap::Clap; use std::path::PathBuf; -use anyhow::{bail, Context, Result}; +use anyhow::{Context, Result}; -use crate::container::Container; +use crate::commands::load_container; #[derive(Clap, Debug)] pub struct Events { @@ -20,13 +20,7 @@ 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 mut container = Container::load(container_dir)?; + 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)) diff --git a/src/commands/kill.rs b/src/commands/kill.rs index 47e2adba8..ff91b4246 100644 --- a/src/commands/kill.rs +++ b/src/commands/kill.rs @@ -1,10 +1,10 @@ //! Contains functionality of kill container command -use std::{fs, path::PathBuf}; +use std::path::PathBuf; -use anyhow::{bail, Context, Result}; +use anyhow::{Context, Result}; use clap::Clap; -use crate::{container::Container, signal::ToSignal}; +use crate::{commands::load_container, signal::ToSignal}; #[derive(Clap, Debug)] pub struct Kill { @@ -15,18 +15,7 @@ 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()?; + let mut container = load_container(root_path, &self.container_id)?; let signal = self .signal .to_signal() diff --git a/src/commands/mod.rs b/src/commands/mod.rs index bcabba00c..eb545a900 100644 --- a/src/commands/mod.rs +++ b/src/commands/mod.rs @@ -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; @@ -12,3 +17,17 @@ pub mod run; pub mod spec_json; pub mod start; pub mod state; + +fn load_container>(root_path: P, container_id: &str) -> Result { + // 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)) +} diff --git a/src/commands/pause.rs b/src/commands/pause.rs index 2db2a8fbd..464ee35ca 100644 --- a/src/commands/pause.rs +++ b/src/commands/pause.rs @@ -1,9 +1,8 @@ //! Contains functionality of pause container command -use crate::container::Container; -use std::fs::canonicalize; +use crate::commands::load_container; use std::path::PathBuf; -use anyhow::{bail, Result}; +use anyhow::{Context, Result}; use clap::Clap; /// Structure to implement pause command @@ -21,14 +20,9 @@ pub struct Pause { impl Pause { 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 mut container = Container::load(container_root)?; - container.pause() + let mut container = load_container(root_path, &self.container_id)?; + container + .pause() + .with_context(|| format!("failed to pause container {}", self.container_id)) } } diff --git a/src/commands/resume.rs b/src/commands/resume.rs index 447b1b22e..b872840bc 100644 --- a/src/commands/resume.rs +++ b/src/commands/resume.rs @@ -1,11 +1,10 @@ //! Contains functionality of resume container command -use std::fs::canonicalize; use std::path::PathBuf; -use anyhow::{bail, Context, Result}; +use anyhow::{Context, Result}; use clap::Clap; -use crate::container::Container; +use crate::commands::load_container; /// Structure to implement resume command #[derive(Clap, Debug)] @@ -22,13 +21,7 @@ pub struct Resume { impl Resume { pub fn exec(&self, root_path: PathBuf) -> Result<()> { log::debug!("start resuming 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) - } - - let mut container = Container::load(container_root)?; + let mut container = load_container(root_path, &self.container_id)?; container .resume() .with_context(|| format!("failed to resume container {}", self.container_id)) diff --git a/src/commands/run.rs b/src/commands/run.rs index 17bb54946..683e6f8fa 100644 --- a/src/commands/run.rs +++ b/src/commands/run.rs @@ -1,8 +1,8 @@ use std::path::PathBuf; -use crate::commands::create::Create; -use crate::commands::start::Start; -use anyhow::Result; +use crate::container::builder::ContainerBuilder; +use crate::syscall::syscall::create_syscall; +use anyhow::{Context, Result}; use clap::Clap; /// Create and start a container. /// a shortcut for create followed by start. @@ -28,17 +28,18 @@ pub struct Run { impl Run { pub fn exec(&self, root_path: PathBuf, systemd_cgroup: bool) -> Result<()> { - Create::new( - self.container_id.clone(), - self.pid_file.clone(), - self.bundle.clone(), - self.console_socket.clone(), - self.preserve_fds, - ) - .exec(root_path.clone(), systemd_cgroup)?; + let syscall = create_syscall(); + let mut container = ContainerBuilder::new(self.container_id.clone(), syscall.as_ref()) + .with_pid_file(self.pid_file.as_ref()) + .with_console_socket(self.console_socket.as_ref()) + .with_root_path(root_path) + .with_preserved_fds(self.preserve_fds) + .as_init(&self.bundle) + .with_systemd(systemd_cgroup) + .build()?; - Start::new(self.container_id.clone()).exec(root_path)?; - - Ok(()) + container + .start() + .with_context(|| format!("failed to start container {}", self.container_id)) } } diff --git a/src/commands/start.rs b/src/commands/start.rs index 1756a272a..1da20729b 100644 --- a/src/commands/start.rs +++ b/src/commands/start.rs @@ -2,10 +2,10 @@ use std::path::PathBuf; -use anyhow::{bail, Context, Result}; +use anyhow::{Context, Result}; use clap::Clap; -use crate::container::Container; +use crate::commands::load_container; #[derive(Clap, Debug)] pub struct Start { @@ -14,16 +14,8 @@ pub struct Start { } impl Start { - pub fn new(container_id: String) -> Self { - Self { container_id } - } - pub fn exec(&self, root_path: PathBuf) -> Result<()> { - let container_root = root_path.join(&self.container_id); - if !container_root.exists() { - bail!("{} doesn't exist.", self.container_id) - } - let mut container = Container::load(container_root)?.refresh_status()?; + let mut container = load_container(root_path, &self.container_id)?; container .start() .with_context(|| format!("failed to start container {}", self.container_id)) diff --git a/src/container/container_delete.rs b/src/container/container_delete.rs index 75619cd2b..c22ef6ba4 100644 --- a/src/container/container_delete.rs +++ b/src/container/container_delete.rs @@ -4,13 +4,12 @@ use crate::utils; use anyhow::{bail, Context, Result}; use cgroups; use nix::sys::signal; -use oci_spec::runtime::Spec; use std::fs; impl Container { pub fn delete(&mut self, force: bool) -> Result<()> { self.refresh_status() - .with_context(|| format!("failed to refresh status of container {}", self.state.id))?; + .context("failed to refresh container status")?; if self.can_kill() && force { let sig = signal::Signal::SIGKILL; log::debug!("kill signal {} to {}", sig, self.pid().unwrap()); @@ -21,9 +20,9 @@ impl Container { log::debug!("container status: {:?}", self.status()); if self.can_delete() { if self.root.exists() { - let config_absolute_path = self.root.join("config.json"); - log::debug!("load spec from {:?}", config_absolute_path); - let spec = Spec::load(config_absolute_path)?; + let spec = self.spec().with_context(|| { + format!("failed to load runtime spec for container {}", self.id()) + })?; log::debug!("spec: {:?}", spec); // remove the directory storing container state @@ -41,8 +40,7 @@ impl Container { // check https://man7.org/linux/man-pages/man7/cgroups.7.html // creating and removing cgroups section for more information on cgroups let use_systemd = self - .state - .use_systemd + .systemd() .context("container state does not contain cgroup manager")?; let cmanager = cgroups::common::create_cgroup_manager(&cgroups_path, use_systemd) .with_context(|| format!("failed to create cgroup manager"))?; diff --git a/src/container/container_kill.rs b/src/container/container_kill.rs index a8d261320..eb2d711c3 100644 --- a/src/container/container_kill.rs +++ b/src/container/container_kill.rs @@ -1,9 +1,11 @@ use super::{Container, ContainerStatus}; -use anyhow::{bail, Result}; +use anyhow::{bail, Context, Result}; use nix::sys::signal::{self, Signal}; impl Container { - pub fn kill(&self, signal: Signal) -> Result<()> { + pub fn kill(&mut self, signal: Signal) -> Result<()> { + self.refresh_status() + .context("failed to refresh container status")?; if self.can_kill() { log::debug!("kill signal {} to {}", signal, self.pid().unwrap()); signal::kill(self.pid().unwrap(), signal)?; diff --git a/src/container/container_resume.rs b/src/container/container_resume.rs index b99a0db9c..be2b8a8c3 100644 --- a/src/container/container_resume.rs +++ b/src/container/container_resume.rs @@ -8,7 +8,7 @@ use cgroups::common::FreezerState; impl Container { pub fn resume(&mut self) -> Result<()> { self.refresh_status() - .with_context(|| format!("failed to refresh status of container"))?; + .with_context(|| format!("failed to refresh container status"))?; // check if container can be resumed : // for example, a running process cannot be resumed if !self.can_resume() { diff --git a/src/container/init_builder.rs b/src/container/init_builder.rs index acb87ca37..5de542452 100644 --- a/src/container/init_builder.rs +++ b/src/container/init_builder.rs @@ -38,12 +38,12 @@ impl<'a> InitContainerBuilder<'a> { } /// Creates a new container - pub fn build(self) -> Result<()> { + pub fn build(self) -> Result { let spec = self.load_spec()?; let container_dir = self.create_container_dir()?; self.save_spec(&spec, &container_dir)?; - let container_state = self + let mut container_state = self .create_container_state(&container_dir)? .set_systemd(self.use_systemd) .set_annotations(spec.annotations.clone()); @@ -77,12 +77,12 @@ impl<'a> InitContainerBuilder<'a> { rootfs, rootless, notify_path, - container: Some(container_state), + container: Some(container_state.clone()), preserve_fds: self.base.preserve_fds, }; builder_impl.create()?; - Ok(()) + Ok(container_state.refresh_status()?) } fn create_container_dir(&self) -> Result { From 42a17b8594ebd0d8db99913e84d88e875f574819 Mon Sep 17 00:00:00 2001 From: Furisto <24721048+Furisto@users.noreply.github.com> Date: Thu, 23 Sep 2021 23:05:35 +0200 Subject: [PATCH 08/10] Rework container state management --- src/commands/list.rs | 2 +- src/commands/ps.rs | 2 +- src/commands/state.rs | 2 +- src/container/builder_impl.rs | 4 +- src/container/container.rs | 149 +++++++++++++++--------------- src/container/container_delete.rs | 3 +- src/container/container_kill.rs | 2 +- src/container/container_pause.rs | 2 +- src/container/container_resume.rs | 2 +- src/container/container_start.rs | 5 +- src/container/init_builder.rs | 10 +- src/container/state.rs | 4 +- src/container/tenant_builder.rs | 2 +- 13 files changed, 93 insertions(+), 96 deletions(-) diff --git a/src/commands/list.rs b/src/commands/list.rs index a6294575e..bbc4df732 100644 --- a/src/commands/list.rs +++ b/src/commands/list.rs @@ -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 { diff --git a/src/commands/ps.rs b/src/commands/ps.rs index 210553da0..5dcc7c60f 100644 --- a/src/commands/ps.rs +++ b/src/commands/ps.rs @@ -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); diff --git a/src/commands/state.rs b/src/commands/state.rs index 278e8598f..55fd4cf74 100644 --- a/src/commands/state.rs +++ b/src/commands/state.rs @@ -16,7 +16,7 @@ impl State { pub fn exec(&self, root_path: PathBuf) -> Result<()> { let root_path = fs::canonicalize(root_path)?; let container_root = root_path.join(&self.container_id); - let container = Container::load(container_root)?.refresh_status()?; + let container = Container::load(container_root)?; println!("{}", serde_json::to_string_pretty(&container.state)?); std::process::exit(0); } diff --git a/src/container/builder_impl.rs b/src/container/builder_impl.rs index a1ea85553..9e16c9b6c 100644 --- a/src/container/builder_impl.rs +++ b/src/container/builder_impl.rs @@ -175,10 +175,10 @@ impl<'a> ContainerBuilderImpl<'a> { fs::write(&pid_file, format!("{}", init_pid)).context("Failed to write pid file")?; } - if let Some(container) = &self.container { + if let Some(container) = &mut self.container { // update status and pid of the container process container - .update_status(ContainerStatus::Created) + .set_status(ContainerStatus::Created) .set_creator(nix::unistd::geteuid().as_raw()) .set_pid(init_pid.as_raw()) .save() diff --git a/src/container/container.rs b/src/container/container.rs index afcf76e1a..0a13febdc 100644 --- a/src/container/container.rs +++ b/src/container/container.rs @@ -53,49 +53,6 @@ impl Container { self.state.id.as_str() } - pub fn status(&self) -> ContainerStatus { - self.state.status - } - - pub fn refresh_status(&mut self) -> Result { - let new_status = match self.pid() { - Some(pid) => { - // Note that Process::new does not spawn a new process - // but instead creates a new Process structure, and fill - // it with information about the process with given pid - if let Ok(proc) = Process::new(pid.as_raw()) { - use procfs::process::ProcState; - match proc.stat.state().unwrap() { - ProcState::Zombie | ProcState::Dead => ContainerStatus::Stopped, - _ => match self.status() { - ContainerStatus::Creating - | ContainerStatus::Created - | ContainerStatus::Paused => self.status(), - _ => ContainerStatus::Running, - }, - } - } else { - ContainerStatus::Stopped - } - } - None => ContainerStatus::Stopped, - }; - Ok(self.update_status(new_status)) - } - - pub fn refresh_state(&self) -> Result { - let state = State::load(&self.root)?; - Ok(Self { - state, - root: self.root.clone(), - }) - } - - pub fn save(&self) -> Result<()> { - log::debug!("Save container status: {:?} in {:?}", self, self.root); - self.state.save(&self.root) - } - pub fn can_start(&self) -> bool { self.state.status.can_start() } @@ -120,29 +77,28 @@ impl Container { self.state.status.can_resume() } + pub fn bundle(&self) -> &PathBuf { + &self.state.bundle + } + + pub fn set_annotations(&mut self, annotations: Option>) -> &mut Self { + self.state.annotations = annotations; + self + } + pub fn pid(&self) -> Option { self.state.pid.map(Pid::from_raw) } - pub fn set_pid(&self, pid: i32) -> Self { - let mut new_state = self.state.clone(); - new_state.pid = Some(pid); - - Self { - state: new_state, - root: self.root.clone(), - } + pub fn set_pid(&mut self, pid: i32) -> &mut Self { + self.state.pid = Some(pid); + self } pub fn created(&self) -> Option> { self.state.created } - pub fn set_creator(mut self, uid: u32) -> Self { - self.state.creator = Some(uid); - self - } - pub fn creator(&self) -> Option { if let Some(uid) = self.state.creator { let command = create_syscall(); @@ -155,46 +111,84 @@ impl Container { None } - pub fn bundle(&self) -> &PathBuf { - &self.state.bundle + pub fn set_creator(&mut self, uid: u32) -> &mut Self { + self.state.creator = Some(uid); + self } - pub fn set_systemd(mut self, should_use: bool) -> Self { - self.state.use_systemd = Some(should_use); - self + pub fn systemd(&self) -> Option { + self.state.use_systemd } - pub fn set_annotations(mut self, annotations: Option>) -> Self { - self.state.annotations = annotations; + pub fn set_systemd(&mut self, should_use: bool) -> &mut Self { + self.state.use_systemd = Some(should_use); self } - pub fn systemd(&self) -> Option { - self.state.use_systemd + pub fn status(&self) -> ContainerStatus { + self.state.status } - pub fn update_status(&self, status: ContainerStatus) -> Self { + pub fn set_status(&mut self, status: ContainerStatus) -> &mut Self { let created = match (status, self.state.created) { (ContainerStatus::Created, None) => Some(Utc::now()), _ => self.state.created, }; - let mut new_state = self.state.clone(); - new_state.created = created; - new_state.status = status; + self.state.created = created; + self.state.status = status; - Self { - state: new_state, - root: self.root.clone(), - } + self + } + + pub fn refresh_status(&mut self) -> Result<()> { + let new_status = match self.pid() { + Some(pid) => { + // Note that Process::new does not spawn a new process + // but instead creates a new Process structure, and fill + // it with information about the process with given pid + if let Ok(proc) = Process::new(pid.as_raw()) { + use procfs::process::ProcState; + match proc.stat.state().unwrap() { + ProcState::Zombie | ProcState::Dead => ContainerStatus::Stopped, + _ => match self.status() { + ContainerStatus::Creating + | ContainerStatus::Created + | ContainerStatus::Paused => self.status(), + _ => ContainerStatus::Running, + }, + } + } else { + ContainerStatus::Stopped + } + } + None => ContainerStatus::Stopped, + }; + + self.set_status(new_status); + Ok(()) + } + + pub fn refresh_state(&mut self) -> Result<&mut Self> { + let state = State::load(&self.root)?; + self.state = state; + + Ok(self) } pub fn load(container_root: PathBuf) -> Result { let state = State::load(&container_root)?; - Ok(Self { + let mut container = Self { state, root: container_root, - }) + }; + container.refresh_status()?; + Ok(container) + } + + pub fn save(&self) -> Result<()> { + log::debug!("Save container status: {:?} in {:?}", self, self.root); + self.state.save(&self.root) } pub fn spec(&self) -> Result { @@ -213,8 +207,9 @@ mod tests { #[test] fn test_set_id() -> Result<()> { let dir = env::temp_dir(); - let container = Container::new("container_id", ContainerStatus::Created, None, &dir, &dir)?; - let container = container.set_pid(1); + let mut container = + Container::new("container_id", ContainerStatus::Created, None, &dir, &dir)?; + container.set_pid(1); assert_eq!(container.pid(), Some(Pid::from_raw(1))); Ok(()) } diff --git a/src/container/container_delete.rs b/src/container/container_delete.rs index c22ef6ba4..474e48e75 100644 --- a/src/container/container_delete.rs +++ b/src/container/container_delete.rs @@ -14,8 +14,7 @@ impl Container { let sig = signal::Signal::SIGKILL; log::debug!("kill signal {} to {}", sig, self.pid().unwrap()); signal::kill(self.pid().unwrap(), sig)?; - self.update_status(ContainerStatus::Stopped); - self.save()?; + self.set_status(ContainerStatus::Stopped).save()?; } log::debug!("container status: {:?}", self.status()); if self.can_delete() { diff --git a/src/container/container_kill.rs b/src/container/container_kill.rs index eb2d711c3..ea42f5219 100644 --- a/src/container/container_kill.rs +++ b/src/container/container_kill.rs @@ -9,7 +9,7 @@ impl Container { if self.can_kill() { log::debug!("kill signal {} to {}", signal, self.pid().unwrap()); signal::kill(self.pid().unwrap(), signal)?; - self.update_status(ContainerStatus::Stopped).save()?; + self.set_status(ContainerStatus::Stopped).save()?; std::process::exit(0) } else { bail!( diff --git a/src/container/container_pause.rs b/src/container/container_pause.rs index aee802ee5..6b9feb069 100644 --- a/src/container/container_pause.rs +++ b/src/container/container_pause.rs @@ -30,7 +30,7 @@ impl Container { cmanager.freeze(FreezerState::Frozen)?; log::debug!("saving paused status"); - self.update_status(ContainerStatus::Paused).save()?; + self.set_status(ContainerStatus::Paused).save()?; log::debug!("container {} paused", self.id()); Ok(()) diff --git a/src/container/container_resume.rs b/src/container/container_resume.rs index be2b8a8c3..dd4d3359e 100644 --- a/src/container/container_resume.rs +++ b/src/container/container_resume.rs @@ -34,7 +34,7 @@ impl Container { cmanager.freeze(FreezerState::Thawed)?; log::debug!("saving running status"); - self.update_status(ContainerStatus::Running).save()?; + self.set_status(ContainerStatus::Running).save()?; log::debug!("container {} resumed", self.id()); Ok(()) diff --git a/src/container/container_start.rs b/src/container/container_start.rs index 5a4593322..a810fa2c7 100644 --- a/src/container/container_start.rs +++ b/src/container/container_start.rs @@ -37,8 +37,9 @@ impl Container { let mut notify_socket = NotifySocket::new(&self.root.join(NOTIFY_FILE)); notify_socket.notify_container_start()?; - self.update_status(ContainerStatus::Running); - self.save()?; + self.set_status(ContainerStatus::Running) + .save() + .with_context(|| format!("could not save state for container {}", self.id()))?; // Run post start hooks. It runs after the container process is started. // It is called in the runtime namespace. diff --git a/src/container/init_builder.rs b/src/container/init_builder.rs index 5de542452..5f927a461 100644 --- a/src/container/init_builder.rs +++ b/src/container/init_builder.rs @@ -43,8 +43,8 @@ impl<'a> InitContainerBuilder<'a> { let container_dir = self.create_container_dir()?; self.save_spec(&spec, &container_dir)?; - let mut container_state = self - .create_container_state(&container_dir)? + let mut container = self.create_container_state(&container_dir)?; + container .set_systemd(self.use_systemd) .set_annotations(spec.annotations.clone()); @@ -77,12 +77,14 @@ impl<'a> InitContainerBuilder<'a> { rootfs, rootless, notify_path, - container: Some(container_state.clone()), + container: Some(container.clone()), preserve_fds: self.base.preserve_fds, }; builder_impl.create()?; - Ok(container_state.refresh_status()?) + container.refresh_state()?; + + Ok(container) } fn create_container_dir(&self) -> Result { diff --git a/src/container/state.rs b/src/container/state.rs index 4d2d1f527..3d64f8eb7 100644 --- a/src/container/state.rs +++ b/src/container/state.rs @@ -129,8 +129,8 @@ impl State { .append(false) .create(true) .truncate(true) - .open(state_file_path) - .expect("Unable to open"); + .open(&state_file_path) + .with_context(|| format!("failed to open {}", state_file_path.display()))?; serde_json::to_writer(&file, self)?; Ok(()) } diff --git a/src/container/tenant_builder.rs b/src/container/tenant_builder.rs index 42059d66a..1cfff85d6 100644 --- a/src/container/tenant_builder.rs +++ b/src/container/tenant_builder.rs @@ -145,7 +145,7 @@ impl<'a> TenantContainerBuilder<'a> { } fn load_container_state(&self, container_dir: PathBuf) -> Result { - let container = Container::load(container_dir)?.refresh_status()?; + let container = Container::load(container_dir)?; if !container.can_exec() { bail!( "Cannot exec as container is in state {}", From fd2db0f30063f88a12a44b6f3c6558fe2fc91d8c Mon Sep 17 00:00:00 2001 From: Furisto <24721048+Furisto@users.noreply.github.com> Date: Thu, 23 Sep 2021 23:26:11 +0200 Subject: [PATCH 09/10] Clippy --- src/container/container_delete.rs | 2 +- src/container/container_events.rs | 2 +- src/container/container_pause.rs | 2 +- src/container/container_resume.rs | 4 ++-- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/container/container_delete.rs b/src/container/container_delete.rs index 474e48e75..21e51242b 100644 --- a/src/container/container_delete.rs +++ b/src/container/container_delete.rs @@ -42,7 +42,7 @@ impl Container { .systemd() .context("container state does not contain cgroup manager")?; let cmanager = cgroups::common::create_cgroup_manager(&cgroups_path, use_systemd) - .with_context(|| format!("failed to create cgroup manager"))?; + .context("failed to create cgroup manager")?; cmanager.remove().with_context(|| { format!("failed to remove cgroup {}", cgroups_path.display()) })?; diff --git a/src/container/container_events.rs b/src/container/container_events.rs index 7b0c323a0..7e115a68c 100644 --- a/src/container/container_events.rs +++ b/src/container/container_events.rs @@ -15,7 +15,7 @@ impl Container { let cgroups_path = utils::get_cgroup_path( &self.spec()?.linux.context("no linux in spec")?.cgroups_path, - &self.id(), + self.id(), ); let use_systemd = self .systemd() diff --git a/src/container/container_pause.rs b/src/container/container_pause.rs index 6b9feb069..5b594a058 100644 --- a/src/container/container_pause.rs +++ b/src/container/container_pause.rs @@ -20,7 +20,7 @@ impl Container { let spec = self.spec()?; let cgroups_path = utils::get_cgroup_path( &spec.linux.context("no linux in spec")?.cgroups_path, - &self.id(), + self.id(), ); let use_systemd = self diff --git a/src/container/container_resume.rs b/src/container/container_resume.rs index dd4d3359e..a562e1d8f 100644 --- a/src/container/container_resume.rs +++ b/src/container/container_resume.rs @@ -8,7 +8,7 @@ use cgroups::common::FreezerState; impl Container { pub fn resume(&mut self) -> Result<()> { self.refresh_status() - .with_context(|| format!("failed to refresh container status"))?; + .context("failed to refresh container status")?; // check if container can be resumed : // for example, a running process cannot be resumed if !self.can_resume() { @@ -22,7 +22,7 @@ impl Container { let spec = self.spec()?; let cgroups_path = utils::get_cgroup_path( &spec.linux.context("no linux in spec")?.cgroups_path, - &self.id(), + self.id(), ); // create cgroup manager structure from the config at the path From a4bac01fd0ffa18a4410160b584c48a2bfcc8912 Mon Sep 17 00:00:00 2001 From: Furisto <24721048+Furisto@users.noreply.github.com> Date: Fri, 24 Sep 2021 11:58:15 +0200 Subject: [PATCH 10/10] Add documentation --- src/container/container_delete.rs | 17 +++++++++++++++++ src/container/container_events.rs | 17 +++++++++++++++++ src/container/container_kill.rs | 18 ++++++++++++++++++ src/container/container_pause.rs | 17 +++++++++++++++++ src/container/container_resume.rs | 17 +++++++++++++++++ src/container/container_start.rs | 17 +++++++++++++++++ src/container/mod.rs | 12 ++++++------ 7 files changed, 109 insertions(+), 6 deletions(-) diff --git a/src/container/container_delete.rs b/src/container/container_delete.rs index 21e51242b..217a483aa 100644 --- a/src/container/container_delete.rs +++ b/src/container/container_delete.rs @@ -7,6 +7,23 @@ use nix::sys::signal; use std::fs; impl Container { + /// Deletes the container + /// + /// # Example + /// + /// ```no_run + /// use youki::container::builder::ContainerBuilder; + /// use youki::syscall::syscall::create_syscall;; + /// + /// # fn main() -> anyhow::Result<()> { + /// let mut container = ContainerBuilder::new("74f1a4cb3801".to_owned(), create_syscall().as_ref()) + /// .as_init("/var/run/docker/bundle") + /// .build()?; + /// + /// container.delete(true)?; + /// # Ok(()) + /// # } + /// ``` pub fn delete(&mut self, force: bool) -> Result<()> { self.refresh_status() .context("failed to refresh container status")?; diff --git a/src/container/container_events.rs b/src/container/container_events.rs index 7e115a68c..9933afc2b 100644 --- a/src/container/container_events.rs +++ b/src/container/container_events.rs @@ -6,6 +6,23 @@ use super::{Container, ContainerStatus}; use anyhow::{bail, Context, Result}; impl Container { + /// Displays container events + /// + /// # Example + /// + /// ```no_run + /// use youki::container::builder::ContainerBuilder; + /// use youki::syscall::syscall::create_syscall;; + /// + /// # fn main() -> anyhow::Result<()> { + /// let mut container = ContainerBuilder::new("74f1a4cb3801".to_owned(), create_syscall().as_ref()) + /// .as_init("/var/run/docker/bundle") + /// .build()?; + /// + /// container.events(5000, false)?; + /// # Ok(()) + /// # } + /// ``` pub fn events(&mut self, interval: u32, stats: bool) -> Result<()> { self.refresh_status() .context("failed to refresh container status")?; diff --git a/src/container/container_kill.rs b/src/container/container_kill.rs index ea42f5219..9d88bdf58 100644 --- a/src/container/container_kill.rs +++ b/src/container/container_kill.rs @@ -3,6 +3,24 @@ use anyhow::{bail, Context, Result}; use nix::sys::signal::{self, Signal}; impl Container { + /// Sends the specified signal to the container init process + /// + /// # Example + /// + /// ```no_run + /// use youki::container::builder::ContainerBuilder; + /// use youki::syscall::syscall::create_syscall;; + /// use nix::sys::signal::Signal; + /// + /// # fn main() -> anyhow::Result<()> { + /// let mut container = ContainerBuilder::new("74f1a4cb3801".to_owned(), create_syscall().as_ref()) + /// .as_init("/var/run/docker/bundle") + /// .build()?; + /// + /// container.kill(Signal::SIGKILL)?; + /// # Ok(()) + /// # } + /// ``` pub fn kill(&mut self, signal: Signal) -> Result<()> { self.refresh_status() .context("failed to refresh container status")?; diff --git a/src/container/container_pause.rs b/src/container/container_pause.rs index 5b594a058..74b0439f1 100644 --- a/src/container/container_pause.rs +++ b/src/container/container_pause.rs @@ -5,6 +5,23 @@ use anyhow::{bail, Context, Result}; use cgroups::common::FreezerState; impl Container { + /// Suspends all processes within the container + /// + /// # Example + /// + /// ```no_run + /// use youki::container::builder::ContainerBuilder; + /// use youki::syscall::syscall::create_syscall;; + /// + /// # fn main() -> anyhow::Result<()> { + /// let mut container = ContainerBuilder::new("74f1a4cb3801".to_owned(), create_syscall().as_ref()) + /// .as_init("/var/run/docker/bundle") + /// .build()?; + /// + /// container.pause()?; + /// # Ok(()) + /// # } + /// ``` pub fn pause(&mut self) -> Result<()> { self.refresh_status() .context("failed to refresh container status")?; diff --git a/src/container/container_resume.rs b/src/container/container_resume.rs index a562e1d8f..8d7570d91 100644 --- a/src/container/container_resume.rs +++ b/src/container/container_resume.rs @@ -6,6 +6,23 @@ use anyhow::{bail, Context, Result}; use cgroups::common::FreezerState; impl Container { + /// Resumes all processes within the container + /// + /// # Example + /// + /// ```no_run + /// use youki::container::builder::ContainerBuilder; + /// use youki::syscall::syscall::create_syscall;; + /// + /// # fn main() -> anyhow::Result<()> { + /// let mut container = ContainerBuilder::new("74f1a4cb3801".to_owned(), create_syscall().as_ref()) + /// .as_init("/var/run/docker/bundle") + /// .build()?; + /// + /// container.resume()?; + /// # Ok(()) + /// # } + /// ``` pub fn resume(&mut self) -> Result<()> { self.refresh_status() .context("failed to refresh container status")?; diff --git a/src/container/container_start.rs b/src/container/container_start.rs index a810fa2c7..e1074c62d 100644 --- a/src/container/container_start.rs +++ b/src/container/container_start.rs @@ -8,6 +8,23 @@ use anyhow::{bail, Context, Result}; use nix::unistd; impl Container { + /// Starts a previously created container + /// + /// # Example + /// + /// ```no_run + /// use youki::container::builder::ContainerBuilder; + /// use youki::syscall::syscall::create_syscall;; + /// + /// # fn main() -> anyhow::Result<()> { + /// let mut container = ContainerBuilder::new("74f1a4cb3801".to_owned(), create_syscall().as_ref()) + /// .as_init("/var/run/docker/bundle") + /// .build()?; + /// + /// container.start(); + /// # Ok(()) + /// # } + /// ``` pub fn start(&mut self) -> Result<()> { self.refresh_status() .context("failed to refresh container status")?; diff --git a/src/container/mod.rs b/src/container/mod.rs index d6ab211a1..2290d1088 100644 --- a/src/container/mod.rs +++ b/src/container/mod.rs @@ -8,12 +8,12 @@ pub mod builder; mod builder_impl; #[allow(clippy::module_inception)] mod container; -pub mod container_delete; -pub mod container_events; -pub mod container_kill; -pub mod container_pause; -pub mod container_resume; -pub mod container_start; +mod container_delete; +mod container_events; +mod container_kill; +mod container_pause; +mod container_resume; +mod container_start; pub mod init_builder; pub mod state; pub mod tenant_builder;