1
0
Fork 0
mirror of https://github.com/containers/youki synced 2024-05-23 07:56:07 +02:00
youki/crates/youki/src/workload/wasmtime.rs
Yashodhan 342ad60927
update wasmtime dep to 19.0.1, replace wasmtime-wasi with wasi-cmmon (#2752)
Signed-off-by: Yashodhan Joshi <yjdoc2@gmail.com>
2024-04-17 12:53:10 +05:30

120 lines
3.9 KiB
Rust

use libcontainer::oci_spec::runtime::Spec;
use wasi_common::sync::{add_to_linker, WasiCtxBuilder};
use wasmtime::{Engine, Linker, Module, Store};
use libcontainer::workload::{Executor, ExecutorError, ExecutorValidationError, EMPTY};
const EXECUTOR_NAME: &str = "wasmtime";
#[derive(Clone)]
pub struct WasmtimeExecutor {}
impl Executor for WasmtimeExecutor {
fn exec(&self, spec: &Spec) -> Result<(), ExecutorError> {
if !can_handle(spec) {
return Err(ExecutorError::CantHandle(EXECUTOR_NAME));
}
tracing::debug!("executing workload with wasmtime handler");
let process = spec.process().as_ref();
let args = spec
.process()
.as_ref()
.and_then(|p| p.args().as_ref())
.unwrap_or(&EMPTY);
if args.is_empty() {
tracing::error!("at least one process arg must be specified");
return Err(ExecutorError::InvalidArg);
}
if !args[0].ends_with(".wasm") && !args[0].ends_with(".wat") {
tracing::error!(
"first argument must be a wasm or wat module, but was {}",
args[0]
);
return Err(ExecutorError::InvalidArg);
}
let mut cmd = args[0].clone();
let stripped = args[0].strip_prefix(std::path::MAIN_SEPARATOR);
if let Some(cmd_stripped) = stripped {
cmd = cmd_stripped.to_string();
}
let envs: Vec<(String, String)> = process
.and_then(|p| p.env().as_ref())
.unwrap_or(&EMPTY)
.iter()
.filter_map(|e| {
e.split_once('=')
.map(|kv| (kv.0.trim().to_string(), kv.1.trim().to_string()))
})
.collect();
let engine = Engine::default();
let module = Module::from_file(&engine, &cmd).map_err(|err| {
tracing::error!(err = ?err, file = ?cmd, "could not load wasm module from file");
ExecutorError::Other("could not load wasm module from file".to_string())
})?;
let mut linker = Linker::new(&engine);
add_to_linker(&mut linker, |s| s).map_err(|err| {
tracing::error!(err = ?err, "cannot add wasi context to linker");
ExecutorError::Other("cannot add wasi context to linker".to_string())
})?;
let wasi = WasiCtxBuilder::new()
.inherit_stdio()
.args(args)
.map_err(|err| {
ExecutorError::Other(format!("cannot add args to wasi context: {}", err))
})?
.envs(&envs)
.map_err(|err| {
ExecutorError::Other(format!("cannot add envs to wasi context: {}", err))
})?
.build();
let mut store = Store::new(&engine, wasi);
let instance = linker.instantiate(&mut store, &module).map_err(|err| {
tracing::error!(err = ?err, "wasm module could not be instantiated");
ExecutorError::Other("wasm module could not be instantiated".to_string())
})?;
let start = instance.get_func(&mut store, "_start").ok_or_else(|| {
ExecutorError::Other("could not retrieve wasm module main function".into())
})?;
start
.call(&mut store, &[], &mut [])
.map_err(|err| ExecutorError::Execution(err.into()))
}
fn validate(&self, spec: &Spec) -> Result<(), ExecutorValidationError> {
if !can_handle(spec) {
return Err(ExecutorValidationError::CantHandle(EXECUTOR_NAME));
}
Ok(())
}
}
pub fn get_executor() -> WasmtimeExecutor {
WasmtimeExecutor {}
}
fn can_handle(spec: &Spec) -> bool {
if let Some(annotations) = spec.annotations() {
if let Some(handler) = annotations.get("run.oci.handler") {
return handler == "wasm";
}
if let Some(variant) = annotations.get("module.wasm.image/variant") {
return variant == "compat";
}
}
false
}