Skip to content

Commit

Permalink
Merge pull request #1854 from fermyon/resources
Browse files Browse the repository at this point in the history
Resources in Spin
  • Loading branch information
rylev authored Oct 11, 2023
2 parents 2a02056 + 46ee340 commit 11ebf50
Show file tree
Hide file tree
Showing 88 changed files with 3,029 additions and 2,395 deletions.
407 changes: 142 additions & 265 deletions Cargo.lock

Large diffs are not rendered by default.

12 changes: 8 additions & 4 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -107,10 +107,14 @@ members = ["crates/*", "sdk/rust", "sdk/rust/macro"]

[workspace.dependencies]
tracing = { version = "0.1", features = ["log"] }
wasmtime-wasi = { version = "13.0.0", features = ["tokio"] }
wasi-common-preview1 = { version = "13.0.0", package = "wasi-common" }
wasmtime = { version = "13.0.0", features = ["component-model"] }
spin-componentize = { git = "https://github.com/fermyon/spin-componentize", rev = "f29cdb26a6b1700ae1fe48c94bbef26a8069d566" }
wasmtime-wasi = { git = "https://github.com/bytecodealliance/wasmtime", rev = "c796ce7376a57a40605f03e74bd78cefcc9acf3a", features = [
"tokio",
] }
wasi-common-preview1 = { git = "https://github.com/bytecodealliance/wasmtime", rev = "c796ce7376a57a40605f03e74bd78cefcc9acf3a", package = "wasi-common" }
wasmtime = { git = "https://github.com/bytecodealliance/wasmtime", rev = "c796ce7376a57a40605f03e74bd78cefcc9acf3a", features = [
"component-model",
] }
spin-componentize = { git = "https://github.com/fermyon/spin-componentize", rev = "84286054d632ccb8ae06c6419940b5d328229e59" }

[[bin]]
name = "spin"
Expand Down
2 changes: 1 addition & 1 deletion crates/config/src/host_component.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ use anyhow::Result;
use once_cell::sync::OnceCell;
use spin_app::{AppComponent, DynamicHostComponent};
use spin_core::{async_trait, HostComponent};
use spin_world::config;
use spin_world::v1::config;

use crate::{Error, Key, Provider, Resolver};

Expand Down
64 changes: 47 additions & 17 deletions crates/core/src/store.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,15 @@ use anyhow::{anyhow, Result};
use std::{
io::{Read, Write},
path::{Path, PathBuf},
sync::Mutex,
time::{Duration, Instant},
};
use system_interface::io::ReadReady;
use tokio::io::{AsyncRead, AsyncWrite};
use wasi_common_preview1 as wasi_preview1;
use wasmtime_wasi as wasmtime_wasi_preview1;
use wasmtime_wasi::preview2 as wasi_preview2;
use wasmtime_wasi::preview2::{self as wasi_preview2, StdinStream, StdoutStream};
use wasmtime_wasi_preview1::preview2::{HostInputStream, HostOutputStream};

use crate::{
host_component::{HostComponents, HostComponentsData},
Expand Down Expand Up @@ -172,10 +174,9 @@ impl StoreBuilder {
ctx.set_stdin(Box::new(wasi_preview1::pipe::ReadPipe::new(r)))
}
WasiCtxBuilder::Preview2(ctx) => {
ctx.stdin(
ctx.stdin(MyStdinStream(Mutex::new(Some(Box::new(
wasi_preview2::pipe::AsyncReadStream::new(r),
wasi_preview2::IsATTY::No,
);
)))));
}
})
}
Expand Down Expand Up @@ -212,10 +213,9 @@ impl StoreBuilder {
ctx.set_stdout(Box::new(wasi_preview1::pipe::WritePipe::new(w)))
}
WasiCtxBuilder::Preview2(ctx) => {
ctx.stdout(
ctx.stdout(MyStdoutStream(Mutex::new(Some(Box::new(
wasi_preview2::pipe::AsyncWriteStream::new(1024 * 1024, w),
wasi_preview2::IsATTY::No,
);
)))));
}
})
}
Expand All @@ -230,7 +230,7 @@ impl StoreBuilder {
"`Store::stdout_buffered` only supported with WASI Preview 2"
)),
WasiCtxBuilder::Preview2(ctx) => {
ctx.stdout(buffer.writer(), wasi_preview2::IsATTY::No);
ctx.stdout(MyStdoutStream(Mutex::new(Some(Box::new(buffer.writer())))));
Ok(())
}
})?;
Expand All @@ -256,10 +256,9 @@ impl StoreBuilder {
ctx.set_stderr(Box::new(wasi_preview1::pipe::WritePipe::new(w)))
}
WasiCtxBuilder::Preview2(ctx) => {
ctx.stderr(
ctx.stderr(MyStdoutStream(Mutex::new(Some(Box::new(
wasi_preview2::pipe::AsyncWriteStream::new(1024 * 1024, w),
wasi_preview2::IsATTY::No,
);
)))));
}
})
}
Expand Down Expand Up @@ -364,8 +363,7 @@ impl StoreBuilder {
///
/// If `T: Default`, it may be preferable to use [`Store::build`].
pub fn build_with_data<T>(self, inner_data: T) -> Result<Store<T>> {
let mut table = wasi_preview2::Table::new();
let wasi = self.wasi.map_err(anyhow::Error::msg)?.build(&mut table)?;
let wasi = self.wasi.map_err(anyhow::Error::msg)?.build();

let mut inner = wasmtime::Store::new(
&self.engine,
Expand All @@ -374,7 +372,7 @@ impl StoreBuilder {
wasi,
host_components_data: self.host_components_data,
store_limits: self.store_limits,
table,
table: wasi_preview2::Table::new(),
},
);

Expand Down Expand Up @@ -420,6 +418,38 @@ impl StoreBuilder {
}
}

struct MyStdinStream(Mutex<Option<Box<dyn HostInputStream>>>);

impl StdinStream for MyStdinStream {
fn stream(&self) -> Box<dyn HostInputStream> {
self.0
.lock()
.unwrap()
.take()
.expect("MyStdinStream::stream should only be called once")
}

fn isatty(&self) -> bool {
false
}
}

struct MyStdoutStream(Mutex<Option<Box<dyn HostOutputStream>>>);

impl StdoutStream for MyStdoutStream {
fn stream(&self) -> Box<dyn HostOutputStream> {
self.0
.lock()
.unwrap()
.take()
.expect("MyStdoutStream::stream should only be called once")
}

fn isatty(&self) -> bool {
false
}
}

/// A builder of a `WasiCtx` for all versions of Wasi
#[allow(clippy::large_enum_variant)]
enum WasiCtxBuilder {
Expand All @@ -439,10 +469,10 @@ impl From<WasiVersion> for WasiCtxBuilder {
}

impl WasiCtxBuilder {
fn build(self, table: &mut wasi_preview2::Table) -> anyhow::Result<Wasi> {
fn build(self) -> Wasi {
match self {
WasiCtxBuilder::Preview1(ctx) => Ok(Wasi::Preview1(ctx)),
WasiCtxBuilder::Preview2(mut b) => b.build(table).map(Wasi::Preview2),
WasiCtxBuilder::Preview1(ctx) => Wasi::Preview1(ctx),
WasiCtxBuilder::Preview2(mut b) => Wasi::Preview2(b.build()),
}
}
}
4 changes: 2 additions & 2 deletions crates/core/tests/core-wasi-test/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
[package]
name = "core-wasi-test"
name = "core-wasi-test"
version = "0.1.0"
edition = "2021"

[profile.release]
debug = true

[dependencies]
wit-bindgen = "0.8"
wit-bindgen = { git = "https://github.com/bytecodealliance/wit-bindgen", rev = "46fba30bb667a3a4962f63b1cc28b84427b49114" }

[workspace]
58 changes: 42 additions & 16 deletions crates/key-value-sqlite/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -144,8 +144,9 @@ impl Store for SqliteStore {
#[cfg(test)]
mod test {
use super::*;
use spin_core::wasmtime::component::Resource;
use spin_key_value::{DelegatingStoreManager, KeyValueDispatch};
use spin_world::key_value::Host;
use spin_world::v2::key_value::HostStore;

#[tokio::test(flavor = "multi_thread", worker_threads = 1)]
async fn all() -> Result<()> {
Expand All @@ -162,7 +163,7 @@ mod test {
);

assert!(matches!(
kv.exists(42, "bar".to_owned()).await?,
kv.exists(Resource::new_own(42), "bar".to_owned()).await?,
Err(Error::InvalidStore)
));

Expand All @@ -176,41 +177,66 @@ mod test {
));

let store = kv.open("default".to_owned()).await??;
let rep = store.rep();

assert!(!kv.exists(store, "bar".to_owned()).await??);
assert!(
!kv.exists(Resource::new_own(rep), "bar".to_owned())
.await??
);

assert!(matches!(
kv.get(store, "bar".to_owned()).await?,
kv.get(Resource::new_own(rep), "bar".to_owned()).await?,
Err(Error::NoSuchKey)
));

kv.set(store, "bar".to_owned(), b"baz".to_vec()).await??;
kv.set(Resource::new_own(rep), "bar".to_owned(), b"baz".to_vec())
.await??;

assert!(kv.exists(store, "bar".to_owned()).await??);
assert!(
kv.exists(Resource::new_own(rep), "bar".to_owned())
.await??
);

assert_eq!(b"baz" as &[_], &kv.get(store, "bar".to_owned()).await??);
assert_eq!(
b"baz" as &[_],
&kv.get(Resource::new_own(rep), "bar".to_owned()).await??
);

kv.set(store, "bar".to_owned(), b"wow".to_vec()).await??;
kv.set(Resource::new_own(rep), "bar".to_owned(), b"wow".to_vec())
.await??;

assert_eq!(b"wow" as &[_], &kv.get(store, "bar".to_owned()).await??);
assert_eq!(
b"wow" as &[_],
&kv.get(Resource::new_own(rep), "bar".to_owned()).await??
);

assert_eq!(&["bar".to_owned()] as &[_], &kv.get_keys(store).await??);
assert_eq!(
&["bar".to_owned()] as &[_],
&kv.get_keys(Resource::new_own(rep)).await??
);

kv.delete(store, "bar".to_owned()).await??;
kv.delete(Resource::new_own(rep), "bar".to_owned())
.await??;

assert!(!kv.exists(store, "bar".to_owned()).await??);
assert!(
!kv.exists(Resource::new_own(rep), "bar".to_owned())
.await??
);

assert_eq!(&[] as &[String], &kv.get_keys(store).await??);
assert_eq!(
&[] as &[String],
&kv.get_keys(Resource::new_own(rep)).await??
);

assert!(matches!(
kv.get(store, "bar".to_owned()).await?,
kv.get(Resource::new_own(rep), "bar".to_owned()).await?,
Err(Error::NoSuchKey)
));

kv.close(store).await?;
kv.drop(Resource::new_own(rep))?;

assert!(matches!(
kv.exists(store, "bar".to_owned()).await?,
kv.exists(Resource::new_own(rep), "bar".to_owned()).await?,
Err(Error::InvalidStore)
));

Expand Down
33 changes: 33 additions & 0 deletions crates/key-value/src/host_component.rs
Original file line number Diff line number Diff line change
Expand Up @@ -102,3 +102,36 @@ impl DynamicHostComponent for KeyValueComponent {
}
}
}

pub struct LegacyKeyValueComponent(KeyValueComponent);

impl LegacyKeyValueComponent {
pub fn new(new: KeyValueComponent) -> Self {
Self(new)
}
}

impl HostComponent for LegacyKeyValueComponent {
type Data = KeyValueDispatch;

fn add_to_linker<T: Send>(
linker: &mut spin_core::Linker<T>,
get: impl Fn(&mut spin_core::Data<T>) -> &mut Self::Data + Send + Sync + Copy + 'static,
) -> anyhow::Result<()> {
spin_world::v1::key_value::add_to_linker(linker, get)
}

fn build_data(&self) -> Self::Data {
self.0.build_data()
}
}

impl DynamicHostComponent for LegacyKeyValueComponent {
fn update_data(&self, data: &mut Self::Data, component: &AppComponent) -> anyhow::Result<()> {
self.0.update_data(data, component)
}

fn validate_app(&self, app: &spin_app::App) -> anyhow::Result<()> {
self.0.validate_app(app)
}
}
Loading

0 comments on commit 11ebf50

Please sign in to comment.