Skip to content
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

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 26 additions & 1 deletion crates/libcgroups/src/v1/util.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,8 @@
use std::{collections::HashMap, path::PathBuf};
use std::{
collections::HashMap,
fs, io,
path::{Path, PathBuf},
};

use procfs::{process::Process, ProcError};

Expand Down Expand Up @@ -79,3 +83,24 @@ pub fn get_subsystem_mount_point(subsystem: &ControllerType) -> Result<PathBuf,
subsystem: *subsystem,
})
}

pub fn get_subsystem_path(pid: i32, subsystem: &str) -> Result<PathBuf, io::Error> {
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),
))
}
Comment on lines +87 to +106
Copy link
Collaborator

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?

Copy link
Contributor Author

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.

2 changes: 2 additions & 0 deletions tests/contest/contest/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,7 @@ fn main() -> Result<()> {
let cgroup_v1_memory = cgroups::memory::get_test_group();
let cgroup_v1_network = cgroups::network::get_test_group();
let cgroup_v1_blkio = cgroups::blkio::get_test_group();
let cgroup_v1_relative_memory = cgroups::relative_memory::get_test_group();
let seccomp = get_seccomp_test();
let seccomp_notify = get_seccomp_notify_test();
let ro_paths = get_ro_paths_test();
Expand All @@ -120,6 +121,7 @@ fn main() -> Result<()> {
tm.add_test_group(Box::new(cgroup_v1_memory));
tm.add_test_group(Box::new(cgroup_v1_network));
tm.add_test_group(Box::new(cgroup_v1_blkio));
tm.add_test_group(Box::new(cgroup_v1_relative_memory));
tm.add_test_group(Box::new(seccomp));
tm.add_test_group(Box::new(seccomp_notify));
tm.add_test_group(Box::new(ro_paths));
Expand Down
13 changes: 8 additions & 5 deletions tests/contest/contest/src/tests/cgroups/memory.rs
Original file line number Diff line number Diff line change
@@ -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";

Expand Down Expand Up @@ -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
});
Expand All @@ -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 {
Expand Down
1 change: 1 addition & 0 deletions tests/contest/contest/src/tests/cgroups/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ pub mod cpu;
pub mod memory;
pub mod network;
pub mod pids;
pub mod relative_memory;

pub fn cleanup_v1() -> Result<()> {
for subsystem in list_subsystem_mount_points()? {
Expand Down
68 changes: 68 additions & 0 deletions tests/contest/contest/src/tests/cgroups/relative_memory.rs
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
Copy link
Collaborator

Choose a reason for hiding this comment

The 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
}
108 changes: 108 additions & 0 deletions tests/contest/contest/src/utils/linux_resource_memory.rs
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
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we can do without the as_ref calls here, as spec is already a immutable ref? Please check once.

Copy link
Contributor Author

Choose a reason for hiding this comment

The 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()?)
}
1 change: 1 addition & 0 deletions tests/contest/contest/src/utils/mod.rs
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::{
Expand Down