Skip to content

Commit

Permalink
feat(example): add blank slate rust provider
Browse files Browse the repository at this point in the history
Signed-off-by: Brooks Townsend <brooksmtownsend@gmail.com>
  • Loading branch information
brooksmtownsend committed May 24, 2024
1 parent c354c57 commit 73ad00a
Show file tree
Hide file tree
Showing 24 changed files with 621 additions and 0 deletions.
1 change: 1 addition & 0 deletions examples/rust/providers/blank-slate/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Cargo.lock
22 changes: 22 additions & 0 deletions examples/rust/providers/blank-slate/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
[package]
name = "wasmcloud-example-blank-slate"
version = "0.1.0"
edition = "2021"
description = """
A blank slate capability provider built for quick implementation of custom capabilities.
"""

[workspace]

[badges.maintenance]
status = "actively-developed"

[dependencies]
anyhow = "1.0.82"
async-nats = "0.33.0"
serde = { version = "1.0.197" , features = ["derive"] }
serde_json = "1.0.115"
tokio = { version = "1.37.0", features = [ "full" ] }
tracing = "0.1"
wasmcloud-provider-sdk = "0.5.0"
wit-bindgen-wrpc = "0.3.7"
62 changes: 62 additions & 0 deletions examples/rust/providers/blank-slate/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
# Blank Slate Capability Provider

This capability provider is a blank slate for creating providers with custom capabilities. It uses the [wasmcloud-provider-sdk](https://crates.io/crates/wasmcloud-provider-sdk) and implements the [Provider](https://docs.rs/wasmcloud-provider-sdk/0.5.0/wasmcloud_provider_sdk/trait.Provider.html) trait with an example handler that will persist the links that target the provider (target links) and links where the provider is the source and targets a component (source links).

The purpose of this example is to provide comprehensive comments on the usage of our wasmCloud provider SDK, from serving RPC exports to invoking component imports. The code is informative to read through and provides a base for extending wasmCloud with custom capabilities.

## Building

Prerequisites:

1. [Rust toolchain](https://www.rust-lang.org/tools/install)
1. [wash](https://wasmcloud.com/docs/installation)

You can build this capability provider by running `wash build`. You can build the included test component with `wash build -p ./component`.

## Running to test

Prerequisites:

1. [Rust toolchain](https://www.rust-lang.org/tools/install)
1. [nats-server](https://github.com/nats-io/nats-server)
1. [nats-cli](https://github.com/nats-io/natscli)

You can run this capability provider as a binary by passing a simple base64 encoded [HostData](https://docs.rs/wasmcloud-core/0.6.0/wasmcloud_core/host/struct.HostData.html) struct, in order to do basic testing. For example:

```bash
nats-server -js &
echo '{"lattice_rpc_url": "0.0.0.0:4222", "lattice_rpc_prefix": "default", "provider_key": "blank-slate", "config": {"foo": "bar"}, "env_values": {}, "link_definitions": [], "otel_config": {"enable_observability": false}}' | base64 | cargo run
```

And in another terminal, you can request the health of the provider using the NATS CLI

```bash
nats req "wasmbus.rpc.default.blank-slate.default.health '{}'
```
Additionally, you can invoke the provider directly which will send test data to each linked component
```bash
wash call blank-slate wasmcloud:example/system-info.call
```
## Running as an application
You can deploy this provider, along with a [prebuilt component](./component/) for testing, by deploying the [wadm.yaml](./wadm.yaml) application.
```bash
# Launch wasmCloud in the background
wash up -d
# Deploy the application
wash app deploy ./wadm.yaml
```
## Customizing
Customizing this provider to meet your needs of a custom capability takes just a few steps.
1. Update the [wit/world.wit](./wit/world.wit) to include the data types and functions that model your custom capability. You can use the example as a base and the [component model WIT reference](https://component-model.bytecodealliance.org/design/wit.html) as a guide for types and keywords.
1. Implement any provider `export`s in [src/provider.rs](./src/provider.rs) inside of the `impl Handler {}` block.
1. Use the methods inside of the `impl Provider {}` block to handle invoking components. For inspiration, take a look at our other capability providers that implement various capabilities like HTTP, Messaging, Key-Value in the [crates/provider-\*](../../../../crates/) folder.
Have any questions? Please feel free to [file an issue](https://github.com/wasmCloud/wasmCloud/issues/new/choose) and/or join us on the [wasmCloud slack](https://slack.wasmcloud.com)!
5 changes: 5 additions & 0 deletions examples/rust/providers/blank-slate/component/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
# Rust build artifacts
Cargo.lock

# Wash build artifacts
build/
12 changes: 12 additions & 0 deletions examples/rust/providers/blank-slate/component/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
[package]
name = "blank-slate-test-component"
edition = "2021"
version = "0.1.0"

[workspace]

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

[dependencies]
wit-bindgen = { version = "0.24", features = ["default"] }
11 changes: 11 additions & 0 deletions examples/rust/providers/blank-slate/component/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
# Blank slate test component

This component is meant to test the [blank slate capability provider](../) by an implementation of the interface on the component.

## Build

Use `wash build` to build this component. A prebuilt component is included for easy deployment of the provider.

## Deploy

Use the [wadm.yaml](../wadm.yaml) in the parent directory to deploy this component alongside the provider.
Binary file not shown.
20 changes: 20 additions & 0 deletions examples/rust/providers/blank-slate/component/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
wit_bindgen::generate!();

use crate::exports::wasmcloud::example::process_data::Data;
use crate::exports::wasmcloud::example::process_data::Guest;
use crate::wasi::logging::logging::*;
use crate::wasmcloud::example::system_info::Kind;

struct BlankSlateComponent;

impl Guest for BlankSlateComponent {
fn process(data: Data) -> String {
log(Level::Info, "", &format!("Data received: {:?}", data));
// Request OS and architecture information
let os = crate::wasmcloud::example::system_info::request_info(Kind::Os);
let arch = crate::wasmcloud::example::system_info::request_info(Kind::Arch);
format!("Provider is running on {os}-{arch}").to_string()
}
}

export!(BlankSlateComponent);
5 changes: 5 additions & 0 deletions examples/rust/providers/blank-slate/component/wasmcloud.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
name = "Blank slate test component"
language = "rust"
type = "component"

[component]
9 changes: 9 additions & 0 deletions examples/rust/providers/blank-slate/component/wit/deps.lock
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
[blankslate]
path = "../../wit"
sha256 = "3af8ebbb348273a8a541440c4fbad2c739d03257903ab172d6722c68a694f86c"
sha512 = "2d83b11fd3ac592c4ed87a05feefae1d26803b626f5a425a109202196b6d951bfb407cb4f97c6209891ef70aeb3b86b8b8338c880d46fc1976fa7a9b5ca7201b"

[logging]
url = "https://github.com/WebAssembly/wasi-logging/archive/main.tar.gz"
sha256 = "9676b482485bb0fd2751a390374c1108865a096b7037f4b5dbe524f066bfb06e"
sha512 = "30a621a6d48a0175e8047c062e618523a85f69c45a7c31918da2b888f7527fce1aca67fa132552222725d0f6cdcaed95be7f16c28488d9468c0fad00cb7450b9"
2 changes: 2 additions & 0 deletions examples/rust/providers/blank-slate/component/wit/deps.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
blankslate = "../../wit"
logging = "https://github.com/WebAssembly/wasi-logging/archive/main.tar.gz"
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
package wasmcloud:example;

// This interface is generic and includes a function to process some
// data, returning a string result.
// We'll use this to send structured data to a component for processing.
interface process-data {
record data {
name: string,
count: u32,
}

// Send structured data to the component for processing
process: func(data: data) -> string;
}

// While processing data, sometimes a component may need to request
// information about the system it's running on. The component isn't
// allowed to access this information directly, so it can request it
// from the provider.
interface system-info {
enum kind {
OS,
ARCH,
}

// Request information about the system the provider is running on
request-info: func(kind: kind) -> string;

// Example export to call from the provider for testing
call: func() -> string;
}

// The `world` defines all of the imports and exports our provider can use / must implement.
world provider {
// Providers `import` functions that it can call on a component
import process-data;
// Providers `export` functions that a component can call
export system-info;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
/// WASI Logging is a logging API intended to let users emit log messages with
/// simple priority levels and context values.
interface logging {
/// A log level, describing a kind of message.
enum level {
/// Describes messages about the values of variables and the flow of
/// control within a program.
trace,

/// Describes messages likely to be of interest to someone debugging a
/// program.
debug,

/// Describes messages likely to be of interest to someone monitoring a
/// program.
info,

/// Describes messages indicating hazardous situations.
warn,

/// Describes messages indicating serious errors.
error,

/// Describes messages indicating fatal errors.
critical,
}

/// Emit a log message.
///
/// A log message has a `level` describing what kind of message is being
/// sent, a context, which is an uninterpreted string meant to help
/// consumers group similar messages, and a string containing the message
/// text.
log: func(level: level, context: string, message: string);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package wasi:logging;

world imports {
import logging;
}
12 changes: 12 additions & 0 deletions examples/rust/providers/blank-slate/component/wit/world.wit
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package wasmcloud:blankslate;

world component {
// Import logging for processing data
import wasi:logging/logging;

// Notice here the component has the inverse direction for importing/exporting interfaces
// compared to the provider. This allows us to compose the component with the provider, each
// import linking up to an export.
import wasmcloud:example/system-info;
export wasmcloud:example/process-data;
}
12 changes: 12 additions & 0 deletions examples/rust/providers/blank-slate/project-generate.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
[template]

raw = [
"*.par.gz",
"*.par",
]
exclude = [
"target/",
"keys/",
"build/",
"*.lock",
]
21 changes: 21 additions & 0 deletions examples/rust/providers/blank-slate/src/config.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
use std::collections::HashMap;

use serde::{Deserialize, Serialize};

/// Configuration for this provider, which is passed to the provider from the host.
#[derive(Debug, Default, Clone, Serialize, Deserialize, PartialEq)]
pub struct ProviderConfig {
values: HashMap<String, String>,
}

impl From<&HashMap<String, String>> for ProviderConfig {
/// Construct configuration struct from the passed config values.
///
/// For this example, we just store the values directly for any later reference.
/// You can use this as a base to create your own strongly typed configuration struct.
fn from(values: &HashMap<String, String>) -> ProviderConfig {
ProviderConfig {
values: values.clone(),
}
}
}
20 changes: 20 additions & 0 deletions examples/rust/providers/blank-slate/src/main.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
//! A blank slate provider that's meant to inform developers how to build a capability provider.
//! The implementation in `./provider.rs` uses the `wasmcloud-provider-sdk` to provide a scaffold
//! for building a capability provider with a custom interface. Take note of the documentation
//! comments in the code to understand how to build a capability provider.

mod config;
mod provider;

use provider::BlankSlateProvider;

/// Capability providers are native executables, so the entrypoint is the same as any other Rust
/// binary, `main()`. Typically the `main` function is kept simple and the provider logic is
/// implemented in a separate module. Head to the `provider.rs` file to see the implementation of
/// the `BlankSlateProvider`.
#[tokio::main]
async fn main() -> anyhow::Result<()> {
BlankSlateProvider::run().await?;
eprintln!("Blank slate provider exiting");
Ok(())
}
Loading

0 comments on commit 73ad00a

Please sign in to comment.