-
Notifications
You must be signed in to change notification settings - Fork 356
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add test for cgroups_relative_memory #2686
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,68 @@ | ||
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}; | ||
|
||
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<Spec> { | ||
let spec = SpecBuilder::default() | ||
.linux( | ||
LinuxBuilder::default() | ||
.cgroups_path(Path::new(RELATIVE_CGROUPS_PATH).join(cgroup_name)) | ||
.resources( | ||
LinuxResourcesBuilder::default() | ||
.memory( | ||
LinuxMemoryBuilder::default() | ||
.limit(limit) | ||
.swappiness(swappiness) | ||
.build() | ||
.context("failed to build memory spec")?, | ||
) | ||
.build() | ||
.context("failed to build resource spec")?, | ||
) | ||
.build() | ||
.context("failed to build linux spec")?, | ||
) | ||
.build() | ||
.context("failed to build spec")?; | ||
|
||
Ok(spec) | ||
} | ||
|
||
fn test_relative_memory_cgroups() -> TestResult { | ||
let cgroup_name = "test_relative_memory_cgroups"; | ||
|
||
let spec = test_result!(create_spec(cgroup_name, 50593792, 10)); | ||
|
||
test_outside_container(spec.clone(), &|data| { | ||
test_result!(validate_linux_resource_memory(&spec, data)); | ||
|
||
TestResult::Passed | ||
}) | ||
} | ||
|
||
fn can_run() -> bool { | ||
Path::new(CGROUP_MEMORY_LIMIT).exists() && Path::new(CGROUP_MEMORY_SWAPPINESS).exists() | ||
} | ||
Comment on lines
+53
to
+55
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If this is cgroups v1 specific test, we should also check that cgroups is v1 here |
||
|
||
pub fn get_test_group() -> TestGroup { | ||
let mut test_group = TestGroup::new("cgroup_v1_relative_memory"); | ||
let linux_cgroups_memory = ConditionalTest::new( | ||
"test_linux_cgroups_relative_memory", | ||
Box::new(can_run), | ||
Box::new(test_relative_memory_cgroups), | ||
); | ||
|
||
test_group.add(vec![Box::new(linux_cgroups_memory)]); | ||
|
||
test_group | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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(); | ||
Comment on lines
+10
to
+18
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think we can do without the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Oh ,the linux() function call returns a &Option, so as_ref() was needed to make it a Option<&Linux> |
||
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<LinuxMemory, Box<dyn std::error::Error>> { | ||
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::<i64>()?; | ||
memory_data = memory_data.limit(limit); | ||
} | ||
"memory.soft_limit_in_bytes" => { | ||
let reservation = value.trim().parse::<i64>()?; | ||
memory_data = memory_data.reservation(reservation); | ||
} | ||
"memory.memsw.limit_in_bytes" => { | ||
let swap = value.trim().parse::<i64>()?; | ||
memory_data = memory_data.swap(swap); | ||
} | ||
"memory.kmem.limit_in_bytes" => { | ||
let kernel = value.trim().parse::<i64>()?; | ||
memory_data = memory_data.kernel(kernel); | ||
} | ||
"memory.kmem.tcp.limit_in_bytes" => { | ||
let kernel_tcp = value.trim().parse::<i64>()?; | ||
memory_data = memory_data.kernel_tcp(kernel_tcp); | ||
} | ||
"memory.swappiness" => { | ||
let swappiness = value.trim().parse::<u64>()?; | ||
memory_data = memory_data.swappiness(swappiness); | ||
} | ||
"memory.oom_control" => { | ||
let oom_control = value.split_whitespace().collect::<Vec<&str>>(); | ||
let oom_control = oom_control | ||
.get(1) | ||
.ok_or("Failed to get oom_control")? | ||
.parse::<u64>()?; | ||
memory_data = memory_data.disable_oom_killer(oom_control == 1); | ||
} | ||
_ => unreachable!(), | ||
}; | ||
} | ||
} | ||
Ok(memory_data.build()?) | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,3 +1,4 @@ | ||
pub mod linux_resource_memory; | ||
pub mod support; | ||
pub mod test_utils; | ||
pub use support::{ | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think we already have something similar to this existing somewhere in the libcgroup crate. Can you check and confirm if we really need a new function for this?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes there is, but it is tied to V1Manager and is for the current process only.