Merge pull request #82 from ericho/namespace-uts

agent: Add unit tests for `namespace.rs`
This commit is contained in:
Yang Bo 2019-11-14 07:39:56 +08:00 committed by GitHub
commit 60d713e84d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 198 additions and 89 deletions

View File

@ -6,37 +6,22 @@
use nix::mount::MsFlags;
use nix::sched::{unshare, CloneFlags};
use nix::unistd::{getpid, gettid};
use std::collections::HashMap;
use std::fmt;
use std::fs;
use std::fs::File;
use std::os::unix::io::AsRawFd;
use std::path::Path;
use std::path::{Path, PathBuf};
use std::thread;
use crate::mount::{BareMount, FLAGS};
use slog::Logger;
//use container::Process;
const PERSISTENT_NS_DIR: &'static str = "/var/run/sandbox-ns";
pub const NSTYPEIPC: &'static str = "ipc";
pub const NSTYPEUTS: &'static str = "uts";
pub const NSTYPEPID: &'static str = "pid";
lazy_static! {
static ref CLONE_FLAG_TABLE: HashMap<&'static str, CloneFlags> = {
let mut m = HashMap::new();
m.insert(NSTYPEIPC, CloneFlags::CLONE_NEWIPC);
m.insert(NSTYPEUTS, CloneFlags::CLONE_NEWUTS);
m
};
}
#[derive(Debug, Default)]
pub struct Namespace {
pub path: String,
}
pub fn get_current_thread_ns_path(ns_type: &str) -> String {
format!(
"/proc/{}/task/{}/ns/{}",
@ -46,34 +31,75 @@ pub fn get_current_thread_ns_path(ns_type: &str) -> String {
)
}
#[derive(Debug)]
pub struct Namespace {
logger: Logger,
pub path: String,
persistent_ns_dir: String,
ns_type: NamespaceType,
}
impl Namespace {
pub fn new(logger: &Logger) -> Self {
Namespace {
logger: logger.clone(),
path: String::from(""),
persistent_ns_dir: String::from(PERSISTENT_NS_DIR),
ns_type: NamespaceType::IPC,
}
}
pub fn as_ipc(mut self) -> Self {
self.ns_type = NamespaceType::IPC;
self
}
pub fn as_uts(mut self) -> Self {
self.ns_type = NamespaceType::UTS;
self
}
pub fn set_root_dir(mut self, dir: &str) -> Self {
self.persistent_ns_dir = dir.to_string();
self
}
// setup_persistent_ns creates persistent namespace without switchin to it.
// Note, pid namespaces cannot be persisted.
pub fn setup_persistent_ns(logger: Logger, ns_type: &'static str) -> Result<Namespace, String> {
if let Err(err) = fs::create_dir_all(PERSISTENT_NS_DIR) {
pub fn setup(mut self) -> Result<Self, String> {
if let Err(err) = fs::create_dir_all(&self.persistent_ns_dir) {
return Err(err.to_string());
}
let ns_path = Path::new(PERSISTENT_NS_DIR);
let new_ns_path = ns_path.join(ns_type);
let ns_path = PathBuf::from(&self.persistent_ns_dir);
let ns_type = self.ns_type.clone();
let logger = self.logger.clone();
let new_ns_path = ns_path.join(&ns_type.get());
if let Err(err) = File::create(new_ns_path.as_path()) {
return Err(err.to_string());
}
self.path = new_ns_path.into_os_string().into_string().unwrap();
let new_thread = thread::spawn(move || {
let origin_ns_path = get_current_thread_ns_path(ns_type);
let ns_path = ns_path.clone();
let ns_type = ns_type.clone();
let logger = logger;
let new_ns_path = ns_path.join(&ns_type.get());
let origin_ns_path = get_current_thread_ns_path(&ns_type.get());
let _origin_ns_fd = match File::open(Path::new(&origin_ns_path)) {
Err(err) => return Err(err.to_string()),
Ok(file) => file.as_raw_fd(),
};
// Create a new netns on the current thread.
let cf = match CLONE_FLAG_TABLE.get(ns_type) {
None => return Err(format!("Failed to get ns type {}", ns_type).to_string()),
Some(cf) => cf,
};
let cf = ns_type.get_flags().clone();
if let Err(err) = unshare(*cf) {
if let Err(err) = unshare(cf) {
return Err(err.to_string());
}
@ -112,8 +138,96 @@ pub fn setup_persistent_ns(logger: Logger, ns_type: &'static str) -> Result<Name
Err(err) => return Err(format!("Failed to join thread {:?}!", err)),
}
let new_ns_path = ns_path.join(ns_type);
Ok(Namespace {
path: new_ns_path.into_os_string().into_string().unwrap(),
})
Ok(self)
}
}
/// Represents the Namespace type.
#[derive(Clone, Copy)]
enum NamespaceType {
IPC,
UTS,
PID,
}
impl NamespaceType {
/// Get the string representation of the namespace type.
pub fn get(&self) -> String {
match *self {
Self::IPC => String::from("ipc"),
Self::UTS => String::from("uts"),
Self::PID => String::from("pid"),
}
}
/// Get the associate flags with the namespace type.
pub fn get_flags(&self) -> CloneFlags {
match *self {
Self::IPC => CloneFlags::CLONE_NEWIPC,
Self::UTS => CloneFlags::CLONE_NEWUTS,
Self::PID => CloneFlags::CLONE_NEWPID,
}
}
}
impl fmt::Debug for NamespaceType {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.get())
}
}
impl Default for NamespaceType {
fn default() -> Self {
NamespaceType::IPC
}
}
#[cfg(test)]
mod tests {
use super::{Namespace, NamespaceType};
use crate::{mount::remove_mounts, skip_if_not_root};
use nix::sched::CloneFlags;
use tempfile::Builder;
#[test]
fn test_setup_persistent_ns() {
skip_if_not_root!();
// Create dummy logger and temp folder.
let logger = slog::Logger::root(slog::Discard, o!());
let tmpdir = Builder::new().prefix("ipc").tempdir().unwrap();
let ns_ipc = Namespace::new(&logger)
.as_ipc()
.set_root_dir(tmpdir.path().to_str().unwrap())
.setup();
assert!(ns_ipc.is_ok());
assert!(remove_mounts(&vec![ns_ipc.unwrap().path]).is_ok());
let logger = slog::Logger::root(slog::Discard, o!());
let tmpdir = Builder::new().prefix("ipc").tempdir().unwrap();
let ns_uts = Namespace::new(&logger)
.as_uts()
.set_root_dir(tmpdir.path().to_str().unwrap())
.setup();
assert!(ns_uts.is_ok());
assert!(remove_mounts(&vec![ns_uts.unwrap().path]).is_ok());
}
#[test]
fn test_namespace_type() {
let ipc = NamespaceType::IPC;
assert_eq!("ipc", ipc.get());
assert_eq!(CloneFlags::CLONE_NEWIPC, ipc.get_flags());
let uts = NamespaceType::UTS;
assert_eq!("uts", uts.get());
assert_eq!(CloneFlags::CLONE_NEWUTS, uts.get_flags());
let pid = NamespaceType::PID;
assert_eq!("pid", pid.get());
assert_eq!(CloneFlags::CLONE_NEWPID, pid.get_flags());
}
}

View File

@ -5,7 +5,7 @@
//use crate::container::Container;
use crate::mount::{get_mount_fs_type, remove_mounts, TYPEROOTFS};
use crate::namespace::{setup_persistent_ns, Namespace, NSTYPEIPC, NSTYPEUTS};
use crate::namespace::Namespace;
use crate::netlink::{RtnlHandle, NETLINK_ROUTE};
use crate::network::Network;
use libc::pid_t;
@ -48,7 +48,7 @@ impl Sandbox {
let logger = logger.new(o!("subsystem" => "sandbox"));
Ok(Sandbox {
logger: logger,
logger: logger.clone(),
id: "".to_string(),
hostname: "".to_string(),
network: Network::new(),
@ -56,12 +56,8 @@ impl Sandbox {
mounts: Vec::new(),
container_mounts: HashMap::new(),
pci_device_map: HashMap::new(),
shared_utsns: Namespace {
path: "".to_string(),
},
shared_ipcns: Namespace {
path: "".to_string(),
},
shared_utsns: Namespace::new(&logger),
shared_ipcns: Namespace::new(&logger),
storages: HashMap::new(),
running: false,
no_pivot_root: fs_type.eq(TYPEROOTFS),
@ -129,29 +125,28 @@ impl Sandbox {
pub fn setup_shared_namespaces(&mut self) -> Result<bool> {
// Set up shared IPC namespace
self.shared_ipcns = match setup_persistent_ns(self.logger.clone(), NSTYPEIPC) {
self.shared_ipcns = match Namespace::new(&self.logger).as_ipc().setup() {
Ok(ns) => ns,
Err(err) => {
return Err(ErrorKind::ErrorCode(format!(
"Failed to setup persisten IPC namespace with error: {}",
&err
"Failed to setup persistent IPC namespace with error: {}",
err
))
.into())
}
};
// Set up shared UTS namespace
self.shared_utsns = match setup_persistent_ns(self.logger.clone(), NSTYPEUTS) {
// // Set up shared UTS namespace
self.shared_utsns = match Namespace::new(&self.logger).as_uts().setup() {
Ok(ns) => ns,
Err(err) => {
return Err(ErrorKind::ErrorCode(format!(
"Failed to setup persisten UTS namespace with error: {} ",
&err
"Failed to setup persistent UTS namespace with error: {}",
err
))
.into())
}
};
Ok(true)
}