diff --git a/Cargo.lock b/Cargo.lock index 0d298367b..eac8b52a8 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -583,7 +583,7 @@ dependencies = [ "tracing", "tracing-subscriber", "uuid", - "which 6.0.3", + "which 7.0.0", ] [[package]] @@ -5488,9 +5488,9 @@ dependencies = [ [[package]] name = "which" -version = "6.0.3" +version = "7.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b4ee928febd44d98f2f459a4a79bd4d928591333a494a10a868418ac1b39cf1f" +checksum = "c9cad3279ade7346b96e38731a641d7343dd6a53d55083dd54eadfa5a1b38c6b" dependencies = [ "either", "home", diff --git a/crates/libcontainer/src/rootfs/rootfs.rs b/crates/libcontainer/src/rootfs/rootfs.rs index e74138333..a378e52b4 100644 --- a/crates/libcontainer/src/rootfs/rootfs.rs +++ b/crates/libcontainer/src/rootfs/rootfs.rs @@ -31,17 +31,14 @@ impl RootFS { } } - pub fn prepare_rootfs( + pub fn mount_to_rootfs( &self, + linux: &Linux, spec: &Spec, rootfs: &Path, - bind_devices: bool, cgroup_ns: bool, ) -> Result<()> { - tracing::debug!(?rootfs, "prepare rootfs"); let mut flags = MsFlags::MS_REC; - let linux = spec.linux().as_ref().ok_or(MissingSpecError::Linux)?; - match linux.rootfs_propagation().as_deref() { Some("shared") => flags |= MsFlags::MS_SHARED, Some("private") => flags |= MsFlags::MS_PRIVATE, @@ -92,6 +89,20 @@ impl RootFS { mounter.setup_mount(mount, &global_options)?; } } + Ok(()) + } + + pub fn prepare_rootfs( + &self, + spec: &Spec, + rootfs: &Path, + bind_devices: bool, + cgroup_ns: bool, + ) -> Result<()> { + tracing::debug!(?rootfs, "prepare rootfs"); + let linux = spec.linux().as_ref().ok_or(MissingSpecError::Linux)?; + + self.mount_to_rootfs(linux, spec, rootfs, cgroup_ns)?; let symlinker = Symlink::new(); symlinker.setup_kcore_symlink(rootfs)?; diff --git a/tests/contest/contest/Cargo.toml b/tests/contest/contest/Cargo.toml index d0103b541..30906a92c 100644 --- a/tests/contest/contest/Cargo.toml +++ b/tests/contest/contest/Cargo.toml @@ -20,7 +20,7 @@ serde_json = "1.0" tar = "0.4" test_framework = { path = "../test_framework" } uuid = "1.11" -which = "6.0.3" +which = "7.0.0" tempfile = "3" scopeguard = "1.2.0" tracing = { version = "0.1.40", features = ["attributes"]} diff --git a/tests/contest/contest/src/main.rs b/tests/contest/contest/src/main.rs index 5f3c13174..7b90de798 100644 --- a/tests/contest/contest/src/main.rs +++ b/tests/contest/contest/src/main.rs @@ -21,6 +21,7 @@ use crate::tests::linux_ns_itype::get_ns_itype_tests; use crate::tests::mounts_recursive::get_mounts_recursive_test; use crate::tests::no_pivot::get_no_pivot_test; use crate::tests::pidfile::get_pidfile_test; +use crate::tests::process_rlimits::get_process_rlimits_test; use crate::tests::readonly_paths::get_ro_paths_test; use crate::tests::root_readonly_true::get_root_readonly_test; use crate::tests::scheduler::get_scheduler_test; @@ -116,6 +117,7 @@ fn main() -> Result<()> { let io_priority_test = get_io_priority_test(); let devices = get_devices_test(); let root_readonly = get_root_readonly_test(); + let process_rlimtis = get_process_rlimits_test(); let no_pivot = get_no_pivot_test(); tm.add_test_group(Box::new(cl)); @@ -141,6 +143,7 @@ fn main() -> Result<()> { tm.add_test_group(Box::new(scheduler)); tm.add_test_group(Box::new(devices)); tm.add_test_group(Box::new(root_readonly)); + tm.add_test_group(Box::new(process_rlimtis)); tm.add_test_group(Box::new(no_pivot)); tm.add_test_group(Box::new(io_priority_test)); diff --git a/tests/contest/contest/src/tests/mod.rs b/tests/contest/contest/src/tests/mod.rs index b53a0a361..b5729c2c9 100644 --- a/tests/contest/contest/src/tests/mod.rs +++ b/tests/contest/contest/src/tests/mod.rs @@ -11,6 +11,7 @@ pub mod linux_ns_itype; pub mod mounts_recursive; pub mod no_pivot; pub mod pidfile; +pub mod process_rlimits; pub mod readonly_paths; pub mod root_readonly_true; pub mod scheduler; diff --git a/tests/contest/contest/src/tests/process_rlimits/mod.rs b/tests/contest/contest/src/tests/process_rlimits/mod.rs new file mode 100644 index 000000000..2e23c3cfd --- /dev/null +++ b/tests/contest/contest/src/tests/process_rlimits/mod.rs @@ -0,0 +1,2 @@ +mod process_rlimits_test; +pub use process_rlimits_test::get_process_rlimits_test; diff --git a/tests/contest/contest/src/tests/process_rlimits/process_rlimits_test.rs b/tests/contest/contest/src/tests/process_rlimits/process_rlimits_test.rs new file mode 100644 index 000000000..7d5456d50 --- /dev/null +++ b/tests/contest/contest/src/tests/process_rlimits/process_rlimits_test.rs @@ -0,0 +1,67 @@ +use anyhow::{Context, Ok, Result}; +use oci_spec::runtime::{ + PosixRlimit, PosixRlimitBuilder, PosixRlimitType, ProcessBuilder, Spec, SpecBuilder, +}; +use test_framework::{test_result, Test, TestGroup, TestResult}; + +use crate::utils::test_inside_container; + +const GIGABYTES: u64 = 1024 * 1024 * 1024; + +fn create_rlimit( + rlimit_type: PosixRlimitType, + hard_val: u64, + soft_val: u64, +) -> Result { + let rlimit = PosixRlimitBuilder::default() + .typ(rlimit_type) + .hard(hard_val) + .soft(soft_val) + .build()?; + Ok(rlimit) +} + +#[allow(clippy::identity_op)] +fn create_spec() -> Result { + let spec = SpecBuilder::default() + .process( + ProcessBuilder::default() + .args(vec![ + "runtimetest".to_string(), + "process_rlimits".to_string(), + ]) + .rlimits(vec![ + create_rlimit(PosixRlimitType::RlimitAs, 2 * GIGABYTES, 1 * GIGABYTES).unwrap(), + create_rlimit(PosixRlimitType::RlimitCore, 4 * GIGABYTES, 3 * GIGABYTES) + .unwrap(), + create_rlimit(PosixRlimitType::RlimitData, 6 * GIGABYTES, 5 * GIGABYTES) + .unwrap(), + create_rlimit(PosixRlimitType::RlimitFsize, 8 * GIGABYTES, 7 * GIGABYTES) + .unwrap(), + create_rlimit(PosixRlimitType::RlimitStack, 10 * GIGABYTES, 9 * GIGABYTES) + .unwrap(), + create_rlimit(PosixRlimitType::RlimitCpu, 120, 60).unwrap(), + create_rlimit(PosixRlimitType::RlimitNofile, 4000, 3000).unwrap(), + ]) + .build() + .expect("error in creating process config"), + ) + .build() + .context("failed to build spec")?; + + Ok(spec) +} + +fn process_rlimits_test() -> TestResult { + let spec = test_result!(create_spec()); + test_inside_container(spec, &|_| Ok(())) +} + +pub fn get_process_rlimits_test() -> TestGroup { + let mut process_rlimits_test_group = TestGroup::new("process_rlimits"); + + let test = Test::new("process_rlimits_test", Box::new(process_rlimits_test)); + process_rlimits_test_group.add(vec![Box::new(test)]); + + process_rlimits_test_group +} diff --git a/tests/contest/runtimetest/src/main.rs b/tests/contest/runtimetest/src/main.rs index 7dc96ca34..2de13ead5 100644 --- a/tests/contest/runtimetest/src/main.rs +++ b/tests/contest/runtimetest/src/main.rs @@ -45,6 +45,7 @@ fn main() { "io_priority_class_idle" => tests::test_io_priority_class(&spec, IoprioClassIdle), "devices" => tests::validate_devices(&spec), "root_readonly" => tests::test_validate_root_readonly(&spec), + "process_rlimits" => tests::validate_process_rlimits(&spec), "no_pivot" => tests::validate_rootfs(), _ => eprintln!("error due to unexpected execute test name: {execute_test}"), } diff --git a/tests/contest/runtimetest/src/tests.rs b/tests/contest/runtimetest/src/tests.rs index 715e779b0..6b4495f81 100644 --- a/tests/contest/runtimetest/src/tests.rs +++ b/tests/contest/runtimetest/src/tests.rs @@ -5,10 +5,13 @@ use std::path::Path; use anyhow::{bail, Result}; use nix::errno::Errno; use nix::libc; +use nix::sys::resource::{getrlimit, Resource}; use nix::sys::utsname; use nix::unistd::getcwd; use oci_spec::runtime::IOPriorityClass::{self, IoprioClassBe, IoprioClassIdle, IoprioClassRt}; -use oci_spec::runtime::{LinuxDevice, LinuxDeviceType, LinuxSchedulerPolicy, Spec}; +use oci_spec::runtime::{ + LinuxDevice, LinuxDeviceType, LinuxSchedulerPolicy, PosixRlimit, PosixRlimitType, Spec, +}; use crate::utils::{self, test_dir_read_access, test_dir_write_access, test_read_access, test_write_access}; @@ -574,6 +577,53 @@ pub fn test_validate_root_readonly(spec: &Spec) { } } +pub fn validate_process_rlimits(spec: &Spec) { + let process = spec.process().as_ref().unwrap(); + let spec_rlimits: &Vec = process.rlimits().as_ref().unwrap(); + + for spec_rlimit in spec_rlimits.iter() { + let (soft_limit, hard_limit) = getrlimit(change_resource_type(spec_rlimit.typ())).unwrap(); + if spec_rlimit.hard() != hard_limit { + eprintln!( + "error type of {:?} hard rlimit expected {:?} , got {:?}", + spec_rlimit.typ(), + spec_rlimit.hard(), + hard_limit + ) + } + + if spec_rlimit.soft() != soft_limit { + eprintln!( + "error type of {:?} soft rlimit expected {:?} , got {:?}", + spec_rlimit.typ(), + spec_rlimit.soft(), + soft_limit + ) + } + } +} + +fn change_resource_type(resource_type: PosixRlimitType) -> Resource { + match resource_type { + PosixRlimitType::RlimitCpu => Resource::RLIMIT_CPU, + PosixRlimitType::RlimitFsize => Resource::RLIMIT_FSIZE, + PosixRlimitType::RlimitData => Resource::RLIMIT_DATA, + PosixRlimitType::RlimitStack => Resource::RLIMIT_STACK, + PosixRlimitType::RlimitCore => Resource::RLIMIT_CORE, + PosixRlimitType::RlimitRss => Resource::RLIMIT_RSS, + PosixRlimitType::RlimitNproc => Resource::RLIMIT_NPROC, + PosixRlimitType::RlimitNofile => Resource::RLIMIT_NOFILE, + PosixRlimitType::RlimitMemlock => Resource::RLIMIT_MEMLOCK, + PosixRlimitType::RlimitAs => Resource::RLIMIT_AS, + PosixRlimitType::RlimitLocks => Resource::RLIMIT_LOCKS, + PosixRlimitType::RlimitSigpending => Resource::RLIMIT_SIGPENDING, + PosixRlimitType::RlimitMsgqueue => Resource::RLIMIT_MSGQUEUE, + PosixRlimitType::RlimitNice => Resource::RLIMIT_NICE, + PosixRlimitType::RlimitRtprio => Resource::RLIMIT_RTPRIO, + PosixRlimitType::RlimitRttime => Resource::RLIMIT_RTTIME, + } +} + // the validate_rootfs function is used to validate the rootfs of the container is // as expected. This function is used in the no_pivot test to validate the rootfs pub fn validate_rootfs() {