From a61689332fdf939faee9d9ae38a7fb2734e16112 Mon Sep 17 00:00:00 2001 From: Yashodhan Joshi Date: Mon, 20 Dec 2021 20:13:58 +0530 Subject: [PATCH 01/10] Add test_inside_container function --- crates/integration_test/src/utils/mod.rs | 4 +- .../integration_test/src/utils/test_utils.rs | 69 ++++++++++++++++++- 2 files changed, 68 insertions(+), 5 deletions(-) diff --git a/crates/integration_test/src/utils/mod.rs b/crates/integration_test/src/utils/mod.rs index e173ff097..69e473c51 100644 --- a/crates/integration_test/src/utils/mod.rs +++ b/crates/integration_test/src/utils/mod.rs @@ -6,6 +6,6 @@ pub use support::{ }; pub use temp_dir::{create_temp_dir, TempDir}; pub use test_utils::{ - create_container, delete_container, get_state, kill_container, test_outside_container, - ContainerData, State, + create_container, delete_container, get_state, kill_container, test_inside_container, + test_outside_container, ContainerData, State, }; diff --git a/crates/integration_test/src/utils/test_utils.rs b/crates/integration_test/src/utils/test_utils.rs index 684839824..0ee6c3335 100644 --- a/crates/integration_test/src/utils/test_utils.rs +++ b/crates/integration_test/src/utils/test_utils.rs @@ -1,7 +1,7 @@ +use super::{generate_uuid, prepare_bundle, set_config}; ///! Contains utility functions for testing ///! Similar to https://github.com/opencontainers/runtime-tools/blob/master/validation/util/test.go -use super::get_runtime_path; -use super::{generate_uuid, prepare_bundle, set_config}; +use super::{get_runtime_path, TempDir}; use anyhow::{anyhow, bail, Context, Result}; use oci_spec::runtime::Spec; use serde::{Deserialize, Serialize}; @@ -10,7 +10,7 @@ use std::path::{Path, PathBuf}; use std::process::{Child, Command, ExitStatus, Stdio}; use std::thread::sleep; use std::time::Duration; -use test_framework::TestResult; +use test_framework::{test_result, TestResult}; use uuid::Uuid; const SLEEP_TIME: Duration = Duration::from_millis(150); @@ -103,6 +103,19 @@ pub fn get_state>(id: &Uuid, dir: P) -> Result<(String, String)> Ok((stdout, stderr)) } +pub fn start_container>(id: &Uuid, dir: P) -> Result { + let res = Command::new(get_runtime_path()) + .stdout(Stdio::piped()) + .stderr(Stdio::piped()) + .arg("--root") + .arg(dir.as_ref().join("runtime")) + .arg("start") + .arg(id.to_string()) + .spawn() + .context("could not start container")?; + Ok(res) +} + pub fn test_outside_container( spec: Spec, execute_test: &dyn Fn(ContainerData) -> TestResult, @@ -128,6 +141,56 @@ pub fn test_outside_container( test_result } +// mostly needs a name that better expresses what this actually does +pub fn test_inside_container( + spec: Spec, + execute_test: &dyn Fn(&TempDir) -> Result<()>, +) -> TestResult { + let id = generate_uuid(); + let bundle = prepare_bundle(&id).unwrap(); + + test_result!(execute_test(&bundle)); + + set_config(&bundle, &spec).unwrap(); + let create_result = create_container(&id, &bundle).unwrap().wait(); + let (out, err) = get_state(&id, &bundle).unwrap(); + let state: Option = match serde_json::from_str(&out) { + Ok(v) => Some(v), + Err(_) => None, + }; + let data = ContainerData { + id: id.to_string(), + state, + state_err: err, + create_result, + }; + test_result!(check_container_created(&data)); + let start_result = test_result!(create_container(&id, &bundle).unwrap().wait_with_output()); + let stderr = String::from_utf8_lossy(&start_result.stderr); + if !stderr.is_empty() { + return TestResult::Failed(anyhow!( + "container start stderr was not empty : found {}", + stderr + )); + } + // we sleep here once again for contingency + // and make sure that the container stops before we call the get_state + // TODO decide the time to sleep by trial-and-error + sleep(SLEEP_TIME); + let (out, err) = get_state(&id, &bundle).unwrap(); + + 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, &bundle).unwrap().wait().unwrap(); + delete_container(&id, &bundle).unwrap().wait().unwrap(); + TestResult::Passed +} + pub fn check_container_created(data: &ContainerData) -> Result<()> { match &data.create_result { Ok(exit_status) => { From 9acb01309b21a0b5a1eb1e00f0ba02c566b2d860 Mon Sep 17 00:00:00 2001 From: Yashodhan Joshi Date: Wed, 22 Dec 2021 11:15:37 +0530 Subject: [PATCH 02/10] WIP commit for readonly_paths test --- crates/integration_test/src/main.rs | 3 + crates/integration_test/src/tests/mod.rs | 1 + .../src/tests/readonly_paths/mod.rs | 2 + .../tests/readonly_paths/readonly_paths.rs | 104 ++++++++++++++++++ .../integration_test/src/utils/test_utils.rs | 17 ++- 5 files changed, 122 insertions(+), 5 deletions(-) create mode 100644 crates/integration_test/src/tests/readonly_paths/mod.rs create mode 100644 crates/integration_test/src/tests/readonly_paths/readonly_paths.rs diff --git a/crates/integration_test/src/main.rs b/crates/integration_test/src/main.rs index 39255d941..02cba6635 100644 --- a/crates/integration_test/src/main.rs +++ b/crates/integration_test/src/main.rs @@ -4,6 +4,7 @@ mod utils; use crate::tests::lifecycle::{ContainerCreate, ContainerLifecycle}; use crate::tests::linux_ns_itype::get_ns_itype_tests; use crate::tests::pidfile::get_pidfile_test; +use crate::tests::readonly_paths::get_ro_paths_test; use crate::tests::seccomp_notify::get_seccomp_notify_test; use crate::tests::tlb::get_tlb_test; use crate::utils::support::set_runtime_path; @@ -78,6 +79,7 @@ fn main() -> Result<()> { let cgroup_v1_network = cgroups::network::get_test_group(); let cgroup_v1_blkio = cgroups::blkio::get_test_group(); let seccomp_notify = get_seccomp_notify_test(); + let ro_paths = get_ro_paths_test(); tm.add_test_group(&cl); tm.add_test_group(&cc); @@ -91,6 +93,7 @@ fn main() -> Result<()> { tm.add_test_group(&cgroup_v1_network); tm.add_test_group(&cgroup_v1_blkio); tm.add_test_group(&seccomp_notify); + tm.add_test_group(&ro_paths); tm.add_cleanup(Box::new(cgroups::cleanup_v1)); tm.add_cleanup(Box::new(cgroups::cleanup_v2)); diff --git a/crates/integration_test/src/tests/mod.rs b/crates/integration_test/src/tests/mod.rs index f1d00d6d5..40ec8cfb6 100644 --- a/crates/integration_test/src/tests/mod.rs +++ b/crates/integration_test/src/tests/mod.rs @@ -2,5 +2,6 @@ pub mod cgroups; pub mod lifecycle; pub mod linux_ns_itype; pub mod pidfile; +pub mod readonly_paths; pub mod seccomp_notify; pub mod tlb; diff --git a/crates/integration_test/src/tests/readonly_paths/mod.rs b/crates/integration_test/src/tests/readonly_paths/mod.rs new file mode 100644 index 000000000..ff2f5710a --- /dev/null +++ b/crates/integration_test/src/tests/readonly_paths/mod.rs @@ -0,0 +1,2 @@ +mod readonly_paths; +pub use readonly_paths::get_ro_paths_test; diff --git a/crates/integration_test/src/tests/readonly_paths/readonly_paths.rs b/crates/integration_test/src/tests/readonly_paths/readonly_paths.rs new file mode 100644 index 000000000..61d7116ff --- /dev/null +++ b/crates/integration_test/src/tests/readonly_paths/readonly_paths.rs @@ -0,0 +1,104 @@ +use crate::utils::test_inside_container; +use anyhow::bail; +use oci_spec::runtime::LinuxBuilder; +use oci_spec::runtime::{Spec, SpecBuilder}; +use std::path::PathBuf; +use test_framework::{Test, TestGroup, TestResult}; + +fn get_spec(readonly_paths: Vec) -> Spec { + SpecBuilder::default() + .linux( + LinuxBuilder::default() + .readonly_paths(readonly_paths) + .build() + .expect("could not build"), + ) + .build() + .unwrap() +} + +fn check_readonly_paths() -> TestResult { + // here we abbreviate 'readonly' as ro for variable names, + // purely for ease of writing + + let ro_dir = "readonly_dir"; + let ro_subdir = "readonly_subdir"; + let ro_file = "readonly_file"; + + // in the runtime-tools tests, they start these with a '/', + // but in that case, when joined with any path later, + // the '/' takes preference, and path is not actually joined + // eg : (test).join(t1) = test/t1 + // (test).join(.t1) = /t1 + // which is not what we want, so we leave them without '/' + let ro_dir_top = PathBuf::from(ro_dir); + let ro_file_top = PathBuf::from(ro_file); + + let ro_dir_sub = ro_dir_top.join(ro_subdir); + let ro_file_sub = ro_dir_top.join(ro_file); + let ro_file_sub_sub = ro_dir_sub.join(ro_file); + + let root = PathBuf::from("/"); + + let ro_paths = vec![ + root.join(&ro_dir_top).to_string_lossy().to_string(), + root.join(&ro_file_top).to_string_lossy().to_string(), + root.join(&ro_dir_sub).to_string_lossy().to_string(), + root.join(&ro_file_sub).to_string_lossy().to_string(), + root.join(&ro_file_sub_sub).to_string_lossy().to_string(), + ]; + + let spec = get_spec(ro_paths); + test_inside_container(spec, &|bundle| { + // use std::{fs, io}; + // let bundle_path = bundle.as_ref(); + // let test_dir = bundle_path.join(&ro_dir_sub); + + // match fs::create_dir_all(&test_dir) { + // io::Result::Ok(_) => { /*This is expected*/ } + // io::Result::Err(e) => { + // bail!(e) + // } + // } + + // match fs::File::create(test_dir.join("tmp")) { + // io::Result::Ok(_) => { /*This is expected*/ } + // io::Result::Err(e) => { + // bail!(e) + // } + // } + + // let test_sub_sub_file = bundle_path.join(&ro_file_sub_sub); + // match fs::File::create(&test_sub_sub_file) { + // io::Result::Ok(_) => { /*This is expected*/ } + // io::Result::Err(e) => { + // bail!(e) + // } + // } + + // let test_sub_file = bundle_path.join(&ro_file_sub); + // match fs::File::create(&test_sub_file) { + // io::Result::Ok(_) => { /*This is expected*/ } + // io::Result::Err(e) => { + // bail!(e) + // } + // } + + // let test_file = bundle_path.join(&ro_file); + // match fs::File::create(&test_file) { + // io::Result::Ok(_) => { /*This is expected*/ } + // io::Result::Err(e) => { + // bail!(e) + // } + // } + + Ok(()) + }) +} + +pub fn get_ro_paths_test<'a>() -> TestGroup<'a> { + let ro_paths = Test::new("readonly_paths", Box::new(check_readonly_paths)); + let mut tg = TestGroup::new("readonly_paths"); + tg.add(vec![Box::new(ro_paths)]); + tg +} diff --git a/crates/integration_test/src/utils/test_utils.rs b/crates/integration_test/src/utils/test_utils.rs index 0ee6c3335..0c16db456 100644 --- a/crates/integration_test/src/utils/test_utils.rs +++ b/crates/integration_test/src/utils/test_utils.rs @@ -165,7 +165,11 @@ pub fn test_inside_container( create_result, }; test_result!(check_container_created(&data)); - let start_result = test_result!(create_container(&id, &bundle).unwrap().wait_with_output()); + let start_result = match start_container(&id, &bundle).unwrap().wait_with_output() { + std::io::Result::Ok(c) => c, + std::io::Result::Err(e) => return TestResult::Failed(anyhow!(e)), + }; + let stderr = String::from_utf8_lossy(&start_result.stderr); if !stderr.is_empty() { return TestResult::Failed(anyhow!( @@ -173,11 +177,14 @@ pub fn test_inside_container( stderr )); } - // we sleep here once again for contingency - // and make sure that the container stops before we call the get_state - // TODO decide the time to sleep by trial-and-error - sleep(SLEEP_TIME); + let (out, err) = get_state(&id, &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, From 7d8518d87106cf5fd310caa0ff101b2c98125b38 Mon Sep 17 00:00:00 2001 From: Yashodhan Joshi Date: Fri, 31 Dec 2021 17:08:05 +0530 Subject: [PATCH 03/10] WIP Add readonly_paths tests --- Cargo.lock | 1 + crates/integration_test/Cargo.toml | 1 + .../tests/readonly_paths/readonly_paths.rs | 247 +++++++++++++++--- 3 files changed, 211 insertions(+), 38 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 23217e502..941d83b8a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -652,6 +652,7 @@ dependencies = [ "clap", "clap_derive", "flate2", + "libc", "libcgroups", "libcontainer", "log", diff --git a/crates/integration_test/Cargo.toml b/crates/integration_test/Cargo.toml index af47eed0a..04577bd29 100644 --- a/crates/integration_test/Cargo.toml +++ b/crates/integration_test/Cargo.toml @@ -23,6 +23,7 @@ tar = "0.4" test_framework = { path = "../test_framework" } uuid = "0.8" which = "4.2.2" +libc = "0.2.112" [dependencies.clap] version = "=3.0.0-beta.5" diff --git a/crates/integration_test/src/tests/readonly_paths/readonly_paths.rs b/crates/integration_test/src/tests/readonly_paths/readonly_paths.rs index 61d7116ff..e7044f83d 100644 --- a/crates/integration_test/src/tests/readonly_paths/readonly_paths.rs +++ b/crates/integration_test/src/tests/readonly_paths/readonly_paths.rs @@ -50,55 +50,226 @@ fn check_readonly_paths() -> TestResult { let spec = get_spec(ro_paths); test_inside_container(spec, &|bundle| { - // use std::{fs, io}; - // let bundle_path = bundle.as_ref(); - // let test_dir = bundle_path.join(&ro_dir_sub); - - // match fs::create_dir_all(&test_dir) { - // io::Result::Ok(_) => { /*This is expected*/ } - // io::Result::Err(e) => { - // bail!(e) - // } - // } + use std::{fs, io}; + let bundle_path = bundle.as_ref(); + let test_dir = bundle_path.join(&ro_dir_sub); - // match fs::File::create(test_dir.join("tmp")) { - // io::Result::Ok(_) => { /*This is expected*/ } - // io::Result::Err(e) => { - // bail!(e) - // } - // } + match fs::create_dir_all(&test_dir) { + io::Result::Ok(_) => { /*This is expected*/ } + io::Result::Err(e) => { + bail!(e) + } + } - // let test_sub_sub_file = bundle_path.join(&ro_file_sub_sub); - // match fs::File::create(&test_sub_sub_file) { - // io::Result::Ok(_) => { /*This is expected*/ } - // io::Result::Err(e) => { - // bail!(e) - // } - // } + match fs::File::create(test_dir.join("tmp")) { + io::Result::Ok(_) => { /*This is expected*/ } + io::Result::Err(e) => { + bail!(e) + } + } - // let test_sub_file = bundle_path.join(&ro_file_sub); - // match fs::File::create(&test_sub_file) { - // io::Result::Ok(_) => { /*This is expected*/ } - // io::Result::Err(e) => { - // bail!(e) - // } - // } + let test_sub_sub_file = bundle_path.join(&ro_file_sub_sub); + match fs::File::create(&test_sub_sub_file) { + io::Result::Ok(_) => { /*This is expected*/ } + io::Result::Err(e) => { + bail!(e) + } + } - // let test_file = bundle_path.join(&ro_file); - // match fs::File::create(&test_file) { - // io::Result::Ok(_) => { /*This is expected*/ } - // io::Result::Err(e) => { - // bail!(e) - // } - // } + let test_sub_file = bundle_path.join(&ro_file_sub); + match fs::File::create(&test_sub_file) { + io::Result::Ok(_) => { /*This is expected*/ } + io::Result::Err(e) => { + bail!(e) + } + } + + let test_file = bundle_path.join(&ro_file); + match fs::File::create(&test_file) { + io::Result::Ok(_) => { /*This is expected*/ } + io::Result::Err(e) => { + bail!(e) + } + } Ok(()) }) } +fn check_readonly_rel_path() -> TestResult { + let ro_rel_path = "readonly_relpath"; + let ro_paths = vec![ro_rel_path.to_string()]; + let spec = get_spec(ro_paths); + + test_inside_container(spec, &|bundle| { + use std::{fs, io}; + let bundle_path = bundle.as_ref(); + let test_file = bundle_path.join(ro_rel_path); + + match fs::metadata(&test_file) { + io::Result::Ok(md) => { + bail!( + "reading path {:?} should have given error, found {:?} instead", + test_file, + md + ) + } + io::Result::Err(e) => { + let err = e.kind(); + if let io::ErrorKind::NotFound = err { + return Ok(()); + } else { + bail!("expected not found error, got {:?}", err); + } + } + } + }) +} + +fn check_readonly_symlinks() -> TestResult { + let root = PathBuf::from("/"); + let ro_symlink = "readonly_symlink"; + let ro_paths = vec![root.join(&ro_symlink).to_string_lossy().to_string()]; + + let spec = get_spec(ro_paths); + + test_inside_container(spec, &|bundle| { + use std::{fs, io}; + let bundle_path = bundle.as_ref(); + let test_file = bundle_path.join(ro_symlink); + + match std::os::unix::fs::symlink("../readonly_symlink", &test_file) { + io::Result::Ok(_) => { /* This is expected */ } + io::Result::Err(e) => { + bail!("error in creating symlink, to {:?} {:?}", test_file, e); + } + } + let r_path = match fs::read_link(&test_file) { + io::Result::Ok(p) => p, + io::Result::Err(e) => { + bail!("error in reading symlink at {:?} : {:?}", test_file, e); + } + }; + + match fs::metadata(&r_path) { + io::Result::Ok(md) => { + bail!( + "reading symlink for {:?} should have given error, found {:?} instead", + test_file, + md + ) + } + io::Result::Err(e) => { + let err = e.kind(); + if let io::ErrorKind::NotFound = err { + return Ok(()); + } else { + bail!("expected not found error, got {:?}", err); + } + } + } + }) +} + +fn test_node(mode: u32) -> TestResult { + let root = PathBuf::from("/"); + let ro_device = "readonly_device"; + let ro_paths = vec![root.join(&ro_device).to_string_lossy().to_string()]; + + let spec = get_spec(ro_paths); + + test_inside_container(spec, &|bundle| { + use std::{fs, io}; + + let bundle_path = bundle.as_ref(); + let test_file = bundle_path.join(&ro_device); + // NOTE + // yes, I know using unsafe willy-nilly is a bad idea, + // especially given that OpenOptionsExt in std::os::unix::fs does provide a method + // to set mode in open options, like this : + + // use std::os::unix::fs::OpenOptionsExt; + // let mut opts = fs::OpenOptions::new(); + // opts.mode(mode); + // opts.create(true); + // if let io::Result::Err(e) = opts.open(&test_file) { + // bail!( + // "could not create device node at {:?} with mode {}, got error {:?}", + // test_file, + // mode ^ 0o666, + // e + // ); + // } + + // but that gives OsErr 22, invalid arguments. + // That is why we directly use mknod from lib here + + let _path = test_file.to_string_lossy().as_ptr() as *const i8; + let r = unsafe { libc::mknod(_path, mode, 0) }; + if r != 0 { + bail!( + "error in creating a device node at {:?} with mode {:?}, got return code {}", + test_file, + mode, + r + ); + } + + match fs::metadata(&test_file) { + io::Result::Ok(_) => Ok(()), + io::Result::Err(e) => { + bail!("error in creating device node, {:?}", e) + } + } + }) +} + +fn check_readonly_device_nodes() -> TestResult { + let modes = [ + libc::S_IFBLK | 0o666, + libc::S_IFCHR | 0o666, + libc::S_IFIFO | 0o666, + ]; + for mode in modes { + let res = test_node(mode); + if let TestResult::Failed(_) = res { + return res; + } + std::thread::sleep(std::time::Duration::from_millis(1000)); + } + TestResult::Passed +} + pub fn get_ro_paths_test<'a>() -> TestGroup<'a> { let ro_paths = Test::new("readonly_paths", Box::new(check_readonly_paths)); + let ro_rel_paths = Test::new("readonly_rel_paths", Box::new(check_readonly_rel_path)); + let ro_symlinks = Test::new("readonly_symlinks", Box::new(check_readonly_symlinks)); + // let ro_device_nodes = Test::new( + // "readonly_device_nodes", + // Box::new(check_readonly_device_nodes), + // ); + let ro_device_nodes_blk = Test::new( + "readonly_device_nodes_blk", + Box::new(|| test_node(libc::S_IFBLK | 0o666)), + ); + let ro_device_nodes_chr = Test::new( + "readonly_device_node_chr", + Box::new(|| test_node(libc::S_IFCHR | 0o666)), + ); + + let ro_device_nodes_fifo = Test::new( + "readonly_device_nodes_fifo", + Box::new(|| test_node(libc::S_IFIFO | 0o666)), + ); let mut tg = TestGroup::new("readonly_paths"); - tg.add(vec![Box::new(ro_paths)]); + tg.add(vec![ + Box::new(ro_paths), + Box::new(ro_rel_paths), + Box::new(ro_symlinks), + // Box::new(ro_device_nodes), + Box::new(ro_device_nodes_blk), + Box::new(ro_device_nodes_chr), + Box::new(ro_device_nodes_fifo), + ]); tg } From 5c6a83368949370dd41f87637470ed41c7b7a3f7 Mon Sep 17 00:00:00 2001 From: Yashodhan Joshi Date: Wed, 5 Jan 2022 11:40:11 +0530 Subject: [PATCH 04/10] Fix OpenOptions issue Remove unnecessary unsafe{} Remove libc direct dependency and use nix instead --- Cargo.lock | 1 - crates/integration_test/Cargo.toml | 1 - .../src/tests/readonly_paths/mod.rs | 4 +- ...donly_paths.rs => readonly_paths_tests.rs} | 74 ++++++------------- 4 files changed, 25 insertions(+), 55 deletions(-) rename crates/integration_test/src/tests/readonly_paths/{readonly_paths.rs => readonly_paths_tests.rs} (78%) diff --git a/Cargo.lock b/Cargo.lock index 941d83b8a..23217e502 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -652,7 +652,6 @@ dependencies = [ "clap", "clap_derive", "flate2", - "libc", "libcgroups", "libcontainer", "log", diff --git a/crates/integration_test/Cargo.toml b/crates/integration_test/Cargo.toml index 04577bd29..af47eed0a 100644 --- a/crates/integration_test/Cargo.toml +++ b/crates/integration_test/Cargo.toml @@ -23,7 +23,6 @@ tar = "0.4" test_framework = { path = "../test_framework" } uuid = "0.8" which = "4.2.2" -libc = "0.2.112" [dependencies.clap] version = "=3.0.0-beta.5" diff --git a/crates/integration_test/src/tests/readonly_paths/mod.rs b/crates/integration_test/src/tests/readonly_paths/mod.rs index ff2f5710a..5c059f5e9 100644 --- a/crates/integration_test/src/tests/readonly_paths/mod.rs +++ b/crates/integration_test/src/tests/readonly_paths/mod.rs @@ -1,2 +1,2 @@ -mod readonly_paths; -pub use readonly_paths::get_ro_paths_test; +mod readonly_paths_tests; +pub use readonly_paths_tests::get_ro_paths_test; diff --git a/crates/integration_test/src/tests/readonly_paths/readonly_paths.rs b/crates/integration_test/src/tests/readonly_paths/readonly_paths_tests.rs similarity index 78% rename from crates/integration_test/src/tests/readonly_paths/readonly_paths.rs rename to crates/integration_test/src/tests/readonly_paths/readonly_paths_tests.rs index e7044f83d..807610e4d 100644 --- a/crates/integration_test/src/tests/readonly_paths/readonly_paths.rs +++ b/crates/integration_test/src/tests/readonly_paths/readonly_paths_tests.rs @@ -1,5 +1,6 @@ use crate::utils::test_inside_container; use anyhow::bail; +use nix::sys::stat::SFlag; use oci_spec::runtime::LinuxBuilder; use oci_spec::runtime::{Spec, SpecBuilder}; use std::path::PathBuf; @@ -117,7 +118,7 @@ fn check_readonly_rel_path() -> TestResult { io::Result::Err(e) => { let err = e.kind(); if let io::ErrorKind::NotFound = err { - return Ok(()); + Ok(()) } else { bail!("expected not found error, got {:?}", err); } @@ -162,7 +163,7 @@ fn check_readonly_symlinks() -> TestResult { io::Result::Err(e) => { let err = e.kind(); if let io::ErrorKind::NotFound = err { - return Ok(()); + Ok(()) } else { bail!("expected not found error, got {:?}", err); } @@ -179,39 +180,26 @@ fn test_node(mode: u32) -> TestResult { let spec = get_spec(ro_paths); test_inside_container(spec, &|bundle| { + use std::os::unix::fs::OpenOptionsExt; use std::{fs, io}; let bundle_path = bundle.as_ref(); let test_file = bundle_path.join(&ro_device); - // NOTE - // yes, I know using unsafe willy-nilly is a bad idea, - // especially given that OpenOptionsExt in std::os::unix::fs does provide a method - // to set mode in open options, like this : - // use std::os::unix::fs::OpenOptionsExt; - // let mut opts = fs::OpenOptions::new(); - // opts.mode(mode); - // opts.create(true); - // if let io::Result::Err(e) = opts.open(&test_file) { - // bail!( - // "could not create device node at {:?} with mode {}, got error {:?}", - // test_file, - // mode ^ 0o666, - // e - // ); - // } - - // but that gives OsErr 22, invalid arguments. - // That is why we directly use mknod from lib here - - let _path = test_file.to_string_lossy().as_ptr() as *const i8; - let r = unsafe { libc::mknod(_path, mode, 0) }; - if r != 0 { + let mut opts = fs::OpenOptions::new(); + opts.mode(mode); + opts.create(true); + if let io::Result::Err(e) = fs::OpenOptions::new() + .mode(mode) + .create(true) + .write(true) + .open(&test_file) + { bail!( - "error in creating a device node at {:?} with mode {:?}, got return code {}", + "could not create device node at {:?} with mode {}, got error {:?}", test_file, - mode, - r + mode ^ 0o666, + e ); } @@ -226,9 +214,9 @@ fn test_node(mode: u32) -> TestResult { fn check_readonly_device_nodes() -> TestResult { let modes = [ - libc::S_IFBLK | 0o666, - libc::S_IFCHR | 0o666, - libc::S_IFIFO | 0o666, + SFlag::S_IFBLK.bits() | 0o666, + SFlag::S_IFCHR.bits() | 0o666, + SFlag::S_IFIFO.bits() | 0o666, ]; for mode in modes { let res = test_node(mode); @@ -244,32 +232,16 @@ pub fn get_ro_paths_test<'a>() -> TestGroup<'a> { let ro_paths = Test::new("readonly_paths", Box::new(check_readonly_paths)); let ro_rel_paths = Test::new("readonly_rel_paths", Box::new(check_readonly_rel_path)); let ro_symlinks = Test::new("readonly_symlinks", Box::new(check_readonly_symlinks)); - // let ro_device_nodes = Test::new( - // "readonly_device_nodes", - // Box::new(check_readonly_device_nodes), - // ); - let ro_device_nodes_blk = Test::new( - "readonly_device_nodes_blk", - Box::new(|| test_node(libc::S_IFBLK | 0o666)), - ); - let ro_device_nodes_chr = Test::new( - "readonly_device_node_chr", - Box::new(|| test_node(libc::S_IFCHR | 0o666)), - ); - - let ro_device_nodes_fifo = Test::new( - "readonly_device_nodes_fifo", - Box::new(|| test_node(libc::S_IFIFO | 0o666)), + let ro_device_nodes = Test::new( + "readonly_device_nodes", + Box::new(check_readonly_device_nodes), ); let mut tg = TestGroup::new("readonly_paths"); tg.add(vec![ Box::new(ro_paths), Box::new(ro_rel_paths), Box::new(ro_symlinks), - // Box::new(ro_device_nodes), - Box::new(ro_device_nodes_blk), - Box::new(ro_device_nodes_chr), - Box::new(ro_device_nodes_fifo), + Box::new(ro_device_nodes), ]); tg } From 78b7e942d77e0deb642a3c03b7b6933f72e48712 Mon Sep 17 00:00:00 2001 From: Yashodhan Joshi Date: Thu, 6 Jan 2022 16:30:19 +0530 Subject: [PATCH 05/10] Add runtime test crate and integrate it with integration tests --- .../integration_tests_validation.yaml | 4 +- .gitignore | 1 + Cargo.lock | 4 + build.sh | 11 + crates/integration_test/Cargo.lock | 757 ------------------ crates/integration_test/README.md | 26 +- crates/integration_test/run_tests.sh | 18 - crates/integration_test/src/main.rs | 33 +- .../readonly_paths/readonly_paths_tests.rs | 8 +- crates/integration_test/src/utils/mod.rs | 3 +- crates/integration_test/src/utils/support.rs | 9 + .../integration_test/src/utils/test_utils.rs | 79 +- crates/integration_test/tests.sh | 36 + crates/runtimetest/Cargo.toml | 8 + crates/runtimetest/README.md | 41 + crates/runtimetest/src/main.rs | 3 + docs/src/SUMMARY.md | 1 + docs/src/developer/integration_test.md | 13 + docs/src/developer/runtimetest.md | 41 + 19 files changed, 258 insertions(+), 838 deletions(-) delete mode 100644 crates/integration_test/Cargo.lock delete mode 100755 crates/integration_test/run_tests.sh create mode 100755 crates/integration_test/tests.sh create mode 100644 crates/runtimetest/Cargo.toml create mode 100644 crates/runtimetest/README.md create mode 100644 crates/runtimetest/src/main.rs create mode 100644 docs/src/developer/runtimetest.md diff --git a/.github/workflows/integration_tests_validation.yaml b/.github/workflows/integration_tests_validation.yaml index 02bdb1e10..687ebfa7a 100644 --- a/.github/workflows/integration_tests_validation.yaml +++ b/.github/workflows/integration_tests_validation.yaml @@ -36,6 +36,6 @@ jobs: - run: sudo apt-get -y update - run: sudo apt-get install -y pkg-config libsystemd-dev libdbus-glib-1-dev libelf-dev libseccomp-dev - name: Validate tests on runc - run: cd ./crates/integration_test && ./run_tests.sh runc + run: cd ./crates/integration_test && ./tests.sh run runc - name: Validate tests on youki - run: cd ./crates/integration_test && ./run_tests.sh ./youki + run: cd ./crates/integration_test && ./tests.sh run ./youki diff --git a/.gitignore b/.gitignore index fc143149e..468e4affe 100644 --- a/.gitignore +++ b/.gitignore @@ -13,6 +13,7 @@ tags.temp youki !youki/ youki_integration_test +runtimetest .vscode diff --git a/Cargo.lock b/Cargo.lock index 23217e502..8a4998b38 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1290,6 +1290,10 @@ version = "0.6.25" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f497285884f3fcff424ffc933e56d7cbca511def0c9831a7f9b5f6153e3cc89b" +[[package]] +name = "runtimetest" +version = "0.0.1" + [[package]] name = "rustc_version" version = "0.4.0" diff --git a/build.sh b/build.sh index e12f16a32..bd99d05f5 100755 --- a/build.sh +++ b/build.sh @@ -11,7 +11,18 @@ if [ "$1" == "--release" ]; then VERSION=release fi +# We have to build the three binaries seprately for the following reason : +# The runtimetest MUST be compiled from its own directory, if compiled from root, +# it will not work as intended to test the runtime from inside +# So we just compile all thre binaries separately. +# To see why runtime test must be compiled in its own directory, see its Readme or its docs + +cargo build --bin youki --verbose $TGT $1 +cargo build --bin integration_test --verbose $TGT $1 +cd crates/runtimetest cargo build --verbose $TGT $1 +cd ../../ cp target/$TARGET/$VERSION/youki . cp target/$TARGET/$VERSION/integration_test ./youki_integration_test +cp target/$TARGET/$VERSION/runtimetest ./runtimetest diff --git a/crates/integration_test/Cargo.lock b/crates/integration_test/Cargo.lock deleted file mode 100644 index 8c807c3d5..000000000 --- a/crates/integration_test/Cargo.lock +++ /dev/null @@ -1,757 +0,0 @@ -# This file is automatically @generated by Cargo. -# It is not intended for manual editing. -version = 3 - -[[package]] -name = "adler" -version = "1.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" - -[[package]] -name = "anyhow" -version = "1.0.44" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "61604a8f862e1d5c3229fdd78f8b02c68dcf73a4c4b05fd636d12240aaa242c1" - -[[package]] -name = "autocfg" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a" - -[[package]] -name = "bitflags" -version = "1.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693" - -[[package]] -name = "cc" -version = "1.0.71" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "79c2681d6594606957bbb8631c4b90a7fcaaa72cdb714743a437b156d6a7eedd" - -[[package]] -name = "byteorder" -version = "1.4.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" - -[[package]] -name = "cfg-if" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" - -[[package]] -name = "chrono" -version = "0.4.19" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "670ad68c9088c2a963aaa298cb369688cf3f9465ce5e2d4ca10e6e0098a1ce73" -dependencies = [ - "libc", - "num-integer", - "num-traits", - "time", - "winapi", -] - -[[package]] -name = "clap" -version = "3.0.0-beta.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4bd1061998a501ee7d4b6d449020df3266ca3124b941ec56cf2005c3779ca142" -dependencies = [ - "bitflags", - "clap_derive", - "indexmap", - "lazy_static", - "os_str_bytes", - "strsim", - "textwrap", - "unicode-width", - "vec_map", -] - -[[package]] -name = "clap_derive" -version = "3.0.0-beta.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "370f715b81112975b1b69db93e0b56ea4cd4e5002ac43b2da8474106a54096a1" -dependencies = [ - "heck", - "proc-macro-error", - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "crc32fast" -version = "1.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81156fece84ab6a9f2afdb109ce3ae577e42b1228441eded99bd77f627953b1a" -dependencies = [ - "cfg-if", -] - -[[package]] -name = "crossbeam" -version = "0.8.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ae5588f6b3c3cb05239e90bd110f257254aecd01e4635400391aeae07497845" -dependencies = [ - "cfg-if", - "crossbeam-channel", - "crossbeam-deque", - "crossbeam-epoch", - "crossbeam-queue", - "crossbeam-utils", -] - -[[package]] -name = "crossbeam-channel" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "06ed27e177f16d65f0f0c22a213e17c696ace5dd64b14258b52f9417ccb52db4" -dependencies = [ - "cfg-if", - "crossbeam-utils", -] - -[[package]] -name = "crossbeam-deque" -version = "0.8.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6455c0ca19f0d2fbf751b908d5c55c1f5cbc65e03c4225427254b46890bdde1e" -dependencies = [ - "cfg-if", - "crossbeam-epoch", - "crossbeam-utils", -] - -[[package]] -name = "crossbeam-epoch" -version = "0.9.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ec02e091aa634e2c3ada4a392989e7c3116673ef0ac5b72232439094d73b7fd" -dependencies = [ - "cfg-if", - "crossbeam-utils", - "lazy_static", - "memoffset", - "scopeguard", -] - -[[package]] -name = "crossbeam-queue" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b10ddc024425c88c2ad148c1b0fd53f4c6d38db9697c9f1588381212fa657c9" -dependencies = [ - "cfg-if", - "crossbeam-utils", -] - -[[package]] -name = "crossbeam-utils" -version = "0.8.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d82cfc11ce7f2c3faef78d8a684447b40d503d9681acebed6cb728d45940c4db" -dependencies = [ - "cfg-if", - "lazy_static", -] - -[[package]] -name = "darling" -version = "0.12.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f2c43f534ea4b0b049015d00269734195e6d3f0f6635cb692251aca6f9f8b3c" -dependencies = [ - "darling_core", - "darling_macro", -] - -[[package]] -name = "darling_core" -version = "0.12.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e91455b86830a1c21799d94524df0845183fa55bafd9aa137b01c7d1065fa36" -dependencies = [ - "fnv", - "ident_case", - "proc-macro2", - "quote", - "strsim", - "syn", -] - -[[package]] -name = "darling_macro" -version = "0.12.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "29b5acf0dea37a7f66f7b25d2c5e93fd46f8f6968b1a5d7a3e02e97768afc95a" -dependencies = [ - "darling_core", - "quote", - "syn", -] - -[[package]] -name = "derive_builder" -version = "0.10.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d13202debe11181040ae9063d739fa32cfcaaebe2275fe387703460ae2365b30" -dependencies = [ - "derive_builder_macro", -] - -[[package]] -name = "derive_builder_core" -version = "0.10.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "66e616858f6187ed828df7c64a6d71720d83767a7f19740b2d1b6fe6327b36e5" -dependencies = [ - "darling", - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "derive_builder_macro" -version = "0.10.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "58a94ace95092c5acb1e97a7e846b310cfbd499652f72297da7493f618a98d73" -dependencies = [ - "derive_builder_core", - "syn", -] - -[[package]] -name = "either" -version = "1.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e78d4f1cc4ae33bbfc157ed5d5a5ef3bc29227303d595861deb238fcec4e9457" - -[[package]] -name = "filetime" -version = "0.2.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "975ccf83d8d9d0d84682850a38c8169027be83368805971cc4f238c2b245bc98" -dependencies = [ - "cfg-if", - "libc", - "redox_syscall", - "winapi", -] - -[[package]] -name = "flate2" -version = "1.0.22" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e6988e897c1c9c485f43b47a529cef42fde0547f9d8d41a7062518f1d8fc53f" -dependencies = [ - "cfg-if", - "crc32fast", - "libc", - "miniz_oxide", -] - -[[package]] -name = "fnv" -version = "1.0.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" - -[[package]] -name = "getrandom" -version = "0.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7fcd999463524c52659517fe2cea98493cfe485d10565e7b0fb07dbba7ad2753" -dependencies = [ - "cfg-if", - "libc", - "wasi", -] - -[[package]] -name = "getset" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "24b328c01a4d71d2d8173daa93562a73ab0fe85616876f02500f53d82948c504" -dependencies = [ - "proc-macro-error", - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "hashbrown" -version = "0.11.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ab5ef0d4909ef3724cc8cce6ccc8572c5c817592e9285f5464f8e86f8bd3726e" - -[[package]] -name = "heck" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6d621efb26863f0e9924c6ac577e8275e5e6b77455db64ffa6c65c904e9e132c" -dependencies = [ - "unicode-segmentation", -] - -[[package]] -name = "hex" -version = "0.4.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" - -[[package]] -name = "ident_case" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" - -[[package]] -name = "indexmap" -version = "1.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bc633605454125dec4b66843673f01c7df2b89479b32e0ed634e43a91cff62a5" -dependencies = [ - "autocfg", - "hashbrown", -] - -[[package]] -name = "itoa" -version = "0.4.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b71991ff56294aa922b450139ee08b3bfc70982c6b2c7562771375cf73542dd4" - -[[package]] -name = "lazy_static" -version = "1.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" - -[[package]] -name = "libc" -version = "0.2.104" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b2f96d100e1cf1929e7719b7edb3b90ab5298072638fccd77be9ce942ecdfce" - -[[package]] -name = "memoffset" -version = "0.6.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "59accc507f1338036a0477ef61afdae33cde60840f4dfe481319ce3ad116ddf9" -dependencies = [ - "autocfg", -] - -[[package]] -name = "miniz_oxide" -version = "0.4.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a92518e98c078586bc6c934028adcca4c92a53d6a958196de835170a01d84e4b" -dependencies = [ - "adler", - "autocfg", -] - -[[package]] -name = "num-integer" -version = "0.1.44" -name = "nix" -version = "0.22.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3bb9a13fa32bc5aeb64150cd3f32d6cf4c748f8f8a417cce5d2eb976a8370ba" -dependencies = [ - "bitflags", - "cc", - "cfg-if", - "libc", - "memoffset", -] - -[[package]] -name = "oci-spec" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d2cc698a63b549a70bc047073d2949cce27cd1c7b0a4a862d08a8031bc2801db" -dependencies = [ - "autocfg", - "num-traits", -] - -[[package]] -name = "num-traits" -version = "0.2.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a64b1ec5cda2586e284722486d802acf1f7dbdc623e2bfc57e65ca1cd099290" -dependencies = [ - "autocfg", -] - -[[package]] -name = "oci-spec" -version = "0.5.2" -source = "git+https://github.com/containers/oci-spec-rs?rev=3d5132a18c305be59d58187201429d8f0243b513#3d5132a18c305be59d58187201429d8f0243b513" -dependencies = [ - "derive_builder", - "getset", - "serde", - "serde_json", - "thiserror", -] - -[[package]] -name = "once_cell" -version = "1.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "692fcb63b64b1758029e0a96ee63e049ce8c5948587f2f7208df04625e5f6b56" - -[[package]] -name = "os_str_bytes" -version = "2.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "afb2e1c3ee07430c2cf76151675e583e0f19985fa6efae47d6848a3e2c824f85" - -[[package]] -name = "ppv-lite86" -version = "0.2.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3ca011bd0129ff4ae15cd04c4eef202cadf6c51c21e47aba319b4e0501db741" - -[[package]] -name = "proc-macro-error" -version = "1.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" -dependencies = [ - "proc-macro-error-attr", - "proc-macro2", - "quote", - "syn", - "version_check", -] - -[[package]] -name = "proc-macro-error-attr" -version = "1.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" -dependencies = [ - "proc-macro2", - "quote", - "version_check", -] - -[[package]] -name = "proc-macro2" -version = "1.0.30" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "edc3358ebc67bc8b7fa0c007f945b0b18226f78437d61bec735a9eb96b61ee70" -dependencies = [ - "unicode-xid", -] - -[[package]] -name = "procfs" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f2e7eea7c1d7beccbd5acc1e37ac844afccf176525674aad26ece3de1fc7733" -dependencies = [ - "bitflags", - "byteorder", - "chrono", - "flate2", - "hex", - "lazy_static", - "libc", -] - -[[package]] -name = "quote" -version = "1.0.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38bc8cc6a5f2e3655e0899c1b848643b2562f853f114bfec7be120678e3ace05" -dependencies = [ - "proc-macro2", -] - -[[package]] -name = "rand" -version = "0.8.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2e7573632e6454cf6b99d7aac4ccca54be06da05aca2ef7423d22d27d4d4bcd8" -dependencies = [ - "libc", - "rand_chacha", - "rand_core", - "rand_hc", -] - -[[package]] -name = "rand_chacha" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" -dependencies = [ - "ppv-lite86", - "rand_core", -] - -[[package]] -name = "rand_core" -version = "0.6.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d34f1408f55294453790c48b2f1ebbb1c5b4b7563eb1f418bcfcfdbb06ebb4e7" -dependencies = [ - "getrandom", -] - -[[package]] -name = "rand_hc" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d51e9f596de227fda2ea6c84607f5558e196eeaf43c986b724ba4fb8fdf497e7" -dependencies = [ - "rand_core", -] - -[[package]] -name = "redox_syscall" -version = "0.2.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8383f39639269cde97d255a32bdb68c047337295414940c68bdd30c2e13203ff" -dependencies = [ - "bitflags", -] - -[[package]] -name = "ryu" -version = "1.0.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "71d301d4193d031abdd79ff7e3dd721168a9572ef3fe51a1517aba235bd8f86e" - -[[package]] -name = "scopeguard" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" - -[[package]] -name = "serde" -version = "1.0.130" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f12d06de37cf59146fbdecab66aa99f9fe4f78722e3607577a5375d66bd0c913" -dependencies = [ - "serde_derive", -] - -[[package]] -name = "serde_derive" -version = "1.0.130" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d7bc1a1ab1961464eae040d96713baa5a724a8152c1222492465b54322ec508b" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "serde_json" -version = "1.0.68" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0f690853975602e1bfe1ccbf50504d67174e3bcf340f23b5ea9992e0587a52d8" -dependencies = [ - "itoa", - "ryu", - "serde", -] - -[[package]] -name = "strsim" -version = "0.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" - -[[package]] -name = "syn" -version = "1.0.80" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d010a1623fbd906d51d650a9916aaefc05ffa0e4053ff7fe601167f3e715d194" -dependencies = [ - "proc-macro2", - "quote", - "unicode-xid", -] - -[[package]] -name = "tar" -version = "0.4.37" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d6f5515d3add52e0bbdcad7b83c388bb36ba7b754dda3b5f5bc2d38640cdba5c" -dependencies = [ - "filetime", - "libc", - "xattr", -] - -[[package]] -name = "test_framework" -version = "0.1.0" -dependencies = [ - "anyhow", - "crossbeam", -] - -[[package]] -name = "textwrap" -version = "0.12.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "203008d98caf094106cfaba70acfed15e18ed3ddb7d94e49baec153a2b462789" -dependencies = [ - "unicode-width", -] - -[[package]] -name = "thiserror" -version = "1.0.30" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "854babe52e4df1653706b98fcfc05843010039b406875930a70e4d9644e5c417" -dependencies = [ - "thiserror-impl", -] - -[[package]] -name = "thiserror-impl" -version = "1.0.30" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aa32fd3f627f367fe16f893e2597ae3c05020f8bba2666a4e6ea73d377e5714b" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "time" -version = "0.1.43" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ca8a50ef2360fbd1eeb0ecd46795a87a19024eb4b53c5dc916ca1fd95fe62438" -dependencies = [ - "libc", - "winapi", -] - -[[package]] -name = "unicode-segmentation" -version = "1.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8895849a949e7845e06bd6dc1aa51731a103c42707010a5b591c0038fb73385b" - -[[package]] -name = "unicode-width" -version = "0.1.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3ed742d4ea2bd1176e236172c8429aaf54486e7ac098db29ffe6529e0ce50973" - -[[package]] -name = "unicode-xid" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3" - -[[package]] -name = "uuid" -version = "0.8.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bc5cf98d8186244414c848017f0e2676b3fcb46807f6668a97dfe67359a3c4b7" - -[[package]] -name = "vec_map" -version = "0.8.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1bddf1187be692e79c5ffeab891132dfb0f236ed36a43c7ed39f1165ee20191" - -[[package]] -name = "version_check" -version = "0.9.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5fecdca9a5291cc2b8dcf7dc02453fee791a280f3743cb0905f8822ae463b3fe" - -[[package]] -name = "wasi" -version = "0.10.2+wasi-snapshot-preview1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fd6fbd9a79829dd1ad0cc20627bf1ed606756a7f77edff7b66b7064f9cb327c6" - -[[package]] -name = "which" -version = "4.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ea187a8ef279bc014ec368c27a920da2024d2a711109bfbe3440585d5cf27ad9" -dependencies = [ - "either", - "lazy_static", - "libc", -] - -[[package]] -name = "winapi" -version = "0.3.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" -dependencies = [ - "winapi-i686-pc-windows-gnu", - "winapi-x86_64-pc-windows-gnu", -] - -[[package]] -name = "winapi-i686-pc-windows-gnu" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" - -[[package]] -name = "winapi-x86_64-pc-windows-gnu" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" - -[[package]] -name = "xattr" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "244c3741f4240ef46274860397c7c74e50eb23624996930e484c16679633a54c" -dependencies = [ - "libc", -] - -[[package]] -name = "youki_integration_test" -version = "0.1.0" -dependencies = [ - "anyhow", - "clap", - "clap_derive", - "flate2", - "nix", - "oci-spec", - "once_cell", - "procfs", - "rand", - "serde", - "serde_json", - "tar", - "test_framework", - "uuid", - "which", -] diff --git a/crates/integration_test/README.md b/crates/integration_test/README.md index 87ec8f8d9..e875cb484 100644 --- a/crates/integration_test/README.md +++ b/crates/integration_test/README.md @@ -46,8 +46,9 @@ This framework also has some test utils, meant to help doing common operations i - kill_container: runs the runtime command with kill argument, with given id and with given bundle directory - delete_container : runs the runtime command with delete argument, with given id and with given bundle directory - get_state : runs the runtime command with state argument, with given id and with given bundle directory -- test_outside_container : this is meant to mimic [validateOutsideContainer](https://github.com/opencontainers/runtime-tools/blob/59cdde06764be8d761db120664020f0415f36045/validation/util/test.go#L263) function of original tests. -- check_container_created: this checks if the container was created succesfully. +- test_outside_container : this is meant to mimic [RuntimeOutsideValidate](https://github.com/opencontainers/runtime-tools/blob/59cdde06764be8d761db120664020f0415f36045/validation/util/test.go#L263) function of original tests. +- test_inside_container : this is meant to mimic [RuntimeInsideValidate](https://github.com/opencontainers/runtime-tools/blob/59cdde06764be8d761db120664020f0415f36045/validation/util/test.go#L180) function of original tests. +- check_container_created: this checks if the container was created successfully. - test_result!: this is a macro, that allows you to convert from a Result to a TestResult Note that even though all of the above functions are provided, most of the time the only required function is test_outside_container, as it does all the work of setting up the bundle, creating and running the container, getting the state of the container, killing the container and then deleting the container. @@ -73,25 +74,6 @@ Usually the test creation workflow will be something like : This lists some of the things that can be tricky, and can cause issues in running tests. **In case you encounter something, please update this list**. -- The create command should always have its stdout and stderror as null, and should always be waited by `wait`, and not `wait_with_output` on it after spawning. The reason is, runtime process forks itself to create the container, and then keeps running to start, get state etc for the container. Thus if we try to `wait_with_output` on it, it hangs until that process keeps running. Trying to kill tests by `Ctrl+C` will cause the system to stay in modified state (/tmp directories, cgroup directories etc). In case you do this and need to end tests, open a new terminal and send a kill signal to the runtime process, that way it will exit and tests will continue. +- The create command should be waited by `wait`, and not `wait_with_output` on it after spawning if you are simply creating the container. The reason is, runtime process forks itself to create the container, and then keeps running to start the container. Thus if we try to `wait_with_output` on it, without having called `start` on it, it hangs. Trying to kill tests by `Ctrl+C` will cause the system to stay in modified state (/tmp directories, cgroup directories etc). In case you do this and need to end tests, open a new terminal and send a kill signal to the runtime process, that way that youki process will exit and the tests will continue. - The kill and state commands take time. Thus whenever running these, call `wait` or `wait_with_output` on the spawned process to make sure you do not accidentally modify the directories that these use. One example is when running tests, as temp directory deletes itself when dropped, it can cause a race condition when state, or kill command is spawned and not waited. This will cause the directory in /tmp to be deleted first in the drop, and then to get created again due to kill / state command. _In the start of this implementation this problem caused several days to be spent on debugging where the directory in /tmp is getting created from_. - -## Test list - -Update when adding a new test. -Currently, there are the following test groups and tests: - -- lifecycle - - create - - start - - kill - - state - - delete -- create - - empty_id - - valid_id - - duplicate_id -- huge_tlb - - invalid_tlb - - valid_tlb diff --git a/crates/integration_test/run_tests.sh b/crates/integration_test/run_tests.sh deleted file mode 100755 index f73b1666c..000000000 --- a/crates/integration_test/run_tests.sh +++ /dev/null @@ -1,18 +0,0 @@ -#!/bin/bash -cd ../../ -./build.sh --release -cp ./youki ./crates/integration_test -cp ./youki_integration_test ./crates/integration_test -cd ./crates/integration_test -RUNTIME=./youki -if [[ -n "$1" ]]; then - RUNTIME="$1" -fi -logfile="./test_log.log" -touch $logfile -sudo ./youki_integration_test run -r $RUNTIME > $logfile -if [ 0 -ne $(grep "not ok" $logfile | wc -l ) ]; then - cat $logfile - exit 1 -fi -echo "Validation successful for runtime $RUNTIME" \ No newline at end of file diff --git a/crates/integration_test/src/main.rs b/crates/integration_test/src/main.rs index b82369cae..ff1643ee6 100644 --- a/crates/integration_test/src/main.rs +++ b/crates/integration_test/src/main.rs @@ -7,7 +7,7 @@ use crate::tests::pidfile::get_pidfile_test; use crate::tests::readonly_paths::get_ro_paths_test; use crate::tests::seccomp_notify::get_seccomp_notify_test; use crate::tests::tlb::get_tlb_test; -use crate::utils::support::set_runtime_path; +use crate::utils::support::{set_runtime_path, set_runtimetest_path}; use anyhow::{Context, Result}; use clap::Parser; use integration_test::logger; @@ -28,15 +28,20 @@ struct Opts { #[derive(Parser, Debug)] enum SubCommand { + /// run the integration tests Run(Run), + /// list available integration tests List, } #[derive(Parser, Debug)] struct Run { /// Path for the container runtime to be tested - #[clap(short, long)] + #[clap(long)] runtime: PathBuf, + /// Path for the runtimetest binary, which will be used to run tests inside the container + #[clap(long)] + runtimetest: PathBuf, /// Selected tests to be run, format should be /// space separated groups, eg /// -t group1::test1,test3 group2 group3::test5 @@ -107,19 +112,27 @@ fn main() -> Result<()> { Ok(()) } -fn run(opts: &Run, test_manager: &TestManager) -> Result<()> { - match std::fs::canonicalize(&opts.runtime) { - // runtime path is relative or resolved correctly - Ok(path) => set_runtime_path(&path), - // runtime path is name of program which probably exists in $PATH - Err(_) => match which::which(&opts.runtime) { - Ok(path) => set_runtime_path(&path), +fn get_abs_path(rel_path: &PathBuf) -> PathBuf { + match std::fs::canonicalize(rel_path) { + // path is relative or resolved correctly + Ok(path) => path, + // path is name of program which probably exists in $PATH + Err(_) => match which::which(rel_path) { + Ok(path) => path, Err(e) => { - eprintln!("Error in finding runtime : {}\nexiting.", e); + eprintln!("Error in finding path {:?} : {}\nexiting.", rel_path, e); std::process::exit(66); } }, } +} + +fn run(opts: &Run, test_manager: &TestManager) -> Result<()> { + let runtime_path = get_abs_path(&opts.runtime); + set_runtime_path(&runtime_path); + + let runtimetest_path = get_abs_path(&opts.runtimetest); + set_runtimetest_path(&runtimetest_path); if let Some(tests) = &opts.tests { let tests_to_run = parse_tests(tests); diff --git a/crates/integration_test/src/tests/readonly_paths/readonly_paths_tests.rs b/crates/integration_test/src/tests/readonly_paths/readonly_paths_tests.rs index 807610e4d..32fde6d93 100644 --- a/crates/integration_test/src/tests/readonly_paths/readonly_paths_tests.rs +++ b/crates/integration_test/src/tests/readonly_paths/readonly_paths_tests.rs @@ -2,7 +2,7 @@ use crate::utils::test_inside_container; use anyhow::bail; use nix::sys::stat::SFlag; use oci_spec::runtime::LinuxBuilder; -use oci_spec::runtime::{Spec, SpecBuilder}; +use oci_spec::runtime::{ProcessBuilder, Spec, SpecBuilder}; use std::path::PathBuf; use test_framework::{Test, TestGroup, TestResult}; @@ -14,6 +14,12 @@ fn get_spec(readonly_paths: Vec) -> Spec { .build() .expect("could not build"), ) + .process( + ProcessBuilder::default() + .args(vec!["runtimetest".to_string()]) + .build() + .unwrap(), + ) .build() .unwrap() } diff --git a/crates/integration_test/src/utils/mod.rs b/crates/integration_test/src/utils/mod.rs index 69e473c51..68ae575af 100644 --- a/crates/integration_test/src/utils/mod.rs +++ b/crates/integration_test/src/utils/mod.rs @@ -2,7 +2,8 @@ pub mod support; pub mod temp_dir; pub mod test_utils; pub use support::{ - generate_uuid, get_project_path, get_runtime_path, prepare_bundle, set_config, set_runtime_path, + generate_uuid, get_project_path, get_runtime_path, get_runtimetest_path, prepare_bundle, + set_config, set_runtime_path, }; pub use temp_dir::{create_temp_dir, TempDir}; pub use test_utils::{ diff --git a/crates/integration_test/src/utils/support.rs b/crates/integration_test/src/utils/support.rs index 0dafdcd85..c89012605 100644 --- a/crates/integration_test/src/utils/support.rs +++ b/crates/integration_test/src/utils/support.rs @@ -11,6 +11,7 @@ use tar::Archive; use uuid::Uuid; static RUNTIME_PATH: OnceCell = OnceCell::new(); +static RUNTIMETEST_PATH: OnceCell = OnceCell::new(); pub fn set_runtime_path(path: &Path) { RUNTIME_PATH.set(path.to_owned()).unwrap(); @@ -20,6 +21,14 @@ pub fn get_runtime_path() -> &'static PathBuf { RUNTIME_PATH.get().expect("Runtime path is not set") } +pub fn set_runtimetest_path(path: &Path) { + RUNTIMETEST_PATH.set(path.to_owned()).unwrap(); +} + +pub fn get_runtimetest_path() -> &'static PathBuf { + RUNTIMETEST_PATH.get().expect("Runtimetest path is not set") +} + #[allow(dead_code)] pub fn get_project_path() -> PathBuf { let current_dir_path_result = env::current_dir(); diff --git a/crates/integration_test/src/utils/test_utils.rs b/crates/integration_test/src/utils/test_utils.rs index 0c16db456..4accf9fb7 100644 --- a/crates/integration_test/src/utils/test_utils.rs +++ b/crates/integration_test/src/utils/test_utils.rs @@ -1,7 +1,7 @@ use super::{generate_uuid, prepare_bundle, set_config}; ///! Contains utility functions for testing ///! Similar to https://github.com/opencontainers/runtime-tools/blob/master/validation/util/test.go -use super::{get_runtime_path, TempDir}; +use super::{get_runtime_path, get_runtimetest_path, TempDir}; use anyhow::{anyhow, bail, Context, Result}; use oci_spec::runtime::Spec; use serde::{Deserialize, Serialize}; @@ -45,9 +45,13 @@ pub struct ContainerData { /// Starts the runtime with given directory as root directory pub fn create_container>(id: &Uuid, dir: P) -> Result { let res = Command::new(get_runtime_path()) - .stdin(Stdio::null()) - .stdout(Stdio::null()) - .stderr(Stdio::null()) + // set stdio so that we can get o/p of runtimetest + // in test_inside_container function + .stdout(Stdio::piped()) + .stderr(Stdio::piped()) + // set log level to error only, otherwise + // we get warnings in stderr + .env("YOUKI_LOG_LEVEL", "error") .arg("--root") .arg(dir.as_ref().join("runtime")) .arg("create") @@ -105,8 +109,6 @@ pub fn get_state>(id: &Uuid, dir: P) -> Result<(String, String)> pub fn start_container>(id: &Uuid, dir: P) -> Result { let res = Command::new(get_runtime_path()) - .stdout(Stdio::piped()) - .stderr(Stdio::piped()) .arg("--root") .arg(dir.as_ref().join("runtime")) .arg("start") @@ -144,36 +146,59 @@ pub fn test_outside_container( // mostly needs a name that better expresses what this actually does pub fn test_inside_container( spec: Spec, - execute_test: &dyn Fn(&TempDir) -> Result<()>, + setup_for_test: &dyn Fn(&TempDir) -> Result<()>, ) -> TestResult { let id = generate_uuid(); let bundle = prepare_bundle(&id).unwrap(); - test_result!(execute_test(&bundle)); + // This will do the required setup for the test + test_result!(setup_for_test(&bundle)); set_config(&bundle, &spec).unwrap(); - let create_result = create_container(&id, &bundle).unwrap().wait(); - let (out, err) = get_state(&id, &bundle).unwrap(); - let state: Option = match serde_json::from_str(&out) { - Ok(v) => Some(v), - Err(_) => None, - }; - let data = ContainerData { - id: id.to_string(), - state, - state_err: err, - create_result, - }; - test_result!(check_container_created(&data)); - let start_result = match start_container(&id, &bundle).unwrap().wait_with_output() { - std::io::Result::Ok(c) => c, - std::io::Result::Err(e) => return TestResult::Failed(anyhow!(e)), + // 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(&id, &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(2000)); + match start_container(&id, &bundle).unwrap().wait_with_output() { + Ok(c) => c, + Err(e) => return TestResult::Failed(anyhow!("container start failed : {:?}", e)), }; - let stderr = String::from_utf8_lossy(&start_result.stderr); + let create_output = create_process + .wait_with_output() + .context("getting output after starting the container failed") + .unwrap(); + + let stderr = String::from_utf8_lossy(&create_output.stderr); if !stderr.is_empty() { return TestResult::Failed(anyhow!( - "container start stderr was not empty : found {}", + "container stderr was not empty, found : {}", stderr )); } @@ -203,7 +228,7 @@ pub fn check_container_created(data: &ContainerData) -> Result<()> { Ok(exit_status) => { if !exit_status.success() { bail!( - "container creation was not successfull. Exit code was {:?}", + "container creation was not successful. Exit code was {:?}", exit_status.code() ) } diff --git a/crates/integration_test/tests.sh b/crates/integration_test/tests.sh new file mode 100755 index 000000000..a78a9758a --- /dev/null +++ b/crates/integration_test/tests.sh @@ -0,0 +1,36 @@ +#!/bin/bash +cd ../../ +./build.sh --release +cp ./youki ./crates/integration_test/youki +cp ./youki_integration_test ./crates/integration_test/youki_integration_test +cp ./runtimetest ./crates/integration_test/runtimetest +cd ./crates/integration_test + +RUNTIME=./youki + +# syntax is +# test.sh build +# test.sh run +# test.sh run runtime-name + +if [[ "$1" = "build" ]]; then + exit 0 +fi + +# if second argument is non-empty, consider it as runtime name +# else the consider first argument as runtime name +if [[ -n "$2" ]]; then + RUNTIME="$2" +elif [[-n "$1" ]] + RUNTIME="$1" +fi + + +logfile="./test_log.log" +touch $logfile +sudo ./youki_integration_test run --runtime $RUNTIME --runtimetest ./runtimetest > $logfile +if [ 0 -ne $(grep "not ok" $logfile | wc -l ) ]; then + cat $logfile + exit 1 +fi +echo "Validation successful for runtime $RUNTIME" \ No newline at end of file diff --git a/crates/runtimetest/Cargo.toml b/crates/runtimetest/Cargo.toml new file mode 100644 index 000000000..ea84166c4 --- /dev/null +++ b/crates/runtimetest/Cargo.toml @@ -0,0 +1,8 @@ +[package] +name = "runtimetest" +version = "0.0.1" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] diff --git a/crates/runtimetest/README.md b/crates/runtimetest/README.md new file mode 100644 index 000000000..ac010dec2 --- /dev/null +++ b/crates/runtimetest/README.md @@ -0,0 +1,41 @@ +# Runtime test + +This is the binary which runs the tests inside the container process, and checks that constraints and restrictions are upheld from inside the container. + +This is primarily used from the `test_inside_container` function related tests in the integration tests. + +## Conventions + +The main function will call the different tests functions, one by one to check that all required guarantees hold. This might be parallelized in future, but initially the tests are run serially. + +The path of config spec will always be /spec.json , and this is fixed so that no additional env or cmd arg is required, and we don't need to depend on clap or manual parsing for that. + +Make sure to consider failure cases, and try not to panic from any functions. If any error occur, or if some test fails, then it should write the error to the stderr, and return. The integration test will check stderr to be empty as an indication of all tests passing, and in case stderr is not empty, it will consider some test to be failing, and show the error as the contents of stderr. Thus make sure to include enough information in stderr message from failing tests to understand what failed in which test. +There is currently no convention of explicit indication of tests passing, the passing test may write `OK` or something similar to stdout, but as of now, the stdout will be completely ignored by integration test. + +## Special Notes + +This package must be compiled as a statically linked binary, as otherwise the rust compile will make it dynamically link to /lib64/ld-linux-x86-64.so , which is not available inside the container, and thus making the binary not usable inside the container process. + +**Note** that the dynamically linked binary does not give a `segmentation fault` or similar error when tried to run inside the container, but instead gives `no such file or directory found` or `executable not found` error, even though the executable exists in the container. This made this tricky to debug correctly when originally developing, so if you decide on chaing the compilation or configuration of this , please make absolutely sure that the changes work and do not accidentally break something. + +**Another Note** is that `cargo build` must be run from inside this directory only to make the binary be a statically linked binary. This is due to the fact that to make it statically linked, appropriate rustflags must be set in the .cargo/config.toml . But in case of a workspace, as like this, when run `cargo build` from project root, it ignores crate specific config, and only checks the project root .cargo/config , which currently does not allow setting rustflags for specific crate, which means either all binaries must be statically linked, or none can be. Thus currently the only way is to run the build command inside this directory. + +you can use + +```bash +readelf -l path/to/binary | grep "program interpreter" # should give empty output +file path/to/binary # should specify statically linked in output +``` + +to find out if the binary is dynamically or statically linked. + +Reading the Readme of integration tests can be helpful to understand how the integration tests and the runtime tests interoperate with one another. + +see + +https://stackoverflow.com/questions/31770604/how-to-generate-statically-linked-executables +https://superuser.com/questions/248512/why-do-i-get-command-not-found-when-the-binary-file-exists +https://doc.rust-lang.org/cargo/reference/config.html + +for more info diff --git a/crates/runtimetest/src/main.rs b/crates/runtimetest/src/main.rs new file mode 100644 index 000000000..2103ca8aa --- /dev/null +++ b/crates/runtimetest/src/main.rs @@ -0,0 +1,3 @@ +fn main() { + println!("This is where the internal tests will go later..."); +} diff --git a/docs/src/SUMMARY.md b/docs/src/SUMMARY.md index 10de2053d..c910a8dbe 100644 --- a/docs/src/SUMMARY.md +++ b/docs/src/SUMMARY.md @@ -28,3 +28,4 @@ - [youki](./developer/youki.md) - [test_framework](./developer/test_framework.md) - [integration_test](./developer/integration_test.md) + - [runtimetest](./developer/runtimetest.md) diff --git a/docs/src/developer/integration_test.md b/docs/src/developer/integration_test.md index dc5a4c4de..94999c3f4 100644 --- a/docs/src/developer/integration_test.md +++ b/docs/src/developer/integration_test.md @@ -7,3 +7,16 @@ Other than that, those tests also showed some issues while running on some local Thus we decided to port the tests to Rust, and validate them, so that we have a set of unit tests as well of integration tests to validate the working of runtime. These tests are still under development, and you can check the [tracking issue](https://github.com/containers/youki/issues/361) for more details. More details on working of these tests can be found at [https://github.com/containers/youki/tree/main/crates/integration_test](https://github.com/containers/youki/tree/main/crates/integration_test). As these tests are under development, these are validated on a standard runtime such as runc in the GitHub CI, so validate the tests themselves. + +## Notes + +### About the create container function + +The test_utils provides a create_container function which can be used to run the `youki create` command. It returns the child process struct, which can be either `wait()` or `wait_with_output()` to wait for it finishing. Unless you know what are you doing, it is recommended to call `wait()` on it, as otherwise the process will hang. As explained in the [youki docs](../youki.md) , the `youki create` process, after starting forks, and the forked process keeps waiting for another youki process to send it the `start` signal , and after receiving it, that forked process execs the container program. If you are simply trying to create a container, such as in case of `test_outside_runtime` then calling `wait_with_output()` will cause it to hand. If you are actually going to start a container, and need output from the container process, then you must keep the `Child struct` returned by `create` function and call `wait_with_output()` on it **AFTER** you have called the start command on that container, which will give you the `stdout` and `stderr` of the process running inside the container. + +To understand how this works, take a look at [handling stdio](https://github.com/opencontainers/runc/blob/master/docs/terminals.md) of the runc, specially the [detached pass-through mode](https://github.com/opencontainers/runc/blob/master/docs/terminals.md#detached-pass-through) section. As explained in it, we setup the stdio for the original youki process in `youki create` by setting the stdio to `Stdio::piped()` in the `create` function. then we set the `terminal` option to `false` (which is the default anyways) in the spec, which makes it run in the pass-through mode. Then when the create process is done its work, and its forked process is waiting for the start signal, it uses the same stdio pipes. Thus calling `wait_with_output()` without starting will keep it hanged up, and after calling start, stdio of the program to be run inside the container can be obtained from the `youki create`'s process. + +### How test inside container works + +We use test_inside_container for making sure that the restrictions and constraints are uphold from inside the container process. +For that, first whichever integration test needs to use it, must define the runtimetest as the container process in the spec, and then use `test_inside_container` function. It requires a function which will do the necessary setup for the tests that are to be run inside. Then the counterpart for the test should be added to the `runtimetest` crate, which will run inside the container and and if there is any error, print it to the `stderr`. The `test_inside_container` function will wait for the tests to be over and then check the `stderr` to be empty. If it is not, the the test is assumed to fail. diff --git a/docs/src/developer/runtimetest.md b/docs/src/developer/runtimetest.md new file mode 100644 index 000000000..d5fc89af5 --- /dev/null +++ b/docs/src/developer/runtimetest.md @@ -0,0 +1,41 @@ +# Runtime Test + +This crate provides a binary which is used by integration tests to verify that the restrictions and constraints applied to the container are upheld by the container process, from inside the container process. This runs the tests one-by-one, and the failing test prints the error to the stderr. + +## Notes + +This binary must be compiled with the option of static linking to crt0 given to the rustc. If compiled without it, it will add a linking to /lib64/ld-linux-x86-64.so . The binary compiled this way cannot be run inside the container process, as they do not have access to /lib64/... Thus the runtime test must be statically linked to crt0. + +Also this option can be given through .cargo/config.toml rustflags option, but this works only if the cargo build is invoked within the runtimetest directory. If invoked from the project root, the .cargo/config in the project root will take preference and the rustflags will be ignored. + +To see if a binary can be run inside the container process, run + +```console +readelf -l path/to/binary |grep "program interpreter" +``` + +`[Requesting program interpreter: /lib64/ld-linux-x86-64.so.2]` means that the binary is not statically linked, and cannot be run inside the container process. If the above command gives no output, that means it does not require any program interpreter and can be run inside the container. + +Another way is to run + +```console +file path/to/binary +``` + +```console +./youki: ELF 64-bit LSB pie executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, BuildID[sha1]=...., for GNU/Linux 3.2.0, with debug_info, not stripped` +``` + +This output indicates that the binary is dynamically linked, thus cannot be run inside the container process + +```console +./runtimetest: ELF 64-bit LSB executable, x86-64, version 1 (GNU/Linux), statically linked, BuildID[sha1]=...., for GNU/Linux 3.2.0, with debug_info, not stripped +``` + +This output indicates that the binary is statically linked, and can be run inside the container process + +Some links to help : + +- [how to generate static executable](https://stackoverflow.com/questions/31770604/how-to-generate-statically-linked-executables) +- [understanding the error which dynamically linked library gives](https://superuser.com/questions/248512/why-do-i-get-command-not-found-when-the-binary-file-exists) +- [Rust cargo config for rustflags](https://doc.rust-lang.org/cargo/reference/config.html) From 815c5c39225286cf7f4e2626e6118bdfd65e4ae8 Mon Sep 17 00:00:00 2001 From: Yashodhan Joshi Date: Thu, 6 Jan 2022 20:51:23 +0530 Subject: [PATCH 06/10] WIP add readonly paths to runtimetest --- Cargo.lock | 4 ++++ .../readonly_paths/readonly_paths_tests.rs | 13 ++++------- .../integration_test/src/utils/test_utils.rs | 9 +++++--- crates/runtimetest/Cargo.toml | 2 ++ crates/runtimetest/README.md | 2 +- crates/runtimetest/src/main.rs | 22 ++++++++++++++++++- 6 files changed, 38 insertions(+), 14 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 8a4998b38..d3ece6bf5 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1293,6 +1293,10 @@ checksum = "f497285884f3fcff424ffc933e56d7cbca511def0c9831a7f9b5f6153e3cc89b" [[package]] name = "runtimetest" version = "0.0.1" +dependencies = [ + "nix", + "oci-spec 0.5.2 (git+https://github.com/containers/oci-spec-rs?rev=54c5e386f01ab37c9305cc4a83404eb157e42440)", +] [[package]] name = "rustc_version" diff --git a/crates/integration_test/src/tests/readonly_paths/readonly_paths_tests.rs b/crates/integration_test/src/tests/readonly_paths/readonly_paths_tests.rs index 32fde6d93..a22cbbc33 100644 --- a/crates/integration_test/src/tests/readonly_paths/readonly_paths_tests.rs +++ b/crates/integration_test/src/tests/readonly_paths/readonly_paths_tests.rs @@ -56,9 +56,8 @@ fn check_readonly_paths() -> TestResult { ]; let spec = get_spec(ro_paths); - test_inside_container(spec, &|bundle| { + test_inside_container(spec, &|bundle_path| { use std::{fs, io}; - let bundle_path = bundle.as_ref(); let test_dir = bundle_path.join(&ro_dir_sub); match fs::create_dir_all(&test_dir) { @@ -108,9 +107,8 @@ 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| { + test_inside_container(spec, &|bundle_path| { use std::{fs, io}; - let bundle_path = bundle.as_ref(); let test_file = bundle_path.join(ro_rel_path); match fs::metadata(&test_file) { @@ -140,9 +138,8 @@ fn check_readonly_symlinks() -> TestResult { let spec = get_spec(ro_paths); - test_inside_container(spec, &|bundle| { + test_inside_container(spec, &|bundle_path| { use std::{fs, io}; - let bundle_path = bundle.as_ref(); let test_file = bundle_path.join(ro_symlink); match std::os::unix::fs::symlink("../readonly_symlink", &test_file) { @@ -185,11 +182,9 @@ fn test_node(mode: u32) -> TestResult { let spec = get_spec(ro_paths); - test_inside_container(spec, &|bundle| { + test_inside_container(spec, &|bundle_path| { use std::os::unix::fs::OpenOptionsExt; use std::{fs, io}; - - let bundle_path = bundle.as_ref(); let test_file = bundle_path.join(&ro_device); let mut opts = fs::OpenOptions::new(); diff --git a/crates/integration_test/src/utils/test_utils.rs b/crates/integration_test/src/utils/test_utils.rs index 4accf9fb7..d5a9bf12f 100644 --- a/crates/integration_test/src/utils/test_utils.rs +++ b/crates/integration_test/src/utils/test_utils.rs @@ -1,7 +1,7 @@ use super::{generate_uuid, prepare_bundle, set_config}; ///! Contains utility functions for testing ///! Similar to https://github.com/opencontainers/runtime-tools/blob/master/validation/util/test.go -use super::{get_runtime_path, get_runtimetest_path, TempDir}; +use super::{get_runtime_path, get_runtimetest_path}; use anyhow::{anyhow, bail, Context, Result}; use oci_spec::runtime::Spec; use serde::{Deserialize, Serialize}; @@ -146,13 +146,16 @@ pub fn test_outside_container( // mostly needs a name that better expresses what this actually does pub fn test_inside_container( spec: Spec, - setup_for_test: &dyn Fn(&TempDir) -> Result<()>, + setup_for_test: &dyn Fn(&Path) -> Result<()>, ) -> TestResult { let id = generate_uuid(); let bundle = prepare_bundle(&id).unwrap(); // This will do the required setup for the test - test_result!(setup_for_test(&bundle)); + test_result!(setup_for_test( + &bundle.as_ref().join("bundle").join("rootfs") + )); + // std::thread::sleep_ms(50000); set_config(&bundle, &spec).unwrap(); // as we have to run runtimetest inside the container, and is expects diff --git a/crates/runtimetest/Cargo.toml b/crates/runtimetest/Cargo.toml index ea84166c4..3d8fc4d3a 100644 --- a/crates/runtimetest/Cargo.toml +++ b/crates/runtimetest/Cargo.toml @@ -6,3 +6,5 @@ edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] +oci-spec = { git = "https://github.com/containers/oci-spec-rs", rev = "54c5e386f01ab37c9305cc4a83404eb157e42440" } +nix = "0.23.1" \ No newline at end of file diff --git a/crates/runtimetest/README.md b/crates/runtimetest/README.md index ac010dec2..05752ddb5 100644 --- a/crates/runtimetest/README.md +++ b/crates/runtimetest/README.md @@ -1,6 +1,6 @@ # Runtime test -This is the binary which runs the tests inside the container process, and checks that constraints and restrictions are upheld from inside the container. +This is the binary which runs the tests inside the container process, and checks that constraints and restrictions are upheld from inside the container. This is supposed to be rust version of [runtimetest command](https://github.com/opencontainers/runtime-tools/tree/master/cmd/runtimetest) from runtime tools. This is primarily used from the `test_inside_container` function related tests in the integration tests. diff --git a/crates/runtimetest/src/main.rs b/crates/runtimetest/src/main.rs index 2103ca8aa..0304bdc85 100644 --- a/crates/runtimetest/src/main.rs +++ b/crates/runtimetest/src/main.rs @@ -1,3 +1,23 @@ +mod tests; +mod utils; + +use oci_spec::runtime::Spec; +use std::path::PathBuf; + +const SPEC_PATH: &'static str = "/config.json"; + +fn get_spec() -> Spec { + let path = PathBuf::from(SPEC_PATH); + match Spec::load(path) { + Ok(spec) => spec, + Err(e) => { + eprintln!("Error in loading spec, {:?}", e); + std::process::exit(66); + } + } +} + fn main() { - println!("This is where the internal tests will go later..."); + let spec = get_spec(); + tests::validate_readonly_paths(&spec); } From ae9265ffcc7703b892375a19db717a43082afeab Mon Sep 17 00:00:00 2001 From: Yashodhan Joshi Date: Fri, 7 Jan 2022 11:40:17 +0530 Subject: [PATCH 07/10] Seprate runtimetest from youki workspace and make it its own crate --- .gitignore | 2 + Cargo.lock | 8 - build.sh | 18 +- crates/integration_test/tests.sh | 2 +- docs/src/developer/runtimetest.md | 9 +- runtimetest/.cargo/config.toml | 3 + runtimetest/Cargo.lock | 300 ++++++++++++++++++ .../runtimetest => runtimetest}/Cargo.toml | 6 +- {crates/runtimetest => runtimetest}/README.md | 2 - .../runtimetest => runtimetest}/src/main.rs | 0 runtimetest/src/tests.rs | 56 ++++ runtimetest/src/utils.rs | 134 ++++++++ 12 files changed, 517 insertions(+), 23 deletions(-) create mode 100644 runtimetest/.cargo/config.toml create mode 100644 runtimetest/Cargo.lock rename {crates/runtimetest => runtimetest}/Cargo.toml (62%) rename {crates/runtimetest => runtimetest}/README.md (82%) rename {crates/runtimetest => runtimetest}/src/main.rs (100%) create mode 100644 runtimetest/src/tests.rs create mode 100644 runtimetest/src/utils.rs diff --git a/.gitignore b/.gitignore index 468e4affe..9205dc8e4 100644 --- a/.gitignore +++ b/.gitignore @@ -13,7 +13,9 @@ tags.temp youki !youki/ youki_integration_test +runtimetest_tool runtimetest +!runtimetest/ .vscode diff --git a/Cargo.lock b/Cargo.lock index d3ece6bf5..23217e502 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1290,14 +1290,6 @@ version = "0.6.25" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f497285884f3fcff424ffc933e56d7cbca511def0c9831a7f9b5f6153e3cc89b" -[[package]] -name = "runtimetest" -version = "0.0.1" -dependencies = [ - "nix", - "oci-spec 0.5.2 (git+https://github.com/containers/oci-spec-rs?rev=54c5e386f01ab37c9305cc4a83404eb157e42440)", -] - [[package]] name = "rustc_version" version = "0.4.0" diff --git a/build.sh b/build.sh index bd99d05f5..336e694b0 100755 --- a/build.sh +++ b/build.sh @@ -11,18 +11,16 @@ if [ "$1" == "--release" ]; then VERSION=release fi -# We have to build the three binaries seprately for the following reason : -# The runtimetest MUST be compiled from its own directory, if compiled from root, -# it will not work as intended to test the runtime from inside -# So we just compile all thre binaries separately. -# To see why runtime test must be compiled in its own directory, see its Readme or its docs -cargo build --bin youki --verbose $TGT $1 -cargo build --bin integration_test --verbose $TGT $1 -cd crates/runtimetest +# Runtimetest must be compiled in its dorectory and is +# not a part of youki workspace. For the reasoning behind this, +# please check the docs and readme + +cargo build --verbose $TGT $1 +cd ./runtimetest cargo build --verbose $TGT $1 -cd ../../ +cd .. cp target/$TARGET/$VERSION/youki . cp target/$TARGET/$VERSION/integration_test ./youki_integration_test -cp target/$TARGET/$VERSION/runtimetest ./runtimetest +cp runtimetest/target/$TARGET/$VERSION/runtimetest ./runtimetest_tool diff --git a/crates/integration_test/tests.sh b/crates/integration_test/tests.sh index a78a9758a..d5eb52380 100755 --- a/crates/integration_test/tests.sh +++ b/crates/integration_test/tests.sh @@ -3,7 +3,7 @@ cd ../../ ./build.sh --release cp ./youki ./crates/integration_test/youki cp ./youki_integration_test ./crates/integration_test/youki_integration_test -cp ./runtimetest ./crates/integration_test/runtimetest +cp ./runtimetest_tool ./crates/integration_test/runtimetest cd ./crates/integration_test RUNTIME=./youki diff --git a/docs/src/developer/runtimetest.md b/docs/src/developer/runtimetest.md index d5fc89af5..57d3014de 100644 --- a/docs/src/developer/runtimetest.md +++ b/docs/src/developer/runtimetest.md @@ -6,7 +6,14 @@ This crate provides a binary which is used by integration tests to verify that t This binary must be compiled with the option of static linking to crt0 given to the rustc. If compiled without it, it will add a linking to /lib64/ld-linux-x86-64.so . The binary compiled this way cannot be run inside the container process, as they do not have access to /lib64/... Thus the runtime test must be statically linked to crt0. -Also this option can be given through .cargo/config.toml rustflags option, but this works only if the cargo build is invoked within the runtimetest directory. If invoked from the project root, the .cargo/config in the project root will take preference and the rustflags will be ignored. +While developing, originally this was added to the common workspace of all crates in youki. But then it was realized that this was quite inefficient because : + +- All packages except runtimetest will be compiled with dynamic linking +- Runtimetest will be compiled with static linking + +Now runtimetest needs at least `oci-spec` and `nix` package for its operations, which are also dependencies of other packages in the workspace. Thus both of these, and recursively their dependencies must be compiled twice, each time, once for dynamic linking and once for static. The took a long time in the compilation stage, especially when developing / adding new tests. Separating runtimetest from the workspace allows it to have a separate target/ directory, where it can store the statically compiled dependencies, and the workspace can have its target/ directory, where it can store its dynamically compiled dependencies. That way only the crates which have changes need to be compiled (runtimetest or integration test), and not their dependencies. + +In case in future this separation is not required, or some other configuration is chosen, make sure the multiple compilation issue does not arise, or the advantages of new method outweigh the time spent in double compilation. To see if a binary can be run inside the container process, run diff --git a/runtimetest/.cargo/config.toml b/runtimetest/.cargo/config.toml new file mode 100644 index 000000000..746c51827 --- /dev/null +++ b/runtimetest/.cargo/config.toml @@ -0,0 +1,3 @@ +[build] +target = "x86_64-unknown-linux-gnu" +rustflags = ["-C", "target-feature=+crt-static"] diff --git a/runtimetest/Cargo.lock b/runtimetest/Cargo.lock new file mode 100644 index 000000000..0c6ac10b4 --- /dev/null +++ b/runtimetest/Cargo.lock @@ -0,0 +1,300 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "autocfg" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a" + +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + +[[package]] +name = "cc" +version = "1.0.72" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22a9137b95ea06864e018375b72adfb7db6e6f68cfc8df5a04d00288050485ee" + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "darling" +version = "0.12.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f2c43f534ea4b0b049015d00269734195e6d3f0f6635cb692251aca6f9f8b3c" +dependencies = [ + "darling_core", + "darling_macro", +] + +[[package]] +name = "darling_core" +version = "0.12.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e91455b86830a1c21799d94524df0845183fa55bafd9aa137b01c7d1065fa36" +dependencies = [ + "fnv", + "ident_case", + "proc-macro2", + "quote", + "strsim", + "syn", +] + +[[package]] +name = "darling_macro" +version = "0.12.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29b5acf0dea37a7f66f7b25d2c5e93fd46f8f6968b1a5d7a3e02e97768afc95a" +dependencies = [ + "darling_core", + "quote", + "syn", +] + +[[package]] +name = "derive_builder" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d13202debe11181040ae9063d739fa32cfcaaebe2275fe387703460ae2365b30" +dependencies = [ + "derive_builder_macro", +] + +[[package]] +name = "derive_builder_core" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "66e616858f6187ed828df7c64a6d71720d83767a7f19740b2d1b6fe6327b36e5" +dependencies = [ + "darling", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "derive_builder_macro" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "58a94ace95092c5acb1e97a7e846b310cfbd499652f72297da7493f618a98d73" +dependencies = [ + "derive_builder_core", + "syn", +] + +[[package]] +name = "fnv" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" + +[[package]] +name = "getset" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e45727250e75cc04ff2846a66397da8ef2b3db8e40e0cef4df67950a07621eb9" +dependencies = [ + "proc-macro-error", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "ident_case" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" + +[[package]] +name = "itoa" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1aab8fc367588b89dcee83ab0fd66b72b50b72fa1904d7095045ace2b0c81c35" + +[[package]] +name = "libc" +version = "0.2.112" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b03d17f364a3a042d5e5d46b053bbbf82c92c9430c592dd4c064dc6ee997125" + +[[package]] +name = "memoffset" +version = "0.6.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5aa361d4faea93603064a027415f07bd8e1d5c88c9fbf68bf56a285428fd79ce" +dependencies = [ + "autocfg", +] + +[[package]] +name = "nix" +version = "0.23.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f866317acbd3a240710c63f065ffb1e4fd466259045ccb504130b7f668f35c6" +dependencies = [ + "bitflags", + "cc", + "cfg-if", + "libc", + "memoffset", +] + +[[package]] +name = "oci-spec" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8057bb0f33d7ecdf1f0f7cc74ea5cced7c6c694245e2a8d14700507c3bde32e3" +dependencies = [ + "derive_builder", + "getset", + "serde", + "serde_json", + "thiserror", +] + +[[package]] +name = "proc-macro-error" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" +dependencies = [ + "proc-macro-error-attr", + "proc-macro2", + "quote", + "syn", + "version_check", +] + +[[package]] +name = "proc-macro-error-attr" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" +dependencies = [ + "proc-macro2", + "quote", + "version_check", +] + +[[package]] +name = "proc-macro2" +version = "1.0.36" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c7342d5883fbccae1cc37a2353b09c87c9b0f3afd73f5fb9bba687a1f733b029" +dependencies = [ + "unicode-xid", +] + +[[package]] +name = "quote" +version = "1.0.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "47aa80447ce4daf1717500037052af176af5d38cc3e571d9ec1c7353fc10c87d" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "runtimetest" +version = "0.0.1" +dependencies = [ + "nix", + "oci-spec", +] + +[[package]] +name = "ryu" +version = "1.0.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73b4b750c782965c211b42f022f59af1fbceabdd026623714f104152f1ec149f" + +[[package]] +name = "serde" +version = "1.0.133" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97565067517b60e2d1ea8b268e59ce036de907ac523ad83a0475da04e818989a" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.133" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed201699328568d8d08208fdd080e3ff594e6c422e438b6705905da01005d537" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "serde_json" +version = "1.0.74" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee2bb9cd061c5865d345bb02ca49fcef1391741b672b54a0bf7b679badec3142" +dependencies = [ + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "strsim" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" + +[[package]] +name = "syn" +version = "1.0.85" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a684ac3dcd8913827e18cd09a68384ee66c1de24157e3c556c9ab16d85695fb7" +dependencies = [ + "proc-macro2", + "quote", + "unicode-xid", +] + +[[package]] +name = "thiserror" +version = "1.0.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "854babe52e4df1653706b98fcfc05843010039b406875930a70e4d9644e5c417" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa32fd3f627f367fe16f893e2597ae3c05020f8bba2666a4e6ea73d377e5714b" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "unicode-xid" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3" + +[[package]] +name = "version_check" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" diff --git a/crates/runtimetest/Cargo.toml b/runtimetest/Cargo.toml similarity index 62% rename from crates/runtimetest/Cargo.toml rename to runtimetest/Cargo.toml index 3d8fc4d3a..e70a999ad 100644 --- a/crates/runtimetest/Cargo.toml +++ b/runtimetest/Cargo.toml @@ -3,8 +3,12 @@ name = "runtimetest" version = "0.0.1" edition = "2021" +# This package is not part of any workspace, so we specify it so +[workspace] +members = [] + # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -oci-spec = { git = "https://github.com/containers/oci-spec-rs", rev = "54c5e386f01ab37c9305cc4a83404eb157e42440" } +oci-spec = "0.5.3" nix = "0.23.1" \ No newline at end of file diff --git a/crates/runtimetest/README.md b/runtimetest/README.md similarity index 82% rename from crates/runtimetest/README.md rename to runtimetest/README.md index 05752ddb5..45b66d549 100644 --- a/crates/runtimetest/README.md +++ b/runtimetest/README.md @@ -19,8 +19,6 @@ This package must be compiled as a statically linked binary, as otherwise the ru **Note** that the dynamically linked binary does not give a `segmentation fault` or similar error when tried to run inside the container, but instead gives `no such file or directory found` or `executable not found` error, even though the executable exists in the container. This made this tricky to debug correctly when originally developing, so if you decide on chaing the compilation or configuration of this , please make absolutely sure that the changes work and do not accidentally break something. -**Another Note** is that `cargo build` must be run from inside this directory only to make the binary be a statically linked binary. This is due to the fact that to make it statically linked, appropriate rustflags must be set in the .cargo/config.toml . But in case of a workspace, as like this, when run `cargo build` from project root, it ignores crate specific config, and only checks the project root .cargo/config , which currently does not allow setting rustflags for specific crate, which means either all binaries must be statically linked, or none can be. Thus currently the only way is to run the build command inside this directory. - you can use ```bash diff --git a/crates/runtimetest/src/main.rs b/runtimetest/src/main.rs similarity index 100% rename from crates/runtimetest/src/main.rs rename to runtimetest/src/main.rs diff --git a/runtimetest/src/tests.rs b/runtimetest/src/tests.rs new file mode 100644 index 000000000..ff740b9ff --- /dev/null +++ b/runtimetest/src/tests.rs @@ -0,0 +1,56 @@ +use crate::utils::{test_read_access, test_write_access, AccessibilityStatus}; +use oci_spec::runtime::Spec; + +pub fn validate_readonly_paths(spec: &Spec) { + let linux = spec.linux().as_ref().unwrap(); + let ro_paths = match linux.readonly_paths() { + Some(p) => p, + None => { + eprintln!("in readonly paths, expected some readonly paths to be set, found none"); + return; + } + }; + if ro_paths.is_empty() { + eprintln!("in readonly paths, expected some readonly paths to be set, found none"); + return; + } + + for path in ro_paths { + match test_read_access(path) { + std::io::Result::Err(e) => { + eprintln!( + "in readonly paths, error in testing read access for path {} : {:?}", + path, e + ); + return; + } + Ok(readability) => { + match readability { + AccessibilityStatus::Accessible => { /* This is expected */ } + AccessibilityStatus::Blocked => { + eprintln!("in readonly paths, path {} expected to be readable, found non readable",path); + return; + } + } + } + } + match test_write_access(path) { + std::io::Result::Err(e) => { + eprintln!( + "in readonly paths, error in testing write access for path {} : {:?}", + path, e + ); + return; + } + Ok(readability) => { + match readability { + AccessibilityStatus::Accessible => { + eprintln!("in readonly paths, path {} expected to not be writable, found writable",path); + return; + } + AccessibilityStatus::Blocked => { /* This is expected */ } + } + } + } + } +} diff --git a/runtimetest/src/utils.rs b/runtimetest/src/utils.rs new file mode 100644 index 000000000..428446db5 --- /dev/null +++ b/runtimetest/src/utils.rs @@ -0,0 +1,134 @@ +use std::path::PathBuf; + +use nix::sys::stat::stat; +use nix::sys::stat::SFlag; + +pub enum AccessibilityStatus { + Accessible, + Blocked, +} + +fn test_file_read_access(path: &str) -> Result { + match std::fs::OpenOptions::new() + .create(false) + .read(true) + .open(path) + { + Ok(_) => { + // we can directly return accessible, as if we are allowed to open with read access, + // we can read the file + Ok(AccessibilityStatus::Accessible) + } + Err(e) => { + if e.kind() == std::io::ErrorKind::PermissionDenied { + // we can get permission denied error if we try to read a + // file which we do not have read read access for, in + // which case that is not an error, but a valid accessibility status + Ok(AccessibilityStatus::Blocked) + } else { + Err(e) + } + } + } +} + +fn test_dir_read_access(path: &str) -> Result { + match std::fs::read_dir(path) { + Ok(_) => Ok(AccessibilityStatus::Accessible), + Err(e) => { + if e.kind() == std::io::ErrorKind::PermissionDenied { + Ok(AccessibilityStatus::Blocked) + } else { + Err(e) + } + } + } +} + +fn is_file_like(mode: u32) -> bool { + // for this please refer + // https://stackoverflow.com/questions/40163270/what-is-s-isreg-and-what-does-it-do + // https://linux.die.net/man/2/stat + mode & SFlag::S_IFREG.bits() != 0 + || mode & SFlag::S_IFBLK.bits() != 0 + || mode & SFlag::S_IFCHR.bits() != 0 +} + +fn is_dir(mode: u32) -> bool { + mode & SFlag::S_IFDIR.bits() != 0 +} + +pub fn test_read_access(path: &str) -> Result { + let fstat = stat(path)?; + let mode = fstat.st_mode; + if is_file_like(mode) { + // we have a file or a char/block device + return test_file_read_access(path); + } else if is_dir(mode) { + return test_dir_read_access(path); + } + + Err(std::io::Error::new( + std::io::ErrorKind::Other, + format!( + "cannot test read access for {:?}, has mode {:x}", + path, mode + ), + )) +} + +fn test_file_write_access(path: &str) -> Result { + match std::fs::OpenOptions::new().write(true).open(path) { + std::io::Result::Ok(_) => { + // we don't have to check if we can actually write or not, as + // if we are allowed to open file with write access, we can write to it + Ok(AccessibilityStatus::Accessible) + } + std::io::Result::Err(e) => { + if e.kind() == std::io::ErrorKind::PermissionDenied { + return Ok(AccessibilityStatus::Blocked); + } else { + return Err(e); + } + } + } +} + +fn test_dir_write_access(path: &str) -> Result { + match std::fs::OpenOptions::new() + .create(true) + .write(true) + .open(PathBuf::from(path).join("test.txt")) + { + std::io::Result::Ok(_) => Ok(AccessibilityStatus::Accessible), + std::io::Result::Err(e) => { + if e.kind() == std::io::ErrorKind::PermissionDenied { + // technically we can still get permission denied even if + // we have write access, but do not have execute access, but by default + // dirs are created with execute access so that should not be an issue + return Ok(AccessibilityStatus::Blocked); + } else { + return Err(e); + } + } + } +} + +pub fn test_write_access(path: &str) -> Result { + let fstat = stat(path)?; + let mode = fstat.st_mode; + if is_file_like(mode) { + // we have a file or a char/block device + return test_file_write_access(path); + } else if is_dir(mode) { + return test_dir_write_access(path); + } + + Err(std::io::Error::new( + std::io::ErrorKind::Other, + format!( + "cannot test write access for {:?}, has mode {:x}", + path, mode + ), + )) +} From 5553d5ce6b112a6036a0b2d532fab55327618ebc Mon Sep 17 00:00:00 2001 From: Yashodhan Joshi Date: Sat, 8 Jan 2022 15:01:26 +0530 Subject: [PATCH 08/10] Add readonly paths tests in integration tests and runtimetest --- crates/integration_test/src/main.rs | 4 +- .../readonly_paths/readonly_paths_tests.rs | 13 ++- .../integration_test/src/utils/test_utils.rs | 4 +- runtimetest/src/main.rs | 2 +- runtimetest/src/tests.rs | 51 ++++++----- runtimetest/src/utils.rs | 89 ++++--------------- 6 files changed, 61 insertions(+), 102 deletions(-) diff --git a/crates/integration_test/src/main.rs b/crates/integration_test/src/main.rs index ff1643ee6..07acbff6c 100644 --- a/crates/integration_test/src/main.rs +++ b/crates/integration_test/src/main.rs @@ -11,7 +11,7 @@ use crate::utils::support::{set_runtime_path, set_runtimetest_path}; use anyhow::{Context, Result}; use clap::Parser; use integration_test::logger; -use std::path::PathBuf; +use std::path::{Path, PathBuf}; use test_framework::TestManager; use tests::cgroups; @@ -112,7 +112,7 @@ fn main() -> Result<()> { Ok(()) } -fn get_abs_path(rel_path: &PathBuf) -> PathBuf { +fn get_abs_path(rel_path: &Path) -> PathBuf { match std::fs::canonicalize(rel_path) { // path is relative or resolved correctly Ok(path) => path, diff --git a/crates/integration_test/src/tests/readonly_paths/readonly_paths_tests.rs b/crates/integration_test/src/tests/readonly_paths/readonly_paths_tests.rs index a22cbbc33..84eb8a0cc 100644 --- a/crates/integration_test/src/tests/readonly_paths/readonly_paths_tests.rs +++ b/crates/integration_test/src/tests/readonly_paths/readonly_paths_tests.rs @@ -1,5 +1,5 @@ use crate::utils::test_inside_container; -use anyhow::bail; +use anyhow::{anyhow, bail}; use nix::sys::stat::SFlag; use oci_spec::runtime::LinuxBuilder; use oci_spec::runtime::{ProcessBuilder, Spec, SpecBuilder}; @@ -138,7 +138,7 @@ fn check_readonly_symlinks() -> TestResult { let spec = get_spec(ro_paths); - test_inside_container(spec, &|bundle_path| { + let res = test_inside_container(spec, &|bundle_path| { use std::{fs, io}; let test_file = bundle_path.join(ro_symlink); @@ -172,7 +172,14 @@ fn check_readonly_symlinks() -> TestResult { } } } - }) + }); + if let TestResult::Passed = res { + TestResult::Failed(anyhow!( + "expected error in container creation with invalid symlink, found no error" + )) + } else { + TestResult::Passed + } } fn test_node(mode: u32) -> TestResult { diff --git a/crates/integration_test/src/utils/test_utils.rs b/crates/integration_test/src/utils/test_utils.rs index d5a9bf12f..af3b3188a 100644 --- a/crates/integration_test/src/utils/test_utils.rs +++ b/crates/integration_test/src/utils/test_utils.rs @@ -109,6 +109,8 @@ pub fn get_state>(id: &Uuid, dir: P) -> Result<(String, String)> pub fn start_container>(id: &Uuid, dir: P) -> Result { let res = Command::new(get_runtime_path()) + .stderr(Stdio::piped()) + .stdout(Stdio::piped()) .arg("--root") .arg(dir.as_ref().join("runtime")) .arg("start") @@ -187,7 +189,7 @@ pub fn test_inside_container( // 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(2000)); + std::thread::sleep(std::time::Duration::from_millis(1000)); match start_container(&id, &bundle).unwrap().wait_with_output() { Ok(c) => c, Err(e) => return TestResult::Failed(anyhow!("container start failed : {:?}", e)), diff --git a/runtimetest/src/main.rs b/runtimetest/src/main.rs index 0304bdc85..8c4e751ab 100644 --- a/runtimetest/src/main.rs +++ b/runtimetest/src/main.rs @@ -4,7 +4,7 @@ mod utils; use oci_spec::runtime::Spec; use std::path::PathBuf; -const SPEC_PATH: &'static str = "/config.json"; +const SPEC_PATH: &str = "/config.json"; fn get_spec() -> Spec { let path = PathBuf::from(SPEC_PATH); diff --git a/runtimetest/src/tests.rs b/runtimetest/src/tests.rs index ff740b9ff..6e37f01b1 100644 --- a/runtimetest/src/tests.rs +++ b/runtimetest/src/tests.rs @@ -1,4 +1,5 @@ -use crate::utils::{test_read_access, test_write_access, AccessibilityStatus}; +use crate::utils::{test_read_access, test_write_access}; +use nix::errno::Errno; use oci_spec::runtime::Spec; pub fn validate_readonly_paths(spec: &Spec) { @@ -14,43 +15,47 @@ pub fn validate_readonly_paths(spec: &Spec) { eprintln!("in readonly paths, expected some readonly paths to be set, found none"); return; } + // TODO when https://github.com/rust-lang/rust/issues/86442 stabilizes, + // change manual matching of i32 to e.kind() and match statement for path in ro_paths { - match test_read_access(path) { - std::io::Result::Err(e) => { + if let std::io::Result::Err(e) = test_read_access(path) { + let errno = Errno::from_i32(e.raw_os_error().unwrap()); + // In the integration tests we test for both existing and non-existing readonly paths + // to be specified in the spec, so we allow ENOENT here + if errno == Errno::ENOENT { + /* This is expected */ + } else { eprintln!( "in readonly paths, error in testing read access for path {} : {:?}", path, e ); return; } - Ok(readability) => { - match readability { - AccessibilityStatus::Accessible => { /* This is expected */ } - AccessibilityStatus::Blocked => { - eprintln!("in readonly paths, path {} expected to be readable, found non readable",path); - return; - } - } - } + } else { + /* Expected */ } - match test_write_access(path) { - std::io::Result::Err(e) => { + + if let std::io::Result::Err(e) = test_write_access(path) { + let errno = Errno::from_i32(e.raw_os_error().unwrap()); + // In the integration tests we test for both existing and non-existing readonly paths + // being specified in the spec, so we allow ENOENT, and we expect EROFS as the paths + // should be read-only + if errno == Errno::ENOENT || errno == Errno::EROFS { + /* This is expected */ + } else { eprintln!( "in readonly paths, error in testing write access for path {} : {:?}", path, e ); return; } - Ok(readability) => { - match readability { - AccessibilityStatus::Accessible => { - eprintln!("in readonly paths, path {} expected to not be writable, found writable",path); - return; - } - AccessibilityStatus::Blocked => { /* This is expected */ } - } - } + } else { + eprintln!( + "in readonly paths, path {} expected to not be writable, found writable", + path + ); + return; } } } diff --git a/runtimetest/src/utils.rs b/runtimetest/src/utils.rs index 428446db5..b370be94b 100644 --- a/runtimetest/src/utils.rs +++ b/runtimetest/src/utils.rs @@ -3,62 +3,31 @@ use std::path::PathBuf; use nix::sys::stat::stat; use nix::sys::stat::SFlag; -pub enum AccessibilityStatus { - Accessible, - Blocked, -} - -fn test_file_read_access(path: &str) -> Result { - match std::fs::OpenOptions::new() +fn test_file_read_access(path: &str) -> Result<(), std::io::Error> { + let _ = std::fs::OpenOptions::new() .create(false) .read(true) - .open(path) - { - Ok(_) => { - // we can directly return accessible, as if we are allowed to open with read access, - // we can read the file - Ok(AccessibilityStatus::Accessible) - } - Err(e) => { - if e.kind() == std::io::ErrorKind::PermissionDenied { - // we can get permission denied error if we try to read a - // file which we do not have read read access for, in - // which case that is not an error, but a valid accessibility status - Ok(AccessibilityStatus::Blocked) - } else { - Err(e) - } - } - } + .open(path)?; + Ok(()) } -fn test_dir_read_access(path: &str) -> Result { - match std::fs::read_dir(path) { - Ok(_) => Ok(AccessibilityStatus::Accessible), - Err(e) => { - if e.kind() == std::io::ErrorKind::PermissionDenied { - Ok(AccessibilityStatus::Blocked) - } else { - Err(e) - } - } - } +fn test_dir_read_access(path: &str) -> Result<(), std::io::Error> { + let _ = std::fs::read_dir(path)?; + Ok(()) } fn is_file_like(mode: u32) -> bool { // for this please refer // https://stackoverflow.com/questions/40163270/what-is-s-isreg-and-what-does-it-do // https://linux.die.net/man/2/stat - mode & SFlag::S_IFREG.bits() != 0 - || mode & SFlag::S_IFBLK.bits() != 0 - || mode & SFlag::S_IFCHR.bits() != 0 + mode & SFlag::S_IFREG.bits() != 0 || mode & SFlag::S_IFCHR.bits() != 0 } fn is_dir(mode: u32) -> bool { mode & SFlag::S_IFDIR.bits() != 0 } -pub fn test_read_access(path: &str) -> Result { +pub fn test_read_access(path: &str) -> Result<(), std::io::Error> { let fstat = stat(path)?; let mode = fstat.st_mode; if is_file_like(mode) { @@ -77,44 +46,20 @@ pub fn test_read_access(path: &str) -> Result Result { - match std::fs::OpenOptions::new().write(true).open(path) { - std::io::Result::Ok(_) => { - // we don't have to check if we can actually write or not, as - // if we are allowed to open file with write access, we can write to it - Ok(AccessibilityStatus::Accessible) - } - std::io::Result::Err(e) => { - if e.kind() == std::io::ErrorKind::PermissionDenied { - return Ok(AccessibilityStatus::Blocked); - } else { - return Err(e); - } - } - } +fn test_file_write_access(path: &str) -> Result<(), std::io::Error> { + let _ = std::fs::OpenOptions::new().write(true).open(path)?; + Ok(()) } -fn test_dir_write_access(path: &str) -> Result { - match std::fs::OpenOptions::new() +fn test_dir_write_access(path: &str) -> Result<(), std::io::Error> { + let _ = std::fs::OpenOptions::new() .create(true) .write(true) - .open(PathBuf::from(path).join("test.txt")) - { - std::io::Result::Ok(_) => Ok(AccessibilityStatus::Accessible), - std::io::Result::Err(e) => { - if e.kind() == std::io::ErrorKind::PermissionDenied { - // technically we can still get permission denied even if - // we have write access, but do not have execute access, but by default - // dirs are created with execute access so that should not be an issue - return Ok(AccessibilityStatus::Blocked); - } else { - return Err(e); - } - } - } + .open(PathBuf::from(path).join("test.txt"))?; + Ok(()) } -pub fn test_write_access(path: &str) -> Result { +pub fn test_write_access(path: &str) -> Result<(), std::io::Error> { let fstat = stat(path)?; let mode = fstat.st_mode; if is_file_like(mode) { From f5227f8e1a6859813ceb10b005706f99b10e1095 Mon Sep 17 00:00:00 2001 From: Yashodhan Joshi Date: Sat, 8 Jan 2022 15:16:14 +0530 Subject: [PATCH 09/10] Add runtimetest dir to workflow --- .github/workflows/integration_tests_validation.yaml | 1 + .github/workflows/main.yml | 3 ++- crates/integration_test/tests.sh | 5 +++-- 3 files changed, 6 insertions(+), 3 deletions(-) diff --git a/.github/workflows/integration_tests_validation.yaml b/.github/workflows/integration_tests_validation.yaml index 687ebfa7a..5b49109a5 100644 --- a/.github/workflows/integration_tests_validation.yaml +++ b/.github/workflows/integration_tests_validation.yaml @@ -18,6 +18,7 @@ jobs: with: filters: | ./integration_test: crates/integration_test/** + ./runtimetest : runtimetest/** validate: needs: [changes] if: ${{ !contains(needs.changes.outputs.dirs, '[]') }} diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 85c1ebeeb..bab3d94aa 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -21,7 +21,8 @@ jobs: ./crates/libcontainer: crates/libcontainer/** ./crates/libcgroups: crates/libcgroups/** ./crates/libseccomp: crates/libseccomp/** - ./crates/integration_test: crates/integration_test/** + ./crates/integration_test: crates/integration_test/** + ./runtimetest : runtimetest/** check: needs: [changes] if: ${{ !contains(needs.changes.outputs.dirs, '[]') }} diff --git a/crates/integration_test/tests.sh b/crates/integration_test/tests.sh index d5eb52380..c5dd0e35c 100755 --- a/crates/integration_test/tests.sh +++ b/crates/integration_test/tests.sh @@ -12,6 +12,7 @@ RUNTIME=./youki # test.sh build # test.sh run # test.sh run runtime-name +# test.sh runtime-name if [[ "$1" = "build" ]]; then exit 0 @@ -21,14 +22,14 @@ fi # else the consider first argument as runtime name if [[ -n "$2" ]]; then RUNTIME="$2" -elif [[-n "$1" ]] +elif [[ -n "$1" ]]; then RUNTIME="$1" fi logfile="./test_log.log" touch $logfile -sudo ./youki_integration_test run --runtime $RUNTIME --runtimetest ./runtimetest > $logfile +sudo ./youki_integration_test run --runtime "$RUNTIME" --runtimetest "./runtimetest" > $logfile if [ 0 -ne $(grep "not ok" $logfile | wc -l ) ]; then cat $logfile exit 1 From 8320f0500c62e825b79852ba46e51744b792bf81 Mon Sep 17 00:00:00 2001 From: Yashodhan Joshi Date: Sat, 8 Jan 2022 17:44:35 +0530 Subject: [PATCH 10/10] Minor fix --- crates/integration_test/src/utils/test_utils.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/crates/integration_test/src/utils/test_utils.rs b/crates/integration_test/src/utils/test_utils.rs index af3b3188a..44023a16c 100644 --- a/crates/integration_test/src/utils/test_utils.rs +++ b/crates/integration_test/src/utils/test_utils.rs @@ -1,6 +1,6 @@ -use super::{generate_uuid, prepare_bundle, set_config}; ///! Contains utility functions for testing ///! Similar to https://github.com/opencontainers/runtime-tools/blob/master/validation/util/test.go +use super::{generate_uuid, prepare_bundle, set_config}; use super::{get_runtime_path, get_runtimetest_path}; use anyhow::{anyhow, bail, Context, Result}; use oci_spec::runtime::Spec; @@ -157,7 +157,6 @@ pub fn test_inside_container( test_result!(setup_for_test( &bundle.as_ref().join("bundle").join("rootfs") )); - // std::thread::sleep_ms(50000); set_config(&bundle, &spec).unwrap(); // as we have to run runtimetest inside the container, and is expects