Skip to content

Commit 58c40c0

Browse files
committed
docs: address first round of feedback
docs: update the timer example docs: add page with a general overview of language support ref #136 Signed-off-by: Radu Matei <[email protected]>
1 parent 4701968 commit 58c40c0

28 files changed

+252
-139
lines changed

Cargo.lock

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ members = [
4747
"crates/outbound-http",
4848
"crates/redis",
4949
"crates/templates",
50-
"examples/spin-timer-echo",
50+
"examples/spin-timer",
5151
"sdk/rust",
5252
"sdk/rust/macro"
5353
]

build.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ fn main() {
2323
"crates/http/benches/spin-http-benchmark",
2424
);
2525
build_wasm_test_program("wagi-benchmark.wasm", "crates/http/benches/wagi-benchmark");
26-
build_wasm_test_program("echo.wasm", "examples/spin-timer-echo/example");
26+
build_wasm_test_program("echo.wasm", "examples/spin-timer/example");
2727

2828
cargo_build(RUST_HTTP_INTEGRATION_TEST);
2929
cargo_build(RUST_HTTP_INTEGRATION_ENV_TEST);

docs/content/configuration.md

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -64,8 +64,8 @@ Each `component` object has the following fields:
6464
- a string with the path to a local file containing the WebAssembly module for
6565
the component OR
6666
- a pair of `reference` (REQUIRED) and `parcel` (REQUIRED) fields pointing to
67-
a remote bindle package (Note that this is currently not implemented, see
68-
[#135](https://github.com/fermyon/spin/issues/135)).
67+
a remote bindle package
68+
([Planned in #135](https://github.com/fermyon/spin/issues/135)).
6969
- `environment` (OPTIONAL): Environment variables to be made available inside
7070
the WebAssembly module at runtime.
7171
- `files` (OPTIONAL): Files to be made available inside the WebAssembly module
@@ -75,7 +75,7 @@ Each `component` object has the following fields:
7575
- a mapping of a `source` (REQUIRED), a directory relative to `spin.toml` and
7676
`destination` (REQUIRED), the absolute mount path to be mapped inside the
7777
WebAssembly module. For example
78-
`{ source = "/content/", destination = "/"}`.
78+
`{ source = "content/", destination = "/"}`.
7979
- `allowed_http_hosts` (OPTIONAL): List of HTTP hosts the component is allowed
8080
to make HTTP requests to (using the
8181
[WASI experimental HTTP library](https://github.com/deislabs/wasi-experimental-http))
@@ -104,7 +104,7 @@ Each `component` object has the following fields:
104104
parameters of the request, formatted as arguments. The default is to
105105
follow the CGI specification, and pass `${SCRIPT_NAME} ${ARGS}`
106106
- `entrypoint` (OPTIONAL): The name of the function that should be called
107-
as the entrypoint to this handler. By default, it is `_start` (which in
107+
as the entry point to this handler. By default, it is `_start` (which in
108108
most languages translates to calling `main` in the guest module).
109109
- `redis`: The configuration for a Redis component. This has the following fields:
110110
- `channel` (REQUIRED): The Redis channel for which, whenever a new message
@@ -124,7 +124,7 @@ route = "/static/..."
124124
```
125125

126126
- a Wagi HTTP component that contains file mounts and sets the module `argv` and
127-
invokes a custom export function as the entrypoint:
127+
invokes a custom export function as the entry point:
128128

129129
```toml
130130
[[component]]

docs/content/extending-and-embedding.md

Lines changed: 75 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ date = "2022-03-14T00:22:56Z"
55
url = "https://github.com/fermyon/spin/blob/main/docs/content/extending-and-embedding.md"
66
---
77

8-
> The complete example for extending and embedding Spin [can be found on GitHub](https://github.com/fermyon/spin/tree/main/examples/spin-timer-echo).
8+
> The complete example for extending and embedding Spin [can be found on GitHub](https://github.com/fermyon/spin/tree/main/examples/spin-timer).
99
1010
Spin currently implements triggers and application models for:
1111

@@ -20,36 +20,41 @@ In this document we will explore how to extend Spin with custom event sources
2020
(triggers) and application models built on top of the WebAssembly component
2121
model, as well as how to embed Spin in your application.
2222

23-
The current application types that can be implemented with Spin have entrypoints
23+
In this article we will build a Spin trigger to run the applications based on a
24+
timer, executing Spin components at configured time interval.
25+
26+
The current application types that can be implemented with Spin have entry points
2427
defined using
2528
[WebAssembly Interface (WIT)]((https://github.com/bytecodealliance/wit-bindgen/blob/main/WIT.md)):
2629

2730
```fsharp
28-
// The entrypoint for an HTTP handler.
31+
// The entry point for an HTTP handler.
2932
handle-http-request: function(req: request) -> response
3033
31-
// The entrypoint for a Redis handler.
34+
// The entry point for a Redis handler.
3235
handle-redis-message: function(msg: payload) -> expected<_, error>
3336
```
3437

35-
Let's see how define a new application entrypoint for Spin:
38+
The entry point we want to execute for our timer trigger takes a string as its
39+
only argument (and the trigger will populate that with the current date and time),
40+
and it expects a string as the only return value. This is purposefully chosen
41+
to be a simple function signature:
3642

3743
```fsharp
38-
// examples/spin-timer-echo/echo.wit
39-
echo: function(msg: string) -> string
44+
// examples/spin-timer/spin-timer.wit
45+
handle-timer-request: function(msg: string) -> string
4046
```
4147

42-
This is the function signature that all "echo" components must implement, and
43-
which is used by the "echo" executor when instantiating and invoking the
44-
component.
48+
This is the function that all components executed by the timer trigger must
49+
implement, and which is used by the timer executor when instantiating and
50+
invoking the component.
4551

46-
Let's define a new trigger for our new application type — a timer-based trigger:
52+
Let's have a look at building the timer trigger:
4753

4854
```rust
49-
// examples/spin-timer-echo/src/lib.rs
50-
wit_bindgen_wasmtime::import!("examples/spin-timer-echo/echo.wit");
51-
52-
type ExecutionContext = spin_engine::ExecutionContext<echo::EchoData>;
55+
// examples/spin-timer/src/main.rs
56+
wit_bindgen_wasmtime::import!("spin-timer.wit");
57+
type ExecutionContext = spin_engine::ExecutionContext<spin_timer::SpinTimerData>;
5358

5459
/// A custom timer trigger that executes the
5560
/// first component of an application on every interval.
@@ -66,10 +71,10 @@ pub struct TimerTrigger {
6671

6772
A few important things to note from the start:
6873

69-
- we use the WIT defined entrypoint with the
74+
- we use the WIT defined entry point with the
7075
[Bytecode Alliance `wit-bindgen` project](https://github.com/bytecodealliance/wit-bindgen)
71-
to generate "import" bindings based on the entrypoint — this generates code that
72-
allows us to easily invoke the entrypoint from application components that
76+
to generate "import" bindings based on the entry point — this generates code that
77+
allows us to easily invoke the entry point from application components that
7378
implement our new application model.
7479
- the new trigger has a field that contains a `Configuration<CoreComponent>`
7580
in most cases, either `CoreComponent` will have to be updated with new trigger
@@ -81,7 +86,7 @@ creating the trigger (in the `new` function, you get access to the underlying
8186
Wasmtime store, instance, and linker, which can be configured as necessary).
8287

8388
Finally, whenever there is a new event (in the case of our timer-based trigger
84-
every `n` seconds), we execute the entrypoint of a selected component:
89+
every `n` seconds), we execute the entry point of a selected component:
8590

8691
```rust
8792
/// Execute the first component in the application configuration.
@@ -91,12 +96,15 @@ async fn handle(&self, msg: String) -> Result<()> {
9196
self.engine
9297
.prepare_component(&self.app.components[0].id, None, None, None, None)?;
9398

94-
// spawn a new thread and call the `echo` function from the WebAssembly module
99+
// spawn a new thread and call the entry point function from the WebAssembly module
95100
let res = spawn_blocking(move || -> Result<String> {
96-
// use the auto-generated WIT bindings to get the Wasm exports and call the `echo` export.
97-
let e = echo::Echo::new(&mut store, &instance, |host| host.data.as_mut().unwrap())?;
98-
Ok(e.echo(&mut store, &msg)?)
99-
}).await??;
101+
// use the auto-generated WIT bindings to get the Wasm exports and call the `handle-timer-request` function.
102+
let t = spin_timer::SpinTimer::new(&mut store, &instance, |host| {
103+
host.data.as_mut().unwrap()
104+
})?;
105+
Ok(t.handle_timer_request(&mut store, &msg)?)
106+
})
107+
.await??;
100108
// do something with the result.
101109
log::info!("{}\n", res);
102110
Ok(())
@@ -109,7 +117,7 @@ A few notes:
109117
and it handles taking the Wasmtime pre-instantiated module, mapping all the
110118
component files, environment variables, and allowed HTTP domains, populating
111119
the Wasmtime store with the appropriate data, and returning the store and instance.
112-
- invoking the entrypoint `echo` is done in this example in a new Tokio thread —
120+
- invoking the entry point `handle-timer-request` is done in this example in a new Tokio thread —
113121
this is an implementation choice based on the needs of the trigger.
114122
- the return value from the component (a string in this example) can then be
115123
used — in the case of the HTTP trigger, this is an HTTP response, which is then
@@ -119,13 +127,55 @@ This is very similar to how the [HTTP](/http-trigger) and [Redis](/redis-trigger
119127
triggers are implemented, and it is the recommended way to extend Spin with your
120128
own trigger and application model.
121129

130+
Writing components for the new trigger can be done by using the
131+
[`wit-bindgen` tooling](https://github.com/bytecodealliance/wit-bindgen) from
132+
Rust and other supported languages (see [the example in Rust](https://github.com/fermyon/spin/tree/main/examples/spin-timer/example)):
133+
134+
```rust
135+
// automatically generate Rust bindings that help us implement the
136+
// `handle-timer-request` function that the trigger will execute.
137+
wit_bindgen_rust::export!("../spin-timer.wit");
138+
...
139+
fn handle_timer_request(msg: String) -> String {
140+
format!("ECHO: {}", msg)
141+
}
142+
```
143+
144+
Components can be compiled to WebAssembly, then used from a `spin.toml`
145+
application configuration.
146+
122147
Embedding the new trigger in a Rust application is done by creating a new trigger
123148
instance, then calling its `run` function:
124149

125150
```rust
151+
// app() is a utility function that generates a complete application configuration.
126152
let trigger = TimerTrigger::new(Duration::from_secs(1), app()).await?;
153+
// run the trigger indefinitely
127154
trigger.run().await
128155
```
129156

130157
> We are exploring [APIs for embedding Spin from other programming languages](https://github.com/fermyon/spin/issues/197)
131158
> such as Go or C#.
159+
160+
In this example, we built a simple timer trigger — building more complex triggers
161+
would also involve updating the Spin application configuration, and extending
162+
the application-level trigger configuration, as well as component-level
163+
trigger configuration (an example of component-level trigger configuration
164+
for this scenario would be each component being able to define its own
165+
independent time interval for scheduling the execution).
166+
167+
## Other ways to extend and use Spin
168+
169+
Besides building custom triggers, the internals of Spin could also be used
170+
independently:
171+
172+
- the Spin execution context can be used entirely without a `spin.toml`
173+
application configuration — for embedding scenarios, the configuration for the
174+
execution can be constructed without a `spin.toml` (see [issue #229](https://github.com/fermyon/spin/issues/229)
175+
for context)
176+
- the standard way of distributing a Spin application can be changed by
177+
re-implementing the [`loader`](https://github.com/fermyon/spin/tree/main/crates/loader)
178+
and [`publish`](https://github.com/fermyon/spin/tree/main/crates/publish) crates —
179+
all is required is that loading the application returns a valid
180+
`Configuration<CoreComponent>` that the Spin execution context can use to
181+
instantiate and execute components.

docs/content/go-components.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@ to build programs written in Go as Spin components.
1414
> This guide assumes you are familiar with the Go programming language, and that
1515
> you have
1616
> [configured the TinyGo toolchain locally](https://tinygo.org/getting-started/install/).
17+
Using TinyGo to compile components for Spin is currently required, as the
18+
[Go compiler doesn't currently have support for compiling to WASI](https://github.com/golang/go/issues/31105).
1719

1820
> All examples from this page can be found in [the Spin repository on GitHub](https://github.com/fermyon/spin/tree/main/examples).
1921
@@ -47,7 +49,7 @@ func main() {
4749

4850
The important things to note in the implementation above:
4951

50-
- the entrypoint to the component is the standard `func main()` for Go programs
52+
- the entry point to the component is the standard `func main()` for Go programs
5153
- handling the request is done by calling the `spin.HandleRequest` function,
5254
which takes a `func(w http.ResponseWriter, r *http.Request)` as parameter — these
5355
contain the HTTP request and response writer you can use to handle the request

docs/content/http-trigger.md

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ is used in Spin.
1414
The HTTP trigger in Spin is a web server. It listens for incoming requests and
1515
based on the [application configuration](/configuration), it routes them to an
1616
_executor_ which instantiates the appropriate component, executes its
17-
entrypoint function, then returns an HTTP response.
17+
entry point function, then returns an HTTP response.
1818

1919
Creating an HTTP application is done when [configuring the application](/configuration)
2020
by defining the top-level application trigger:
@@ -33,7 +33,7 @@ and the _HTTP executor_ (see details below about executors). For example:
3333
```toml
3434
[component.trigger]
3535
route = "/hello"
36-
executor = "spin"
36+
executor = { type = "spin" }
3737
```
3838

3939
- an HTTP component configured on the `/goodbye` route that uses the Wagi executor:
@@ -53,7 +53,7 @@ to all component routes defined for that application.
5353

5454
For example, if the application `base` path is `base = /base`, and a component
5555
has defined `route = /foo`, that component will be executed for requests on
56-
`http/s::<spin-up-defined-address-and-port>/base/bar`.
56+
`http/s://<spin-up-defined-address-and-port>/base/foo`.
5757

5858
Components can either define exact routes, for example `route = /bar/baz`, where
5959
the component will be invoked only for requests on `/base/bar/baz`, or they
@@ -103,7 +103,7 @@ community on building exciting new features and tools for it. As a result, the
103103
Spin HTTP _executor_ is defined using WebAssembly interfaces.
104104

105105
> The WebAssembly component model is in its early stages, and during the `0.x`
106-
> releases of Spin, the triggers and application entrypoints will suffer
106+
> releases of Spin, the triggers and application entry points will suffer
107107
> breaking changes, particularly around the primitive types used to defined
108108
> the HTTP objects and function signatures — i.e. bodies will become streams,
109109
> handler functions will become asynchronous.
@@ -149,13 +149,13 @@ record response {
149149
> HTTP requests, and you can see its implementation in
150150
> [the WASI toolkit repository](https://github.com/fermyon/wasi-experimental-toolkit).
151151
152-
Then, we define the entrypoint for a Spin HTTP component:
152+
Then, we define the entry point for a Spin HTTP component:
153153

154154
```fsharp
155155
// wit/ephemeral/spin-http.wit
156156
157157
use * from http-types
158-
// The entrypoint for an HTTP handler.
158+
// The entry point for an HTTP handler.
159159
handle-http-request: function(req: request) -> response
160160
```
161161

docs/content/index.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,8 +30,8 @@ fn hello_world(req: Request) -> Result<Response> {​
3030
Spin applications are comprised of one or more function-based _components_, and
3131
follow the event-driven model — they are executed as the result of events being
3232
generated by _triggers_ (for example an HTTP server receiving requests, or a queue
33-
subscription receiving messages). On each new event, _the entrypoint_ of a
34-
component is executed by Spin. The entrypoints to components are _functions_.
33+
subscription receiving messages). On each new event, _the entry point_ of a
34+
component is executed by Spin. The entry points to components are _functions_.
3535
This, together with the fact that they are invoked in response to events, brings
3636
the Spin application model closer to the Function-as-a-Service model.
3737

docs/content/other-languages.md

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
title = "Building Spin components in other languages"
2+
template = "main"
3+
date = "2022-03-14T00:22:56Z"
4+
[extra]
5+
url = "https://github.com/fermyon/spin/blob/main/docs/content/other-languages.md"
6+
---
7+
8+
> This document is continuously evolving as we improve language SDKs and add
9+
> more examples on how to build Spin components in various programming languages.
10+
11+
> See the document on writing [Rust](/rust-components) and [Go](/go-components)
12+
> components for Spin for detailed guides.
13+
14+
WebAssembly is becoming [a popular compilation target for programming languages](https://www.fermyon.com/wasm-languages/webassembly-language-support), and as language toolchains add support for the
15+
[WebAssembly component model](https://github.com/WebAssembly/component-model),
16+
building Spin components will also become supported.
17+
18+
As a general rule:
19+
20+
- if your language supports the
21+
[WebAssembly component model](https://github.com/WebAssembly/component-model),
22+
building Spin components is supported either through an official Spin SDK
23+
(such as [the Spin SDK for Rust](/rust-components)), or through using
24+
bindings generators like [`wit-bindgen`](https://github.com/bytecodealliance/wit-bindgen)
25+
(for languages such as C and C++)
26+
- if your language compiles to WASI, but doesn't have support for the component
27+
model, you can build [Spin HTTP components](/http-trigger) that use the
28+
Wagi executor — for example in languages such as
29+
[Grain](https://github.com/deislabs/hello-wagi-grain),
30+
[AssemblyScript](https://github.com/deislabs/hello-wagi-as), or
31+
[Python](https://github.com/fermyon/wagi-python).
32+
- if your language doesn't currently compile to WebAssembly, there is no way to
33+
build and run Spin components in that programming language

0 commit comments

Comments
 (0)