-
Notifications
You must be signed in to change notification settings - Fork 42
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
This experiment adds support for running a global shared pause instance side by side to `conmonrs`. The idea is that this instance keeps the required namespaces open and provides them as part of the container creation response to the consumers. Signed-off-by: Sascha Grunert <[email protected]>
- Loading branch information
1 parent
6d21df6
commit cad12b1
Showing
12 changed files
with
1,055 additions
and
334 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -15,6 +15,7 @@ mod init; | |
mod journal; | ||
mod listener; | ||
mod oom_watcher; | ||
mod pause; | ||
mod rpc; | ||
mod server; | ||
mod streams; | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,183 @@ | ||
use anyhow::{bail, Context, Result}; | ||
use getset::{CopyGetters, Getters}; | ||
use libc::pid_t; | ||
use nix::{ | ||
mount::{mount, umount, MsFlags}, | ||
sched::{unshare, CloneFlags}, | ||
sys::signal::{kill, Signal}, | ||
unistd::{fork, ForkResult, Pid}, | ||
}; | ||
use once_cell::sync::OnceCell; | ||
use signal_hook::{consts::TERM_SIGNALS, iterator::Signals}; | ||
use std::{ | ||
env, | ||
fs::{self, File}, | ||
io::Write, | ||
path::{Path, PathBuf}, | ||
process::{exit, Command}, | ||
}; | ||
use strum::{AsRefStr, Display, EnumIter, EnumString, IntoEnumIterator, IntoStaticStr}; | ||
use tracing::{debug, error, info}; | ||
use uuid::Uuid; | ||
|
||
/// The main structure for this module. | ||
#[derive(Debug, CopyGetters, Getters)] | ||
pub struct Pause { | ||
#[get = "pub"] | ||
path: PathBuf, | ||
|
||
#[get_copy] | ||
pid: Pid, | ||
} | ||
|
||
/// The global shared multiple pause instance. | ||
static PAUSE: OnceCell<Pause> = OnceCell::new(); | ||
|
||
/// The global path for storing bin mounted namespaces. | ||
const PAUSE_PATH: &str = "/var/run/conmonrs"; | ||
|
||
/// The file path for storing the pause PID. | ||
const PAUSE_PID_FILE: &str = ".pause_pid"; | ||
|
||
impl Pause { | ||
/// Retrieve the global instance of pause | ||
pub fn shared() -> Result<&'static Pause> { | ||
PAUSE.get_or_try_init(|| Self::init().context("init pause")) | ||
} | ||
|
||
/// Retrieve the global instance of pause if initialized. | ||
pub fn maybe_shared() -> Option<&'static Pause> { | ||
PAUSE.get() | ||
} | ||
|
||
/// Stop the global pause instance. | ||
pub fn stop(&self) { | ||
info!("Stopping pause"); | ||
for namespace in Namespace::iter() { | ||
if let Err(e) = namespace.umount(self.path()) { | ||
debug!("Unable to umount namespace {namespace}: {:#}", e); | ||
} | ||
} | ||
if let Err(e) = fs::remove_dir_all(self.path()) { | ||
error!( | ||
"Unable to remove pause path {}: {:#}", | ||
self.path().display(), | ||
e | ||
); | ||
} | ||
|
||
info!("Killing pause PID: {}", self.pid()); | ||
if let Err(e) = kill(self.pid(), Signal::SIGTERM) { | ||
error!("Unable to kill pause PID {}: {:#}", self.pid(), e); | ||
} | ||
} | ||
|
||
/// Initialize a new pause instance. | ||
fn init() -> Result<Self> { | ||
debug!("Initializing pause"); | ||
|
||
let path = PathBuf::from(PAUSE_PATH).join(Uuid::new_v4().to_string()); | ||
fs::create_dir_all(&path).context("create base path")?; | ||
|
||
let program = env::args().next().context("no args set")?; | ||
let mut child = Command::new(program) | ||
.arg("-p") | ||
.arg(&path) | ||
.spawn() | ||
.context("run pause")?; | ||
|
||
let status = child.wait().context("wait for pause child")?; | ||
if !status.success() { | ||
bail!("exit status not ok: {status}") | ||
} | ||
|
||
let pid = fs::read_to_string(path.join(PAUSE_PID_FILE)) | ||
.context("read pause PID path")? | ||
.trim() | ||
.parse::<u32>() | ||
.context("parse pause PID")?; | ||
info!("Pause PID is: {pid}"); | ||
|
||
Ok(Self { | ||
path, | ||
pid: Pid::from_raw(pid as pid_t), | ||
}) | ||
} | ||
|
||
/// Run a new pause instance. | ||
pub fn run<T: AsRef<Path>>(path: T) -> Result<()> { | ||
let flags = CloneFlags::CLONE_NEWPID | ||
| CloneFlags::CLONE_NEWIPC | ||
| CloneFlags::CLONE_NEWNET | ||
| CloneFlags::CLONE_NEWUTS; | ||
|
||
unshare(flags).context("unshare with clone flags")?; | ||
|
||
match unsafe { fork().context("forking process")? } { | ||
ForkResult::Parent { child } => { | ||
let mut file = File::create(path.as_ref().join(PAUSE_PID_FILE)) | ||
.context("create pause PID file")?; | ||
write!(file, "{child}").context("write child to pause file")?; | ||
exit(0); | ||
} | ||
ForkResult::Child => (), | ||
} | ||
|
||
for namespace in Namespace::iter() { | ||
namespace | ||
.bind(path.as_ref()) | ||
.context("bind namespace to {path}")?; | ||
} | ||
|
||
let mut signals = Signals::new(TERM_SIGNALS).context("register signals")?; | ||
signals.forever().next().context("no signal number")?; | ||
Ok(()) | ||
} | ||
} | ||
|
||
#[derive( | ||
AsRefStr, Clone, Copy, Debug, Display, EnumIter, EnumString, Eq, IntoStaticStr, PartialEq, | ||
)] | ||
#[strum(serialize_all = "lowercase")] | ||
/// All available linux namespaces. | ||
enum Namespace { | ||
/// The PID namespace. The child process becomes PID 1. | ||
Pid, | ||
|
||
/// IPC namespace. This creates new namespace for System V IPC POSIX message queues and | ||
/// similar. | ||
Ipc, | ||
|
||
/// The network namespace. The namespace is empty and has no conectivity, even localhost | ||
/// network, unless some setup is done afterwards. | ||
Net, | ||
|
||
/// The UTS namespace, which allows to change hostname of the new container. | ||
Uts, | ||
} | ||
|
||
impl Namespace { | ||
/// Bind the namespace to the provided path. | ||
pub fn bind<T: AsRef<Path>>(&self, path: T) -> Result<()> { | ||
let bind_path = path.as_ref().join(self.as_ref()); | ||
File::create(&bind_path).context("create namespace bind path")?; | ||
let source_path = PathBuf::from("/proc/self/ns").join(self.as_ref()); | ||
|
||
mount( | ||
Some(&source_path), | ||
&bind_path, | ||
None::<&Path>, | ||
MsFlags::MS_BIND, | ||
None::<&[u8]>, | ||
) | ||
.context("mount namespace")?; | ||
|
||
Ok(()) | ||
} | ||
|
||
/// Umount the namespace. | ||
pub fn umount<T: AsRef<Path>>(&self, path: T) -> Result<()> { | ||
let bind_path = path.as_ref().join(self.as_ref()); | ||
umount(&bind_path).context("umount namespace") | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.