diff --git a/Cargo.lock b/Cargo.lock index d42c1979f..2439e54b7 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -141,9 +141,9 @@ checksum = "70033777eb8b5124a81a1889416543dddef2de240019b674c81285a2635a7e1e" [[package]] name = "anyhow" -version = "1.0.93" +version = "1.0.94" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c95c10ba0b00a02636238b814946408b1322d5ac4760326e6fb8ec956d85775" +checksum = "c1fd03a028ef38ba2276dce7e33fcd6369c158a1bca17946c4b1b701891c1ff7" [[package]] name = "arbitrary" @@ -921,12 +921,12 @@ dependencies = [ [[package]] name = "darling" -version = "0.20.8" +version = "0.20.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "54e36fcd13ed84ffdfda6f5be89b31287cbb80c439841fe69e04841435464391" +checksum = "6f63b86c8a8826a49b8c21f08a2d07338eec8d900540f8630dc76284be802989" dependencies = [ - "darling_core 0.20.8", - "darling_macro 0.20.8", + "darling_core 0.20.10", + "darling_macro 0.20.10", ] [[package]] @@ -945,15 +945,15 @@ dependencies = [ [[package]] name = "darling_core" -version = "0.20.8" +version = "0.20.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c2cf1c23a687a1feeb728783b993c4e1ad83d99f351801977dd809b48d0a70f" +checksum = "95133861a8032aaea082871032f5815eb9e98cef03fa916ab4500513994df9e5" dependencies = [ "fnv", "ident_case", "proc-macro2", "quote", - "strsim 0.10.0", + "strsim 0.11.1", "syn 2.0.87", ] @@ -970,11 +970,11 @@ dependencies = [ [[package]] name = "darling_macro" -version = "0.20.8" +version = "0.20.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a668eda54683121533a393014d8692171709ff57a7d61f187b6e782719f8933f" +checksum = "d336a2a514f6ccccaa3e09b02d41d35330c07ddf03a62165fcec10bb561c7806" dependencies = [ - "darling_core 0.20.8", + "darling_core 0.20.10", "quote", "syn 2.0.87", ] @@ -1032,11 +1032,11 @@ dependencies = [ [[package]] name = "derive_builder" -version = "0.20.1" +version = "0.20.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cd33f37ee6a119146a1781d3356a7c26028f83d779b2e04ecd45fdc75c76877b" +checksum = "507dfb09ea8b7fa618fcf76e953f4f5e192547945816d5358edffe39f6f94947" dependencies = [ - "derive_builder_macro 0.20.1", + "derive_builder_macro 0.20.2", ] [[package]] @@ -1053,11 +1053,11 @@ dependencies = [ [[package]] name = "derive_builder_core" -version = "0.20.1" +version = "0.20.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7431fa049613920234f22c47fdc33e6cf3ee83067091ea4277a3f8c4587aae38" +checksum = "2d5bcf7b024d6835cfb3d473887cd966994907effbe9227e8c8219824d06c4e8" dependencies = [ - "darling 0.20.8", + "darling 0.20.10", "proc-macro2", "quote", "syn 2.0.87", @@ -1075,11 +1075,11 @@ dependencies = [ [[package]] name = "derive_builder_macro" -version = "0.20.1" +version = "0.20.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4abae7035bf79b9877b779505d8cf3749285b80c43941eda66604841889451dc" +checksum = "ab63b0e2bf4d5928aff72e83a7dace85d7bba5fe12dcc3c5a572d78caffd3f3c" dependencies = [ - "derive_builder_core 0.20.1", + "derive_builder_core 0.20.2", "syn 2.0.87", ] @@ -1202,7 +1202,7 @@ version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e08b6c6ab82d70f08844964ba10c7babb716de2ecaeab9be5717918a5177d3af" dependencies = [ - "darling 0.20.8", + "darling 0.20.10", "proc-macro2", "quote", "syn 2.0.87", @@ -1249,12 +1249,12 @@ checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" [[package]] name = "errno" -version = "0.3.9" +version = "0.3.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "534c5cf6194dfab3db3242765c03bbe257cf92f22b38f6bc0c58d59108a820ba" +checksum = "33d852cb9b869c2a9b3df2f71a3074817f01e1844f839a144f5fcef059a4eb5d" dependencies = [ "libc", - "windows-sys 0.52.0", + "windows-sys 0.59.0", ] [[package]] @@ -1326,9 +1326,9 @@ checksum = "1d674e81391d1e1ab681a28d99df07927c6d4aa5b027d7da16ba32d1d21ecd99" [[package]] name = "flate2" -version = "1.0.34" +version = "1.0.35" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1b589b4dc103969ad3cf85c950899926ec64300a1a46d76c03a6072957036f0" +checksum = "c936bfdafb507ebbf50b8074c54fa31c5be9a1e7e5f467dd659697041407d07c" dependencies = [ "crc32fast", "miniz_oxide 0.8.0", @@ -1957,9 +1957,9 @@ dependencies = [ [[package]] name = "libc" -version = "0.2.162" +version = "0.2.167" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "18d287de67fe55fd7e1581fe933d965a5a9477b38e949cfa9f8574ef01506398" +checksum = "09d6582e104315a817dff97f75133544b2e094ee22447d2acf4a74e189ba06fc" [[package]] name = "libcgroups" @@ -1982,7 +1982,7 @@ dependencies = [ "serde_json", "serial_test", "tempfile", - "thiserror 2.0.3", + "thiserror 2.0.4", "tracing", ] @@ -2012,7 +2012,7 @@ dependencies = [ "serde_json", "serial_test", "tempfile", - "thiserror 2.0.3", + "thiserror 2.0.4", "tracing", ] @@ -2023,7 +2023,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0c2a198fb6b0eada2a8df47933734e6d35d350665a33a3593d7164fa52c75c19" dependencies = [ "cfg-if", - "windows-targets 0.48.5", + "windows-targets 0.52.6", ] [[package]] @@ -2231,9 +2231,9 @@ dependencies = [ [[package]] name = "mockall" -version = "0.13.0" +version = "0.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d4c28b3fb6d753d28c20e826cd46ee611fda1cf3cde03a443a974043247c065a" +checksum = "39a6bfcc6c8c7eed5ee98b9c3e33adc726054389233e201c95dab2d41a3839d2" dependencies = [ "cfg-if", "downcast", @@ -2245,9 +2245,9 @@ dependencies = [ [[package]] name = "mockall_derive" -version = "0.13.0" +version = "0.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "341014e7f530314e9a1fdbc7400b244efea7122662c96bfa248c31da5bfb2020" +checksum = "25ca3004c2efe9011bd4e461bd8256445052b9615405b4f7ea43fc8ca5c20898" dependencies = [ "cfg-if", "proc-macro2", @@ -2432,7 +2432,7 @@ version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "da406e58efe2eb5986a6139626d611ce426e5324a824133d76367c765cf0b882" dependencies = [ - "derive_builder 0.20.1", + "derive_builder 0.20.2", "getset", "quickcheck", "regex", @@ -2440,7 +2440,7 @@ dependencies = [ "serde_json", "strum", "strum_macros", - "thiserror 2.0.3", + "thiserror 2.0.4", ] [[package]] @@ -2800,24 +2800,23 @@ dependencies = [ [[package]] name = "procfs" -version = "0.16.0" +version = "0.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "731e0d9356b0c25f16f33b5be79b1c57b562f141ebfcdb0ad8ac2c13a24293b4" +checksum = "cc5b72d8145275d844d4b5f6d4e1eef00c8cd889edb6035c21675d1bb1f45c9f" dependencies = [ "bitflags 2.6.0", "chrono", "flate2", "hex", - "lazy_static", "procfs-core", "rustix", ] [[package]] name = "procfs-core" -version = "0.16.0" +version = "0.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2d3554923a69f4ce04c4a754260c338f505ce22642d3830e049a399fc2059a29" +checksum = "239df02d8349b06fc07398a3a1697b06418223b1c7725085e801e7c0fc6a12ec" dependencies = [ "bitflags 2.6.0", "chrono", @@ -3327,9 +3326,9 @@ dependencies = [ [[package]] name = "rustversion" -version = "1.0.17" +version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "955d28af4278de8121b7ebeb796b6a45735dc01436d898801014aced2773a3d6" +checksum = "0e819f2bc632f285be6d7cd36e25940d45b2391dd6d9b939e79de557f7014248" [[package]] name = "ryu" @@ -3497,9 +3496,9 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.132" +version = "1.0.133" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d726bfaff4b320266d395898905d0eba0345aae23b54aee3a737e260fd46db03" +checksum = "c7fceb2473b9166b2294ef05efcb65a3db80803f0b03ef86a5fc88a2b85ee377" dependencies = [ "itoa", "memchr", @@ -3998,11 +3997,11 @@ dependencies = [ [[package]] name = "thiserror" -version = "2.0.3" +version = "2.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c006c85c7651b3cf2ada4584faa36773bd07bac24acfb39f3c431b36d7e667aa" +checksum = "2f49a1853cf82743e3b7950f77e0f4d622ca36cf4317cba00c767838bac8d490" dependencies = [ - "thiserror-impl 2.0.3", + "thiserror-impl 2.0.4", ] [[package]] @@ -4018,9 +4017,9 @@ dependencies = [ [[package]] name = "thiserror-impl" -version = "2.0.3" +version = "2.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f077553d607adc1caf65430528a576c757a71ed73944b66ebb58ef2bbd243568" +checksum = "8381894bb3efe0c4acac3ded651301ceee58a15d47c2e34885ed1908ad667061" dependencies = [ "proc-macro2", "quote", @@ -4254,9 +4253,9 @@ checksum = "b6bc1c9ce2b5135ac7f93c72918fc37feb872bdc6a5533a8b85eb4b86bfdae52" [[package]] name = "tracing" -version = "0.1.40" +version = "0.1.41" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3523ab5a71916ccf420eebdf5521fcef02141234bbc0b8a49f2fdc4544364ef" +checksum = "784e0ac535deb450455cbfa28a6f0df145ea1bb7ae51b821cf5e7927fdcfbdd0" dependencies = [ "log", "pin-project-lite", @@ -4266,9 +4265,9 @@ dependencies = [ [[package]] name = "tracing-attributes" -version = "0.1.27" +version = "0.1.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" +checksum = "395ae124c09f9e6918a2310af6038fba074bcf474ac352496d5910dd59a2226d" dependencies = [ "proc-macro2", "quote", @@ -4277,9 +4276,9 @@ dependencies = [ [[package]] name = "tracing-core" -version = "0.1.32" +version = "0.1.33" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c06d3da6113f116aaee68e4d601191614c9053067f9ab7f6edbcb161237daa54" +checksum = "e672c95779cf947c5311f83787af4fa8fffd12fb27e4993211a84bdfd9610f9c" dependencies = [ "once_cell", "valuable", @@ -4287,9 +4286,9 @@ dependencies = [ [[package]] name = "tracing-journald" -version = "0.3.0" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba316a74e8fc3c3896a850dba2375928a9fa171b085ecddfc7c054d39970f3fd" +checksum = "fc0b4143302cf1022dac868d521e36e8b27691f72c84b3311750d5188ebba657" dependencies = [ "libc", "tracing-core", @@ -4309,9 +4308,9 @@ dependencies = [ [[package]] name = "tracing-serde" -version = "0.1.3" +version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bc6b213177105856957181934e4920de57730fc69bf42c37ee5bb664d406d9e1" +checksum = "704b1aeb7be0d0a84fc9828cae51dab5970fee5088f83d1dd7ee6f6246fc6ff1" dependencies = [ "serde", "tracing-core", @@ -4319,9 +4318,9 @@ dependencies = [ [[package]] name = "tracing-subscriber" -version = "0.3.18" +version = "0.3.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ad0f048c97dbd9faa9b7df56362b8ebcaa52adb06b498c050d2f4e32f90a7a8b" +checksum = "e8189decb5ac0fa7bc8b96b7cb9b2701d60d48805aca84a238004d665fcc4008" dependencies = [ "matchers", "nu-ansi-term", @@ -4454,12 +4453,12 @@ checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" [[package]] name = "vergen" -version = "9.0.1" +version = "9.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "349ed9e45296a581f455bc18039878f409992999bc1d5da12a6800eb18c8752f" +checksum = "31f25fc8f8f05df455c7941e87f093ad22522a9ff33d7a027774815acf6f0639" dependencies = [ "anyhow", - "derive_builder 0.20.1", + "derive_builder 0.20.2", "rustversion", "time 0.3.36", "vergen-lib", @@ -4467,12 +4466,12 @@ dependencies = [ [[package]] name = "vergen-gitcl" -version = "1.0.1" +version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2a3a7f91caabecefc3c249fd864b11d4abe315c166fbdb568964421bccfd2b7a" +checksum = "0227006d09f98ab00ea69e9a5e055e676a813cfbed4232986176c86a6080b997" dependencies = [ "anyhow", - "derive_builder 0.20.1", + "derive_builder 0.20.2", "rustversion", "time 0.3.36", "vergen", @@ -4481,12 +4480,12 @@ dependencies = [ [[package]] name = "vergen-lib" -version = "0.1.4" +version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "229eaddb0050920816cf051e619affaf18caa3dd512de8de5839ccbc8e53abb0" +checksum = "c0c767e6751c09fc85cde58722cf2f1007e80e4c8d5a4321fc90d83dc54ca147" dependencies = [ "anyhow", - "derive_builder 0.20.1", + "derive_builder 0.20.2", "rustversion", ] diff --git a/crates/libcgroups/Cargo.toml b/crates/libcgroups/Cargo.toml index f9abf1012..b104899fd 100644 --- a/crates/libcgroups/Cargo.toml +++ b/crates/libcgroups/Cargo.toml @@ -21,22 +21,22 @@ cgroupsv2_devices = ["rbpf", "libbpf-sys", "errno", "libc", "nix/dir"] [dependencies] nix = { version = "0.28.0", features = ["signal", "user", "fs"] } -procfs = "0.16.0" +procfs = "0.17.0" oci-spec = { version = "~0.7.1", features = ["runtime"] } fixedbitset = "0.5.7" serde = { version = "1.0", features = ["derive"] } rbpf = { version = "0.3.0", optional = true } libbpf-sys = { version = "1.5.0", optional = true } -errno = { version = "0.3.9", optional = true } -libc = { version = "0.2.162", optional = true } -thiserror = "2.0.3" -tracing = { version = "0.1.40", features = ["attributes"] } +errno = { version = "0.3.10", optional = true } +libc = { version = "0.2.167", optional = true } +thiserror = "2.0.4" +tracing = { version = "0.1.41", features = ["attributes"] } [dev-dependencies] anyhow = "1.0" oci-spec = { version = "~0.7.1", features = ["proptests", "runtime"] } quickcheck = "1" -mockall = { version = "0.13.0", features = [] } +mockall = { version = "0.13.1", features = [] } clap = "4.1.6" serde = { version = "1.0", features = ["derive"] } serde_json = "1.0" diff --git a/crates/libcontainer/Cargo.toml b/crates/libcontainer/Cargo.toml index 5f7b38b15..8428fe264 100644 --- a/crates/libcontainer/Cargo.toml +++ b/crates/libcontainer/Cargo.toml @@ -26,7 +26,7 @@ chrono = { version = "0.4", default-features = false, features = [ "serde", ] } fastrand = "^2.1.1" -libc = "0.2.162" +libc = "0.2.167" nix = { version = "0.28.0", features = [ "socket", "sched", @@ -39,7 +39,7 @@ nix = { version = "0.28.0", features = [ ] } oci-spec = { version = "0.7.1", features = ["runtime"] } once_cell = "1.20.2" -procfs = "0.16.0" +procfs = "0.17.0" prctl = "1.0.0" libcgroups = { path = "../libcgroups", default-features = false, version = "0.4.1" } # MARK: Version libseccomp = { version = "0.3.0", optional = true } @@ -47,8 +47,8 @@ serde = { version = "1.0", features = ["derive"] } serde_json = "1.0" rust-criu = "0.4.0" regex = { version = "1.10.6", default-features = false, features = ["std", "unicode-perl"] } -thiserror = "2.0.3" -tracing = { version = "0.1.40", features = ["attributes"] } +thiserror = "2.0.4" +tracing = { version = "0.1.41", features = ["attributes"] } safe-path = "0.1.0" nc = "0.9.5" diff --git a/crates/libcontainer/src/container/builder.rs b/crates/libcontainer/src/container/builder.rs index 5aa1d39c5..a22642cd5 100644 --- a/crates/libcontainer/src/container/builder.rs +++ b/crates/libcontainer/src/container/builder.rs @@ -1,3 +1,4 @@ +use std::os::fd::OwnedFd; use std::path::PathBuf; use super::init_builder::InitContainerBuilder; @@ -24,6 +25,12 @@ pub struct ContainerBuilder { /// The function that actually runs on the container init process. Default /// is to execute the specified command in the oci spec. pub(super) executor: Box, + // RawFd set to stdin of the container init process. + pub stdin: Option, + // RawFd set to stdout of the container init process. + pub stdout: Option, + // RawFd set to stderr of the container init process. + pub stderr: Option, } /// Builder that can be used to configure the common properties of @@ -70,6 +77,9 @@ impl ContainerBuilder { console_socket: None, preserve_fds: 0, executor: workload::default::get_executor(), + stdin: None, + stdout: None, + stderr: None, } } @@ -257,13 +267,84 @@ impl ContainerBuilder { self.executor = Box::new(executor); self } + + /// Sets the stdin of the container, for those who use libcontainer as a library, + /// the container stdin may have to be set to an opened file descriptor + /// rather than the stdin of the current process. + /// # Example + /// + /// ```no_run + /// # use libcontainer::container::builder::ContainerBuilder; + /// # use libcontainer::syscall::syscall::SyscallType; + /// # use libcontainer::workload::default::DefaultExecutor; + /// # use nix::unistd::pipe; + /// + /// let (r, _w) = pipe().unwrap(); + /// ContainerBuilder::new( + /// "74f1a4cb3801".to_owned(), + /// SyscallType::default(), + /// ) + /// .with_stdin(r); + /// ``` + pub fn with_stdin(mut self, stdin: impl Into) -> Self { + self.stdin = Some(stdin.into()); + self + } + + /// Sets the stdout of the container, for those who use libcontainer as a library, + /// the container stdout may have to be set to an opened file descriptor + /// rather than the stdout of the current process. + /// # Example + /// + /// ```no_run + /// # use libcontainer::container::builder::ContainerBuilder; + /// # use libcontainer::syscall::syscall::SyscallType; + /// # use libcontainer::workload::default::DefaultExecutor; + /// # use nix::unistd::pipe; + /// + /// let (_r, w) = pipe().unwrap(); + /// ContainerBuilder::new( + /// "74f1a4cb3801".to_owned(), + /// SyscallType::default(), + /// ) + /// .with_stdout(w); + /// ``` + pub fn with_stdout(mut self, stdout: impl Into) -> Self { + self.stdout = Some(stdout.into()); + self + } + + /// Sets the stderr of the container, for those who use libcontainer as a library, + /// the container stderr may have to be set to an opened file descriptor + /// rather than the stderr of the current process. + /// # Example + /// + /// ```no_run + /// # use libcontainer::container::builder::ContainerBuilder; + /// # use libcontainer::syscall::syscall::SyscallType; + /// # use libcontainer::workload::default::DefaultExecutor; + /// # use nix::unistd::pipe; + /// + /// let (_r, w) = pipe().unwrap(); + /// ContainerBuilder::new( + /// "74f1a4cb3801".to_owned(), + /// SyscallType::default(), + /// ) + /// .with_stderr(w); + /// ``` + pub fn with_stderr(mut self, stderr: impl Into) -> Self { + self.stderr = Some(stderr.into()); + self + } } #[cfg(test)] mod tests { + use std::os::fd::AsRawFd; use std::path::PathBuf; use anyhow::{Context, Result}; + use nix::unistd::pipe; use crate::container::builder::ContainerBuilder; use crate::syscall::syscall::SyscallType; @@ -334,4 +415,35 @@ mod tests { assert!(result.is_ok()); Ok(()) } + + #[test] + fn test_stdios() -> Result<()> { + let (r, _w) = pipe()?; + let stdin_raw = r.as_raw_fd(); + let builder = + ContainerBuilder::new("74f1a4cb3801".to_owned(), SyscallType::default()).with_stdin(r); + assert_eq!( + builder.stdin.as_ref().map(|o| o.as_raw_fd()), + Some(stdin_raw) + ); + + let (_r, w) = pipe()?; + let stdout_raw = w.as_raw_fd(); + let builder = + ContainerBuilder::new("74f1a4cb3801".to_owned(), SyscallType::default()).with_stdout(w); + assert_eq!( + builder.stdout.as_ref().map(|o| o.as_raw_fd()), + Some(stdout_raw) + ); + + let (_r, w) = pipe()?; + let stderr_raw = w.as_raw_fd(); + let builder = + ContainerBuilder::new("74f1a4cb3801".to_owned(), SyscallType::default()).with_stderr(w); + assert_eq!( + builder.stderr.as_ref().map(|o| o.as_raw_fd()), + Some(stderr_raw) + ); + Ok(()) + } } diff --git a/crates/libcontainer/src/container/builder_impl.rs b/crates/libcontainer/src/container/builder_impl.rs index 0a9e43f52..9c185978e 100644 --- a/crates/libcontainer/src/container/builder_impl.rs +++ b/crates/libcontainer/src/container/builder_impl.rs @@ -1,6 +1,6 @@ use std::fs; use std::io::Write; -use std::os::unix::prelude::RawFd; +use std::os::fd::{AsRawFd, OwnedFd}; use std::path::PathBuf; use std::rc::Rc; @@ -9,7 +9,7 @@ use nix::unistd::Pid; use oci_spec::runtime::Spec; use super::{Container, ContainerStatus}; -use crate::error::{LibcontainerError, MissingSpecError}; +use crate::error::{CreateContainerError, LibcontainerError, MissingSpecError}; use crate::notify_socket::NotifyListener; use crate::process::args::{ContainerArgs, ContainerType}; use crate::process::intel_rdt::delete_resctrl_subdirectory; @@ -36,7 +36,7 @@ pub(super) struct ContainerBuilderImpl { /// container process to the higher level runtime pub pid_file: Option, /// Socket to communicate the file descriptor of the ptty - pub console_socket: Option, + pub console_socket: Option, /// Options for new user namespace pub user_ns_config: Option, /// Path to the Unix Domain Socket to communicate container start @@ -51,6 +51,12 @@ pub(super) struct ContainerBuilderImpl { pub executor: Box, /// If do not use pivot root to jail process inside rootfs pub no_pivot: bool, + // RawFd set to stdin of the container init process. + pub stdin: Option, + // RawFd set to stdout of the container init process. + pub stdout: Option, + // RawFd set to stderr of the container init process. + pub stderr: Option, } impl ContainerBuilderImpl { @@ -60,15 +66,21 @@ impl ContainerBuilderImpl { Err(outer) => { // Only the init container should be cleaned up in the case of // an error. - if matches!(self.container_type, ContainerType::InitContainer) { - self.cleanup_container()?; - } + let cleanup_err = if self.is_init_container() { + self.cleanup_container().err() + } else { + None + }; - Err(outer) + Err(CreateContainerError::new(outer, cleanup_err).into()) } } } + fn is_init_container(&self) -> bool { + matches!(self.container_type, ContainerType::InitContainer) + } + fn run_container(&mut self) -> Result { let linux = self.spec.linux().as_ref().ok_or(MissingSpecError::Linux)?; let cgroups_path = utils::get_cgroup_path(linux.cgroups_path(), &self.container_id); @@ -148,7 +160,7 @@ impl ContainerBuilderImpl { syscall: self.syscall, spec: Rc::clone(&self.spec), rootfs: self.rootfs.to_owned(), - console_socket: self.console_socket, + console_socket: self.console_socket.as_ref().map(|c| c.as_raw_fd()), notify_listener, preserve_fds: self.preserve_fds, container: self.container.to_owned(), @@ -157,6 +169,9 @@ impl ContainerBuilderImpl { detached: self.detached, executor: self.executor.clone(), no_pivot: self.no_pivot, + stdin: self.stdin.as_ref().map(|x| x.as_raw_fd()), + stdout: self.stdout.as_ref().map(|x| x.as_raw_fd()), + stderr: self.stderr.as_ref().map(|x| x.as_raw_fd()), }; let (init_pid, need_to_clean_up_intel_rdt_dir) = diff --git a/crates/libcontainer/src/container/init_builder.rs b/crates/libcontainer/src/container/init_builder.rs index 4ac2104de..4ff2094ed 100644 --- a/crates/libcontainer/src/container/init_builder.rs +++ b/crates/libcontainer/src/container/init_builder.rs @@ -103,6 +103,9 @@ impl InitContainerBuilder { detached: self.detached, executor: self.base.executor, no_pivot: self.no_pivot, + stdin: self.base.stdin, + stdout: self.base.stdout, + stderr: self.base.stderr, }; builder_impl.create()?; diff --git a/crates/libcontainer/src/container/tenant_builder.rs b/crates/libcontainer/src/container/tenant_builder.rs index e54a22cca..22b7eff2a 100644 --- a/crates/libcontainer/src/container/tenant_builder.rs +++ b/crates/libcontainer/src/container/tenant_builder.rs @@ -3,8 +3,7 @@ use std::convert::TryFrom; use std::ffi::{OsStr, OsString}; use std::fs; use std::io::BufReader; -use std::os::fd::AsRawFd; -use std::os::unix::prelude::RawFd; +use std::os::fd::{AsRawFd, OwnedFd}; use std::path::{Path, PathBuf}; use std::rc::Rc; use std::str::FromStr; @@ -143,6 +142,9 @@ impl TenantContainerBuilder { detached: self.detached, executor: self.base.executor, no_pivot: false, + stdin: self.base.stdin, + stdout: self.base.stdout, + stderr: self.base.stderr, }; let pid = builder_impl.create()?; @@ -500,7 +502,7 @@ impl TenantContainerBuilder { Ok(socket_path) } - fn setup_tty_socket(&self, container_dir: &Path) -> Result, LibcontainerError> { + fn setup_tty_socket(&self, container_dir: &Path) -> Result, LibcontainerError> { let tty_name = Self::generate_name(container_dir, TENANT_TTY); let csocketfd = if let Some(console_socket) = &self.base.console_socket { Some(tty::setup_console_socket( diff --git a/crates/libcontainer/src/error.rs b/crates/libcontainer/src/error.rs index f6efed7db..f68a79a16 100644 --- a/crates/libcontainer/src/error.rs +++ b/crates/libcontainer/src/error.rs @@ -62,6 +62,8 @@ pub enum LibcontainerError { CgroupGet(#[from] libcgroups::common::GetCgroupSetupError), #[error[transparent]] Checkpoint(#[from] crate::container::CheckpointError), + #[error[transparent]] + CreateContainerError(#[from] CreateContainerError), // Catch all errors that are not covered by the above #[error("syscall error")] @@ -97,3 +99,54 @@ pub enum ErrInvalidSpec { #[error("invalid scheduler config for process")] Scheduler, } + +#[derive(Debug, thiserror::Error)] +pub struct CreateContainerError(Box, Option>); + +impl CreateContainerError { + pub(crate) fn new( + run_error: LibcontainerError, + cleanup_error: Option, + ) -> Self { + Self(Box::new(run_error), cleanup_error.map(Box::new)) + } +} + +impl std::fmt::Display for CreateContainerError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "failed to create container: {}", self.0)?; + if let Some(cleanup_err) = &self.1 { + write!(f, ". error during cleanup: {}", cleanup_err)?; + } + Ok(()) + } +} + +#[cfg(test)] +mod tests { + use libcgroups::common::CreateCgroupSetupError; + + use super::{CreateContainerError, ErrInvalidID}; + + #[test] + fn test_create_container() { + let create_container_err = + CreateContainerError::new(CreateCgroupSetupError::NonDefault.into(), None); + let msg = format!("{}", create_container_err); + assert_eq!( + "failed to create container: non default cgroup root not supported", + msg + ); + + let create_container_err = CreateContainerError::new( + CreateCgroupSetupError::NonDefault.into(), + Some(ErrInvalidID::Empty.into()), + ); + let msg = format!("{}", create_container_err); + assert_eq!( + "failed to create container: non default cgroup root not supported. \ + error during cleanup: container id can't be empty", + msg + ); + } +} diff --git a/crates/libcontainer/src/process/args.rs b/crates/libcontainer/src/process/args.rs index 1c7d0c395..2ea0dc974 100644 --- a/crates/libcontainer/src/process/args.rs +++ b/crates/libcontainer/src/process/args.rs @@ -44,4 +44,10 @@ pub struct ContainerArgs { pub executor: Box, /// If do not use pivot root to jail process inside rootfs pub no_pivot: bool, + // RawFd set to stdin of the container init process. + pub stdin: Option, + // RawFd set to stdout of the container init process. + pub stdout: Option, + // RawFd set to stderr of the container init process. + pub stderr: Option, } diff --git a/crates/libcontainer/src/process/container_init_process.rs b/crates/libcontainer/src/process/container_init_process.rs index 8d30b74b4..0d16e6094 100644 --- a/crates/libcontainer/src/process/container_init_process.rs +++ b/crates/libcontainer/src/process/container_init_process.rs @@ -7,7 +7,7 @@ use nc; use nix::mount::{MntFlags, MsFlags}; use nix::sched::CloneFlags; use nix::sys::stat::Mode; -use nix::unistd::{self, setsid, Gid, Uid}; +use nix::unistd::{self, close, dup2, setsid, Gid, Uid}; use oci_spec::runtime::{ IOPriorityClass, LinuxIOPriority, LinuxNamespaceType, LinuxSchedulerFlag, LinuxSchedulerPolicy, Scheduler, Spec, User, @@ -370,10 +370,23 @@ pub fn container_init_process( // set up tty if specified if let Some(csocketfd) = args.console_socket { - tty::setup_console(&csocketfd).map_err(|err| { + tty::setup_console(csocketfd).map_err(|err| { tracing::error!(?err, "failed to set up tty"); InitProcessError::Tty(err) })?; + } else { + if let Some(stdin) = args.stdin { + dup2(stdin, 0).map_err(InitProcessError::NixOther)?; + close(stdin).map_err(InitProcessError::NixOther)?; + } + if let Some(stdout) = args.stdout { + dup2(stdout, 1).map_err(InitProcessError::NixOther)?; + close(stdout).map_err(InitProcessError::NixOther)?; + } + if let Some(stderr) = args.stderr { + dup2(stderr, 2).map_err(InitProcessError::NixOther)?; + close(stderr).map_err(InitProcessError::NixOther)?; + } } apply_rest_namespaces(&namespaces, spec, syscall.as_ref())?; diff --git a/crates/libcontainer/src/tty.rs b/crates/libcontainer/src/tty.rs index b9a753127..112471f41 100644 --- a/crates/libcontainer/src/tty.rs +++ b/crates/libcontainer/src/tty.rs @@ -2,12 +2,12 @@ use std::env; use std::io::IoSlice; +use std::os::fd::OwnedFd; use std::os::unix::fs::symlink; use std::os::unix::io::AsRawFd; use std::os::unix::prelude::RawFd; use std::path::{Path, PathBuf}; -use nix::errno::Errno; use nix::sys::socket::{self, UnixAddr}; use nix::unistd::{close, dup2}; @@ -75,12 +75,21 @@ pub fn setup_console_socket( container_dir: &Path, console_socket_path: &Path, socket_name: &str, -) -> Result { +) -> Result { + struct CurrentDirGuard { + path: PathBuf, + } + impl Drop for CurrentDirGuard { + fn drop(&mut self) { + let _ = env::set_current_dir(&self.path); + } + } // Move into the container directory to avoid sun family conflicts with long socket path names. // ref: https://github.com/containers/youki/issues/2910 let prev_dir = env::current_dir().unwrap(); let _ = env::set_current_dir(container_dir); + let _guard = CurrentDirGuard { path: prev_dir }; let linked = PathBuf::from(socket_name); @@ -89,36 +98,29 @@ pub fn setup_console_socket( linked: linked.to_path_buf().into(), console_socket_path: console_socket_path.to_path_buf().into(), })?; - // Using ManuallyDrop to keep the socket open. - let csocketfd = std::mem::ManuallyDrop::new( - socket::socket( - socket::AddressFamily::Unix, - socket::SockType::Stream, - socket::SockFlag::empty(), - None, - ) - .map_err(|err| TTYError::CreateConsoleSocketFd { source: err })?, - ); - let csocketfd = match socket::connect( + let csocketfd = socket::socket( + socket::AddressFamily::Unix, + socket::SockType::Stream, + socket::SockFlag::empty(), + None, + ) + .map_err(|err| TTYError::CreateConsoleSocketFd { source: err })?; + socket::connect( csocketfd.as_raw_fd(), &socket::UnixAddr::new(linked.as_path()).map_err(|err| TTYError::InvalidSocketName { source: err, socket_name: socket_name.to_string(), })?, - ) { - Err(Errno::ENOENT) => -1, - Err(errno) => Err(TTYError::CreateConsoleSocket { - source: errno, - socket_name: socket_name.to_string(), - })?, - Ok(()) => csocketfd.as_raw_fd(), - }; + ) + .map_err(|e| TTYError::CreateConsoleSocket { + source: e, + socket_name: socket_name.to_string(), + })?; - let _ = env::set_current_dir(prev_dir); Ok(csocketfd) } -pub fn setup_console(console_fd: &RawFd) -> Result<()> { +pub fn setup_console(console_fd: RawFd) -> Result<()> { // You can also access pty master, but it is better to use the API. // ref. https://github.com/containerd/containerd/blob/261c107ffc4ff681bc73988f64e3f60c32233b37/vendor/github.com/containerd/go-runc/console.go#L139-L154 let openpty_result = nix::pty::openpty(None, None) @@ -133,21 +135,15 @@ pub fn setup_console(console_fd: &RawFd) -> Result<()> { let fds = [master.as_raw_fd()]; let cmsg = socket::ControlMessage::ScmRights(&fds); - socket::sendmsg::( - console_fd.as_raw_fd(), - &iov, - &[cmsg], - socket::MsgFlags::empty(), - None, - ) - .map_err(|err| TTYError::SendPtyMaster { source: err })?; + socket::sendmsg::(console_fd, &iov, &[cmsg], socket::MsgFlags::empty(), None) + .map_err(|err| TTYError::SendPtyMaster { source: err })?; if unsafe { libc::ioctl(slave.as_raw_fd(), libc::TIOCSCTTY) } < 0 { tracing::warn!("could not TIOCSCTTY"); }; let slave = slave.as_raw_fd(); connect_stdio(&slave, &slave, &slave)?; - close(console_fd.as_raw_fd()).map_err(|err| TTYError::CloseConsoleSocket { source: err })?; + close(console_fd).map_err(|err| TTYError::CloseConsoleSocket { source: err })?; Ok(()) } @@ -174,6 +170,7 @@ fn connect_stdio(stdin: &RawFd, stdout: &RawFd, stderr: &RawFd) -> Result<()> { #[cfg(test)] mod tests { use std::fs::File; + use std::os::fd::IntoRawFd; use std::os::unix::net::UnixListener; use anyhow::{Ok, Result}; @@ -200,8 +197,8 @@ mod tests { fn test_setup_console_socket_empty() -> Result<()> { let testdir = tempfile::tempdir()?; let socket_path = Path::join(testdir.path(), "test-socket"); - let fd = setup_console_socket(testdir.path(), &socket_path, CONSOLE_SOCKET)?; - assert_eq!(fd.as_raw_fd(), -1); + let fd = setup_console_socket(testdir.path(), &socket_path, CONSOLE_SOCKET); + assert!(fd.is_err()); Ok(()) } @@ -232,8 +229,8 @@ mod tests { let lis = UnixListener::bind(&socket_path); assert!(lis.is_ok()); - let fd = setup_console_socket(testdir.path(), &socket_path, CONSOLE_SOCKET); - let status = setup_console(&fd.unwrap()); + let fd = setup_console_socket(testdir.path(), &socket_path, CONSOLE_SOCKET)?; + let status = setup_console(fd.into_raw_fd()); // restore the original std* before doing final assert dup2(old_stdin, StdIO::Stdin.into())?; diff --git a/crates/youki/Cargo.toml b/crates/youki/Cargo.toml index 0561760d9..742e173a6 100644 --- a/crates/youki/Cargo.toml +++ b/crates/youki/Cargo.toml @@ -28,14 +28,14 @@ default-features = false features = ["std", "suggestions", "derive", "cargo", "help", "usage", "error-context"] [dependencies] -anyhow = "1.0.93" +anyhow = "1.0.94" chrono = { version = "0.4", default-features = false, features = ["clock", "serde"] } libcgroups = { path = "../libcgroups", default-features = false, version = "0.4.1" } # MARK: Version libcontainer = { path = "../libcontainer", default-features = false, version = "0.4.1" } # MARK: Version liboci-cli = { path = "../liboci-cli", version = "0.4.1" } # MARK: Version nix = "0.28.0" pentacle = "1.1.0" -procfs = "0.16.0" +procfs = "0.17.0" serde_json = "1.0" tabwriter = "1" clap_complete = "4.1.3" @@ -45,9 +45,9 @@ wasmer-wasix = { version = "0.9.0", optional = true } wasmedge-sdk = { version = "0.14.0", optional = true } wasmtime = { version = "26.0.1", optional = true } wasi-common = { version = "26.0.1", optional = true } -tracing = { version = "0.1.40", features = ["attributes"] } -tracing-subscriber = { version = "0.3.18", features = ["json", "env-filter"] } -tracing-journald = "0.3.0" +tracing = { version = "0.1.41", features = ["attributes"] } +tracing-subscriber = { version = "0.3.19", features = ["json", "env-filter"] } +tracing-journald = "0.3.1" [dev-dependencies] serial_test = "3.1.1" @@ -55,5 +55,5 @@ tempfile = "3" scopeguard = "1.2.0" [build-dependencies] -anyhow = "1.0.93" -vergen-gitcl = { version = "1.0.1", features = ["build"] } +anyhow = "1.0.94" +vergen-gitcl = { version = "1.0.2", features = ["build"] } diff --git a/tests/contest/contest/Cargo.toml b/tests/contest/contest/Cargo.toml index 1469cbe50..52197e94c 100644 --- a/tests/contest/contest/Cargo.toml +++ b/tests/contest/contest/Cargo.toml @@ -13,7 +13,7 @@ num_cpus = "1.16" oci-spec = { version = "0.7.1", features = ["runtime"] } once_cell = "1.20.2" pnet_datalink = "0.35.0" -procfs = "0.16.0" +procfs = "0.17.0" rand = "0.8.5" serde = { version = "1.0", features = ["derive"] } serde_json = "1.0" @@ -23,8 +23,8 @@ uuid = "1.11" which = "7.0.0" tempfile = "3" scopeguard = "1.2.0" -tracing = { version = "0.1.40", features = ["attributes"]} -tracing-subscriber = { version = "0.3.18", features = ["json", "env-filter"] } +tracing = { version = "0.1.41", features = ["attributes"]} +tracing-subscriber = { version = "0.3.19", features = ["json", "env-filter"] } [dependencies.clap] version = "4.1.6" diff --git a/tests/contest/contest/src/main.rs b/tests/contest/contest/src/main.rs index e4d88de42..1f239cd8b 100644 --- a/tests/contest/contest/src/main.rs +++ b/tests/contest/contest/src/main.rs @@ -22,10 +22,15 @@ 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; +<<<<<<< HEAD +======= +use crate::tests::process::get_process_test; +>>>>>>> main use crate::tests::process_oom_score_adj::get_process_oom_score_adj_test; use crate::tests::process_rlimits::get_process_rlimits_test; use crate::tests::process_user::get_process_user_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; use crate::tests::seccomp::get_seccomp_test; use crate::tests::seccomp_notify::get_seccomp_notify_test; @@ -118,13 +123,14 @@ fn main() -> Result<()> { let scheduler = get_scheduler_test(); let io_priority_test = get_io_priority_test(); let devices = get_devices_test(); + let root_readonly = get_root_readonly_test(); + let process = get_process_test(); let process_user = get_process_user_test(); let process_rlimtis = get_process_rlimits_test(); let no_pivot = get_no_pivot_test(); let kill = get_kill_test(); let process_oom_score_adj = get_process_oom_score_adj_test(); - tm.add_test_group(Box::new(cl)); tm.add_test_group(Box::new(cc)); tm.add_test_group(Box::new(huge_tlb)); @@ -147,10 +153,15 @@ fn main() -> Result<()> { tm.add_test_group(Box::new(sysctl)); 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)); tm.add_test_group(Box::new(process_user)); tm.add_test_group(Box::new(process_rlimtis)); tm.add_test_group(Box::new(no_pivot)); +<<<<<<< HEAD tm.add_test_group(Box::new(kill)); +======= +>>>>>>> main tm.add_test_group(Box::new(process_oom_score_adj)); tm.add_test_group(Box::new(io_priority_test)); diff --git a/tests/contest/contest/src/tests/devices/devices_test.rs b/tests/contest/contest/src/tests/devices/devices_test.rs index e83d2a273..0a187510d 100644 --- a/tests/contest/contest/src/tests/devices/devices_test.rs +++ b/tests/contest/contest/src/tests/devices/devices_test.rs @@ -5,6 +5,7 @@ use oci_spec::runtime::{ use test_framework::{test_result, Test, TestGroup, TestResult}; use crate::utils::test_inside_container; +use crate::utils::test_utils::CreateOptions; fn create_spec() -> Result { let device1 = LinuxDeviceBuilder::default() @@ -59,7 +60,7 @@ fn create_spec() -> Result { fn devices_test() -> TestResult { let spec = test_result!(create_spec()); - test_inside_container(spec, &|_| Ok(())) + test_inside_container(spec, &CreateOptions::default(), &|_| Ok(())) } pub fn get_devices_test() -> TestGroup { diff --git a/tests/contest/contest/src/tests/domainname/mod.rs b/tests/contest/contest/src/tests/domainname/mod.rs index 188029372..2e2dacef0 100644 --- a/tests/contest/contest/src/tests/domainname/mod.rs +++ b/tests/contest/contest/src/tests/domainname/mod.rs @@ -1,6 +1,7 @@ use oci_spec::runtime::{ProcessBuilder, Spec, SpecBuilder}; use test_framework::{ConditionalTest, TestGroup, TestResult}; +use crate::utils::test_utils::CreateOptions; use crate::utils::{is_runtime_runc, test_inside_container}; fn get_spec(domainname: &str) -> Spec { @@ -21,7 +22,7 @@ fn get_spec(domainname: &str) -> Spec { fn set_domainname_test() -> TestResult { let spec = get_spec("domainname"); - test_inside_container(spec, &|_| Ok(())) + test_inside_container(spec, &CreateOptions::default(), &|_| Ok(())) } pub fn get_domainname_tests() -> TestGroup { diff --git a/tests/contest/contest/src/tests/example/hello_world.rs b/tests/contest/contest/src/tests/example/hello_world.rs index 316fd6982..231b2c284 100644 --- a/tests/contest/contest/src/tests/example/hello_world.rs +++ b/tests/contest/contest/src/tests/example/hello_world.rs @@ -3,6 +3,7 @@ use oci_spec::runtime::{ProcessBuilder, Spec, SpecBuilder}; use test_framework::{test_result, Test, TestGroup, TestResult}; use crate::utils::test_inside_container; +use crate::utils::test_utils::CreateOptions; ////////// ANCHOR: get_example_spec fn create_spec() -> Result { @@ -25,7 +26,7 @@ fn create_spec() -> Result { ////////// ANCHOR: example_test fn example_test() -> TestResult { let spec = test_result!(create_spec()); - test_inside_container(spec, &|_| Ok(())) + test_inside_container(spec, &CreateOptions::default(), &|_| Ok(())) } ////////// ANCHOR_END: example_test diff --git a/tests/contest/contest/src/tests/hooks/invoke.rs b/tests/contest/contest/src/tests/hooks/invoke.rs index 7cd22dac2..9076f4484 100644 --- a/tests/contest/contest/src/tests/hooks/invoke.rs +++ b/tests/contest/contest/src/tests/hooks/invoke.rs @@ -5,7 +5,7 @@ use anyhow::anyhow; use oci_spec::runtime::{Hook, HookBuilder, HooksBuilder, ProcessBuilder, Spec, SpecBuilder}; use test_framework::{Test, TestGroup, TestResult}; -use crate::utils::test_utils::start_container; +use crate::utils::test_utils::{start_container, CreateOptions}; use crate::utils::{create_container, delete_container, generate_uuid, prepare_bundle, set_config}; const HOOK_OUTPUT_FILE: &str = "output"; @@ -71,7 +71,10 @@ fn get_test(test_name: &'static str) -> Test { let id_str = id.to_string(); let bundle = prepare_bundle().unwrap(); set_config(&bundle, &spec).unwrap(); - create_container(&id_str, &bundle).unwrap().wait().unwrap(); + create_container(&id_str, &bundle, &CreateOptions::default()) + .unwrap() + .wait() + .unwrap(); start_container(&id_str, &bundle).unwrap().wait().unwrap(); delete_container(&id_str, &bundle).unwrap().wait().unwrap(); let log = { diff --git a/tests/contest/contest/src/tests/hostname/mod.rs b/tests/contest/contest/src/tests/hostname/mod.rs index 1a740d8c5..17e0ce49b 100644 --- a/tests/contest/contest/src/tests/hostname/mod.rs +++ b/tests/contest/contest/src/tests/hostname/mod.rs @@ -2,6 +2,7 @@ use oci_spec::runtime::{LinuxBuilder, ProcessBuilder, Spec, SpecBuilder}; use test_framework::{Test, TestGroup, TestResult}; use crate::utils::test_inside_container; +use crate::utils::test_utils::CreateOptions; fn create_spec(hostname: &str) -> Spec { SpecBuilder::default() @@ -25,7 +26,7 @@ fn create_spec(hostname: &str) -> Spec { fn hostname_test() -> TestResult { let spec = create_spec("hostname-specific"); - test_inside_container(spec, &|_| { + test_inside_container(spec, &CreateOptions::default(), &|_| { // As long as the container is created, we expect the hostname to be determined // by the spec, so nothing to prepare prior. Ok(()) @@ -34,7 +35,7 @@ fn hostname_test() -> TestResult { fn empty_hostname() -> TestResult { let spec = create_spec(""); - test_inside_container(spec, &|_| { + test_inside_container(spec, &CreateOptions::default(), &|_| { // As long as the container is created, we expect the hostname to be determined // by the spec, so nothing to prepare prior. Ok(()) diff --git a/tests/contest/contest/src/tests/io_priority/io_priority_test.rs b/tests/contest/contest/src/tests/io_priority/io_priority_test.rs index b1a978a42..85476c087 100644 --- a/tests/contest/contest/src/tests/io_priority/io_priority_test.rs +++ b/tests/contest/contest/src/tests/io_priority/io_priority_test.rs @@ -4,6 +4,7 @@ use oci_spec::runtime::{ }; use test_framework::{test_result, ConditionalTest, TestGroup, TestResult}; +use crate::utils::test_utils::CreateOptions; use crate::utils::{is_runtime_runc, test_inside_container}; fn create_spec( @@ -38,7 +39,7 @@ fn io_priority_class_rt_test() -> TestResult { "io_priority_class_rt", 1, )); - test_inside_container(spec, &|_| Ok(())) + test_inside_container(spec, &CreateOptions::default(), &|_| Ok(())) } fn io_priority_class_be_test() -> TestResult { @@ -47,7 +48,7 @@ fn io_priority_class_be_test() -> TestResult { "io_priority_class_be", 2, )); - test_inside_container(spec, &|_| Ok(())) + test_inside_container(spec, &CreateOptions::default(), &|_| Ok(())) } fn io_priority_class_idle_test() -> TestResult { @@ -56,7 +57,7 @@ fn io_priority_class_idle_test() -> TestResult { "io_priority_class_idle", 3, )); - test_inside_container(spec, &|_| Ok(())) + test_inside_container(spec, &CreateOptions::default(), &|_| Ok(())) } pub fn get_io_priority_test() -> TestGroup { diff --git a/tests/contest/contest/src/tests/mod.rs b/tests/contest/contest/src/tests/mod.rs index 10c980856..a741016f3 100644 --- a/tests/contest/contest/src/tests/mod.rs +++ b/tests/contest/contest/src/tests/mod.rs @@ -12,10 +12,12 @@ pub mod linux_ns_itype; pub mod mounts_recursive; pub mod no_pivot; pub mod pidfile; +pub mod process; pub mod process_oom_score_adj; pub mod process_rlimits; pub mod process_user; pub mod readonly_paths; +pub mod root_readonly_true; pub mod scheduler; pub mod seccomp; pub mod seccomp_notify; diff --git a/tests/contest/contest/src/tests/mounts_recursive/mod.rs b/tests/contest/contest/src/tests/mounts_recursive/mod.rs index 976d5008b..c775ff89d 100644 --- a/tests/contest/contest/src/tests/mounts_recursive/mod.rs +++ b/tests/contest/contest/src/tests/mounts_recursive/mod.rs @@ -18,6 +18,7 @@ use oci_spec::runtime::{ use test_framework::{Test, TestGroup, TestResult}; use crate::utils::test_inside_container; +use crate::utils::test_utils::CreateOptions; fn get_spec(added_mounts: Vec, process_args: Vec) -> Spec { let mut mounts = get_default_mounts(); @@ -112,7 +113,7 @@ fn check_recursive_readonly() -> TestResult { vec!["runtimetest".to_string(), "mounts_recursive".to_string()], ); - let result = test_inside_container(spec, &|_| { + let result = test_inside_container(spec, &CreateOptions::default(), &|_| { setup_mount(&rro_dir_path, &rro_subdir_path); Ok(()) }); @@ -152,7 +153,7 @@ fn check_recursive_nosuid() -> TestResult { ], ); - let result = test_inside_container(spec, &|bundle_path| { + let result = test_inside_container(spec, &CreateOptions::default(), &|bundle_path| { setup_mount(&rnosuid_dir_path, &rnosuid_subdir_path); let executable_file_path = bundle_path.join("bin").join(executable_file_name); @@ -225,7 +226,7 @@ fn check_recursive_rsuid() -> TestResult { vec![mount_spec], vec!["runtimetest".to_string(), "mounts_recursive".to_string()], ); - test_inside_container(spec, &|_| { + test_inside_container(spec, &CreateOptions::default(), &|_| { let original_file_path = rsuid_dir_path.join("file"); let file = File::create(original_file_path)?; let mut permission = file.metadata()?.permissions(); @@ -256,7 +257,7 @@ fn check_recursive_noexec() -> TestResult { vec!["runtimetest".to_string(), "mounts_recursive".to_string()], ); - let result = test_inside_container(spec, &|bundle_path| { + let result = test_inside_container(spec, &CreateOptions::default(), &|bundle_path| { setup_mount(&rnoexec_dir_path, &rnoexec_subdir_path); let executable_file_name = "echo"; @@ -297,7 +298,7 @@ fn check_recursive_rexec() -> TestResult { vec!["runtimetest".to_string(), "mounts_recursive".to_string()], ); - let result = test_inside_container(spec, &|bundle_path| { + let result = test_inside_container(spec, &CreateOptions::default(), &|bundle_path| { setup_mount(&rnoexec_dir_path, &rnoexec_subdir_path); let executable_file_name = "echo"; @@ -338,7 +339,7 @@ fn check_recursive_rdiratime() -> TestResult { vec!["runtimetest".to_string(), "mounts_recursive".to_string()], ); - let result = test_inside_container(spec, &|_| Ok(())); + let result = test_inside_container(spec, &CreateOptions::default(), &|_| Ok(())); fs::remove_dir(rdiratime_base_dir).unwrap(); result @@ -362,7 +363,7 @@ fn check_recursive_rnodiratime() -> TestResult { vec!["runtimetest".to_string(), "mounts_recursive".to_string()], ); - let result = test_inside_container(spec, &|_| Ok(())); + let result = test_inside_container(spec, &CreateOptions::default(), &|_| Ok(())); fs::remove_dir(rnodiratime_base_dir).unwrap(); result } @@ -383,7 +384,7 @@ fn check_recursive_rdev() -> TestResult { vec!["runtimetest".to_string(), "mounts_recursive".to_string()], ); - test_inside_container(spec, &|_| Ok(())) + test_inside_container(spec, &CreateOptions::default(), &|_| Ok(())) } fn check_recursive_rnodev() -> TestResult { @@ -402,7 +403,7 @@ fn check_recursive_rnodev() -> TestResult { vec!["runtimetest".to_string(), "mounts_recursive".to_string()], ); - test_inside_container(spec, &|_| Ok(())) + test_inside_container(spec, &CreateOptions::default(), &|_| Ok(())) } fn check_recursive_readwrite() -> TestResult { @@ -423,7 +424,7 @@ fn check_recursive_readwrite() -> TestResult { vec!["runtimetest".to_string(), "mounts_recursive".to_string()], ); - let result = test_inside_container(spec, &|_| { + let result = test_inside_container(spec, &CreateOptions::default(), &|_| { setup_mount(&rrw_dir_path, &rrw_subdir_path); Ok(()) }); @@ -451,7 +452,7 @@ fn check_recursive_rrelatime() -> TestResult { vec![mount_spec], vec!["runtimetest".to_string(), "mounts_recursive".to_string()], ); - let result = test_inside_container(spec, &|_| Ok(())); + let result = test_inside_container(spec, &CreateOptions::default(), &|_| Ok(())); fs::remove_dir_all(rrelatime_dir_path).unwrap(); result @@ -475,7 +476,7 @@ fn check_recursive_rnorelatime() -> TestResult { vec!["runtimetest".to_string(), "mounts_recursive".to_string()], ); - let result = test_inside_container(spec, &|_| Ok(())); + let result = test_inside_container(spec, &CreateOptions::default(), &|_| Ok(())); fs::remove_dir_all(rnorelatime_dir_path).unwrap(); result @@ -499,7 +500,7 @@ fn check_recursive_rnoatime() -> TestResult { vec!["runtimetest".to_string(), "mounts_recursive".to_string()], ); - let result = test_inside_container(spec, &|_| Ok(())); + let result = test_inside_container(spec, &CreateOptions::default(), &|_| Ok(())); fs::remove_dir_all(rnoatime_dir_path).unwrap(); result @@ -522,7 +523,7 @@ fn check_recursive_rstrictatime() -> TestResult { vec![mount_spec], vec!["runtimetest".to_string(), "mounts_recursive".to_string()], ); - let result = test_inside_container(spec, &|_| Ok(())); + let result = test_inside_container(spec, &CreateOptions::default(), &|_| Ok(())); fs::remove_dir_all(rstrictatime_dir_path).unwrap(); result @@ -548,7 +549,7 @@ fn check_recursive_rnosymfollow() -> TestResult { vec![mount_spec], vec!["runtimetest".to_string(), "mounts_recursive".to_string()], ); - let result = test_inside_container(spec, &|_| { + let result = test_inside_container(spec, &CreateOptions::default(), &|_| { let original_file_path = format!("{}/{}", rnosymfollow_dir_path.to_str().unwrap(), "file"); let file = File::create(&original_file_path)?; let link_file_path = format!("{}/{}", rnosymfollow_dir_path.to_str().unwrap(), "link"); @@ -587,7 +588,7 @@ fn check_recursive_rsymfollow() -> TestResult { vec![mount_spec], vec!["runtimetest".to_string(), "mounts_recursive".to_string()], ); - let result = test_inside_container(spec, &|_| { + let result = test_inside_container(spec, &CreateOptions::default(), &|_| { let original_file_path = format!("{}/{}", rsymfollow_dir_path.to_str().unwrap(), "file"); let file = File::create(&original_file_path)?; let link_file_path = format!("{}/{}", rsymfollow_dir_path.to_str().unwrap(), "link"); diff --git a/tests/contest/contest/src/tests/no_pivot/mod.rs b/tests/contest/contest/src/tests/no_pivot/mod.rs index 8540a058a..644b4365b 100644 --- a/tests/contest/contest/src/tests/no_pivot/mod.rs +++ b/tests/contest/contest/src/tests/no_pivot/mod.rs @@ -2,7 +2,7 @@ use anyhow::{Context, Result}; use oci_spec::runtime::{ProcessBuilder, Spec, SpecBuilder}; use test_framework::{test_result, Test, TestGroup, TestResult}; -use crate::utils::test_utils::test_inside_container_with_no_pivot; +use crate::utils::test_utils::{test_inside_container, CreateOptions}; fn create_spec() -> Result { SpecBuilder::default() @@ -17,7 +17,11 @@ fn create_spec() -> Result { fn no_pivot_test() -> TestResult { let spec = test_result!(create_spec()); - test_inside_container_with_no_pivot(spec, &|_| Ok(())) + test_inside_container( + spec, + &CreateOptions::default().with_no_pivot_root(), + &|_| Ok(()), + ) } pub fn get_no_pivot_test() -> TestGroup { diff --git a/tests/contest/contest/src/tests/process/mod.rs b/tests/contest/contest/src/tests/process/mod.rs new file mode 100644 index 000000000..8237bcd5f --- /dev/null +++ b/tests/contest/contest/src/tests/process/mod.rs @@ -0,0 +1,2 @@ +mod process_test; +pub use process_test::get_process_test; diff --git a/tests/contest/contest/src/tests/process/process_test.rs b/tests/contest/contest/src/tests/process/process_test.rs new file mode 100644 index 000000000..81c6b501f --- /dev/null +++ b/tests/contest/contest/src/tests/process/process_test.rs @@ -0,0 +1,51 @@ +use std::fs; + +use anyhow::{bail, Context, Ok, Result}; +use oci_spec::runtime::{ProcessBuilder, Spec, SpecBuilder}; +use test_framework::{test_result, Test, TestGroup, TestResult}; + +use crate::utils::test_inside_container; +use crate::utils::test_utils::CreateOptions; + +fn create_spec() -> Result { + let mut process = ProcessBuilder::default() + .args(vec!["runtimetest".to_string(), "process".to_string()]) + .cwd("/test") + .build() + .expect("error in creating process config"); + let mut env = process.env().clone().unwrap(); + env.push("testa=valuea".to_string()); + env.push("testb=123".to_string()); + process.set_env(Some(env)); + + let spec = SpecBuilder::default() + .process(process) + .build() + .context("failed to build spec")?; + + Ok(spec) +} + +fn process_test() -> TestResult { + let spec = test_result!(create_spec()); + + test_inside_container(spec, &CreateOptions::default(), &|bundle| { + match fs::create_dir(bundle.join("test")) { + Result::Ok(_) => { /*This is expected*/ } + Err(e) => { + bail!(e) + } + } + + Ok(()) + }) +} + +pub fn get_process_test() -> TestGroup { + let mut process_test_group = TestGroup::new("process"); + + let test = Test::new("process_test", Box::new(process_test)); + process_test_group.add(vec![Box::new(test)]); + + process_test_group +} diff --git a/tests/contest/contest/src/tests/process_oom_score_adj/process_oom_score_adj_test.rs b/tests/contest/contest/src/tests/process_oom_score_adj/process_oom_score_adj_test.rs index ac8167807..03cea34c6 100644 --- a/tests/contest/contest/src/tests/process_oom_score_adj/process_oom_score_adj_test.rs +++ b/tests/contest/contest/src/tests/process_oom_score_adj/process_oom_score_adj_test.rs @@ -4,6 +4,7 @@ use rand::Rng; use test_framework::{test_result, Test, TestGroup, TestResult}; use crate::utils::test_inside_container; +use crate::utils::test_utils::CreateOptions; fn generate_random_number() -> i32 { let mut rng = rand::thread_rng(); @@ -30,7 +31,7 @@ fn create_spec() -> Result { fn process_oom_score_adj_test() -> TestResult { let spec = test_result!(create_spec()); - test_inside_container(spec, &|_| Ok(())) + test_inside_container(spec, &CreateOptions::default(), &|_| Ok(())) } pub fn get_process_oom_score_adj_test() -> TestGroup { 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 index 7d5456d50..18f503351 100644 --- a/tests/contest/contest/src/tests/process_rlimits/process_rlimits_test.rs +++ b/tests/contest/contest/src/tests/process_rlimits/process_rlimits_test.rs @@ -5,6 +5,7 @@ use oci_spec::runtime::{ use test_framework::{test_result, Test, TestGroup, TestResult}; use crate::utils::test_inside_container; +use crate::utils::test_utils::CreateOptions; const GIGABYTES: u64 = 1024 * 1024 * 1024; @@ -54,7 +55,7 @@ fn create_spec() -> Result { fn process_rlimits_test() -> TestResult { let spec = test_result!(create_spec()); - test_inside_container(spec, &|_| Ok(())) + test_inside_container(spec, &CreateOptions::default(), &|_| Ok(())) } pub fn get_process_rlimits_test() -> TestGroup { diff --git a/tests/contest/contest/src/tests/process_user/process_user_test.rs b/tests/contest/contest/src/tests/process_user/process_user_test.rs index 825cb3254..5329ccbbf 100644 --- a/tests/contest/contest/src/tests/process_user/process_user_test.rs +++ b/tests/contest/contest/src/tests/process_user/process_user_test.rs @@ -4,6 +4,7 @@ use rand::Rng; use test_framework::{test_result, Test, TestGroup, TestResult}; use crate::utils::test_inside_container; +use crate::utils::test_utils::CreateOptions; // Generates a Vec with a random number of elements (between 5 and 15), // where each element is a random u32 value between 0 and 65535. @@ -43,7 +44,7 @@ fn create_spec() -> Result { } fn process_user_test() -> TestResult { let spec = test_result!(create_spec()); - test_inside_container(spec, &|_| Ok(())) + test_inside_container(spec, &CreateOptions::default(), &|_| Ok(())) } pub fn get_process_user_test() -> TestGroup { diff --git a/tests/contest/contest/src/tests/readonly_paths/readonly_paths_tests.rs b/tests/contest/contest/src/tests/readonly_paths/readonly_paths_tests.rs index 36b77ccb6..c90784ccf 100644 --- a/tests/contest/contest/src/tests/readonly_paths/readonly_paths_tests.rs +++ b/tests/contest/contest/src/tests/readonly_paths/readonly_paths_tests.rs @@ -6,6 +6,7 @@ use oci_spec::runtime::{LinuxBuilder, ProcessBuilder, Spec, SpecBuilder}; use test_framework::{Test, TestGroup, TestResult}; use crate::utils::test_inside_container; +use crate::utils::test_utils::CreateOptions; fn get_spec(readonly_paths: Vec) -> Spec { SpecBuilder::default() @@ -60,7 +61,7 @@ fn check_readonly_paths() -> TestResult { ]; let spec = get_spec(ro_paths); - test_inside_container(spec, &|bundle_path| { + test_inside_container(spec, &CreateOptions::default(), &|bundle_path| { use std::{fs, io}; let test_dir = bundle_path.join(&ro_dir_sub); @@ -111,7 +112,7 @@ fn check_readonly_rel_path() -> TestResult { let ro_paths = vec![ro_rel_path.to_string()]; let spec = get_spec(ro_paths); - test_inside_container(spec, &|bundle_path| { + test_inside_container(spec, &CreateOptions::default(), &|bundle_path| { use std::{fs, io}; let test_file = bundle_path.join(ro_rel_path); @@ -142,7 +143,7 @@ fn check_readonly_symlinks() -> TestResult { let spec = get_spec(ro_paths); - let res = test_inside_container(spec, &|bundle_path| { + let res = test_inside_container(spec, &CreateOptions::default(), &|bundle_path| { use std::{fs, io}; let test_file = bundle_path.join(ro_symlink); @@ -193,7 +194,7 @@ fn test_node(mode: u32) -> TestResult { let spec = get_spec(ro_paths); - test_inside_container(spec, &|bundle_path| { + test_inside_container(spec, &CreateOptions::default(), &|bundle_path| { use std::os::unix::fs::OpenOptionsExt; use std::{fs, io}; let test_file = bundle_path.join(ro_device); diff --git a/tests/contest/contest/src/tests/root_readonly_true/mod.rs b/tests/contest/contest/src/tests/root_readonly_true/mod.rs new file mode 100644 index 000000000..32bcfe81d --- /dev/null +++ b/tests/contest/contest/src/tests/root_readonly_true/mod.rs @@ -0,0 +1,2 @@ +mod root_readonly_tests; +pub use root_readonly_tests::get_root_readonly_test; diff --git a/tests/contest/contest/src/tests/root_readonly_true/root_readonly_tests.rs b/tests/contest/contest/src/tests/root_readonly_true/root_readonly_tests.rs new file mode 100644 index 000000000..c87b30adf --- /dev/null +++ b/tests/contest/contest/src/tests/root_readonly_true/root_readonly_tests.rs @@ -0,0 +1,44 @@ +use anyhow::{Context, Ok, Result}; +use oci_spec::runtime::{ProcessBuilder, RootBuilder, Spec, SpecBuilder}; +use test_framework::{test_result, Test, TestGroup, TestResult}; + +use crate::utils::test_inside_container; +use crate::utils::test_utils::CreateOptions; + +fn create_spec(readonly: bool) -> Result { + let spec = SpecBuilder::default() + .root(RootBuilder::default().readonly(readonly).build().unwrap()) + .process( + ProcessBuilder::default() + .args(vec!["runtimetest".to_string(), "root_readonly".to_string()]) + .build() + .expect("error in creating config"), + ) + .build() + .context("failed to build spec")?; + + Ok(spec) +} + +fn root_readonly_true_test() -> TestResult { + let spec_true = test_result!(create_spec(true)); + test_inside_container(spec_true, &CreateOptions::default(), &|_| Ok(())) +} + +fn root_readonly_false_test() -> TestResult { + let spec_false = test_result!(create_spec(false)); + test_inside_container(spec_false, &CreateOptions::default(), &|_| Ok(())) +} + +pub fn get_root_readonly_test() -> TestGroup { + let mut root_readonly_test_group = TestGroup::new("root_readonly"); + + let test_true = Test::new("root_readonly_true_test", Box::new(root_readonly_true_test)); + let test_false = Test::new( + "root_readonly_false_test", + Box::new(root_readonly_false_test), + ); + root_readonly_test_group.add(vec![Box::new(test_true), Box::new(test_false)]); + + root_readonly_test_group +} diff --git a/tests/contest/contest/src/tests/scheduler/scheduler_policy.rs b/tests/contest/contest/src/tests/scheduler/scheduler_policy.rs index bbf68712d..4bbc4c6f1 100644 --- a/tests/contest/contest/src/tests/scheduler/scheduler_policy.rs +++ b/tests/contest/contest/src/tests/scheduler/scheduler_policy.rs @@ -4,6 +4,7 @@ use oci_spec::runtime::{ }; use test_framework::{test_result, ConditionalTest, TestGroup, TestResult}; +use crate::utils::test_utils::CreateOptions; use crate::utils::{is_runtime_runc, test_inside_container}; fn create_spec(policy: LinuxSchedulerPolicy, execute_test: &str) -> Result { @@ -33,7 +34,7 @@ fn scheduler_policy_other_test() -> TestResult { LinuxSchedulerPolicy::SchedOther, "scheduler_policy_other" )); - test_inside_container(spec, &|_| Ok(())) + test_inside_container(spec, &CreateOptions::default(), &|_| Ok(())) } fn scheduler_policy_batch_test() -> TestResult { @@ -41,7 +42,7 @@ fn scheduler_policy_batch_test() -> TestResult { LinuxSchedulerPolicy::SchedBatch, "scheduler_policy_batch" )); - test_inside_container(spec, &|_| Ok(())) + test_inside_container(spec, &CreateOptions::default(), &|_| Ok(())) } pub fn get_scheduler_test() -> TestGroup { diff --git a/tests/contest/contest/src/tests/seccomp/mod.rs b/tests/contest/contest/src/tests/seccomp/mod.rs index ba583da28..1f9448abc 100644 --- a/tests/contest/contest/src/tests/seccomp/mod.rs +++ b/tests/contest/contest/src/tests/seccomp/mod.rs @@ -5,6 +5,7 @@ use oci_spec::runtime::{ use test_framework::{Test, TestGroup, TestResult}; use crate::utils::test_inside_container; +use crate::utils::test_utils::CreateOptions; fn create_spec(seccomp: LinuxSeccomp) -> Spec { SpecBuilder::default() @@ -36,7 +37,7 @@ fn seccomp_test() -> TestResult { .build() .unwrap(), ); - test_inside_container(spec, &|_| Ok(())) + test_inside_container(spec, &CreateOptions::default(), &|_| Ok(())) } pub fn get_seccomp_test() -> TestGroup { diff --git a/tests/contest/contest/src/tests/sysctl/mod.rs b/tests/contest/contest/src/tests/sysctl/mod.rs index d80703e44..81c35120c 100644 --- a/tests/contest/contest/src/tests/sysctl/mod.rs +++ b/tests/contest/contest/src/tests/sysctl/mod.rs @@ -4,6 +4,7 @@ use oci_spec::runtime::{LinuxBuilder, ProcessBuilder, Spec, SpecBuilder}; use test_framework::{Test, TestGroup, TestResult}; use crate::utils::test_inside_container; +use crate::utils::test_utils::CreateOptions; fn create_spec(sysctl: HashMap) -> Spec { SpecBuilder::default() @@ -28,7 +29,7 @@ fn sysctl_test() -> TestResult { "net.ipv4.ip_forward".to_string(), "1".to_string(), )])); - test_inside_container(spec, &|_| { + test_inside_container(spec, &CreateOptions::default(), &|_| { // As long as the container is created, we expect the kernel parameters to be determined by // the spec, so nothing to prepare prior. Ok(()) diff --git a/tests/contest/contest/src/utils/test_utils.rs b/tests/contest/contest/src/utils/test_utils.rs index ceb31262e..0964a9a04 100644 --- a/tests/contest/contest/src/utils/test_utils.rs +++ b/tests/contest/contest/src/utils/test_utils.rs @@ -42,7 +42,19 @@ pub struct ContainerData { pub create_result: std::io::Result, } -fn create_container_command>(id: &str, dir: P, with_pivot_root: bool) -> Command { +#[derive(Debug, Default)] +pub struct CreateOptions { + no_pivot: bool, +} + +impl CreateOptions { + pub fn with_no_pivot_root(mut self) -> Self { + self.no_pivot = true; + self + } +} + +fn create_container_command>(id: &str, dir: P, options: &CreateOptions) -> Command { let mut command = Command::new(get_runtime_path()); command .stdout(Stdio::piped()) @@ -53,22 +65,19 @@ fn create_container_command>(id: &str, dir: P, with_pivot_root: b .arg(id) .arg("--bundle") .arg(dir.as_ref().join("bundle")); - if with_pivot_root { + if options.no_pivot { command.arg("--no-pivot"); } command } /// Starts the runtime with given directory as root directory -pub fn create_container>(id: &str, dir: P) -> Result { - let res = create_container_command(id, dir, false) - .spawn() - .context("could not create container")?; - Ok(res) -} - -pub fn create_container_no_pivot>(id: &str, dir: P) -> Result { - let res = create_container_command(id, dir, true) +pub fn create_container>( + id: &str, + dir: P, + options: &CreateOptions, +) -> Result { + let res = create_container_command(id, dir, options) .spawn() .context("could not create container")?; Ok(res) @@ -135,7 +144,8 @@ pub fn test_outside_container( let id_str = id.to_string(); let bundle = prepare_bundle().unwrap(); set_config(&bundle, &spec).unwrap(); - let create_result = create_container(&id_str, &bundle).unwrap().wait(); + let options = CreateOptions::default(); + let create_result = create_container(&id_str, &bundle, &options).unwrap().wait(); let (out, err) = get_state(&id_str, &bundle).unwrap(); let state: Option = match serde_json::from_str(&out) { Ok(v) => Some(v), @@ -156,6 +166,7 @@ pub fn test_outside_container( // mostly needs a name that better expresses what this actually does pub fn test_inside_container( spec: Spec, + options: &CreateOptions, setup_for_test: &dyn Fn(&Path) -> Result<()>, ) -> TestResult { let id = generate_uuid(); @@ -190,101 +201,7 @@ pub fn test_inside_container( .join("runtimetest"), ) .unwrap(); - let create_process = create_container(&id_str, &bundle).unwrap(); - // here we do not wait for the process by calling wait() as in the test_outside_container - // function because we need the output of the runtimetest. If we call wait, it will return - // and we won't have an easy way of getting the stdio of the runtimetest. - // Thus to make sure the container is created, we just wait for sometime, and - // assume that the create command was successful. If it wasn't we can catch that error - // in the start_container, as we can not start a non-created container anyways - std::thread::sleep(std::time::Duration::from_millis(1000)); - match start_container(&id_str, &bundle) - .unwrap() - .wait_with_output() - { - Ok(c) => c, - Err(e) => return TestResult::Failed(anyhow!("container start failed : {:?}", e)), - }; - - let create_output = create_process - .wait_with_output() - .context("getting output after starting the container failed") - .unwrap(); - - let stdout = String::from_utf8_lossy(&create_output.stdout); - if !stdout.is_empty() { - println!( - "{:?}", - anyhow!("container stdout was not empty, found : {}", stdout) - ) - } - let stderr = String::from_utf8_lossy(&create_output.stderr); - if !stderr.is_empty() { - return TestResult::Failed(anyhow!( - "container stderr was not empty, found : {}", - stderr - )); - } - - let (out, err) = get_state(&id_str, &bundle).unwrap(); - if !err.is_empty() { - return TestResult::Failed(anyhow!( - "error in getting state after starting the container : {}", - err - )); - } - - let state: State = match serde_json::from_str(&out) { - Ok(v) => v, - Err(e) => return TestResult::Failed(anyhow!("error in parsing state of container after start in test_inside_container : stdout : {}, parse error : {}",out,e)), - }; - if state.status != "stopped" { - return TestResult::Failed(anyhow!("error : unexpected container status in test_inside_runtime : expected stopped, got {}, container state : {:?}",state.status,state)); - } - kill_container(&id_str, &bundle).unwrap().wait().unwrap(); - delete_container(&id_str, &bundle).unwrap().wait().unwrap(); - TestResult::Passed -} - -// just copy-pasted from test_inside_container for now, but with no pivot root -// need to refactor this to avoid duplication -pub fn test_inside_container_with_no_pivot( - spec: Spec, - setup_for_test: &dyn Fn(&Path) -> Result<()>, -) -> TestResult { - let id = generate_uuid(); - let id_str = id.to_string(); - let bundle = prepare_bundle().unwrap(); - - // This will do the required setup for the test - test_result!(setup_for_test( - &bundle.as_ref().join("bundle").join("rootfs") - )); - - set_config(&bundle, &spec).unwrap(); - // as we have to run runtimetest inside the container, and is expects - // the config.json to be at path /config.json we save it there - let path = bundle - .as_ref() - .join("bundle") - .join("rootfs") - .join("config.json"); - spec.save(path).unwrap(); - - let runtimetest_path = get_runtimetest_path(); - // The config will directly use runtime as the command to be run, so we have to - // save the runtimetest binary at its /bin - std::fs::copy( - runtimetest_path, - bundle - .as_ref() - .join("bundle") - .join("rootfs") - .join("bin") - .join("runtimetest"), - ) - .unwrap(); - let create_process = create_container_no_pivot(&id_str, &bundle).unwrap(); + let create_process = create_container(&id_str, &bundle, options).unwrap(); // here we do not wait for the process by calling wait() as in the test_outside_container // function because we need the output of the runtimetest. If we call wait, it will return // and we won't have an easy way of getting the stdio of the runtimetest. diff --git a/tests/contest/runtimetest/Cargo.toml b/tests/contest/runtimetest/Cargo.toml index e257bb23c..cd0ceaebf 100644 --- a/tests/contest/runtimetest/Cargo.toml +++ b/tests/contest/runtimetest/Cargo.toml @@ -7,5 +7,5 @@ edition = "2021" oci-spec = { version = "0.7.1", features = ["runtime"] } nix = "0.28.0" anyhow = "1.0" -libc = "0.2.162" # TODO (YJDoc2) upgrade to latest +libc = "0.2.167" # TODO (YJDoc2) upgrade to latest nc = "0.9.5" diff --git a/tests/contest/runtimetest/src/main.rs b/tests/contest/runtimetest/src/main.rs index 2de6bb7e0..ef85a35cb 100644 --- a/tests/contest/runtimetest/src/main.rs +++ b/tests/contest/runtimetest/src/main.rs @@ -44,6 +44,8 @@ fn main() { "io_priority_class_be" => tests::test_io_priority_class(&spec, IoprioClassBe), "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" => tests::validate_process(&spec), "process_user" => tests::validate_process_user(&spec), "process_rlimits" => tests::validate_process_rlimits(&spec), "no_pivot" => tests::validate_rootfs(), diff --git a/tests/contest/runtimetest/src/tests.rs b/tests/contest/runtimetest/src/tests.rs index ae2e6772f..721141aaf 100644 --- a/tests/contest/runtimetest/src/tests.rs +++ b/tests/contest/runtimetest/src/tests.rs @@ -1,3 +1,4 @@ +use std::env; use std::fs::{self, read_dir}; use std::os::linux::fs::MetadataExt; use std::os::unix::fs::{FileTypeExt, PermissionsExt}; @@ -15,7 +16,9 @@ use oci_spec::runtime::{ LinuxDevice, LinuxDeviceType, LinuxSchedulerPolicy, PosixRlimit, PosixRlimitType, Spec, }; -use crate::utils::{self, test_read_access, test_write_access}; +use crate::utils::{ + self, test_dir_read_access, test_dir_write_access, test_read_access, test_write_access, +}; ////////// ANCHOR: example_hello_world pub fn hello_world(_spec: &Spec) { @@ -550,6 +553,74 @@ pub fn test_io_priority_class(spec: &Spec, io_priority_class: IOPriorityClass) { } } +pub fn test_validate_root_readonly(spec: &Spec) { + let root = spec.root().as_ref().unwrap(); + if root.readonly().unwrap() { + if let Err(e) = test_dir_write_access("/") { + let errno = Errno::from_raw(e.raw_os_error().unwrap()); + if errno == Errno::EROFS { + /* This is expected */ + } else { + eprintln!( + "readonly root filesystem, error in testing write access for path /, error: {}", + errno + ); + } + } + if let Err(e) = test_dir_read_access("/") { + eprintln!( + "readonly root filesystem, but error in testing read access for path /, error: {}", + e + ); + } + } else { + if let Err(e) = test_dir_write_access("/") { + eprintln!( + "readonly root filesystem is false, but error in testing write access for path /, error: {}", + e + ); + } + if let Err(e) = test_dir_read_access("/") { + eprintln!( + "readonly root filesystem is false, but error in testing read access for path /, error: {}", + e + ); + } + } +} + +pub fn validate_process(spec: &Spec) { + let process = spec.process().as_ref().unwrap(); + let expected_cwd = process.cwd(); + let cwd = &getcwd().unwrap(); + + if expected_cwd != cwd { + eprintln!( + "error due to spec cwd want {:?}, got {:?}", + expected_cwd, cwd + ) + } + + for env_str in process.env().as_ref().unwrap().iter() { + match env_str.split_once("=") { + Some((env_key, expected_val)) => { + let actual_val = env::var(env_key).unwrap(); + if actual_val != expected_val { + eprintln!( + "error due to spec environment value of {:?} want {:?}, got {:?}", + env_key, expected_val, actual_val + ) + } + } + None => { + eprintln!( + "spec env value is not correct : expected key=value format, got {env_str}" + ) + } + } + } +} + pub fn validate_process_user(spec: &Spec) { let process = spec.process().as_ref().unwrap(); let expected_uid = Uid::from(process.user().uid()); diff --git a/tests/contest/runtimetest/src/utils.rs b/tests/contest/runtimetest/src/utils.rs index 4976fe5ae..fd1c1cbde 100644 --- a/tests/contest/runtimetest/src/utils.rs +++ b/tests/contest/runtimetest/src/utils.rs @@ -14,7 +14,7 @@ fn test_file_read_access(path: &str) -> Result<(), std::io::Error> { Ok(()) } -fn test_dir_read_access(path: &str) -> Result<(), std::io::Error> { +pub fn test_dir_read_access(path: &str) -> Result<(), std::io::Error> { let _ = std::fs::read_dir(path)?; Ok(()) } @@ -51,7 +51,7 @@ fn test_file_write_access(path: &str) -> Result<(), std::io::Error> { Ok(()) } -fn test_dir_write_access(path: &str) -> Result<(), std::io::Error> { +pub fn test_dir_write_access(path: &str) -> Result<(), std::io::Error> { let _ = std::fs::OpenOptions::new() .create(true) .truncate(true) diff --git a/tests/contest/test_framework/Cargo.toml b/tests/contest/test_framework/Cargo.toml index bc623b7dc..e10d7f907 100644 --- a/tests/contest/test_framework/Cargo.toml +++ b/tests/contest/test_framework/Cargo.toml @@ -6,5 +6,5 @@ edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -anyhow = "1.0.93" +anyhow = "1.0.94" crossbeam = "0.8.4"