diff --git a/crates/libcgroups/src/v1/util.rs b/crates/libcgroups/src/v1/util.rs index 050967a7a..70fcbcc1b 100644 --- a/crates/libcgroups/src/v1/util.rs +++ b/crates/libcgroups/src/v1/util.rs @@ -1,4 +1,8 @@ -use std::{collections::HashMap, path::PathBuf}; +use std::{ + collections::HashMap, + fs, io, + path::{Path, PathBuf}, +}; use procfs::{process::Process, ProcError}; @@ -79,3 +83,24 @@ pub fn get_subsystem_mount_point(subsystem: &ControllerType) -> Result Result { + let contents = fs::read_to_string(Path::new(&format!("/proc/{}/cgroup", pid))) + .unwrap_or_else(|_| panic!("failed to read /proc/{}/cgroup", pid)); + for line in contents.lines() { + let parts: Vec<&str> = line.splitn(3, ':').collect(); + if parts.len() < 3 { + continue; + } + let subparts: Vec<&str> = parts[1].split(',').collect(); + for subpart in subparts { + if subpart == subsystem { + return Ok(PathBuf::from(parts[2].to_string())); + } + } + } + Err(io::Error::new( + io::ErrorKind::Other, + format!("subsystem {} not found", subsystem), + )) +} diff --git a/tests/contest/contest/src/tests/cgroups/memory.rs b/tests/contest/contest/src/tests/cgroups/memory.rs index a6a20e9a7..ce0b51802 100644 --- a/tests/contest/contest/src/tests/cgroups/memory.rs +++ b/tests/contest/contest/src/tests/cgroups/memory.rs @@ -1,13 +1,13 @@ use std::path::Path; +use crate::utils::{linux_resource_memory::validate_linux_resource_memory, test_outside_container}; use anyhow::{Context, Result}; +use libcgroups::common::{get_cgroup_setup, CgroupSetup}; use oci_spec::runtime::{ LinuxBuilder, LinuxMemoryBuilder, LinuxResourcesBuilder, Spec, SpecBuilder, }; use test_framework::{test_result, ConditionalTest, TestGroup, TestResult}; -use crate::utils::{test_outside_container, test_utils::check_container_created}; - const CGROUP_MEMORY_LIMIT: &str = "/sys/fs/cgroup/memory/memory.limit_in_bytes"; const CGROUP_MEMORY_SWAPPINESS: &str = "/sys/fs/cgroup/memory/memory.swappiness"; @@ -50,8 +50,8 @@ fn test_memory_cgroups() -> TestResult { ]; for spec in cases.into_iter() { - let test_result = test_outside_container(spec, &|data| { - test_result!(check_container_created(&data)); + let test_result = test_outside_container(spec.clone(), &|data| { + test_result!(validate_linux_resource_memory(&spec, data)); TestResult::Passed }); @@ -64,7 +64,10 @@ fn test_memory_cgroups() -> TestResult { } fn can_run() -> bool { - Path::new(CGROUP_MEMORY_LIMIT).exists() && Path::new(CGROUP_MEMORY_SWAPPINESS).exists() + let cgroup_setup = get_cgroup_setup(); + matches!(cgroup_setup, Ok(CgroupSetup::Legacy)) + && Path::new(CGROUP_MEMORY_LIMIT).exists() + && Path::new(CGROUP_MEMORY_SWAPPINESS).exists() } pub fn get_test_group() -> TestGroup { diff --git a/tests/contest/contest/src/tests/cgroups/relative_memory.rs b/tests/contest/contest/src/tests/cgroups/relative_memory.rs index 3036aaad3..438a83215 100644 --- a/tests/contest/contest/src/tests/cgroups/relative_memory.rs +++ b/tests/contest/contest/src/tests/cgroups/relative_memory.rs @@ -1,21 +1,22 @@ use std::path::Path; +use crate::utils::{linux_resource_memory::validate_linux_resource_memory, test_outside_container}; use anyhow::{Context, Result}; use oci_spec::runtime::{ LinuxBuilder, LinuxMemoryBuilder, LinuxResourcesBuilder, Spec, SpecBuilder, }; use test_framework::{test_result, ConditionalTest, TestGroup, TestResult}; -use crate::utils::{test_outside_container, test_utils::check_container_created}; - const CGROUP_MEMORY_LIMIT: &str = "/sys/fs/cgroup/memory/memory.limit_in_bytes"; const CGROUP_MEMORY_SWAPPINESS: &str = "/sys/fs/cgroup/memory/memory.swappiness"; +const RELATIVE_CGROUPS_PATH: &str = "/testdir/runtime-test/container"; + fn create_spec(cgroup_name: &str, limit: i64, swappiness: u64) -> Result { let spec = SpecBuilder::default() .linux( LinuxBuilder::default() - .cgroups_path(Path::new("/testdir/runtime-test/container").join(cgroup_name)) + .cgroups_path(Path::new(RELATIVE_CGROUPS_PATH).join(cgroup_name)) .resources( LinuxResourcesBuilder::default() .memory( @@ -42,8 +43,8 @@ fn test_relative_memory_cgroups() -> TestResult { let spec = test_result!(create_spec(cgroup_name, 50593792, 10)); - test_outside_container(spec, &|data| { - test_result!(check_container_created(&data)); + test_outside_container(spec.clone(), &|data| { + test_result!(validate_linux_resource_memory(&spec, data)); TestResult::Passed }) diff --git a/tests/contest/contest/src/utils/linux_resource_memory.rs b/tests/contest/contest/src/utils/linux_resource_memory.rs new file mode 100644 index 000000000..0eb7924fb --- /dev/null +++ b/tests/contest/contest/src/utils/linux_resource_memory.rs @@ -0,0 +1,108 @@ +use super::ContainerData; +use anyhow::{bail, Result}; +use libcgroups::v1::{ + util::{get_subsystem_mount_point, get_subsystem_path}, + ControllerType, +}; +use oci_spec::runtime::{LinuxMemory, LinuxMemoryBuilder, Spec}; + +pub(crate) fn validate_linux_resource_memory(spec: &Spec, data: ContainerData) -> Result<()> { + let expected_memory = spec + .linux() + .as_ref() + .unwrap() + .resources() + .as_ref() + .unwrap() + .memory() + .as_ref(); + let expected_memory = match expected_memory { + Some(m) => m, + None => bail!("expected memory to be set, but it was not"), + }; + + let memory = get_memory_data(data.state.unwrap().pid.unwrap()); + + if let Err(e) = memory { + bail!("failed to get memory data: {:?}", e); + } + + let expected_limit = expected_memory.limit().unwrap(); + let actual_limit = memory.as_ref().unwrap().limit().unwrap(); + if expected_limit != actual_limit { + bail!("expected memory limit {expected_limit}, but got {actual_limit} instead"); + } + + let expected_swappiness = expected_memory.swappiness().unwrap(); + let actual_swappiness = memory.as_ref().unwrap().swappiness().unwrap(); + if expected_memory.swappiness().unwrap() != actual_swappiness { + bail!("expected memory swappiness {expected_swappiness}, got {actual_swappiness}"); + } + + Ok(()) +} + +fn get_memory_data(pid: i32) -> Result> { + let cgroup_mount_point = get_subsystem_mount_point(&ControllerType::Memory)?; + let mut cgroup_path = get_subsystem_path(pid, "memory")?; + + // Removing the leading slash to convert the path to be relative to the cgroup mount point + if cgroup_path.is_absolute() { + cgroup_path = cgroup_path.strip_prefix("/")?.to_path_buf(); + } + + let mut memory_data = LinuxMemoryBuilder::default(); + let cgroup_memory_files = vec![ + "memory.limit_in_bytes", + "memory.soft_limit_in_bytes", + "memory.memsw.limit_in_bytes", + "memory.kmem.limit_in_bytes", + "memory.kmem.tcp.limit_in_bytes", + "memory.swappiness", + "memory.oom_control", + ]; + + let path = cgroup_mount_point.join(&cgroup_path); + for file in cgroup_memory_files { + let file_path = path.join(file); + if file_path.exists() { + let value = std::fs::read_to_string(&file_path)?; + match file { + "memory.limit_in_bytes" => { + let limit = value.trim().parse::()?; + memory_data = memory_data.limit(limit); + } + "memory.soft_limit_in_bytes" => { + let reservation = value.trim().parse::()?; + memory_data = memory_data.reservation(reservation); + } + "memory.memsw.limit_in_bytes" => { + let swap = value.trim().parse::()?; + memory_data = memory_data.swap(swap); + } + "memory.kmem.limit_in_bytes" => { + let kernel = value.trim().parse::()?; + memory_data = memory_data.kernel(kernel); + } + "memory.kmem.tcp.limit_in_bytes" => { + let kernel_tcp = value.trim().parse::()?; + memory_data = memory_data.kernel_tcp(kernel_tcp); + } + "memory.swappiness" => { + let swappiness = value.trim().parse::()?; + memory_data = memory_data.swappiness(swappiness); + } + "memory.oom_control" => { + let oom_control = value.split_whitespace().collect::>(); + let oom_control = oom_control + .get(1) + .ok_or("Failed to get oom_control")? + .parse::()?; + memory_data = memory_data.disable_oom_killer(oom_control == 1); + } + _ => unreachable!(), + }; + } + } + Ok(memory_data.build()?) +} diff --git a/tests/contest/contest/src/utils/mod.rs b/tests/contest/contest/src/utils/mod.rs index fe546b87a..d2f5db0e1 100644 --- a/tests/contest/contest/src/utils/mod.rs +++ b/tests/contest/contest/src/utils/mod.rs @@ -1,3 +1,4 @@ +pub mod linux_resource_memory; pub mod support; pub mod test_utils; pub use support::{