Skip to content

Commit

Permalink
Integrate wasi-runtime-config into wasmtime-cli (bytecodealliance#8970)
Browse files Browse the repository at this point in the history
  • Loading branch information
iawia002 authored Jul 19, 2024
1 parent 3447849 commit 4b842a0
Show file tree
Hide file tree
Showing 8 changed files with 169 additions and 1 deletion.
2 changes: 2 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -350,6 +350,7 @@ default = [
"wasi-nn",
"wasi-threads",
"wasi-http",
"wasi-runtime-config",

# Most features of Wasmtime are enabled by default.
"wat",
Expand Down Expand Up @@ -395,6 +396,7 @@ disable-logging = ["log/max_level_off", "tracing/max_level_off"]
wasi-nn = ["dep:wasmtime-wasi-nn"]
wasi-threads = ["dep:wasmtime-wasi-threads", "threads"]
wasi-http = ["component-model", "dep:wasmtime-wasi-http", "dep:tokio", "dep:hyper"]
wasi-runtime-config = ["dep:wasmtime-wasi-runtime-config"]
pooling-allocator = ["wasmtime/pooling-allocator", "wasmtime-cli-flags/pooling-allocator"]
component-model = [
"wasmtime/component-model",
Expand Down
10 changes: 10 additions & 0 deletions crates/cli-flags/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -284,6 +284,8 @@ wasmtime_option_group! {
pub threads: Option<bool>,
/// Enable support for WASI HTTP API (experimental)
pub http: Option<bool>,
/// Enable support for WASI runtime config API (experimental)
pub runtime_config: Option<bool>,
/// Inherit environment variables and file descriptors following the
/// systemd listen fd specification (UNIX only)
pub listenfd: Option<bool>,
Expand Down Expand Up @@ -321,6 +323,8 @@ wasmtime_option_group! {
///
/// This option can be further overwritten with `--env` flags.
pub inherit_env: Option<bool>,
/// Pass a wasi runtime config variable to the program.
pub runtime_config_var: Vec<WasiRuntimeConfigVariable>,
}

enum Wasi {
Expand All @@ -334,6 +338,12 @@ pub struct WasiNnGraph {
pub dir: String,
}

#[derive(Debug, Clone, PartialEq)]
pub struct WasiRuntimeConfigVariable {
pub key: String,
pub value: String,
}

/// Common options for commands that translate WebAssembly modules
#[derive(Parser, Clone)]
pub struct CommonOptions {
Expand Down
17 changes: 16 additions & 1 deletion crates/cli-flags/src/opt.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
//! specifying options in a struct-like syntax where all other boilerplate about
//! option parsing is contained exclusively within this module.
use crate::WasiNnGraph;
use crate::{WasiNnGraph, WasiRuntimeConfigVariable};
use anyhow::{bail, Result};
use clap::builder::{StringValueParser, TypedValueParser, ValueParserFactory};
use clap::error::{Error, ErrorKind};
Expand Down Expand Up @@ -396,3 +396,18 @@ impl WasmtimeOptionValue for WasiNnGraph {
})
}
}

impl WasmtimeOptionValue for WasiRuntimeConfigVariable {
const VAL_HELP: &'static str = "=<name>=<val>";
fn parse(val: Option<&str>) -> Result<Self> {
let val = String::parse(val)?;
let mut parts = val.splitn(2, "=");
Ok(WasiRuntimeConfigVariable {
key: parts.next().unwrap().to_string(),
value: match parts.next() {
Some(part) => part.into(),
None => "".to_string(),
},
})
}
}
29 changes: 29 additions & 0 deletions crates/test-programs/src/bin/cli_serve_runtime_config.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
use test_programs::config::wasi::config::runtime;
use test_programs::proxy;
use test_programs::wasi::http::types::{
Fields, IncomingRequest, OutgoingBody, OutgoingResponse, ResponseOutparam,
};

struct T;

proxy::export!(T);

impl proxy::exports::wasi::http::incoming_handler::Guest for T {
fn handle(_: IncomingRequest, outparam: ResponseOutparam) {
let fields = Fields::new();
let resp = OutgoingResponse::new(fields);
let body = resp.body().expect("outgoing response");

ResponseOutparam::set(outparam, Ok(resp));

let out = body.write().expect("outgoing stream");
let config = runtime::get("hello").unwrap().unwrap();
out.blocking_write_and_flush(config.as_bytes())
.expect("writing response");

drop(out);
OutgoingBody::finish(body, None).expect("outgoing-body.finish");
}
}

fn main() {}
7 changes: 7 additions & 0 deletions crates/wasi-runtime-config/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,13 @@ impl<'a> From<&'a WasiRuntimeConfigVariables> for WasiRuntimeConfig<'a> {
}
}

impl<'a> WasiRuntimeConfig<'a> {
/// Create a new view into the `wasi-runtime-config` state.
pub fn new(vars: &'a WasiRuntimeConfigVariables) -> Self {
Self { vars }
}
}

impl generated::Host for WasiRuntimeConfig<'_> {
fn get(&mut self, key: String) -> Result<Result<Option<String>, generated::ConfigError>> {
Ok(Ok(self.vars.0.get(&key).map(|s| s.to_owned())))
Expand Down
37 changes: 37 additions & 0 deletions src/commands/run.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@ use wasmtime_wasi_threads::WasiThreadsCtx;

#[cfg(feature = "wasi-http")]
use wasmtime_wasi_http::WasiHttpCtx;
#[cfg(feature = "wasi-runtime-config")]
use wasmtime_wasi_runtime_config::{WasiRuntimeConfig, WasiRuntimeConfigVariables};

fn parse_preloads(s: &str) -> Result<(String, PathBuf)> {
let parts: Vec<&str> = s.splitn(2, '=').collect();
Expand Down Expand Up @@ -670,6 +672,38 @@ impl RunCommand {
}
}

if self.run.common.wasi.runtime_config == Some(true) {
#[cfg(not(feature = "wasi-runtime-config"))]
{
bail!("Cannot enable wasi-runtime-config when the binary is not compiled with this feature.");
}
#[cfg(all(feature = "wasi-runtime-config", feature = "component-model"))]
{
match linker {
CliLinker::Core(_) => {
bail!("Cannot enable wasi-runtime-config for core wasm modules");
}
CliLinker::Component(linker) => {
let vars = WasiRuntimeConfigVariables::from_iter(
self.run
.common
.wasi
.runtime_config_var
.iter()
.map(|v| (v.key.clone(), v.value.clone())),
);

wasmtime_wasi_runtime_config::add_to_linker(linker, |h| {
WasiRuntimeConfig::new(
Arc::get_mut(h.wasi_runtime_config.as_mut().unwrap()).unwrap(),
)
})?;
store.data_mut().wasi_runtime_config = Some(Arc::new(vars));
}
}
}
}

if self.run.common.wasi.threads == Some(true) {
#[cfg(not(feature = "wasi-threads"))]
{
Expand Down Expand Up @@ -814,6 +848,9 @@ struct Host {
limits: StoreLimits,
#[cfg(feature = "profiling")]
guest_profiler: Option<Arc<wasmtime::GuestProfiler>>,

#[cfg(feature = "wasi-runtime-config")]
wasi_runtime_config: Option<Arc<WasiRuntimeConfigVariables>>,
}

impl Host {
Expand Down
35 changes: 35 additions & 0 deletions src/commands/serve.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ use wasmtime_wasi_http::{body::HyperOutgoingBody, WasiHttpCtx, WasiHttpView};

#[cfg(feature = "wasi-nn")]
use wasmtime_wasi_nn::wit::WasiNnCtx;
#[cfg(feature = "wasi-runtime-config")]
use wasmtime_wasi_runtime_config::{WasiRuntimeConfig, WasiRuntimeConfigVariables};

struct Host {
table: wasmtime::component::ResourceTable,
Expand All @@ -29,6 +31,9 @@ struct Host {

#[cfg(feature = "wasi-nn")]
nn: Option<WasiNnCtx>,

#[cfg(feature = "wasi-runtime-config")]
wasi_runtime_config: Option<WasiRuntimeConfigVariables>,
}

impl WasiView for Host {
Expand Down Expand Up @@ -147,6 +152,8 @@ impl ServeCommand {

#[cfg(feature = "wasi-nn")]
nn: None,
#[cfg(feature = "wasi-runtime-config")]
wasi_runtime_config: None,
};

if self.run.common.wasi.nn == Some(true) {
Expand All @@ -165,6 +172,21 @@ impl ServeCommand {
}
}

if self.run.common.wasi.runtime_config == Some(true) {
#[cfg(feature = "wasi-runtime-config")]
{
let vars = WasiRuntimeConfigVariables::from_iter(
self.run
.common
.wasi
.runtime_config_var
.iter()
.map(|v| (v.key.clone(), v.value.clone())),
);
host.wasi_runtime_config.replace(vars);
}
}

let mut store = Store::new(engine, host);

if self.run.common.wasm.timeout.is_some() {
Expand Down Expand Up @@ -228,6 +250,19 @@ impl ServeCommand {
}
}

if self.run.common.wasi.runtime_config == Some(true) {
#[cfg(not(feature = "wasi-runtime-config"))]
{
bail!("support for wasi-runtime-config was disabled at compile time");
}
#[cfg(feature = "wasi-runtime-config")]
{
wasmtime_wasi_runtime_config::add_to_linker(linker, |h| {
WasiRuntimeConfig::from(h.wasi_runtime_config.as_ref().unwrap())
})?;
}
}

if self.run.common.wasi.threads == Some(true) {
bail!("support for wasi-threads is not available with components");
}
Expand Down
33 changes: 33 additions & 0 deletions tests/all/cli_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1853,6 +1853,39 @@ stderr [1] :: after empty
run_wasmtime(&["run", "--argv0=foo.wasm", CLI_ARGV0, "foo.wasm"])?;
Ok(())
}

#[tokio::test]
async fn cli_serve_runtime_config() -> Result<()> {
let server = WasmtimeServe::new(CLI_SERVE_RUNTIME_CONFIG_COMPONENT, |cmd| {
cmd.arg("-Scli");
cmd.arg("-Sruntime-config");
cmd.arg("-Sruntime-config-var=hello=world");
})?;

let resp = server
.send_request(
hyper::Request::builder()
.uri("http://localhost/")
.body(String::new())
.context("failed to make request")?,
)
.await?;

assert!(resp.status().is_success());
assert_eq!(resp.body(), "world");
Ok(())
}

#[test]
fn cli_runtime_config() -> Result<()> {
run_wasmtime(&[
"run",
"-Sruntime-config",
"-Sruntime-config-var=hello=world",
RUNTIME_CONFIG_GET_COMPONENT,
])?;
Ok(())
}
}

#[test]
Expand Down

0 comments on commit 4b842a0

Please sign in to comment.