diff --git a/.github/workflows/basic.yml b/.github/workflows/basic.yml index dd4cda5..fe4f50a 100644 --- a/.github/workflows/basic.yml +++ b/.github/workflows/basic.yml @@ -61,10 +61,10 @@ jobs: nix-channel --update nix build -f default.nix testn.container docker load < ./result - docker build --tag ghcr.io/githedgehog/testn/n-vm:v0.0.8 . + docker build --tag ghcr.io/githedgehog/testn/n-vm:v0.0.9 . docker run --privileged --rm busybox:latest sh -c "echo 1024 > /sys/kernel/mm/hugepages/hugepages-2048kB/nr_hugepages && cat /sys/kernel/mm/hugepages/hugepages-2048kB/nr_hugepages" - cargo test --package=scratch - docker push ghcr.io/githedgehog/testn/n-vm:v0.0.8 + cargo test --package=n-vm + docker push ghcr.io/githedgehog/testn/n-vm:v0.0.9 - name: "Setup tmate session for debug" if: ${{ failure() && github.event_name == 'workflow_dispatch' && inputs.debug_enabled }} diff --git a/Cargo.lock b/Cargo.lock index 9590c18..a7654e7 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -821,7 +821,7 @@ dependencies = [ [[package]] name = "n-it" -version = "0.0.8" +version = "0.0.9" dependencies = [ "capctl", "nix 0.30.1", @@ -834,7 +834,7 @@ dependencies = [ [[package]] name = "n-vm" -version = "0.0.8" +version = "0.0.9" dependencies = [ "bollard", "capctl", @@ -855,7 +855,7 @@ dependencies = [ [[package]] name = "n-vm-macros" -version = "0.0.8" +version = "0.0.9" dependencies = [ "proc-macro2", "quote", @@ -1130,16 +1130,6 @@ dependencies = [ "serde_json", ] -[[package]] -name = "scratch" -version = "0.0.0" -dependencies = [ - "n-vm", - "tokio", - "tracing", - "tracing-subscriber", -] - [[package]] name = "security-framework" version = "3.5.1" diff --git a/Cargo.toml b/Cargo.toml index 972d96c..d52c50b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -6,7 +6,6 @@ members = [ "n-it", "n-vm", "n-vm-macros", - "scratch", ] [workspace.dependencies] diff --git a/n-it/Cargo.toml b/n-it/Cargo.toml index 6c061ef..3fdf99f 100644 --- a/n-it/Cargo.toml +++ b/n-it/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "n-it" -version = "0.0.8" +version = "0.0.9" edition = "2024" license = "Apache-2.0" publish = false diff --git a/n-it/src/main.rs b/n-it/src/main.rs index e7e8285..f2c8460 100644 --- a/n-it/src/main.rs +++ b/n-it/src/main.rs @@ -141,7 +141,7 @@ impl InitSystem { if let Some(pid) = child.id() { debug!("main process spawned with PID: {pid}"); } else { - fatal!("unable to determine main processs id"); + fatal!("unable to determine main PID"); } (console, child) } @@ -304,7 +304,7 @@ impl InitSystem { .await { Ok(_) => { - // normaly I would use unreachable!() here, but in this case + // normally I would use unreachable!() here, but in this case // it is better to use fatal!() to help ensure that stdio is flushed. fatal!("unreachable code?"); } diff --git a/n-vm-macros/Cargo.toml b/n-vm-macros/Cargo.toml index 7a5d0f3..0601bc3 100644 --- a/n-vm-macros/Cargo.toml +++ b/n-vm-macros/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "n-vm-macros" -version = "0.0.8" +version = "0.0.9" edition = "2024" license = "Apache-2.0" publish = false diff --git a/n-vm/Cargo.toml b/n-vm/Cargo.toml index 7db0031..f61bcca 100644 --- a/n-vm/Cargo.toml +++ b/n-vm/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "n-vm" -version = "0.0.8" +version = "0.0.9" edition = "2024" license = "Apache-2.0" publish = false diff --git a/n-vm/src/lib.rs b/n-vm/src/lib.rs index a1b7353..c0fccbb 100644 --- a/n-vm/src/lib.rs +++ b/n-vm/src/lib.rs @@ -280,7 +280,7 @@ pub async fn run_in_vm(_: F) -> VmTestOutput { ..Default::default() }), pvpanic: Some(true), - landlock_enable: Some(false), + landlock_enable: Some(true), landlock_rules: Some(vec![LandlockConfig { path: "/vm".into(), access: "rw".into(), @@ -567,7 +567,7 @@ pub fn run_test_in_vm(_test_fn: F) -> ContainerState { entrypoint: None, cmd: Some(args), // TODO: this needs to be dynamic somehow. Not sure how to do that yet. - image: Some("ghcr.io/githedgehog/testn/n-vm:v0.0.8".into()), + image: Some("ghcr.io/githedgehog/testn/n-vm:v0.0.9".into()), network_disabled: Some(true), env: Some([ "IN_TEST_CONTAINER=YES".into(), @@ -615,7 +615,7 @@ pub fn run_test_in_vm(_test_fn: F) -> ContainerState { ].into()), tmpfs: Some({ let mut map = std::collections::HashMap::new(); - map.insert("/vm".into(), format!("nodev,noexec,nosuid,mode=0300,uid={uid},gid={gid}")); + map.insert("/vm".into(), format!("nodev,noexec,nosuid,uid={uid},gid={gid}")); map }), privileged: Some(false), @@ -676,92 +676,3 @@ pub fn run_test_in_vm(_test_fn: F) -> ContainerState { exit }) } - -#[cfg(test)] -mod test { - use std::sync::atomic::{AtomicUsize, Ordering}; - - #[test] - fn science_time() { - println!("science time"); - } - - #[test] - fn more_science_time() { - println!("more_science time"); - } - - #[test] - fn container_biscuit() { - mod __user_defined { - pub(super) const SHOULD_PANIC: bool = false; - #[inline(always)] - pub(super) fn container_biscuit() { - println!("stdout"); - eprintln!("stderr"); - println!("hello from container biscuit"); - panic!("oh no!") - } - } - match std::env::var("IN_VM") { - Ok(var) if var == "YES" => { - __user_defined::container_biscuit(); - return; - } - _ => { - if let Ok(val) = std::env::var("IN_TEST_CONTAINER") - && val == "YES" - { - let runtime = tokio::runtime::Builder::new_current_thread() - .enable_io() - .enable_time() - .thread_name_fn(|| { - static ATOMIC_ID: AtomicUsize = AtomicUsize::new(0); - let id = ATOMIC_ID.fetch_add(1, Ordering::SeqCst); - format!("hypervisor-{}", id) - }) - .build() - .unwrap(); - let _guard = runtime.enter(); - runtime.block_on(async { - tracing_subscriber::fmt() - .with_max_level(tracing::Level::INFO) - .with_thread_names(true) - .without_time() - .with_test_writer() - .with_line_number(true) - .with_target(true) - .with_file(true) - .init(); - let _init_span = tracing::span!(tracing::Level::INFO, "hypervisor"); - let _guard = _init_span.enter(); - let output = super::run_in_vm(container_biscuit).await; - eprintln!("{output}"); - assert!(output.success); - }); - return; - } - } - } - eprintln!("•─────⋅☾☾☾☾BEGIN NESTED TEST ENVIRONMENT☽☽☽☽⋅─────•"); - let container_state = super::run_test_in_vm(container_biscuit); - eprintln!("•─────⋅☾☾☾☾END NESTED TEST ENVIRONMENT☽☽☽☽⋅─────•"); - if __user_defined::SHOULD_PANIC { - if let Some(code) = container_state.exit_code { - if code != 0 { - eprintln!("test container was expected to panic"); - } else { - panic!("test container failed as required"); - } - } else { - eprintln!("test container did not return an exit code"); - } - } else if let Some(code) = container_state.exit_code { - if code != 0 { - panic!("test container exited with code {code}"); - } - } else { - panic!("test container not return an exit code"); - } - } -} diff --git a/n-vm/tests/integration.rs b/n-vm/tests/integration.rs new file mode 100644 index 0000000..909a452 --- /dev/null +++ b/n-vm/tests/integration.rs @@ -0,0 +1,48 @@ +use n_vm::in_vm; + +#[test] +#[in_vm] +fn test_which_runs_in_vm() { + assert_eq!(2 + 2, 4); +} + +#[should_panic] +#[test] +#[allow(unreachable_code)] +#[in_vm] +fn test_which_runs_in_vm_control() { + assert_eq!(2 + 2, 4); + panic!("deliberate panic"); +} + +#[test] +fn test_which_does_not_run_in_vm() { + assert_eq!(2 + 2, 4); +} + +#[cfg(false)] // deactivated until needed as control +#[should_panic] +#[test] +fn test_which_does_not_run_in_vm_control() { + assert_eq!(2 + 2, 4); + panic!("deliberate panic"); +} + +#[test] +#[in_vm] +fn root_filesystem_in_vm_is_read_only() { + let error = std::fs::File::create_new("/some.file").unwrap_err(); + assert_eq!(error.kind(), std::io::ErrorKind::ReadOnlyFilesystem); +} + +#[test] +#[in_vm] +fn run_filesystem_in_vm_is_read_write() { + std::fs::File::create_new("/run/some.file").unwrap(); +} + +#[test] +#[in_vm] +fn tmp_filesystem_in_vm_is_read_write() { + std::fs::File::create_new("/tmp/some.file").unwrap(); +} diff --git a/scratch/Cargo.toml b/scratch/Cargo.toml deleted file mode 100644 index 36a7ff6..0000000 --- a/scratch/Cargo.toml +++ /dev/null @@ -1,15 +0,0 @@ -[package] -name = "scratch" -version = "0.0.0" -edition = "2024" -license = "Apache-2.0" -publish = false - -[dependencies] - -# internal -n-vm = { path = "../n-vm" } - -tokio = { workspace = true, default-features = false, features = ["rt", "process", "macros", "fs", "net", "time", "io-util", "net", "io-std"] } -tracing-subscriber = { workspace = true, default-features = false, features = ["fmt"] } -tracing = { workspace = true, default-features = false, features = [] } diff --git a/scratch/src/main.rs b/scratch/src/main.rs deleted file mode 100644 index 5021ed7..0000000 --- a/scratch/src/main.rs +++ /dev/null @@ -1,34 +0,0 @@ -fn main() { - println!("hello world"); -} - -#[cfg(test)] -mod test { - use n_vm::in_vm; - - #[test] - #[in_vm] - fn test_which_runs_in_vm() { - assert_eq!(2 + 2, 4); - } - - #[should_panic] - #[test] - #[in_vm] - fn test_which_runs_in_vm_control() { - assert_eq!(2 + 2, 4); - assert!(false, "deliberate panic"); - } - - #[test] - fn test_which_does_not_run_in_vm() { - assert_eq!(2 + 2, 4); - } - - #[should_panic] - #[test] - fn test_which_does_not_run_in_vm_control() { - assert_eq!(2 + 2, 4); - assert!(false, "deliberate panic"); - } -} diff --git a/shell.nix b/shell.nix index d051ec0..9ed9123 100644 --- a/shell.nix +++ b/shell.nix @@ -14,6 +14,7 @@ # for dev bash docker-client + rust-analyzer-unwrapped rustup ]); runScript = ''bash'';