feat: allow process from external code
Some checks failed
continuous-integration/drone/push Build is failing

Signed-off-by: kjuulh <contact@kjuulh.io>
This commit is contained in:
Kasper Juul Hermansen 2024-12-02 23:12:37 +01:00
parent 21a13f3444
commit 355587234e
No known key found for this signature in database
2 changed files with 85 additions and 6 deletions

View File

@ -1,7 +1,7 @@
use anyhow::Context;
use component::churn_tasks::process::HostProcess;
use futures::StreamExt;
use std::path::PathBuf;
use std::sync::{Arc, RwLock};
use std::sync::Arc;
use tokio::io::AsyncWriteExt;
use tokio::sync::Mutex;
use wasmtime::component::*;
@ -10,10 +10,40 @@ use wasmtime_wasi::{DirPerms, FilePerms, WasiCtx, WasiCtxBuilder, WasiView};
wasmtime::component::bindgen!({
path: "wit/world.wit",
world: "churn",
async: true
//world: "churn",
async: true,
with: {
"component:churn-tasks/process/process": CustomProcess
}
});
#[derive(Default)]
pub struct CustomProcess {}
impl CustomProcess {
pub fn run(&self, args: Vec<String>) -> String {
tracing::info!("calling function");
match args.split_first() {
Some((item, rest)) => {
let mut cmd = std::process::Command::new(item);
match cmd.args(rest).output() {
Ok(output) => std::str::from_utf8(&output.stdout)
.expect("to be able to parse utf8")
.to_string(),
Err(e) => {
tracing::error!("command failed with output: {e}");
e.to_string()
}
}
}
None => {
tracing::warn!("failed to call function because it is empty");
panic!("failed to call function because it is empty")
}
}
}
}
#[derive(Clone)]
pub struct PluginStore {
inner: Arc<Mutex<InnerPluginStore>>,
@ -53,6 +83,12 @@ impl InnerPluginStore {
// Add the command world (aka WASI CLI) to the linker
wasmtime_wasi::add_to_linker_async(&mut linker).context("Failed to link command world")?;
component::churn_tasks::process::add_to_linker(
&mut linker,
|state: &mut ServerWasiView| state,
)?;
let wasi_view = ServerWasiView::new();
let store = Store::new(&engine, wasi_view);
@ -130,7 +166,8 @@ impl InnerPluginStore {
);
let instance = Churn::instantiate_async(&mut self.store, &component, &self.linker)
.await
.context("Failed to instantiate the example world")?;
.context("Failed to instantiate the example world")
.unwrap();
Ok(instance)
}
@ -139,6 +176,7 @@ impl InnerPluginStore {
struct ServerWasiView {
table: ResourceTable,
ctx: WasiCtx,
processes: ResourceTable,
}
impl ServerWasiView {
@ -153,7 +191,11 @@ impl ServerWasiView {
.expect("to be able to open root")
.build();
Self { table, ctx }
Self {
table,
ctx,
processes: ResourceTable::default(),
}
}
}
@ -166,3 +208,32 @@ impl WasiView for ServerWasiView {
&mut self.ctx
}
}
impl component::churn_tasks::process::Host for ServerWasiView {}
#[async_trait::async_trait]
impl HostProcess for ServerWasiView {
async fn new(
&mut self,
) -> wasmtime::component::Resource<component::churn_tasks::process::Process> {
self.processes.push(CustomProcess::default()).unwrap()
}
async fn run_process(
&mut self,
self_: wasmtime::component::Resource<component::churn_tasks::process::Process>,
inputs: wasmtime::component::__internal::Vec<String>,
) -> String {
let process = self.processes.get(&self_).unwrap();
process.run(inputs)
}
async fn drop(
&mut self,
rep: wasmtime::component::Resource<component::churn_tasks::process::Process>,
) -> wasmtime::Result<()> {
self.processes.delete(rep)?;
Ok(())
}
}

View File

@ -1,5 +1,12 @@
package component:churn-tasks@0.1.0;
interface process {
resource process {
constructor();
run-process: func(inputs: list<string>) -> string;
}
}
interface task {
id: func() -> string;
should-run: func() -> bool;
@ -8,4 +15,5 @@ interface task {
world churn {
export task;
import process;
}