use std::process::Command;
use std::thread::sleep;
use std::time::Duration;
use cgroups_rs::fs::cgroup::{
CGROUP_MODE_DOMAIN, CGROUP_MODE_DOMAIN_INVALID, CGROUP_MODE_DOMAIN_THREADED,
CGROUP_MODE_THREADED,
};
use cgroups_rs::fs::memory::MemController;
use cgroups_rs::fs::Controller;
use cgroups_rs::fs::{Cgroup, Subsystem};
use cgroups_rs::CgroupPid;
#[test]
fn test_procs_iterator_cgroup() {
let h = cgroups_rs::fs::hierarchies::auto();
let pid = libc::pid_t::from(nix::unistd::getpid()) as u64;
let cg = Cgroup::new(h, String::from("test_procs_iterator_cgroup")).unwrap();
{
cg.add_task_by_tgid(CgroupPid::from(pid)).unwrap();
let mut procs = cg.procs().into_iter();
assert_eq!(procs.next(), Some(CgroupPid::from(pid)));
assert_eq!(procs.next(), None);
cg.remove_task_by_tgid(CgroupPid::from(pid)).unwrap();
procs = cg.procs().into_iter();
assert_eq!(procs.next(), None);
}
cg.delete().unwrap();
}
#[test]
fn test_tasks_iterator_cgroup_v1() {
if cgroups_rs::fs::hierarchies::is_cgroup2_unified_mode() {
return;
}
let h = cgroups_rs::fs::hierarchies::auto();
let pid = libc::pid_t::from(nix::unistd::getpid()) as u64;
let cg = Cgroup::new(h, String::from("test_tasks_iterator_cgroup_v1")).unwrap();
{
cg.add_task(CgroupPid::from(pid)).unwrap();
let mut tasks = cg.tasks().into_iter();
assert_eq!(tasks.next(), Some(CgroupPid::from(pid)));
assert_eq!(tasks.next(), None);
cg.remove_task(CgroupPid::from(pid)).unwrap();
tasks = cg.tasks().into_iter();
assert_eq!(tasks.next(), None);
}
cg.delete().unwrap();
}
#[test]
fn test_tasks_iterator_cgroup_threaded_mode() {
if !cgroups_rs::fs::hierarchies::is_cgroup2_unified_mode() {
return;
}
let pid = libc::pid_t::from(nix::unistd::getpid()) as u64;
let cg = Cgroup::new(
cgroups_rs::fs::hierarchies::auto(),
String::from("test_tasks_iterator_cgroup_threaded_mode"),
)
.unwrap();
let cg_threaded_sub1 = Cgroup::new_with_specified_controllers(
cgroups_rs::fs::hierarchies::auto(),
String::from("test_tasks_iterator_cgroup_threaded_mode/threaded_sub1"),
Some(vec![String::from("cpuset"), String::from("cpu")]),
)
.unwrap();
let cg_threaded_sub2 = Cgroup::new_with_specified_controllers(
cgroups_rs::fs::hierarchies::auto(),
String::from("test_tasks_iterator_cgroup_threaded_mode/threaded_sub2"),
Some(vec![String::from("cpuset"), String::from("cpu")]),
)
.unwrap();
{
assert_eq!(cg.get_cgroup_type().unwrap(), CGROUP_MODE_DOMAIN);
cg_threaded_sub1
.set_cgroup_type(CGROUP_MODE_THREADED)
.unwrap();
assert_eq!(
cg_threaded_sub1.get_cgroup_type().unwrap(),
CGROUP_MODE_THREADED
);
assert_eq!(
cg_threaded_sub2.get_cgroup_type().unwrap(),
CGROUP_MODE_DOMAIN_INVALID
);
assert_eq!(cg.get_cgroup_type().unwrap(), CGROUP_MODE_DOMAIN_THREADED);
cg_threaded_sub2
.set_cgroup_type(CGROUP_MODE_THREADED)
.unwrap();
assert_eq!(
cg_threaded_sub2.get_cgroup_type().unwrap(),
CGROUP_MODE_THREADED
);
cg.add_task_by_tgid(CgroupPid::from(pid)).unwrap();
let mut procs = cg.procs().into_iter();
assert_eq!(procs.next(), Some(CgroupPid::from(pid)));
assert_eq!(procs.next(), None);
cg_threaded_sub1.add_task(CgroupPid::from(pid)).unwrap();
let mut tasks = cg_threaded_sub1.tasks().into_iter();
assert_eq!(tasks.next(), Some(CgroupPid::from(pid)));
assert_eq!(tasks.next(), None);
cg_threaded_sub1
.move_task_to_parent(CgroupPid::from(pid))
.unwrap();
tasks = cg_threaded_sub1.tasks().into_iter();
assert_eq!(tasks.next(), None);
cg.remove_task_by_tgid(CgroupPid::from(pid)).unwrap();
procs = cg.procs().into_iter();
assert_eq!(procs.next(), None);
}
cg_threaded_sub1.delete().unwrap();
cg_threaded_sub2.delete().unwrap();
cg.delete().unwrap();
}
#[test]
fn test_kill_cgroup() {
if !cgroups_rs::fs::hierarchies::is_cgroup2_unified_mode() {
return;
}
let h = cgroups_rs::fs::hierarchies::auto();
let cg = Cgroup::new(h, String::from("test_kill_cgroup")).unwrap();
{
let mut child = Command::new("sleep").arg("infinity").spawn().unwrap();
cg.add_task_by_tgid(CgroupPid::from(child.id() as u64))
.unwrap();
let cg_procs = cg.procs();
assert_eq!(cg_procs.len(), 1_usize);
cg.kill().unwrap();
let mut tries = 0;
let status: Option<std::process::ExitStatus> = loop {
match child.try_wait() {
Ok(Some(status)) => {
break Some(status);
}
Ok(None) => {
if tries > 3 {
break None;
}
sleep(Duration::from_millis(100));
tries += 1;
}
Err(e) => {
child.kill().unwrap();
panic!("error attempting to wait: {}", e);
}
}
};
assert!(status.is_some());
}
cg.delete().unwrap();
}
#[test]
fn test_cgroup_with_relative_paths() {
if cgroups_rs::fs::hierarchies::is_cgroup2_unified_mode() {
return;
}
let h = cgroups_rs::fs::hierarchies::auto();
let cgroup_root = h.root();
let cgroup_name = "test_cgroup_with_relative_paths";
let cg = Cgroup::load(h, String::from(cgroup_name));
{
let subsystems = cg.subsystems();
subsystems.iter().for_each(|sub| match sub {
Subsystem::Pid(c) => {
let cgroup_path = c.path().to_str().unwrap();
let relative_path = "/pids/";
assert_eq!(
cgroup_path,
format!(
"{}{}{}",
cgroup_root.to_str().unwrap(),
relative_path,
cgroup_name
)
);
}
Subsystem::Mem(c) => {
let cgroup_path = c.path().to_str().unwrap();
assert_eq!(
cgroup_path,
format!("{}/memory/{}", cgroup_root.to_str().unwrap(), cgroup_name)
);
}
_ => {}
});
}
cg.delete().unwrap();
}
#[test]
fn test_cgroup_v2() {
if !cgroups_rs::fs::hierarchies::is_cgroup2_unified_mode() {
return;
}
let h = cgroups_rs::fs::hierarchies::auto();
let cg = Cgroup::new(h, String::from("test_v2")).unwrap();
let mem_controller: &MemController = cg.controller_of().unwrap();
let (mem, swp, rev) = (4 * 1024 * 1000, 2 * 1024 * 1000, 1024 * 1000);
mem_controller.set_limit(mem).unwrap();
mem_controller.set_memswap_limit(swp).unwrap();
mem_controller.set_soft_limit(rev).unwrap();
let memory_stat = mem_controller.memory_stat();
println!("memory_stat {:?}", memory_stat);
assert_eq!(mem, memory_stat.limit_in_bytes);
assert_eq!(rev, memory_stat.soft_limit_in_bytes);
let memswap = mem_controller.memswap();
println!("memswap {:?}", memswap);
assert_eq!(swp, memswap.limit_in_bytes);
cg.delete().unwrap();
}