use itertools::Itertools;
fn is_mac() -> bool {
cfg!(target_os = "macos")
}
struct CGroup<'a> {
#[allow(dead_code)]
hierarchy_id: &'a str,
#[allow(dead_code)]
controllers: &'a str,
path: &'a str,
}
impl<'a> CGroup<'a> {
pub fn parse(line: &'a str) -> Option<Self> {
line.split(':')
.tuples()
.map(|(hierarchy_id, controllers, path)| Self {
hierarchy_id,
controllers,
path,
})
.next()
}
pub fn is_container(&self) -> bool {
let path = self.path;
path.starts_with("/docker") || path.starts_with("/kube")
}
}
fn is_container(text: &str) -> bool {
text.lines()
.flat_map(CGroup::parse)
.any(|cg| cg.is_container())
}
pub fn is_containerized() -> bool {
if is_mac() {
return false;
}
let pid = std::process::id();
let cgroup_path = format!("/proc/{pid}/cgroup");
let contents = std::fs::read_to_string(&cgroup_path).expect("Cannot open cgroup file");
is_container(&contents)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn parse_cgroup_correctly() {
let actual = CGroup::parse("12:cpu,cpuacct:/").unwrap();
assert_eq!(actual.hierarchy_id, "12");
assert_eq!(actual.controllers, "cpu,cpuacct");
assert_eq!(actual.path, "/");
}
#[test]
fn test_is_container() {
let docker_cgroup = r#"
12:cpu,cpuacct:/docker/c6fa62a9938149f6098fd0cdaffc9cdf0f526f25d97b5f6e2a4cc1fccc7f7ce1
11:perf_event:/docker/c6fa62a9938149f6098fd0cdaffc9cdf0f526f25d97b5f6e2a4cc1fccc7f7ce1
10:rdma:/"#;
assert!(is_container(docker_cgroup));
let init_cgroup = r#"
12:cpu,cpuacct:/
11:perf_event:/
0::/init.scope"#;
assert!(!is_container(init_cgroup));
let k8s_cgroup = r#"
12:hugetlb:/kubepods/besteffort/poda00e29fd-7bbd-11e9-8679-fa163ea7e3b8/c4b1403f3d9c7ce261be851df71d9a9773c53419075ccda39ae8fe6a39fd2eb1
11:cpuset:/kubepods/besteffort/poda00e29fd-7bbd-11e9-8679-fa163ea7e3b8/c4b1403f3d9c7ce261be851df71d9a9773c53419075ccda39ae8fe6a39fd2eb1"#;
assert!(is_container(k8s_cgroup));
}
}