Skip to content

#73 wasmer plugin #201

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 7 commits into from
Closed
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
1,036 changes: 847 additions & 189 deletions Cargo.lock

Large diffs are not rendered by default.

7 changes: 4 additions & 3 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@
# Tantivy does not build in the CI, so we exclude it
exclude = ["src-tauri"]
members = [
"server",
"cli",
"lib",
"server",
"cli",
"lib",
"plugin-example"
]
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ The easiest way to share [Atomic Data](https://docs.atomicdata.dev/) on the web.
`atomic-server` is a graph database server for storing and sharing typed linked data.
Demo on [atomicdata.dev](https://atomicdata.dev)

- ⚛️ **Dynamic schema validation** / type checking using [Atomic Schema](https://docs.atomicdata.dev/schema/intro.html). Combines safety of structured data with the
- ⚛️ **Dynamic schema validation** / type checking using [Atomic Schema](https://docs.atomicdata.dev/schema/intro.html), combining the best of RDF, JSON and type safety.
- 🚀 **Fast** (1ms responses on my laptop)
- 🪶 **Lightweight** (15MB binary, no runtime dependencies)
- 💻 **Runs everywhere** (linux, windows, mac, arm)
Expand Down
48 changes: 37 additions & 11 deletions lib/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -7,31 +7,57 @@ name = "atomic_lib"
readme = "README.md"
repository = "https://github.com/joepio/atomic-data-rust"
version = "0.31.0"

[[bench]]
all-features = true
harness = false
name = "benchmarks"
# path = "src/db/benchmarks.rs"

[dependencies]
base64 = "0.13.0"
bincode = {version = "1.3.2", optional = true}
dirs = {version = "4.0.0", optional = true}
rand = {version = "0.8.3"}
regex = "1.4.5"
ring = "0.16.20"
rio_api = {version = "0.6.1", optional = true}
rio_turtle = {version = "0.6.1", optional = true}
serde = {version = "1.0.125", features = ["derive"]}
serde_json = "1.0.64"
sled = {version = "0.34.6", optional = true, features = ["no_logs"]}
toml = {version = "0.5.8", optional = true}
tracing = "0.1.29"
ureq = "1.5.4"
url = "2.2.1"
urlencoding = "2.1.0"

[dependencies.bincode]
optional = true
version = "1.3.2"

[dependencies.dirs]
optional = true
version = "4.0.0"

[dependencies.rand]
version = "0.8.3"

[dependencies.rio_api]
optional = true
version = "0.6.1"

[dependencies.rio_turtle]
optional = true
version = "0.6.1"

[dependencies.serde]
features = ["derive"]
version = "1.0.125"

[dependencies.sled]
features = ["no_logs"]
optional = true
version = "0.34.6"

[dependencies.toml]
optional = true
version = "0.5.8"

[dependencies.wasmer]
optional = true
version = "2.1.1"

[dev-dependencies]
criterion = "0.3"
iai = "0.1"
Expand All @@ -40,5 +66,5 @@ ntest = "0.7.3"

[features]
config = ["dirs", "toml"]
db = ["sled", "bincode"]
db = ["sled", "bincode", "wasmer"]
rdf = ["rio_api", "rio_turtle"]
2 changes: 2 additions & 0 deletions lib/src/endpoints.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ use crate::{
path::path_endpoint,
search::search_endpoint,
versioning::{all_versions_endpoint, version_endpoint},
wasm_demo::wasm_demo_endpoint,
},
urls, Db, Resource, Storelike, Value,
};
Expand Down Expand Up @@ -58,5 +59,6 @@ pub fn default_endpoints() -> Vec<Endpoint> {
path_endpoint(),
search_endpoint(),
upload_endpoint(),
wasm_demo_endpoint(),
]
}
33 changes: 33 additions & 0 deletions lib/src/errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -200,6 +200,8 @@ impl From<Infallible> for AtomicError {
}
}

// LIBRARY ERRORS

#[cfg(feature = "db")]
impl From<sled::Error> for AtomicError {
fn from(error: sled::Error) -> Self {
Expand All @@ -219,3 +221,34 @@ impl From<Box<bincode::ErrorKind>> for AtomicError {
}
}
}

// WASMER ERRORS

#[cfg(feature = "db")]
impl From<wasmer::RuntimeError> for AtomicError {
fn from(error: wasmer::RuntimeError) -> Self {
AtomicError {
message: error.to_string(),
error_type: AtomicErrorType::OtherError,
}
}
}

#[cfg(feature = "db")]
impl From<wasmer::InstantiationError> for AtomicError {
fn from(error: wasmer::InstantiationError) -> Self {
AtomicError {
message: error.to_string(),
error_type: AtomicErrorType::OtherError,
}
}
}
#[cfg(feature = "db")]
impl From<wasmer::ExportError> for AtomicError {
fn from(error: wasmer::ExportError) -> Self {
AtomicError {
message: error.to_string(),
error_type: AtomicErrorType::OtherError,
}
}
}
1 change: 1 addition & 0 deletions lib/src/plugins/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,4 @@ pub mod files;
pub mod path;
pub mod search;
pub mod versioning;
pub mod wasm_demo;
1 change: 1 addition & 0 deletions lib/src/plugins/plugin.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
//! The Plugin Trait should be implemented by Plugins that use WASM.
74 changes: 74 additions & 0 deletions lib/src/plugins/wasm_demo.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
use crate::{endpoints::Endpoint, errors::AtomicResult, urls, Resource, Storelike};
use wasmer::{imports, Instance, Module, Value};

pub fn wasm_demo_endpoint() -> Endpoint {
Endpoint {
path: "/wasm".to_string(),
// Ideally, these params are fully dynamic and constructed from the arguments for the WASM function
params: [urls::SHORTNAME.to_string()].into(),
description: "A WASM demo ".to_string(),
shortname: "wasm".to_string(),
handle: Some(handle_wasm_demo_request),
}
}

fn handle_wasm_demo_request(
url: url::Url,
store: &impl Storelike,
_for_agent: Option<&str>,
) -> AtomicResult<Resource> {
let params = url.query_pairs();
let mut var = None;
for (k, v) in params {
// This check for arguments specific to this function
if let "shortname" = k.as_ref() {
var = Some(v.to_string())
};
}
if var.is_none() {
wasm_demo_endpoint().to_resource(store)
} else {
// Requires compiling atomic-plugin-demo
let module_u8 = include_bytes!(
"../../../target/wasm32-unknown-unknown/release/atomic_plugin_example.wasm"
);
// let module_string = r#"
// (module
// (type $t0 (func (param i32) (result i32)))
// (func $add_one (export "add_one") (type $t0) (param $p0 i32) (result i32)
// get_local $p0
// i32.const 1
// i32.add))
// "#;

let wasm_store = wasmer::Store::default();
// Creating this module can be costly, so it should probably be done earlier (server init)
let module =
Module::new(&wasm_store, module_u8).map_err(|e| format!("module error: {}", e))?;

let fib_name = "fibonacci";
let fib_args = &[Value::I32(10)];

let result = run_wasm(&module, &[], "author")?;
let mut resource = Resource::new("sub".into());
resource.set_propval_string(urls::DESCRIPTION.into(), &result, store)?;
Ok(resource)
}
}

/// Executes a single function from a WASM application.
/// Returns the first result as a String.
fn run_wasm(
// Wasm string representation of executable code
module: &Module,
// Vector of arguments for the function
arguments: &[wasmer::Val],
// Name of the function
fn_name: &str,
) -> AtomicResult<String> {
// The module doesn't import anything, so we create an empty import object.
let import_object = imports! {};
let instance = Instance::new(module, &import_object)?;
let result = instance.exports.get_function(fn_name)?.call(arguments)?;
Ok(result[0].to_string())
}
5 changes: 5 additions & 0 deletions plugin-example/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

16 changes: 16 additions & 0 deletions plugin-example/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
[package]
authors = ["Joep Meindertsma <[email protected]>"]
edition = "2018"
name = "atomic-plugin-example"
version = "0.1.0"
[dependencies]
reqwasm = "0.4.1"

[lib]
crate-type = ["cdylib"]
name = "atomic_plugin_example"

[profile]
[profile.release]
# Make wasm size smaller: https://rustwasm.github.io/docs/book/reference/code-size.html#optimizing-builds-for-code-size
lto = true
21 changes: 21 additions & 0 deletions plugin-example/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
# Atomic Data Plugin Example

A minimal example of an Atomic Plugin, written in Rust, compiled to WASM.

## How Atomic Plugins work

Atomic Plugins are WASM (WebAssembly) applications that are executed in a Sandbox in an Atomic Server.
This makes them safe, performant, and very easy to install - no need to reboot the Server.
Check this issue: https://github.com/joepio/atomic/issues/73.

## How to run

```bash
# Compile to WASM
cargo build --target wasm32-unknown-unknown --release
# Run it using Wasmer, see https://wasmer.io/
wasmer ./target/wasm32-unknown-unknown/release/atomic_plugin_example.wasm -i fibonacci 20
# Or start a server and open it on http://localhost/show?subject=http%3A%2F%2Flocalhost%2Fwasm
```

Inspired by https://codeburst.io/webassembly-and-rust-there-and-back-again-9ad76f61d616
22 changes: 22 additions & 0 deletions plugin-example/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
#[no_mangle]
pub fn fibonacci(n: u32) -> u32 {
// let resp = reqwasm::Request::get("/path").send().await.unwrap();
// assert_eq!(resp.status(), 200);

match n {
0 | 1 => n,
_ => fibonacci(n - 1) + fibonacci(n - 2),
}
}

// #[no_mangle]
// pub async fn fetch() -> String {
// let resp = reqwasm::http::Request::get("/path").send().await.unwrap();
// assert_eq!(resp.status(), 200);
// resp.text().await.unwrap()
// }

#[no_mangle]
pub fn author() -> String {
String::from("Hello from Rust")
}
2 changes: 1 addition & 1 deletion server/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ _Status: Beta. [Breaking changes](../CHANGELOG.md) are expected until 1.0._
**Atomic-server is a graph database server for storing and sharing [Atomic Data](https://docs.atomicdata.dev/).
Demo on [atomicdata.dev](https://atomicdata.dev)**

- ⚛️ **Dynamic schema validation** / type checking using [Atomic Schema](https://docs.atomicdata.dev/schema/intro.html). Combines safety of structured data with the
- ⚛️ **Dynamic schema validation** / type checking using [Atomic Schema](https://docs.atomicdata.dev/schema/intro.html), combining the best of RDF, JSON and type safety.
- 🚀 **Fast** (1ms responses on my laptop)
- 🪶 **Lightweight** (15MB binary, no runtime dependencies)
- 💻 **Runs everywhere** (linux, windows, mac, arm)
Expand Down
Loading