mirror of
https://github.com/kata-containers/kata-containers.git
synced 2025-06-29 08:47:56 +00:00
Merge pull request #82 from ericho/namespace-uts
agent: Add unit tests for `namespace.rs`
This commit is contained in:
commit
60d713e84d
@ -109,7 +109,7 @@ fn main() -> Result<()> {
|
|||||||
if unistd::getpid() == Pid::from_raw(1) {
|
if unistd::getpid() == Pid::from_raw(1) {
|
||||||
// Init a temporary logger used by init agent as init process
|
// Init a temporary logger used by init agent as init process
|
||||||
// since before do the base mount, it wouldn't access "/proc/cmdline"
|
// since before do the base mount, it wouldn't access "/proc/cmdline"
|
||||||
// to get the customzied debug level.
|
// to get the customzied debug level.
|
||||||
let writer = io::stdout();
|
let writer = io::stdout();
|
||||||
let logger = logging::create_logger(NAME, "agent", slog::Level::Debug, writer);
|
let logger = logging::create_logger(NAME, "agent", slog::Level::Debug, writer);
|
||||||
init_agent_as_init(&logger)?;
|
init_agent_as_init(&logger)?;
|
||||||
|
@ -6,37 +6,22 @@
|
|||||||
use nix::mount::MsFlags;
|
use nix::mount::MsFlags;
|
||||||
use nix::sched::{unshare, CloneFlags};
|
use nix::sched::{unshare, CloneFlags};
|
||||||
use nix::unistd::{getpid, gettid};
|
use nix::unistd::{getpid, gettid};
|
||||||
use std::collections::HashMap;
|
use std::fmt;
|
||||||
use std::fs;
|
use std::fs;
|
||||||
use std::fs::File;
|
use std::fs::File;
|
||||||
use std::os::unix::io::AsRawFd;
|
use std::os::unix::io::AsRawFd;
|
||||||
use std::path::Path;
|
use std::path::{Path, PathBuf};
|
||||||
use std::thread;
|
use std::thread;
|
||||||
|
|
||||||
use crate::mount::{BareMount, FLAGS};
|
use crate::mount::{BareMount, FLAGS};
|
||||||
use slog::Logger;
|
use slog::Logger;
|
||||||
|
|
||||||
//use container::Process;
|
//use container::Process;
|
||||||
|
|
||||||
const PERSISTENT_NS_DIR: &'static str = "/var/run/sandbox-ns";
|
const PERSISTENT_NS_DIR: &'static str = "/var/run/sandbox-ns";
|
||||||
pub const NSTYPEIPC: &'static str = "ipc";
|
pub const NSTYPEIPC: &'static str = "ipc";
|
||||||
pub const NSTYPEUTS: &'static str = "uts";
|
pub const NSTYPEUTS: &'static str = "uts";
|
||||||
pub const NSTYPEPID: &'static str = "pid";
|
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 {
|
pub fn get_current_thread_ns_path(ns_type: &str) -> String {
|
||||||
format!(
|
format!(
|
||||||
"/proc/{}/task/{}/ns/{}",
|
"/proc/{}/task/{}/ns/{}",
|
||||||
@ -46,74 +31,203 @@ pub fn get_current_thread_ns_path(ns_type: &str) -> String {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
// setup_persistent_ns creates persistent namespace without switchin to it.
|
#[derive(Debug)]
|
||||||
// Note, pid namespaces cannot be persisted.
|
pub struct Namespace {
|
||||||
pub fn setup_persistent_ns(logger: Logger, ns_type: &'static str) -> Result<Namespace, String> {
|
logger: Logger,
|
||||||
if let Err(err) = fs::create_dir_all(PERSISTENT_NS_DIR) {
|
pub path: String,
|
||||||
return Err(err.to_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,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let ns_path = Path::new(PERSISTENT_NS_DIR);
|
pub fn as_ipc(mut self) -> Self {
|
||||||
let new_ns_path = ns_path.join(ns_type);
|
self.ns_type = NamespaceType::IPC;
|
||||||
|
self
|
||||||
if let Err(err) = File::create(new_ns_path.as_path()) {
|
|
||||||
return Err(err.to_string());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let new_thread = thread::spawn(move || {
|
pub fn as_uts(mut self) -> Self {
|
||||||
let origin_ns_path = get_current_thread_ns_path(ns_type);
|
self.ns_type = NamespaceType::UTS;
|
||||||
let _origin_ns_fd = match File::open(Path::new(&origin_ns_path)) {
|
self
|
||||||
Err(err) => return Err(err.to_string()),
|
}
|
||||||
Ok(file) => file.as_raw_fd(),
|
|
||||||
};
|
|
||||||
|
|
||||||
// Create a new netns on the current thread.
|
pub fn set_root_dir(mut self, dir: &str) -> Self {
|
||||||
let cf = match CLONE_FLAG_TABLE.get(ns_type) {
|
self.persistent_ns_dir = dir.to_string();
|
||||||
None => return Err(format!("Failed to get ns type {}", ns_type).to_string()),
|
self
|
||||||
Some(cf) => cf,
|
}
|
||||||
};
|
|
||||||
|
|
||||||
if let Err(err) = unshare(*cf) {
|
// setup_persistent_ns creates persistent namespace without switchin to it.
|
||||||
|
// Note, pid namespaces cannot be persisted.
|
||||||
|
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());
|
return Err(err.to_string());
|
||||||
}
|
}
|
||||||
|
|
||||||
// Bind mount the new namespace from the current thread onto the mount point to persist it.
|
let ns_path = PathBuf::from(&self.persistent_ns_dir);
|
||||||
let source: &str = origin_ns_path.as_str();
|
let ns_type = self.ns_type.clone();
|
||||||
let destination: &str = new_ns_path.as_path().to_str().unwrap_or("none");
|
let logger = self.logger.clone();
|
||||||
|
|
||||||
let _recursive = true;
|
let new_ns_path = ns_path.join(&ns_type.get());
|
||||||
let _readonly = true;
|
|
||||||
let mut flags = MsFlags::empty();
|
|
||||||
|
|
||||||
match FLAGS.get("rbind") {
|
if let Err(err) = File::create(new_ns_path.as_path()) {
|
||||||
Some(x) => {
|
return Err(err.to_string());
|
||||||
let (_, f) = *x;
|
|
||||||
flags = flags | f;
|
|
||||||
}
|
|
||||||
None => (),
|
|
||||||
};
|
|
||||||
|
|
||||||
let bare_mount = BareMount::new(source, destination, "none", flags, "", &logger);
|
|
||||||
|
|
||||||
if let Err(err) = bare_mount.mount() {
|
|
||||||
return Err(format!(
|
|
||||||
"Failed to mount {} to {} with err:{:?}",
|
|
||||||
source, destination, err
|
|
||||||
));
|
|
||||||
}
|
}
|
||||||
Ok(())
|
|
||||||
});
|
|
||||||
|
|
||||||
match new_thread.join() {
|
self.path = new_ns_path.into_os_string().into_string().unwrap();
|
||||||
Ok(t) => match t {
|
|
||||||
Err(err) => return Err(err),
|
let new_thread = thread::spawn(move || {
|
||||||
Ok(()) => (),
|
let ns_path = ns_path.clone();
|
||||||
},
|
let ns_type = ns_type.clone();
|
||||||
Err(err) => return Err(format!("Failed to join thread {:?}!", err)),
|
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 = ns_type.get_flags().clone();
|
||||||
|
|
||||||
|
if let Err(err) = unshare(cf) {
|
||||||
|
return Err(err.to_string());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Bind mount the new namespace from the current thread onto the mount point to persist it.
|
||||||
|
let source: &str = origin_ns_path.as_str();
|
||||||
|
let destination: &str = new_ns_path.as_path().to_str().unwrap_or("none");
|
||||||
|
|
||||||
|
let _recursive = true;
|
||||||
|
let _readonly = true;
|
||||||
|
let mut flags = MsFlags::empty();
|
||||||
|
|
||||||
|
match FLAGS.get("rbind") {
|
||||||
|
Some(x) => {
|
||||||
|
let (_, f) = *x;
|
||||||
|
flags = flags | f;
|
||||||
|
}
|
||||||
|
None => (),
|
||||||
|
};
|
||||||
|
|
||||||
|
let bare_mount = BareMount::new(source, destination, "none", flags, "", &logger);
|
||||||
|
|
||||||
|
if let Err(err) = bare_mount.mount() {
|
||||||
|
return Err(format!(
|
||||||
|
"Failed to mount {} to {} with err:{:?}",
|
||||||
|
source, destination, err
|
||||||
|
));
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
});
|
||||||
|
|
||||||
|
match new_thread.join() {
|
||||||
|
Ok(t) => match t {
|
||||||
|
Err(err) => return Err(err),
|
||||||
|
Ok(()) => (),
|
||||||
|
},
|
||||||
|
Err(err) => return Err(format!("Failed to join thread {:?}!", err)),
|
||||||
|
}
|
||||||
|
|
||||||
|
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"),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let new_ns_path = ns_path.join(ns_type);
|
/// Get the associate flags with the namespace type.
|
||||||
Ok(Namespace {
|
pub fn get_flags(&self) -> CloneFlags {
|
||||||
path: new_ns_path.into_os_string().into_string().unwrap(),
|
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());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -5,7 +5,7 @@
|
|||||||
|
|
||||||
//use crate::container::Container;
|
//use crate::container::Container;
|
||||||
use crate::mount::{get_mount_fs_type, remove_mounts, TYPEROOTFS};
|
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::netlink::{RtnlHandle, NETLINK_ROUTE};
|
||||||
use crate::network::Network;
|
use crate::network::Network;
|
||||||
use libc::pid_t;
|
use libc::pid_t;
|
||||||
@ -48,7 +48,7 @@ impl Sandbox {
|
|||||||
let logger = logger.new(o!("subsystem" => "sandbox"));
|
let logger = logger.new(o!("subsystem" => "sandbox"));
|
||||||
|
|
||||||
Ok(Sandbox {
|
Ok(Sandbox {
|
||||||
logger: logger,
|
logger: logger.clone(),
|
||||||
id: "".to_string(),
|
id: "".to_string(),
|
||||||
hostname: "".to_string(),
|
hostname: "".to_string(),
|
||||||
network: Network::new(),
|
network: Network::new(),
|
||||||
@ -56,12 +56,8 @@ impl Sandbox {
|
|||||||
mounts: Vec::new(),
|
mounts: Vec::new(),
|
||||||
container_mounts: HashMap::new(),
|
container_mounts: HashMap::new(),
|
||||||
pci_device_map: HashMap::new(),
|
pci_device_map: HashMap::new(),
|
||||||
shared_utsns: Namespace {
|
shared_utsns: Namespace::new(&logger),
|
||||||
path: "".to_string(),
|
shared_ipcns: Namespace::new(&logger),
|
||||||
},
|
|
||||||
shared_ipcns: Namespace {
|
|
||||||
path: "".to_string(),
|
|
||||||
},
|
|
||||||
storages: HashMap::new(),
|
storages: HashMap::new(),
|
||||||
running: false,
|
running: false,
|
||||||
no_pivot_root: fs_type.eq(TYPEROOTFS),
|
no_pivot_root: fs_type.eq(TYPEROOTFS),
|
||||||
@ -129,29 +125,28 @@ impl Sandbox {
|
|||||||
|
|
||||||
pub fn setup_shared_namespaces(&mut self) -> Result<bool> {
|
pub fn setup_shared_namespaces(&mut self) -> Result<bool> {
|
||||||
// Set up shared IPC namespace
|
// 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,
|
Ok(ns) => ns,
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
return Err(ErrorKind::ErrorCode(format!(
|
return Err(ErrorKind::ErrorCode(format!(
|
||||||
"Failed to setup persisten IPC namespace with error: {}",
|
"Failed to setup persistent IPC namespace with error: {}",
|
||||||
&err
|
err
|
||||||
))
|
))
|
||||||
.into())
|
.into())
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// Set up shared UTS namespace
|
// // Set up shared UTS namespace
|
||||||
self.shared_utsns = match setup_persistent_ns(self.logger.clone(), NSTYPEUTS) {
|
self.shared_utsns = match Namespace::new(&self.logger).as_uts().setup() {
|
||||||
Ok(ns) => ns,
|
Ok(ns) => ns,
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
return Err(ErrorKind::ErrorCode(format!(
|
return Err(ErrorKind::ErrorCode(format!(
|
||||||
"Failed to setup persisten UTS namespace with error: {} ",
|
"Failed to setup persistent UTS namespace with error: {}",
|
||||||
&err
|
err
|
||||||
))
|
))
|
||||||
.into())
|
.into())
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
Ok(true)
|
Ok(true)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user