Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ use std::thread;

use anyhow::Context;
use chrono::Utc;
use libc::{SIGINT, SIGKILL};
use libcontainer::container::{Container, ContainerStatus};
use libcontainer::signal::Signal;
use log::error;
Expand All @@ -16,6 +15,7 @@ use crate::sandbox::error::Error;
use crate::sandbox::instance::{ExitCode, Wait};
use crate::sandbox::instance_utils::{get_instance_root, instance_exists};
use crate::sandbox::{Instance, InstanceConfig};
use crate::sys::signals::{SIGINT, SIGKILL};

/// LibcontainerInstance is a trait that gets implemented by a WASI runtime that
/// uses youki's libcontainer library as the container runtime.
Expand Down
10 changes: 1 addition & 9 deletions crates/containerd-shim-wasm/src/sandbox/instance.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,17 +5,12 @@ use std::sync::{Arc, Condvar, Mutex};
use std::thread;

use chrono::{DateTime, Utc};
use libc::{SIGINT, SIGTERM};

use super::error::Error;
use crate::sys::signals::*;

pub type ExitCode = Arc<(Mutex<Option<(u32, DateTime<Utc>)>>, Condvar)>;

#[cfg(unix)]
use libc::SIGKILL;
#[cfg(windows)]
const SIGKILL: i32 = 9;

/// Generic options builder for creating a wasm instance.
/// This is passed to the `Instance::new` method.
#[derive(Clone)]
Expand Down Expand Up @@ -221,9 +216,6 @@ mod noptests {
use std::sync::mpsc::channel;
use std::time::Duration;

#[cfg(unix)]
use libc::SIGHUP;

use super::*;

#[test]
Expand Down
103 changes: 93 additions & 10 deletions crates/containerd-shim-wasm/src/sandbox/testutil.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,15 @@
//! Testing utilities used across different modules

use std::collections::HashMap;
use std::fs::{create_dir, File};
use std::path::Path;
use std::process::{Command, Stdio};
use std::sync::mpsc::channel;
use std::time::Duration;

use super::{Error, Result};
use anyhow::{bail, ensure, Result};

use crate::sys::signals::SIGKILL;

fn normalize_test_name(test: &str) -> Result<&str> {
let closure_removed = test.trim_end_matches("::{{closure}}");
Expand Down Expand Up @@ -50,15 +57,9 @@ pub fn run_test_with_sudo(test: &str) -> Result<()> {
std::io::copy(&mut stderr, &mut std::io::stderr()).unwrap();
});

cmd.wait()
.and_then(|status| {
if status.success() {
Ok(())
} else {
Err(std::io::ErrorKind::Other.into())
}
})
.map_err(Error::from)
ensure!(cmd.wait()?.success(), "running test with sudo failed");

Ok(())
}

#[macro_export]
Expand All @@ -76,7 +77,12 @@ macro_rules! function {

#[cfg(unix)]
use caps::{CapSet, Capability};
use chrono::{DateTime, Utc};
pub use function;
use oci_spec::runtime::{ProcessBuilder, RootBuilder, SpecBuilder};

use super::instance::Wait;
use super::{Instance, InstanceConfig};

/// Determines if the current process has the CAP_SYS_ADMIN capability in its effective set.
pub fn has_cap_sys_admin() -> bool {
Expand All @@ -91,3 +97,80 @@ pub fn has_cap_sys_admin() -> bool {
false
}
}

pub fn run_wasi_test<WasmInstance: Instance>(
dir: impl AsRef<Path>,
wasmbytes: impl AsRef<[u8]>,
start_fn: Option<&str>,
) -> Result<(u32, DateTime<Utc>)>
where
WasmInstance::Engine: Default,
{
create_dir(dir.as_ref().join("rootfs"))?;
let rootdir = dir.as_ref().join("runwasi");
create_dir(&rootdir)?;
let opts = HashMap::from([("root", rootdir)]);
let opts_file = std::fs::File::create(dir.as_ref().join("options.json"))?;
serde_json::to_writer(opts_file, &opts)?;

let filename = if wasmbytes.as_ref().starts_with(b"\0asm") {
"hello.wasm"
} else {
"hello.wat"
};

let wasm_path = dir.as_ref().join("rootfs").join(filename);
std::fs::write(&wasm_path, wasmbytes)?;

#[cfg(unix)]
std::fs::set_permissions(
&wasm_path,
std::os::unix::prelude::PermissionsExt::from_mode(0o755),
)?;

let stdout = File::create(dir.as_ref().join("stdout"))?;
drop(stdout);

let entrypoint = match start_fn {
Some(s) => format!("./{filename}#{s}"),
None => format!("./{filename}"),
};
let spec = SpecBuilder::default()
.root(RootBuilder::default().path("rootfs").build()?)
.process(
ProcessBuilder::default()
.cwd("/")
.args(vec![entrypoint])
.build()?,
)
.build()?;

spec.save(dir.as_ref().join("config.json"))?;

let mut cfg = InstanceConfig::new(
WasmInstance::Engine::default(),
"test_namespace".into(),
"/containerd/address".into(),
);
let cfg = cfg
.set_bundle(dir.as_ref().to_str().unwrap().to_string())
.set_stdout(dir.as_ref().join("stdout").to_str().unwrap().to_string());

let wasi = WasmInstance::new("test".to_string(), Some(cfg));

wasi.start()?;

let (tx, rx) = channel();
let waiter = Wait::new(tx);
wasi.wait(&waiter).unwrap();

let res = match rx.recv_timeout(Duration::from_secs(10)) {
Ok(res) => Ok(res),
Err(e) => {
wasi.kill(SIGKILL as u32).unwrap();
bail!("error waiting for module to finish: {e}");
}
};
wasi.delete()?;
res
}
1 change: 1 addition & 0 deletions crates/containerd-shim-wasm/src/sys/unix/mod.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
pub mod metrics;
pub mod networking;
pub mod signals;
pub mod stdio;
1 change: 1 addition & 0 deletions crates/containerd-shim-wasm/src/sys/unix/signals.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
pub use libc::{SIGHUP, SIGINT, SIGKILL, SIGTERM};
1 change: 1 addition & 0 deletions crates/containerd-shim-wasm/src/sys/windows/mod.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
pub mod metrics;
pub mod networking;
pub mod signals;
pub mod stdio;
2 changes: 2 additions & 0 deletions crates/containerd-shim-wasm/src/sys/windows/signals.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
pub use libc::{SIGINT, SIGTERM};
pub const SIGKILL: i32 = 9;
96 changes: 11 additions & 85 deletions crates/containerd-shim-wasmedge/src/instance/instance_linux.rs
Original file line number Diff line number Diff line change
Expand Up @@ -84,24 +84,18 @@ impl LibcontainerInstance for Wasi {

#[cfg(test)]
mod wasitest {
use std::borrow::Cow;
use std::collections::HashMap;
use std::fs::{create_dir, read_to_string, File, OpenOptions};
use std::io::Write;

use std::fs::read_to_string;
use std::os::unix::io::RawFd;
use std::os::unix::prelude::OpenOptionsExt;
use std::sync::mpsc::channel;
use std::time::Duration;

use chrono::{DateTime, Utc};
use containerd_shim_wasm::function;
use containerd_shim_wasm::sandbox::instance::Wait;
use containerd_shim_wasm::sandbox::testutil::{has_cap_sys_admin, run_test_with_sudo};
use containerd_shim_wasm::sandbox::testutil::{
has_cap_sys_admin, run_test_with_sudo, run_wasi_test,
};
use containerd_shim_wasm::sandbox::Instance;
use libc::{dup2, SIGKILL, STDERR_FILENO, STDIN_FILENO, STDOUT_FILENO};
use oci_spec::runtime::{ProcessBuilder, RootBuilder, SpecBuilder};
use libc::{dup2, STDERR_FILENO, STDIN_FILENO, STDOUT_FILENO};
use serial_test::serial;
use tempfile::{tempdir, TempDir};
use tempfile::tempdir;
use wasmedge_sdk::wat2wasm;

use super::*;
Expand Down Expand Up @@ -162,74 +156,6 @@ mod wasitest {
"#
.as_bytes();

fn run_wasi_test(dir: &TempDir, wasmbytes: Cow<[u8]>) -> Result<(u32, DateTime<Utc>), Error> {
create_dir(dir.path().join("rootfs"))?;
let rootdir = dir.path().join("runwasi");
create_dir(rootdir)?;
let rootdir = PathBuf::from("/path/to/root");
let mut opts = HashMap::new();
opts.insert("root", rootdir);
let serialized = serde_json::to_string(&opts)?;
let opts_file = OpenOptions::new()
.read(true)
.create(true)
.truncate(true)
.write(true)
.open(dir.path().join("options.json"))?;
write!(&opts_file, "{}", serialized)?;

let wasm_path = dir.path().join("rootfs/hello.wasm");
let mut f = OpenOptions::new()
.write(true)
.create(true)
.truncate(true)
.mode(0o755)
.open(wasm_path)?;
f.write_all(&wasmbytes)?;

let stdout = File::create(dir.path().join("stdout"))?;
drop(stdout);

let spec = SpecBuilder::default()
.root(RootBuilder::default().path("rootfs").build()?)
.process(
ProcessBuilder::default()
.cwd("/")
.args(vec!["./hello.wasm".to_string()])
.build()?,
)
.build()?;

spec.save(dir.path().join("config.json"))?;

let mut cfg =
InstanceConfig::new((), "test_namespace".into(), "/containerd/address".into());
let cfg = cfg
.set_bundle(dir.path().to_str().unwrap().to_string())
.set_stdout(dir.path().join("stdout").to_str().unwrap().to_string());

let wasi = Wasi::new("test".to_string(), Some(cfg));

wasi.start()?;

let (tx, rx) = channel();
let waiter = Wait::new(tx);
wasi.wait(&waiter).unwrap();

let res = match rx.recv_timeout(Duration::from_secs(10)) {
Ok(res) => Ok(res),
Err(e) => {
wasi.kill(SIGKILL as u32).unwrap();
return Err(Error::Others(format!(
"error waiting for module to finish: {0}",
e
)));
}
};
wasi.delete()?;
res
}

#[test]
#[serial]
fn test_delete_after_create() {
Expand All @@ -246,7 +172,7 @@ mod wasitest {

#[test]
#[serial]
fn test_wasi() -> Result<(), Error> {
fn test_wasi() -> anyhow::Result<()> {
if !has_cap_sys_admin() {
println!("running test with sudo: {}", function!());
return run_test_with_sudo(function!());
Expand All @@ -256,7 +182,7 @@ mod wasitest {
let path = dir.path();
let wasm_bytes = wat2wasm(WASI_HELLO_WAT).unwrap();

let res = run_wasi_test(&dir, wasm_bytes)?;
let res = run_wasi_test::<Wasi>(&dir, wasm_bytes, None)?;

assert_eq!(res.0, 0);

Expand All @@ -269,7 +195,7 @@ mod wasitest {

#[test]
#[serial]
fn test_wasi_error() -> Result<(), Error> {
fn test_wasi_error() -> anyhow::Result<()> {
if !has_cap_sys_admin() {
println!("running test with sudo: {}", function!());
return run_test_with_sudo(function!());
Expand All @@ -278,7 +204,7 @@ mod wasitest {
let dir = tempdir()?;
let wasm_bytes = wat2wasm(WASI_RETURN_ERROR).unwrap();

let res = run_wasi_test(&dir, wasm_bytes)?;
let res = run_wasi_test::<Wasi>(&dir, wasm_bytes, None)?;

// Expect error code from the run.
assert_eq!(res.0, 137);
Expand Down
Loading