Merge pull request #1032 from devimc/2020-10-21/unitests/container.rs

Improve unit test coverage for rustjail/container.rs
This commit is contained in:
Julio Montes 2020-10-28 16:08:23 -06:00 committed by GitHub
commit 196e8d81cf
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 402 additions and 7 deletions

View File

@ -0,0 +1,74 @@
// Copyright (c) 2020 Intel Corporation
//
// SPDX-License-Identifier: Apache-2.0
//
use protobuf::{CachedSize, SingularPtrField, UnknownFields};
use crate::cgroups::Manager as CgroupManager;
use crate::protocols::agent::{BlkioStats, CgroupStats, CpuStats, MemoryStats, PidsStats};
use anyhow::Result;
use cgroups::freezer::FreezerState;
use libc::{self, pid_t};
use oci::LinuxResources;
use std::collections::HashMap;
use std::string::String;
#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct Manager {
pub paths: HashMap<String, String>,
pub mounts: HashMap<String, String>,
pub cpath: String,
}
impl CgroupManager for Manager {
fn apply(&self, _: pid_t) -> Result<()> {
Ok(())
}
fn set(&self, _: &LinuxResources, _: bool) -> Result<()> {
Ok(())
}
fn get_stats(&self) -> Result<CgroupStats> {
Ok(CgroupStats {
cpu_stats: SingularPtrField::some(CpuStats::default()),
memory_stats: SingularPtrField::some(MemoryStats::new()),
pids_stats: SingularPtrField::some(PidsStats::new()),
blkio_stats: SingularPtrField::some(BlkioStats::new()),
hugetlb_stats: HashMap::new(),
unknown_fields: UnknownFields::default(),
cached_size: CachedSize::default(),
})
}
fn freeze(&self, _: FreezerState) -> Result<()> {
Ok(())
}
fn destroy(&mut self) -> Result<()> {
Ok(())
}
fn get_pids(&self) -> Result<Vec<pid_t>> {
Ok(Vec::new())
}
}
impl Manager {
pub fn new(cpath: &str) -> Result<Self> {
Ok(Self {
paths: HashMap::new(),
mounts: HashMap::new(),
cpath: cpath.to_string(),
})
}
pub fn update_cpuset_path(&self, _: &str) -> Result<()> {
Ok(())
}
pub fn get_cg_path(&self, _: &str) -> Option<String> {
Some("".to_string())
}
}

View File

@ -10,6 +10,7 @@ use protocols::agent::CgroupStats;
use cgroups::freezer::FreezerState;
pub mod fs;
pub mod mock;
pub mod notifier;
pub mod systemd;

View File

@ -20,7 +20,10 @@ use std::time::SystemTime;
use cgroups::freezer::FreezerState;
use crate::capabilities::{self, CAPSMAP};
#[cfg(not(test))]
use crate::cgroups::fs::Manager as FsManager;
#[cfg(test)]
use crate::cgroups::mock::Manager as FsManager;
use crate::cgroups::Manager;
use crate::log_child;
use crate::process::Process;
@ -694,11 +697,15 @@ impl BaseContainer for LinuxContainer {
}
fn state(&self) -> Result<State> {
Err(anyhow!("not suppoerted"))
Err(anyhow!("not supported"))
}
fn oci_state(&self) -> Result<OCIState> {
let oci = self.config.spec.as_ref().unwrap();
let oci = match self.config.spec.as_ref() {
Some(s) => s,
None => return Err(anyhow!("Unable to get OCI state: spec not found")),
};
let status = self.status();
let pid = if status != Status::STOPPED {
self.init_process_pid
@ -706,9 +713,17 @@ impl BaseContainer for LinuxContainer {
0
};
let root = oci.root.as_ref().unwrap().path.as_str();
let root = match oci.root.as_ref() {
Some(s) => s.path.as_str(),
None => return Err(anyhow!("Unable to get root path: oci.root is none")),
};
let path = fs::canonicalize(root)?;
let bundle = path.parent().unwrap().to_str().unwrap().to_string();
let bundle = match path.parent() {
Some(s) => s.to_str().unwrap().to_string(),
None => return Err(anyhow!("could not get root parent: root path {:?}", path)),
};
Ok(OCIState {
version: oci.version.clone(),
id: self.id(),
@ -1004,7 +1019,7 @@ impl BaseContainer for LinuxContainer {
}
self.status.transition(Status::STOPPED);
nix::mount::umount2(
mount::umount2(
spec.root.as_ref().unwrap().path.as_str(),
MntFlags::MNT_DETACH,
)?;
@ -1628,9 +1643,18 @@ fn execute_hook(logger: &Logger, h: &Hook, st: &OCIState) -> Result<()> {
#[cfg(test)]
mod tests {
use super::*;
use crate::process::Process;
use crate::skip_if_not_root;
use std::fs;
use std::os::unix::fs::MetadataExt;
use std::os::unix::io::AsRawFd;
use tempfile::tempdir;
macro_rules! sl {
() => {
slog_scope::logger()
};
}
#[test]
fn test_status_transtition() {
@ -1672,4 +1696,295 @@ mod tests {
// restore the uid
set_stdio_permissions(old_uid).unwrap();
}
#[test]
fn test_status_fmt() {
assert_eq!("\"created\"", format!("{:?}", Status::CREATED));
assert_eq!("\"running\"", format!("{:?}", Status::RUNNING));
assert_eq!("\"paused\"", format!("{:?}", Status::PAUSED));
assert_eq!("\"stopped\"", format!("{:?}", Status::STOPPED));
}
#[test]
fn test_namespaces() {
lazy_static::initialize(&NAMESPACES);
assert_eq!(NAMESPACES.len(), 7);
let ns = NAMESPACES.get("user");
assert!(ns.is_some());
let ns = NAMESPACES.get("ipc");
assert!(ns.is_some());
let ns = NAMESPACES.get("pid");
assert!(ns.is_some());
let ns = NAMESPACES.get("network");
assert!(ns.is_some());
let ns = NAMESPACES.get("mount");
assert!(ns.is_some());
let ns = NAMESPACES.get("uts");
assert!(ns.is_some());
let ns = NAMESPACES.get("cgroup");
assert!(ns.is_some());
}
#[test]
fn test_typetoname() {
lazy_static::initialize(&TYPETONAME);
assert_eq!(TYPETONAME.len(), 7);
let ns = TYPETONAME.get("user");
assert!(ns.is_some());
let ns = TYPETONAME.get("ipc");
assert!(ns.is_some());
let ns = TYPETONAME.get("pid");
assert!(ns.is_some());
let ns = TYPETONAME.get("network");
assert!(ns.is_some());
let ns = TYPETONAME.get("mount");
assert!(ns.is_some());
let ns = TYPETONAME.get("uts");
assert!(ns.is_some());
let ns = TYPETONAME.get("cgroup");
assert!(ns.is_some());
}
fn create_dummy_opts() -> CreateOpts {
let mut root = oci::Root::default();
root.path = "/tmp".to_string();
let linux = Linux::default();
let mut spec = Spec::default();
spec.root = Some(root).into();
spec.linux = Some(linux).into();
CreateOpts {
cgroup_name: "".to_string(),
use_systemd_cgroup: false,
no_pivot_root: false,
no_new_keyring: false,
spec: Some(spec),
rootless_euid: false,
rootless_cgroup: false,
}
}
fn new_linux_container<U, F: FnOnce(LinuxContainer) -> Result<U, anyhow::Error>>(
op: F,
) -> Result<U, anyhow::Error> {
// Create a temporal directory
tempdir()
.map_err(|e| anyhow!(e).context("tempdir failed"))
.and_then(|p: tempfile::TempDir| {
// Create a new container
LinuxContainer::new(
"some_id",
&p.path().join("rootfs").to_str().unwrap(),
create_dummy_opts(),
&slog_scope::logger(),
)
.and_then(op)
})
}
#[test]
fn test_linuxcontainer_pause_bad_status() {
let ret = new_linux_container(|mut c: LinuxContainer| {
// Change state to pause, c.pause() should fail
c.status.transition(Status::PAUSED);
c.pause().map_err(|e| anyhow!(e))
});
assert!(ret.is_err(), "Expecting error, Got {:?}", ret);
assert!(format!("{:?}", ret).contains("failed to pause container"))
}
#[test]
fn test_linuxcontainer_pause_cgroupmgr_is_none() {
let ret = new_linux_container(|mut c: LinuxContainer| {
c.cgroup_manager = None;
c.pause().map_err(|e| anyhow!(e))
});
assert!(ret.is_err(), "Expecting error, Got {:?}", ret);
}
#[test]
fn test_linuxcontainer_pause() {
let ret = new_linux_container(|mut c: LinuxContainer| {
c.cgroup_manager = FsManager::new("").ok();
c.pause().map_err(|e| anyhow!(e))
});
assert!(ret.is_ok(), "Expecting Ok, Got {:?}", ret);
}
#[test]
fn test_linuxcontainer_resume_bad_status() {
let ret = new_linux_container(|mut c: LinuxContainer| {
// Change state to created, c.resume() should fail
c.status.transition(Status::CREATED);
c.resume().map_err(|e| anyhow!(e))
});
assert!(ret.is_err(), "Expecting error, Got {:?}", ret);
assert!(format!("{:?}", ret).contains("not paused"))
}
#[test]
fn test_linuxcontainer_resume_cgroupmgr_is_none() {
let ret = new_linux_container(|mut c: LinuxContainer| {
c.status.transition(Status::PAUSED);
c.cgroup_manager = None;
c.resume().map_err(|e| anyhow!(e))
});
assert!(ret.is_err(), "Expecting error, Got {:?}", ret);
}
#[test]
fn test_linuxcontainer_resume() {
let ret = new_linux_container(|mut c: LinuxContainer| {
c.cgroup_manager = FsManager::new("").ok();
// Change status to paused, this way we can resume it
c.status.transition(Status::PAUSED);
c.resume().map_err(|e| anyhow!(e))
});
assert!(ret.is_ok(), "Expecting Ok, Got {:?}", ret);
}
#[test]
fn test_linuxcontainer_state() {
let ret = new_linux_container(|c: LinuxContainer| c.state());
assert!(ret.is_err(), "Expecting Err, Got {:?}", ret);
assert!(
format!("{:?}", ret).contains("not supported"),
"Got: {:?}",
ret
)
}
#[test]
fn test_linuxcontainer_oci_state_no_root_parent() {
let ret = new_linux_container(|mut c: LinuxContainer| {
c.config.spec.as_mut().unwrap().root.as_mut().unwrap().path = "/".to_string();
c.oci_state()
});
assert!(ret.is_err(), "Expecting Err, Got {:?}", ret);
assert!(
format!("{:?}", ret).contains("could not get root parent"),
"Got: {:?}",
ret
)
}
#[test]
fn test_linuxcontainer_oci_state() {
let ret = new_linux_container(|c: LinuxContainer| c.oci_state());
assert!(ret.is_ok(), "Expecting Ok, Got {:?}", ret);
}
#[test]
fn test_linuxcontainer_config() {
let ret = new_linux_container(|c: LinuxContainer| Ok(c));
assert!(ret.is_ok(), "Expecting ok, Got {:?}", ret);
assert!(
ret.as_ref().unwrap().config().is_ok(),
"Expecting ok, Got {:?}",
ret
);
}
#[test]
fn test_linuxcontainer_processes() {
let ret = new_linux_container(|c: LinuxContainer| c.processes());
assert!(ret.is_ok(), "Expecting Ok, Got {:?}", ret);
}
#[test]
fn test_linuxcontainer_get_process_not_found() {
let _ = new_linux_container(|mut c: LinuxContainer| {
let p = c.get_process("123");
assert!(p.is_err(), "Expecting Err, Got {:?}", p);
Ok(())
});
}
#[test]
fn test_linuxcontainer_get_process() {
let _ = new_linux_container(|mut c: LinuxContainer| {
c.processes.insert(
1,
Process::new(&sl!(), &oci::Process::default(), "123", true, 1).unwrap(),
);
let p = c.get_process("123");
assert!(p.is_ok(), "Expecting Ok, Got {:?}", p);
Ok(())
});
}
#[test]
fn test_linuxcontainer_stats() {
let ret = new_linux_container(|c: LinuxContainer| c.stats());
assert!(ret.is_ok(), "Expecting Ok, Got {:?}", ret);
}
#[test]
fn test_linuxcontainer_set() {
let ret =
new_linux_container(|mut c: LinuxContainer| c.set(oci::LinuxResources::default()));
assert!(ret.is_ok(), "Expecting Ok, Got {:?}", ret);
}
#[test]
fn test_linuxcontainer_start() {
let ret = new_linux_container(|mut c: LinuxContainer| {
c.start(Process::new(&sl!(), &oci::Process::default(), "123", true, 1).unwrap())
});
assert!(ret.is_err(), "Expecting Err, Got {:?}", ret);
}
#[test]
fn test_linuxcontainer_run() {
let ret = new_linux_container(|mut c: LinuxContainer| {
c.run(Process::new(&sl!(), &oci::Process::default(), "123", true, 1).unwrap())
});
assert!(ret.is_err(), "Expecting Err, Got {:?}", ret);
}
#[test]
fn test_linuxcontainer_destroy() {
let ret = new_linux_container(|mut c: LinuxContainer| c.destroy());
assert!(ret.is_ok(), "Expecting Ok, Got {:?}", ret);
}
#[test]
fn test_linuxcontainer_signal() {
let ret =
new_linux_container(|c: LinuxContainer| c.signal(nix::sys::signal::SIGCONT, true));
assert!(ret.is_ok(), "Expecting Ok, Got {:?}", ret);
}
#[test]
fn test_linuxcontainer_exec() {
let ret = new_linux_container(|mut c: LinuxContainer| c.exec());
assert!(ret.is_err(), "Expecting Err, Got {:?}", ret);
}
#[test]
fn test_linuxcontainer_do_init_child() {
let ret = do_init_child(std::io::stdin().as_raw_fd());
assert!(ret.is_err(), "Expecting Err, Got {:?}", ret);
}
}

View File

@ -112,7 +112,12 @@ lazy_static! {
#[inline(always)]
#[allow(unused_variables)]
fn mount<P1: ?Sized + NixPath, P2: ?Sized + NixPath, P3: ?Sized + NixPath, P4: ?Sized + NixPath>(
pub fn mount<
P1: ?Sized + NixPath,
P2: ?Sized + NixPath,
P3: ?Sized + NixPath,
P4: ?Sized + NixPath,
>(
source: Option<&P1>,
target: &P2,
fstype: Option<&P3>,
@ -127,7 +132,7 @@ fn mount<P1: ?Sized + NixPath, P2: ?Sized + NixPath, P3: ?Sized + NixPath, P4: ?
#[inline(always)]
#[allow(unused_variables)]
fn umount2<P: ?Sized + NixPath>(
pub fn umount2<P: ?Sized + NixPath>(
target: &P,
flags: MntFlags,
) -> std::result::Result<(), nix::Error> {