docs: update documentation website

This commit updates the documentation website with more sections,
language guides for Rust and Go, as well as example for embedding.

Signed-off-by: Radu Matei <radu.matei@fermyon.com>
This commit is contained in:
Radu Matei 2022-03-18 07:50:51 +02:00
parent 491e5557eb
commit 127907b313
No known key found for this signature in database
GPG Key ID: 53A4E7168B7782C2
43 changed files with 4230 additions and 4560 deletions

1
.gitignore vendored
View File

@ -4,3 +4,4 @@ cache
node_modules
ignored-assets
main.wasm
.parcel-cache

183
Cargo.lock generated
View File

@ -73,9 +73,9 @@ dependencies = [
[[package]]
name = "async-stream"
version = "0.3.2"
version = "0.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "171374e7e3b2504e0e5236e3b59260560f9fe94bfe9ac39ba5e4e929c5590625"
checksum = "dad5c83079eae9969be7fadefe640a1c566901f05ff91ab221de4b6f68d9507e"
dependencies = [
"async-stream-impl",
"futures-core",
@ -83,9 +83,9 @@ dependencies = [
[[package]]
name = "async-stream-impl"
version = "0.3.2"
version = "0.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "648ed8c8d2ce5409ccd57453d9d1b214b342a0d69376a6feda1fd6cae3299308"
checksum = "10f203db73a71dfa2fb6dd22763990fa26f3d2625a6da2da900d23b87d26be27"
dependencies = [
"proc-macro2",
"quote",
@ -318,9 +318,9 @@ checksum = "c4872d67bab6358e59559027aa3b9157c53d9358c51423c17554809a8858e0f8"
[[package]]
name = "cap-fs-ext"
version = "0.24.1"
version = "0.24.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "71f5b22f0dc04ed3404bfae102654978bfd425c5568851a89eb4d4b8f58e9590"
checksum = "16812cf8cc096ebfddba83ad6cc768635f4d53eb6bd060f7b80866556493874a"
dependencies = [
"cap-primitives",
"cap-std",
@ -330,9 +330,9 @@ dependencies = [
[[package]]
name = "cap-primitives"
version = "0.24.1"
version = "0.24.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "defd283f080043a702c362203c2646a4e0a2fff99baede1eea1416239f0af220"
checksum = "ef65294d0067dd0167a84e5d50aaa52d999ab4fc7cfa59ff2ad3906afb033b36"
dependencies = [
"ambient-authority",
"errno",
@ -349,9 +349,9 @@ dependencies = [
[[package]]
name = "cap-rand"
version = "0.24.1"
version = "0.24.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "12627a93bdbbe4fb32c1ea2b2c2665ba54c49eb48f1139440ce4c6a279701461"
checksum = "a124986fcbe880abe9ca6aad4189de44b37dbcf6ac0079f8dd9f94b379484b94"
dependencies = [
"ambient-authority",
"rand 0.8.5",
@ -359,9 +359,9 @@ dependencies = [
[[package]]
name = "cap-std"
version = "0.24.1"
version = "0.24.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7cbe56c8ed4cdf8360deb80dcced538968d3bac45e71befee65e95a0e262caf2"
checksum = "3430d1b5ba78fa381eb227525578d2b85d77b42ff49be85d1e72a94f305e603c"
dependencies = [
"cap-primitives",
"io-extras",
@ -372,9 +372,9 @@ dependencies = [
[[package]]
name = "cap-time-ext"
version = "0.24.1"
version = "0.24.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7a34e5a4375b768b3acaec25dd7b53bcc15c801258c43a0ef2da2027f2028e46"
checksum = "b9ee4390904645f384bd4e03125a4ed4d1167e9f195931c41323bacc8a2328ce"
dependencies = [
"cap-primitives",
"once_cell",
@ -505,9 +505,9 @@ dependencies = [
[[package]]
name = "cpufeatures"
version = "0.2.1"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "95059428f66df56b63431fdb4e1947ed2190586af5c5a8a8b71122bdf5a7f469"
checksum = "59a6001667ab124aebae2a495118e11d30984c3a653e99d86d58971708cf5e4b"
dependencies = [
"libc",
]
@ -650,9 +650,9 @@ dependencies = [
[[package]]
name = "crossbeam-channel"
version = "0.5.2"
version = "0.5.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e54ea8bc3fb1ee042f5aace6e3c6e025d3874866da222930f70ce62aceba0bfa"
checksum = "5aaa7bd5fb665c6864b5f963dd9097905c54125909c7aa94c9e18507cdbe6c53"
dependencies = [
"cfg-if",
"crossbeam-utils",
@ -671,10 +671,11 @@ dependencies = [
[[package]]
name = "crossbeam-epoch"
version = "0.9.7"
version = "0.9.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c00d6d2ea26e8b151d99093005cb442fb9a37aeaca582a03ec70946f49ab5ed9"
checksum = "1145cf131a2c6ba0615079ab6a638f7e1973ac9c2634fcbeaaad6114246efe8c"
dependencies = [
"autocfg",
"cfg-if",
"crossbeam-utils",
"lazy_static",
@ -684,9 +685,9 @@ dependencies = [
[[package]]
name = "crossbeam-utils"
version = "0.8.7"
version = "0.8.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b5e5bed1f1c269533fa816a0a5492b3545209a205ca1a54842be180eb63a16a6"
checksum = "0bf124c720b7686e3c2663cf54062ab0f68a88af2fb6a030e87e30bf721fcb38"
dependencies = [
"cfg-if",
"lazy_static",
@ -694,9 +695,9 @@ dependencies = [
[[package]]
name = "crossterm"
version = "0.23.0"
version = "0.23.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "77b75a27dc8d220f1f8521ea69cd55a34d720a200ebb3a624d9aa19193d3b432"
checksum = "f1fd7173631a4e9e2ca8b32ae2fad58aab9843ea5aaf56642661937d87e28a3e"
dependencies = [
"bitflags",
"crossterm_winapi",
@ -837,9 +838,9 @@ dependencies = [
[[package]]
name = "dirs-sys"
version = "0.3.6"
version = "0.3.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "03d86534ed367a67548dc68113a0f5db55432fdfbb6e6f9d77704397d95d5780"
checksum = "1b1d1d91c932ef41c0f2663aa8b0ca0342d444d842c06914aa0a7e352d0bada6"
dependencies = [
"libc",
"redox_users",
@ -882,9 +883,9 @@ checksum = "453440c271cf5577fd2a40e4942540cb7d0d2f85e27c8d07dd0023c925a67541"
[[package]]
name = "ed25519"
version = "1.4.0"
version = "1.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "eed12bbf7b5312f8da1c2722bc06d8c6b12c2d86a7fb35a194c7f3e6fc2bbe39"
checksum = "3d5c4b5e5959dc2c2b89918d8e2cc40fcdd623cef026ed09d2f0ee05199dc8e4"
dependencies = [
"signature",
]
@ -1234,9 +1235,9 @@ checksum = "9b919933a397b79c37e33b77bb2aa3dc8eb6e165ad809e58ff75bc7db2e34574"
[[package]]
name = "h2"
version = "0.3.11"
version = "0.3.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d9f1f717ddc7b2ba36df7e871fd88db79326551d3d6f1fc406fbfd28b582ff8e"
checksum = "62eeb471aa3e3c9197aa4bfeabfe02982f6dc96f750486c0bb0009ac58b26d2b"
dependencies = [
"bytes 1.1.0",
"fnv",
@ -1411,7 +1412,7 @@ dependencies = [
"rustls 0.20.4",
"rustls-native-certs",
"tokio",
"tokio-rustls 0.23.2",
"tokio-rustls 0.23.3",
]
[[package]]
@ -1619,9 +1620,9 @@ checksum = "884e2677b40cc8c339eaefcb701c32ef1fd2493d71118dc0ca4b6a736c93bd67"
[[package]]
name = "libc"
version = "0.2.119"
version = "0.2.121"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1bf2e165bb3457c8e098ea76f3e3bc9db55f87aa90d52d0e6be741470916aaa4"
checksum = "efaa7b300f3b5fe8eb6bf21ce3895e1751d9665086af2d64b42f19701015ff4f"
[[package]]
name = "libgit2-sys"
@ -1768,9 +1769,9 @@ dependencies = [
[[package]]
name = "mini-internal"
version = "0.1.23"
version = "0.1.24"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "68a9cec2df85e49afd5bd4c4afb21678e2177736ebe31e4b8836b821ffd806bd"
checksum = "60bbf2d78a45808eba478a0660f050bbce70dafc011e275cf00f6f8500a8be88"
dependencies = [
"proc-macro2",
"quote",
@ -1779,9 +1780,9 @@ dependencies = [
[[package]]
name = "miniserde"
version = "0.1.23"
version = "0.1.24"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6b4f376d897db27bdf4e318542322eca8fd41839cc56a12cc8f8d8a05b4386af"
checksum = "a89fcaf0064d3361240ae91a1f773d874302a62b4e6fdbd37b2888cb7d5aba3e"
dependencies = [
"itoa 1.0.1",
"mini-internal",
@ -1813,14 +1814,15 @@ dependencies = [
[[package]]
name = "mio"
version = "0.8.0"
version = "0.8.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ba272f85fa0b41fc91872be579b3bbe0f56b792aa361a380eb669469f68dafb2"
checksum = "52da4364ffb0e4fe33a9841a98a3f3014fb964045ce4f7a45a398243c8d6b0c9"
dependencies = [
"libc",
"log",
"miow",
"ntapi",
"wasi 0.11.0+wasi-snapshot-preview1",
"winapi",
]
@ -1996,9 +1998,9 @@ dependencies = [
[[package]]
name = "num_threads"
version = "0.1.3"
version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "97ba99ba6393e2c3734791401b66902d981cb03bf190af674ca69949b6d5fb15"
checksum = "aba1801fb138d8e85e11d0fc70baf4fe1cdfffda7c6cd34a854905df588e5ed0"
dependencies = [
"libc",
]
@ -2179,18 +2181,18 @@ checksum = "0744126afe1a6dd7f394cb50a716dbe086cb06e255e53d8d0185d82828358fb5"
[[package]]
name = "path-absolutize"
version = "3.0.11"
version = "3.0.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b288298a7a3a7b42539e3181ba590d32f2d91237b0691ed5f103875c754b3bf5"
checksum = "0a2a79d7c1c4eab523515c4561459b10516d6e7014aa76edc3ea05680d5c5d2d"
dependencies = [
"path-dedot",
]
[[package]]
name = "path-dedot"
version = "3.0.14"
version = "3.0.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4bfa72956f6be8524f7f7e2b07972dda393cb0008a6df4451f658b7e1bd1af80"
checksum = "f326e2a3331685a5e3d4633bb9836bd92126e08037cb512252f3612f616a0b28"
dependencies = [
"once_cell",
]
@ -2358,9 +2360,9 @@ dependencies = [
[[package]]
name = "quote"
version = "1.0.15"
version = "1.0.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "864d3e96a899863136fc6e99f3d7cae289dafe43bf2c5ac19b70df7210c0a145"
checksum = "b4af2ec4714533fcdf07e886f17025ace8b997b9ce51204ee69b6da831c3da57"
dependencies = [
"proc-macro2",
]
@ -2492,12 +2494,13 @@ dependencies = [
[[package]]
name = "redox_users"
version = "0.4.0"
version = "0.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "528532f3d801c87aec9def2add9ca802fe569e44a544afe633765267840abe64"
checksum = "7776223e2696f1aa4c6b0170e83212f47296a00424305117d013dfe86fb0fe55"
dependencies = [
"getrandom 0.2.5",
"redox_syscall",
"thiserror",
]
[[package]]
@ -2513,9 +2516,9 @@ dependencies = [
[[package]]
name = "regex"
version = "1.5.4"
version = "1.5.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d07a8629359eb56f1e2fb1652bb04212c072a87ba68546a04065d525673ac461"
checksum = "1a11647b6b25ff05a515cb92c365cec08801e83423a235b51e231e1808747286"
dependencies = [
"aho-corasick",
"memchr",
@ -2560,9 +2563,9 @@ dependencies = [
[[package]]
name = "reqwest"
version = "0.11.9"
version = "0.11.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "87f242f1488a539a79bac6dbe7c8609ae43b7914b7736210f239a37cccb32525"
checksum = "46a1f7aa4f35e5e8b4160449f51afc758f0ce6454315a9fa7d0d113e958c41eb"
dependencies = [
"base64 0.13.0",
"bytes 1.1.0",
@ -2584,13 +2587,13 @@ dependencies = [
"percent-encoding 2.1.0",
"pin-project-lite",
"rustls 0.20.4",
"rustls-pemfile 0.2.1",
"rustls-pemfile 0.3.0",
"serde",
"serde_json",
"serde_urlencoded",
"tokio",
"tokio-native-tls",
"tokio-rustls 0.23.2",
"tokio-rustls 0.23.3",
"tokio-util",
"url 2.2.2",
"wasm-bindgen",
@ -2638,9 +2641,9 @@ dependencies = [
[[package]]
name = "rustix"
version = "0.33.3"
version = "0.33.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a9466f25b92a648960ac1042fd3baa6b0bf285e60f754d7e5070770c813a177a"
checksum = "ef7ec6a44fba95d21fa522760c03c16ca5ee95cebb6e4ef579cab3e6d7ba6c06"
dependencies = [
"bitflags",
"errno",
@ -3142,7 +3145,7 @@ dependencies = [
"spin-engine",
"tls-listener",
"tokio",
"tokio-rustls 0.23.2",
"tokio-rustls 0.23.3",
"tracing",
"tracing-futures",
"tracing-subscriber 0.3.9",
@ -3273,6 +3276,28 @@ dependencies = [
"walkdir",
]
[[package]]
name = "spin-timer-echo"
version = "0.1.0"
dependencies = [
"anyhow",
"async-trait",
"chrono",
"env_logger",
"futures",
"log",
"spin-config",
"spin-engine",
"tokio",
"tracing",
"tracing-futures",
"tracing-subscriber 0.3.9",
"wasi-common",
"wasmtime",
"wasmtime-wasi",
"wit-bindgen-wasmtime",
]
[[package]]
name = "stable_deref_trait"
version = "1.2.0"
@ -3342,9 +3367,9 @@ checksum = "a7973cce6668464ea31f176d85b13c7ab3bba2cb3b77a2ed26abd7801688010a"
[[package]]
name = "syn"
version = "1.0.86"
version = "1.0.89"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8a65b3f4ffa0092e9887669db0eae07941f023991ab58ea44da8fe8e2d511c6b"
checksum = "ea297be220d52398dcc07ce15a209fce436d361735ac1db700cab3b6cdfb9f54"
dependencies = [
"proc-macro2",
"quote",
@ -3504,16 +3529,16 @@ checksum = "cda74da7e1a664f795bb1f8a87ec406fb89a02522cf6e50620d016add6dbbf5c"
[[package]]
name = "tls-listener"
version = "0.4.0"
version = "0.4.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "42257668593ac35c772fa8bfb4bfd4d357298d5a6b816cb9a4462ec3b7627d26"
checksum = "6e8a215badde081a06ee0a7fbc9c9f0d580c022fbdc547065f62103aef71e178"
dependencies = [
"futures-util",
"hyper",
"pin-project-lite",
"thiserror",
"tokio",
"tokio-rustls 0.23.2",
"tokio-rustls 0.23.3",
]
[[package]]
@ -3525,7 +3550,7 @@ dependencies = [
"bytes 1.1.0",
"libc",
"memchr",
"mio 0.8.0",
"mio 0.8.2",
"num_cpus",
"once_cell",
"parking_lot 0.12.0",
@ -3570,9 +3595,9 @@ dependencies = [
[[package]]
name = "tokio-rustls"
version = "0.23.2"
version = "0.23.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a27d5f2b839802bd8267fa19b0530f5a08b9c08cd417976be2a65d130fe1c11b"
checksum = "4151fda0cf2798550ad0b34bcfc9b9dcc2a9d2471c895c68f3a8818e54f2389e"
dependencies = [
"rustls 0.20.4",
"tokio",
@ -3649,9 +3674,9 @@ checksum = "360dfd1d6d30e05fda32ace2c8c70e9c0a9da713275777f5a4dbb8a1893930c6"
[[package]]
name = "tracing"
version = "0.1.31"
version = "0.1.32"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f6c650a8ef0cd2dd93736f033d21cbd1224c5a967aa0c258d00fcf7dafef9b9f"
checksum = "4a1bdf54a7c28a2bbf701e1d2233f6c77f473486b94bee4f9678da5a148dca7f"
dependencies = [
"cfg-if",
"log",
@ -3662,9 +3687,9 @@ dependencies = [
[[package]]
name = "tracing-attributes"
version = "0.1.19"
version = "0.1.20"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8276d9a4a3a558d7b7ad5303ad50b53d58264641b82914b7ada36bd762e7a716"
checksum = "2e65ce065b4b5c53e73bb28912318cb8c9e9ad3921f1d669eb0e68b4c8143a2b"
dependencies = [
"proc-macro2",
"quote",
@ -3673,9 +3698,9 @@ dependencies = [
[[package]]
name = "tracing-core"
version = "0.1.22"
version = "0.1.23"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "03cfcb51380632a72d3111cb8d3447a8d908e577d31beeac006f836383d29a23"
checksum = "aa31669fa42c09c34d94d8165dd2012e8ff3c66aca50f3bb226b68f216f2706c"
dependencies = [
"lazy_static",
"valuable",
@ -3875,9 +3900,9 @@ dependencies = [
[[package]]
name = "url-escape"
version = "0.1.0"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "55c7bdc6152ebf118ddb53188af83c07b586778661434a21a3c475f06335771c"
checksum = "44e0ce4d1246d075ca5abec4b41d33e87a6054d08e2366b63205665e950db218"
dependencies = [
"percent-encoding 2.1.0",
]
@ -4060,6 +4085,12 @@ version = "0.10.0+wasi-snapshot-preview1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1a143597ca7c7793eff794def352d41792a93c481eb1042423ff7ff72ba2c31f"
[[package]]
name = "wasi"
version = "0.11.0+wasi-snapshot-preview1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
[[package]]
name = "wasi-cap-std-sync"
version = "0.34.1"
@ -4609,9 +4640,9 @@ checksum = "504a2476202769977a040c6364301a3f65d0cc9e3fb08600b2bda150a0488316"
[[package]]
name = "winreg"
version = "0.7.0"
version = "0.10.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0120db82e8a1e0b9fb3345a539c478767c0048d842860994d96113d5b667bd69"
checksum = "80d0f4e272c85def139476380b12f9ac60926689dd2e01d4923222f40580869d"
dependencies = [
"winapi",
]

View File

@ -39,7 +39,18 @@ hyper = { version = "0.14", features = [ "full" ] }
cargo-target-dep = { git = "https://github.com/fermyon/cargo-target-dep", rev = "b7b1989fe0984c0f7c4966398304c6538e52fe49" }
[workspace]
members = [ "crates/config", "crates/engine", "crates/http", "crates/loader", "crates/outbound-http", "crates/redis", "crates/templates", "sdk/rust", "sdk/rust/macro" ]
members = [
"crates/config",
"crates/engine",
"crates/http",
"crates/loader",
"crates/outbound-http",
"crates/redis",
"crates/templates",
"examples/spin-timer-echo",
"sdk/rust",
"sdk/rust/macro"
]
[[bin]]
name = "spin"

View File

@ -23,6 +23,7 @@ fn main() {
"crates/http/benches/spin-http-benchmark",
);
build_wasm_test_program("wagi-benchmark.wasm", "crates/http/benches/wagi-benchmark");
build_wasm_test_program("echo.wasm", "examples/spin-timer-echo/example");
cargo_build(RUST_HTTP_INTEGRATION_TEST);
cargo_build(RUST_HTTP_INTEGRATION_ENV_TEST);

Binary file not shown.

View File

@ -1,13 +1,8 @@
title = "Architecture and Internals"
title = "Spin architecture and internals"
template = "main"
date = "2022-03-14T00:22:56Z"
[extra]
author = "Fermyon"
---
# Spin architecture and internals
This document aims to offer an overview to the implementation of Spin, as well
as explain how the code is structured and how all parts fit together. This
document is continuously evolving, and if you want even more detailed
@ -23,26 +18,24 @@ be pushed to the registry then referenced using its remote ID
Regardless of the application origin (local file or remote reference from the
registry), a Spin application is defined by
`spin_config::Configuration<CoreComponent>` (contained in the
[`spin-config`](../crates/config) crate), which is the canonical representation
of a Spin application.
[`spin-config`](https://github.com/fermyon/spin/tree/main/crates/config) crate),
which is the canonical representation of a Spin application.
The crate responsible for transforming a custom configuration into a canonical
Spin application is [`spin-loader`](../crates/loader), which implements loading
applications from local `spin.toml` files and from remote Bindle references (and
ensures files referenced in the application configuration are copied and mounted
at the location expected in the WebAssmebly module). Once the canonical
representation is loaded from an application source, it is passed to a trigger —
currently, the only trigger implemented is the HTTP trigger, and we will use it
as an example throughout this document.
Spin application is [`spin-loader`](https://github.com/fermyon/spin/tree/main/crates/loader),
which implements loading applications from local `spin.toml` files and from
remote Bindle references (and ensures files referenced in the application
configuration are copied and mounted at the location expected in the WebAssembly
module). Once the canonical representation is loaded from an application source,
it is passed to a trigger.
The HTTP trigger (defined in the [`spin-http`](../crates/http) crate) takes an
The HTTP trigger (defined in the `spin-http` crate) takes an
application configuration ([#40](https://github.com/fermyon/spin/issues/40)
explores a trigger handling multiple applications), starts an HTTP listener, and
for each new request, it routes it to the component configured in the
application configuration. Then, it instantiates the WebAssembly module (using a
`spin_engine::ExecutionContext`) and uses the appropriate executor (either the
[`SpinHttpExecutor`](../crates/http/src/spin.rs) or the
[`WagiHttpExecutor`](../crates/http/src/wagi.rs), based on the component
`SpinHttpExecutor` or the `WagiHttpExecutor`, based on the component
configuration) to handle the request and return the response.
## The Spin execution context
@ -50,7 +43,7 @@ configuration) to handle the request and return the response.
The Spin execution context (or "Spin engine") is the part of Spin that executes
WebAssembly components using the
[Wasmtime](https://github.com/bytecodealliance/wasmtime) WebAssembly runtime. It
is implemented in the [`spin-engine`](../crates/engine/) crate, and serves as
is implemented in the `spin-engine` crate, and serves as
the part of Spin that takes a fully formed application configuration and creates
Wasm instances based on the component configurations.

View File

@ -1,13 +1,8 @@
title = "Configuration for Spin applications"
template = "main"
date = "2022-03-14T00:22:56Z"
[extra]
author = "Fermyon"
---
# Configuration for Spin applications
Spin applications are comprised of general information, and a collection of at
least one _component_. In the example below we can see a simple HTTP application
with a single component executed when the `/hello` endpoint is accessed:
@ -26,28 +21,37 @@ source = "target/wasm32-wasi/release/spinhelloworld.wasm"
route = "/hello"
```
## Application configuration
## Configuration reference
### Application configuration
The following are the fields supported by the `spin.toml` configuration file:
- `spin_version` (REQUIRED): Spin API version. Currently, this value MUST be
`"0.1.0"`.
`"1"`.
- `name` (REQUIRED): Name of the application.
- `version` (REQUIRED): Version of the application.
- `description` (OPTIONAL): Description of the application.
- `authors` (OPTIONAL): List with the authors of the application.
- `trigger` (REQUIRED): Trigger for the application. Currently, all components
of the application must be invoked as a result of the same trigger type.
Currently, the only implemented application trigger is `http`, with the
following configuration fields:
- `type` (REQUIRED): The application trigger type with the value `"http"`.
- `base` (REQUIRED): The base path for the HTTP application which will be
prepended to the routes of all components. (For example, if `base = "/foo"`
and a component has `route = "/bar"`, the component will be invoked for
requests on `/foo/bar`.)
- `trigger` (REQUIRED): Trigger for the application. Currently, the two
implemented trigger types are:
- `http`: All components of the application are invoked as a result of
incoming HTTP requests. [The HTTP trigger](/http-trigger) configuration has
the following fields:
- `type` (REQUIRED): The application trigger type with the value `"http"`.
- `base` (REQUIRED): The base path for the HTTP application which will be
prepended to the routes of all components. (For example, if `base = "/foo"`
and a component has `route = "/bar"`, the component will be invoked for
requests on `/foo/bar`.)
- `redis`: All components of the application are invoked as a result of messages
being published on the queues of Redis instance. [The Redis trigger](/redis-trigger)
configuration has the following fields:
- `type` (REQUIRED): The application trigger type with the value `"redis"`.
- `address` (REQUIRED): The address of the Redis instance the components
are using for message subscriptions.
- a list of `component` objects (REQUIRED) defining the application components.
## Component configuration
### Component configuration
Each `component` object has the following fields:
@ -75,34 +79,38 @@ Each `component` object has the following fields:
[WASI experimental HTTP library](https://github.com/deislabs/wasi-experimental-http))
- `trigger` (REQUIRED): Trigger configuration for the component. Triggers are
the components that generate events that cause the execution of components.
Since the only implemented Spin trigger is HTTP, the component trigger
configuration currently contains HTTP component trigger configuration, which
has the following fields:
- `route` (REQUIRED): The HTTP route the component will be invoked for. It can
either be an exact route (for example `/foo/test`), or it can contain a
wildcard (`/foo/test/...`) as the last path segment, which means the
component will be invoked for every request starting with the `/foo/test`
prefix (for example `/foo/test/abc/def`).
- `executor` (REQUIRED): The executor for the HTTP component. There are
currently two executor `type`s:
- `spin` (DEFAULT): the Spin HTTP executor, which uses
[the WebAssembly component model](https://github.com/WebAssembly/component-model)
OR
- `wagi`: the Wagi CGI executor, which can be used to write components in
any language that compiles to WASI. The Wagi executor has the following
optional fields:
- `argv` (OPTIONAL): The string representation of the `argv` list that
should be passed into the handler. `${SCRIPT_NAME}` will be replaced
with the script name, and `${ARGS}` will be replaced with the query
parameters of the request, formatted as arguments. The default is to
follow the CGI specification, and pass `${SCRIPT_NAME} ${ARGS}`
- `entrypoint` (OPTIONAL): The name of the function that should be called
as the entrypoint to this handler. By default, it is `_start` (which in
most languages translates to calling `main` in the guest module).
The trigger configuration for a component must be compatible with the top-level
trigger type of the application. As such, there are two possible trigger
configurations for components, HTTP or Redis:
- `http`: The configuration for an HTTP component. This has the following fields:
- `route` (REQUIRED): The HTTP route the component will be invoked for. It can
either be an exact route (for example `/foo/test`), or it can contain a
wildcard (`/foo/test/...`) as the last path segment, which means the
component will be invoked for every request starting with the `/foo/test`
prefix (for example `/foo/test/abc/def`).
- `executor` (REQUIRED): The executor for the HTTP component. There are
currently two executor `type`s:
- `spin` (DEFAULT): the Spin HTTP executor, which uses
[the WebAssembly component model](https://github.com/WebAssembly/component-model)
OR
- `wagi`: the Wagi CGI executor, which can be used to write components in
any language that compiles to WASI. The Wagi executor has the following
optional fields:
- `argv` (OPTIONAL): The string representation of the `argv` list that
should be passed into the handler. `${SCRIPT_NAME}` will be replaced
with the script name, and `${ARGS}` will be replaced with the query
parameters of the request, formatted as arguments. The default is to
follow the CGI specification, and pass `${SCRIPT_NAME} ${ARGS}`
- `entrypoint` (OPTIONAL): The name of the function that should be called
as the entrypoint to this handler. By default, it is `_start` (which in
most languages translates to calling `main` in the guest module).
- `redis`: The configuration for a Redis component. This has the following fields:
- `channel` (REQUIRED): The Redis channel for which, whenever a new message
is published, the component will be invoked.
## Examples
- Spin HTTP component that contains the files in `static/` mapped to `/`:
- a Spin HTTP component that contains the files in `static/` mapped to `/`:
```toml
[[component]]
@ -113,7 +121,7 @@ files = [ { source = "static/", destination = "/" } ]
route = "/static/..."
```
- a Wagi component that contains file mounts and sets the module `argv` and
- a Wagi HTTP component that contains file mounts and sets the module `argv` and
invokes a custom export function as the entrypoint:
```toml
@ -123,5 +131,15 @@ id = "env"
files = [ "content/**/*" , "templates/*", "scripts/*", "config/*"]
[component.trigger]
route = "/..."
executor = { type="wagi", argv="test ${SCRIPT_NAME} ${ARGS} done", entrypoint = "some-other-export-function" }
executor = { type = "wagi", argv = "test ${SCRIPT_NAME} ${ARGS} done", entrypoint = "some-other-export-function" }
```
- a Redis component that is invoked for new messages on the `messages` channel:
```toml
[[component]]
id = "echo-message"
source = "spinredis.wasm"
[component.trigger]
channel = "messages"
```

View File

@ -1,26 +1,20 @@
title = "Contributing to Spin"
template = "main"
date = "2022-03-14T00:22:56Z"
[extra]
author = "Fermyon"
---
# Contributing to Spin
We are delighted that you are interested in making Spin better! Thank you! This
document will guide you in making your first contribution to the project.
First, any contribution and interaction on any Fermyon project MUST follow our
[code of conduct](https://www.fermyon.com/code-of-conduct). Thank you for being
part of an inclusive and open community!
We welcome and appreciate contributions of all types — opening issues, fixing
typos, adding examples, one-liner code fixes, tests, or complete features.
If you plan on contributing anything complex, please go through the issue and PR
queues first to make sure someone else has not started working on it. If it
doesn't exist already, please open an issue so you have a change to get feedback
doesn't exist already, please open an issue so you have a chance to get feedback
from the community and the maintainers before you start working on your feature.
## Making code contributions to Spin
@ -49,7 +43,7 @@ configured:
Once you have set up the prerequisites and identified the contribution you want
to make to Spin, make sure you can correctly build the project:
```shell
```
# clone the repository
$ git clone https://github.com/fermyon/spin && cd spin
# add a new remote pointing to your fork of the project
@ -71,9 +65,9 @@ $ make test
Now you should be ready to start making your contribution. To familiarize
yourself with the Spin project, please read the
[architecture document](./architecture.md). Since most of Spin is implemented in
[document about extending Spin](/extending-and-embedding). Since most of Spin is implemented in
Rust, we try to follow the common Rust coding conventions (keep an eye on the
recommendations from Clippy!) If applicable, add units or integration tests to
recommendations from Clippy!) If applicable, add unit or integration tests to
ensure your contribution is correct.
Build the project and run the tests (`make build test`), and if everything is

View File

@ -0,0 +1,42 @@
title = "Packaging and distributing Spin applications"
template = "main"
date = "2022-03-14T00:22:56Z"
---
Packaging and distributing Spin applications is done using [Bindle](https://github.com/deislabs/bindle),
an open source aggregate object storage system. This allows the packaging of the
application configuration, components, and static assets together, and
takes advantage of the features of a modern object storage system.
To distribute applications, we first need a Bindle registry. You can
[install Bindle v0.8 release](https://github.com/deislabs/bindle/tree/main/docs#from-the-binary-releases),
or use the
[`autobindle`](https://marketplace.visualstudio.com/items?itemName=fermyon.autobindle)
VS Code extension (through the `Bindle: Start` command):
```bash
$ bindle-server --address 127.0.0.1:8000 --directory .
```
Let's push the application from the [quickstart](/quickstart) to the registry:
```bash
$ export BINDLE_URL=http://localhost:8080/v1
$ spin bindle push --file spin.toml
pushed: spin-hello-world/1.0.0
```
Now we can run the application using `spin up` directly from the registry:
```bash
$ spin up --bindle spin-hello-world/1.0.0
```
> A known error when running an application (with a high number of static assets)
> from Bindle on macOS is around
> [too many open files](https://github.com/fermyon/spin/issues/180). This issue
> can be worked around by setting a higher `ulimit -n` for the current shell
> session.
The application can also be prepared in a local directory before pushing to the
registry by running `spin bindle prepare`.

View File

@ -0,0 +1,133 @@
title = "Extending and embedding Spin"
template = "main"
date = "2022-03-14T00:22:56Z"
---
> The complete example for extending and embedding Spin [can be found on GitHub](https://github.com/fermyon/spin/tree/main/examples/spin-timer-echo).
Spin currently implements triggers and application models for:
- [HTTP applications](/http-trigger) that are triggered by incoming HTTP
requests, and that return an HTTP response
- [Redis applications](/redis-trigger) that are triggered by messages on Redis
channels
The Spin internals and execution context (the part of Spin executing
components) are agnostic of the event source and application model.
In this document we will explore how to extend Spin with custom event sources
(triggers) and application models built on top of the WebAssembly component
model, as well as how to embed Spin in your application.
The current application types that can be implemented with Spin have entrypoints
defined using
[WebAssembly Interface (WIT)]((https://github.com/bytecodealliance/wit-bindgen/blob/main/WIT.md)):
```fsharp
// The entrypoint for an HTTP handler.
handler: function(req: request) -> response
// The entrypoint for a Redis handler.
handler: function(msg: payload) -> expected<_, error>
```
> `handler` as the entrypoint name is a convention that [might change](https://github.com/fermyon/spin/issues/196)
> in the future to allow a single WebAssembly module to implement the entrypoints
> for multiple component types.
Let's see how define a new application entrypoint for Spin:
```fsharp
// examples/spin-timer-echo/echo.wit
echo: function(msg: string) -> string
```
This is the function signature that all "echo" components must implement, and
which is used by the "echo" executor when instantiating and invoking the
component.
Let's define a new trigger for our new application type — a timer-based trigger:
```rust
// examples/spin-timer-echo/src/lib.rs
wit_bindgen_wasmtime::import!("examples/spin-timer-echo/echo.wit");
type ExecutionContext = spin_engine::ExecutionContext<echo::EchoData>;
/// A custom timer trigger that executes the
/// first component of an application on every interval.
#[derive(Clone)]
pub struct TimerTrigger {
/// The interval at which the component is executed.
pub interval: Duration,
/// The application configuration.
app: Configuration<CoreComponent>,
/// The Spin execution context.
engine: Arc<ExecutionContext>,
}
```
A few important things to note from the start:
- we use the WIT defined entrypoint with the
[Bytecode Alliance `wit-bindgen` project](https://github.com/bytecodealliance/wit-bindgen)
to generate "import" bindings based on the entrypoint — this generates code that
allows us to easily invoke the entrypoint from application components that
implement our new application model.
- the new trigger has a field that contains a `Configuration<CoreComponent>`
in most cases, either `CoreComponent` will have to be updated with new trigger
and component configuration (not the case for our simple application model),
or an entirely new component can be defined and used in `Configuration<T>`.
- the trigger has a field that contains the Spin execution context — this is the
part of Spin that instantiates and helps execute the WebAssembly modules. When
creating the trigger (in the `new` function, you get access to the underlying
Wasmtime store, instance, and linker, which can be configured as necessary).
Finally, whenever there is a new event (in the case of our timer-based trigger
every `n` seconds), we execute the entrypoint of a selected component:
```rust
/// Execute the first component in the application configuration.
async fn handle(&self, msg: String) -> Result<()> {
// create a new Wasmtime store and instance based on the first component's WebAssembly module.
let (mut store, instance) =
self.engine
.prepare_component(&self.app.components[0].id, None, None, None, None)?;
// spawn a new thread and call the `echo` function from the WebAssembly module
let res = spawn_blocking(move || -> Result<String> {
// use the auto-generated WIT bindings to get the Wasm exports and call the `echo` export.
let e = echo::Echo::new(&mut store, &instance, |host| host.data.as_mut().unwrap())?;
Ok(e.echo(&mut store, &msg)?)
}).await??;
// do something with the result.
log::info!("{}\n", res);
Ok(())
}
```
A few notes:
- `prepare_component` is a function implemented by the Spin execution context,
and it handles taking the Wasmtime pre-instantiated module, mapping all the
component files, environment variables, and allowed HTTP domains, populating
the Wasmtime store with the appropriate data, and returning the store and instance.
- invoking the entrypoint `echo` is done in this example in a new Tokio thread —
this is an implementation choice based on the needs of the trigger.
- the return value from the component (a string in this example) can then be
used — in the case of the HTTP trigger, this is an HTTP response, which is then
returned to the client.
This is very similar to how the [HTTP](/http-trigger) and [Redis](/redis-trigger)
triggers are implemented, and it is the recommended way to extend Spin with your
own trigger and application model.
Embedding the new trigger in a Rust application is done by creating a new trigger
instance, then calling its `run` function:
```rust
let trigger = TimerTrigger::new(Duration::from_secs(1), app()).await?;
trigger.run().await
```
> We are exploring [APIs for embedding Spin from other programming languages](https://github.com/fermyon/spin/issues/197)
> such as Go or C#.

View File

@ -0,0 +1,145 @@
title = "Building Spin components in Go"
template = "main"
date = "2022-03-14T00:22:56Z"
---
[TinyGo](https://tinygo.org/) is an implementation of the
[Go programming language](https://go.dev/) for embedded systems and WebAssembly.
The Spin SDK for Go uses
[TinyGo's WASI support](https://tinygo.org/docs/reference/usage/important-options/)
to build programs written in Go as Spin components.
> This guide assumes you are familiar with the Go programming language, and that
> you have
> [configured the TinyGo toolchain locally](https://tinygo.org/getting-started/install/).
> All examples from this page can be found in [the Spin repository on GitHub](https://github.com/fermyon/spin/tree/main/examples).
## HTTP components
In Spin, HTTP components are triggered by the occurrence of an HTTP request, and
must return an HTTP response at the end of their execution. Components can be
built in any language that compiles to WASI, and Go has improved support for
writing applications, through its SDK.
Building a Spin HTTP component using the Go SDK means writing a single function,
`main` — below is a complete implementation for such a component:
```go
// A Spin component written in Go that returns "Hello, Fermyon!"
package main
import (
"fmt"
"net/http"
spin "github.com/fermyon/spin-sdk/http"
)
func main() {
spin.HandleRequest(func(w http.ResponseWriter, r *http.Request) {
fmt.Fprintln(w, "Hello, Fermyon!")
})
}
```
The important things to note in the implementation above:
- the entrypoint to the component is the standard `func main()` for Go programs
- handling the request is done by calling the `spin.HandleRequest` function,
which takes a `func(w http.ResponseWriter, r *http.Request)` as parameter — these
contain the HTTP request and response writer you can use to handle the request
- the HTTP objects (`*http.Request`, `http.Response`, and `http.ResponseWriter`)
are the Go objects from the standard library, so working with them should feel
familiar if you are a Go developer
## Sending outbound HTTP requests
If allowed, Spin components can send outbound requests to HTTP endpoints. Let's
see an example of a component that makes a request to
[an API that returns random dog facts](https://some-random-api.ml/facts/dog) and
inserts a custom header into the response before returning:
```go
// A Spin component written in Go that sends a request to an API
// with random dog facts.
package main
import (
"fmt"
"net/http"
"os"
spin_http "github.com/fermyon/spin-sdk/http"
)
func main() {
spin_http.HandleRequest(func(w http.ResponseWriter, r *http.Request) {
res, err := spin_http.Get("https://some-random-api.ml/facts/dog")
if err != nil {
fmt.Fprintf(os.Stderr, "Cannot send HTTP request: %v", err)
}
// optionally writing a response header
w.Header().Add("server", "spin/0.1.0")
fmt.Fprintln(w, res.Body)
})
}
```
The component can be built using the `tingygo` toolchain:
```bash
$ tinygo build -wasm-abi=generic -target=wasi -o main.wasm main.go
```
Before we can execute this component, we need to add the
`https://some-random-api.ml` domain to the application configuration
list containing the list of domains the component is allowed to make HTTP
requests to:
```toml
# spin.toml
spin_version = "1"
name = "spin-hello-tinygo"
trigger = { type = "http", base = "/" }
version = "1.0.0"
[[component]]
id = "tinygo-hello"
source = "main.wasm"
allowed_http_hosts = [ "https://some-random-api.ml" ]
[component.trigger]
route = "/hello"
executor = { type = "wagi" }
```
> Spin HTTP components written in Go must currently use the Wagi executor.
Running the application using `spin up --file spin.toml` will start the HTTP
listener locally (by default on `localhost:3000`), and our component can
now receive requests in route `/hello`:
```bash
$ curl -i localhost:3000/hello
HTTP/1.1 200 OK
content-type: text/plain; charset=utf-8
server: spin/0.1.0
content-length: 85
date: Fri, 18 Mar 2022 23:27:33 GMT
{{"fact":"Seventy percent of people sign their dog's name on their holiday cards."}}
```
> Without the `allowed_http_hosts` field populated properly in `spin.toml`,
> the component would not be allowed to send HTTP requests, and sending the
> request would generate in a "Destination not allowed" error.
## Using Go packages in Spin components
Any
[package from the Go standard library](https://tinygo.org/docs/reference/lang-support/stdlib/) that can be imported in TinyGo and that compiles to
WASI can be used when implementing a Spin component.
> Make sure to read [the page describing the HTTP trigger](/http-trigger) for more
> details about building HTTP applications.

View File

@ -0,0 +1,260 @@
title = "The Spin HTTP trigger"
template = "main"
date = "2022-03-14T00:22:56Z"
---
An important workload in event-driven environments is represented by HTTP
applications, and Spin has built-in support for creating and running HTTP
components. This document presents an overview of the HTTP trigger, as well as
some implementation details around the WebAssembly component model and how it
is used in Spin.
The HTTP trigger in Spin is a web server. It listens for incoming requests and
based on the [application configuration](/configuration), it routes them to an
_executor_ which instantiates the appropriate component, executes its
entrypoint function, then returns an HTTP response.
Creating an HTTP application is done when [configuring the application](/configuration)
by defining the top-level application trigger:
```toml
# spin.toml
trigger = { type = "http", base = "/" }
```
Then, when defining the component (in `spin.toml`), there are two pieces of
configuration that can be set for the component trigger: the route,
and the _HTTP executor_ (see details below about executors). For example:
- an HTTP component configured on the `/hello` route that uses the Spin executor:
```toml
[component.trigger]
route = "/hello"
executor = "spin"
```
- an HTTP component configured on the `/goodbye` route that uses the Wagi executor:
```toml
[component.trigger]
route = "/goodbye"
executor = { type = "wagi" }
```
## Routing
Routing an incoming request to a particular component is done using the
application base path (`base` in `spin.toml`) and the component defined routes
(`route` in the component configuration) by prefixing the application base path
to all component routes defined for that application.
For example, if the application `base` path is `base = /base`, and a component
has defined `route = /foo`, that component will be executed for requests on
`http/s::<spin-up-defined-address-and-port>/base/bar`.
Components can either define exact routes, for example `route = /bar/baz`, where
the component will be invoked only for requests on `/base/bar/baz`, or they
can define a wildcard as the last path segment, for example `route = /bar/baz/...`,
which means the component will be invoked for every request starting with the
`/base/bar/baz/` prefix (such as `/base/bar/baz`, `/base/bar/baz/qux`,
`/base/bar/baz/qux/quux` and so on).
If multiple components could potentially handle the same request based on their
defined routes, the last component defined in `spin.toml` takes precedence.
In the following example:
```toml
# spin.toml
trigger = { type = "http", base = "/"}
[[component]]
id = "component-1"
[component.trigger]
route = "/..."
[[component]]
id = "component-2"
[component.trigger]
route = "/foo/..."
```
Any request starting with the `/foo/` prefix will be handled by `component-2`,
which is the last one defined in `spin.toml`.
Every HTTP application has a special route always configured at `/healthz`, which
returns `OK 200` when the Spin instance is healthy.
Once Spin selects a component to handle an incoming request based on the route
configuration, it will instantiate and execute that component based on its
defined _HTTP executor_, and the next sections explore the two ways of building
HTTP components based on the two available executors.
## The Spin HTTP executor
Spin is built on top of the
[WebAssembly component model](https://github.com/WebAssembly/component-model).
We _strongly_ believe the component model represents the future of WebAssembly,
and we are working with the [Bytecode Alliance](https://bytecodealliance.org)
community on building exciting new features and tools for it. As a result, the
Spin HTTP _executor_ is defined using WebAssembly interfaces.
> The WebAssembly component model is in its early stages, and during the `0.x`
> releases of Spin, the triggers and application entrypoints will suffer
> breaking changes, particularly around the primitive types used to defined
> the HTTP objects and function signatures — i.e. bodies will become streams,
> handler functions will become asynchronous.
We define the HTTP objects as
[WebAssembly Interface (WIT)]((https://github.com/bytecodealliance/wit-bindgen/blob/main/WIT.md))
objects, currently using _records_:
```fsharp
// wit/ephemeral/http-types.wit
// The HTTP status code.
type http-status = u16
// The HTTP body.
type body = list<u8>
// The HTTP headers represented as a list of (name, value) pairs.
type headers = list<tuple<string, string>>
// The HTTP parameter queries, represented as a list of (name, value) pairs.
type params = list<tuple<string, string>>
// The HTTP URI of the current request.
type uri = string
// The HTTP method.
enum method { get, post, put,... }
// An HTTP request.
record request {
method: method,
uri: uri,
headers: headers,
params: params,
body: option<body>,
}
// An HTTP response.
record response {
status: http-status,
headers: option<headers>,
body: option<body>,
}
```
> The same HTTP types are also used to model the API for sending outbound
> HTTP requests, and you can see its implementation in
> [the WASI toolkit repository](https://github.com/fermyon/wasi-experimental-toolkit).
Then, we define the entrypoint for a Spin HTTP component:
```fsharp
// wit/ephemeral/spin-http.wit
use * from http-types
// The entrypoint for an HTTP handler.
handler: function(req: request) -> response
```
This is the function signature that all HTTP components must implement, and
which is used by the Spin HTTP executor when instantiating and invoking the
component.
This interface (`spin-http.wit`) can be directly used together with the
[Bytecode Alliance `wit-bindgen` project](https://github.com/bytecodealliance/wit-bindgen)
to build a component that the Spin HTTP executor can invoke.
This is exactly how [the Rust SDK for Spin](/rust-components) is built, and,
as more languages add support for the component model, how we plan to add
support for them as well.
## The Wagi HTTP executor
The WebAssembly component model proposal is currently in its early stages, which
means only a few programming languages fully implement it. While the language
communities implement toolchain support for the component model (for emitting
components and for automatically generating bindings for importing other
components), we want to allow developers to use any language that compiles to
WASI to build Spin HTTP applications. This is why Spin currently implements an
HTTP executor based on [Wagi](https://github.com/deislabs/wagi), or the
WebAssembly Gateway Interface, a project that implements the
[Common Gateway Interface](https://datatracker.ietf.org/doc/html/rfc3875)
specification for WebAssembly.
> Spin will keep supporting the Wagi-based executor while language toolchains
> add support for the WebAssembly component model. When enough programming
> languages have implemented the component model, we will work with the Spin
> community to decide when to deprecate the Wagi executor.
Wagi allows a module built in any programming language that compiles to [WASI](https://wasi.dev/)
to handle an HTTP request by passing the HTTP request information to the module's
standard input, environment variables, and arguments, and expecting the HTTP
responses through the module's standard output.
This means that if a language has support for the WebAssembly System Interface,
it can be used to build Spin HTTP components.
The Wagi model is only used to parse the HTTP request and response. Everything
else — defining the application, running it, or [distributing](/distributing-apps)
is done the same way as a component that uses the Spin executor.
Building a Wagi component in a particular programming language that can compile
to `wasm32-wasi` does not require any special libraries — instead,
[building Wagi components](https://github.com/deislabs/wagi/tree/main/docs) can
be done by reading the HTTP request from the standard input and environment
variables, and sending the HTTP response to the module's standard output.
In pseudo-code, this is the minimum required in a Wagi component:
- either the `content-media` or `location` headers must be set — this is done by
printing its value to standard output
- an empty line between the headers and the body
- the response body printed to standard output
```
print("content-type: text/html; charset=UTF-8\n\n");
print("hello world\n");
```
The [Go SDK for Spin](/go-components) is built on the Wagi executor support.
Here is another example, written in [Grain](https://grain-lang.org/),
a new programming language that natively targets WebAssembly:
```js
import Process from "sys/process";
import Array from "array";
print("content-type: text/plain\n");
// This will print all the Wagi env variables
print("==== Environment: ====");
Array.forEach(print, Process.env());
// This will print the route path followed by each query
// param. So /foo?bar=baz will be ["/foo", "bar=baz"].
print("==== Args: ====");
Array.forEach(print, Process.argv());
```
> You can find examples on how to build Wagi applications in
> [the DeisLabs GitHub organization](https://github.com/deislabs?q=wagi&type=public&language=&sort=).
## The default headers set in Spin HTTP components
Spin sets a few default headers on the request based on the base path, component
route, and request URI, which will always be available when writing a module:
- `X_FULL_URL` - the full URL of the request —
`http://localhost:3000/test/wagi/abc/def?foo=bar`
- `PATH_INFO` - the path info, relative to both the base application path _and_
component route — in our example, where the base path is `/test`, and the
component route is `/hello`, this is `/abc/def`.
- `X_MATCHED_ROUTE` - the base path and route pattern matched (including the
wildcard pattern, if applicable) (this updates the header set in Wagi to
include the base path) — in our case `"/test/hello/..."`.
- `X_RAW_COMPONENT_ROUTE` - the route pattern matched (including the wildcard
pattern, if applicable) — in our case `/hello/...`.
- `X_COMPONENT_ROUTE` - the route path matched (stripped of the wildcard
pattern) — in our case `/hello`
- `X_BASE_PATH` - the application base path — in our case `/test`.
Besides the headers above, components that use the Wagi executor also have
available
[all headers set by Wagi, following the CGI spec](https://github.com/deislabs/wagi/blob/main/docs/environment_variables.md).

View File

@ -1,13 +1,8 @@
title = "Spin Docs"
title = "Introducing Spin"
template = "main"
date = "2022-03-14T00:22:56Z"
[extra]
author = "Fermyon"
---
# Introducing Spin
Spin is an open source framework for building and running fast, secure, and
composable cloud microservices with WebAssembly. It aims to be the easiest way
to get started with WebAssembly microservices, and takes advantage of the latest
@ -15,19 +10,27 @@ developments in the
[WebAssembly component model](https://github.com/WebAssembly/component-model)
and [Wasmtime](https://wasmtime.dev/) runtime.
Spin offers a simple CLI that helps you create, distribute, and execute
applications, and in the next sections we will learn more about Spin
applications and how to get started.
Spin offers a simple framework that helps you write, distribute, and execute
[fast](https://fermyon.github.io/spin-benchmarks/criterion/reports/),
stateless, event-driven applications, with components based on functions:
## Spin applications
```rust
#[http_component]
fn hello_world(req: Request) -> Result<Response> {
Ok(Response::builder()
.status(200)
.body(Some("Hello, Fermyon!".into()))?)
}
```
Spin applications are comprised of one or more _components_, and follow the
event-driven model — they are executed as the result of events being generated
by _triggers_ (for example an HTTP server receiving requests, or a queue
### Overview
Spin applications are comprised of one or more function-based _components_, and
follow the event-driven model — they are executed as the result of events being
generated by _triggers_ (for example an HTTP server receiving requests, or a queue
subscription receiving messages). On each new event, _the entrypoint_ of a
component is executed by Spin. The entrypoints to components are _functions_.
This, together with the fact that they are invoked in response to events, brings
the Spin application model closer to the Function-as-a-Service model.
In the next section, we will [take Spin for a spin](./quickstart.md).
In the next section, we will [take Spin for a spin](/quickstart).

View File

@ -1,32 +0,0 @@
title = "Introducing Spin"
template = "main"
date = "2022-03-14T00:22:56Z"
[extra]
author = "Fermyon"
---
# Introducing Spin
Spin is an open source framework for building and running fast, secure, and
composable cloud microservices with WebAssembly. It aims to be the easiest way
to get started with WebAssembly microservices, and takes advantage of the latest
developments in the
[WebAssembly component model](https://github.com/WebAssembly/component-model)
and [Wasmtime](https://wasmtime.dev/) runtime.
Spin offers a simple CLI that helps you create, distribute, and execute
applications, and in the next sections we will learn more about Spin
applications and how to get started.
## Spin applications
Spin applications are comprised of one or more _components_, and follow the
event-driven model — they are executed as the result of events being generated
by _triggers_ (for example an HTTP server receiving requests, or a queue
subscription receiving messages). On each new event, _the entrypoint_ of a
component is executed by Spin. The entrypoints to components are _functions_.
This, together with the fact that they are invoked in response to events, brings
the Spin application model closer to the Function-as-a-Service model.
In the next section, we will [take Spin for a spin](./quickstart.md).

View File

@ -1,13 +1,8 @@
title = "Taking Spin for a Spin"
template = "main"
date = "2022-03-14T00:22:56Z"
[extra]
author = "Fermyon"
---
# Taking Spin for a Spin
## Getting the `spin` binary
<!-- You can download the [latest release](https://github.com/fermyon/spin/releases).
@ -23,12 +18,12 @@ $ ./spin --help
> running
> `brew install openssl@1.1 && sudo ln -s /opt/homebrew/Cellar/openssl@1.1/1.1.1m /usr/local/openssl-aarch64` -->
First, [follow the contribution guide](./contributing.md) for a detailed guide
First, [follow the contribution document](/contributing) for a detailed guide
on getting building Spin from source:
```shell
```bash
$ git clone https://github.com/fermyon/spin
$ cd spin && cargo build --release
$ cd spin && make build
$ ./target/release/spin --help
```
@ -56,17 +51,14 @@ $ spin new --repo fermyon --template spin-http --path spin-hello-world
$ cd spin-hello-world
``` -->
Now let's look at the example applications from the `examples/` directory.
## Building the example applications
Let's first look at the Rust example from the `examples/http-rust` directory.
Let's have a look at `spin.toml`:
Let's explore the Rust example from the `examples/http-rust` directory,
focusing first on `spin.toml`:
```toml
spin_version = "1"
name = "spin-hello-world"
description = "A simple application that returns hello world."
trigger = { type = "http", base = "/" }
version = "1.0.0"
@ -77,16 +69,16 @@ source = "target/wasm32-wasi/release/spinhelloworld.wasm"
route = "/hello"
```
Since this is an HTTP application, the application trigger is of `type = http`,
and there is one component that responds to requests on route `/hello` using the
`spinhelloworld.wasm` WebAssembly module. (See the
[configuration document](./configuration.md) for a detailed guide on the Spin
This is a simple Spin HTTP application (triggered by an HTTP request), with a
single component called `hello`. Spin will execute the `spinhelloworld.wasm`
WebAssembly module for HTTP requests on the route `/hello`.
(See the [configuration document](/configuration) for a detailed guide on the Spin
application configuration.)
Now let's have a look at the `hello` component — below is the complete source
code for a Spin HTTP component written in Rust. It is a regular Rust function
that takes an HTTP request and returns an HTTP response, annotated with the
`http_component` macro:
Now let's have a look at the `hello` component. Below is the complete source
code for a Spin HTTP component written in Rust — a regular Rust function that
takes an HTTP request as a parameter and returns an HTTP response, and it is
annotated with the `http_component` macro:
```rust
use anyhow::Result;
@ -98,7 +90,6 @@ use spin_sdk::{
/// A simple Spin HTTP component.
#[http_component]
fn hello_world(req: Request) -> Result<Response> {
println!("{:?}", req.headers());
Ok(http::Response::builder()
.status(200)
.header("foo", "bar")
@ -106,8 +97,7 @@ fn hello_world(req: Request) -> Result<Response> {
}
```
> See
> [the section on building HTTP applications with Spin for a detailed guide](./writing-http-apps.md).
> See [the section on building HTTP applications with Spin for a detailed guide](/writing-http-apps).
We can build this component using the regular Rust toolchain, targeting
`wasm32-wasi`, which will produce the WebAssembly module referenced in
@ -122,8 +112,9 @@ $ cargo build --target wasm32-wasi --release
Now that we configured the application and built our component, we can _spin up_
the application (pun intended):
```shell
# optionally, use RUST_LOG=spin=trace to see detailed logs
```bash
# optionally, set the RUST_LOG environment variable for detailed logs
$ export RUST_LOG=spin=trace
$ spin up --file spin.toml
INFO spin_http_engine: Serving HTTP on address 127.0.0.1:3000
```
@ -143,32 +134,12 @@ Hello, Fermyon!
```
You can add as many components as needed in `spin.toml`, mount files and
directories, allow granular outbound HTTP connections, or environment variables.
(see the [configuration document](./configuration.md) for a detailed guide on
directories, allow granular outbound HTTP connections, or set environment variables
(see the [configuration document](/configuration) for a detailed guide on
the Spin application configuration) and iterate locally with
`spin up --file spin.toml` until you are ready to distribute the application.
## Distributing the application
First, we need to start the registry. You can
[install the latest Bindle release](https://github.com/deislabs/bindle/tree/main/docs#from-the-binary-releases),
or use the
[`autobindle`](https://marketplace.visualstudio.com/items?itemName=fermyon.autobindle)
VS Code extension, which automatically downloads and starts Bindle on
`http://localhost:8080/v1`. Now we can package the entire application, the
components, and all the referenced files and publishes them to the registry:
```
$ export BINDLE_URL=http://localhost:8080/v1
$ spin bindle push --file spin.toml
pushed: spin-hello-world/1.0.0
```
Now we can run the application using `spin up` directly from the registry:
```
$ spin up --bindle spin-hello-world/1.0.0
```
Congratulations! You just completed writing, building, publishing, and running
your first Spin application.
Congratulations! You just completed building and running your first Spin
application!
Next, check out the [Rust](/rust-components) or [Go](/go-components) language
guides.

View File

@ -0,0 +1,66 @@
title = "The Spin Redis trigger"
template = "main"
date = "2022-03-14T00:22:56Z"
---
Spin applications can be triggered by a new message on a [Redis channel](https://redis.io/topics/pubsub).
Spin will connect to a configured Redis instance and will invoke components for
new messages on the configured channels.
> See the [Rust language guide](/rust-components) for details on using Rust to
> build Redis components.
The Redis instance address is specified in the application trigger:
```toml
# spin.toml
trigger = { type = "redis", address = "redis://localhost:6379" }
```
> We are [exploring adding authentication for connecting to Redis](https://github.com/fermyon/spin/issues/192)
> and [adding host support for connecting to Redis databases](https://github.com/fermyon/spin/issues/181),
> from components, which would allow using the key/value store and publishing
> messages to channels.
Then, all components in the application are triggered when new messages are
published to channels in the instance. [Configuring](/configuration) the channel
is done by setting the `channel` field in the component trigger configuration.
```toml
[component.trigger]
channel = "messages"
```
## The WebAssembly interface
The Redis trigger is built on top of the
[WebAssembly component model](https://github.com/WebAssembly/component-model).
The current interface is defined using the
[WebAssembly Interface (WIT)]((https://github.com/bytecodealliance/wit-bindgen/blob/main/WIT.md))
format, and is a function that takes the message payload as its only parameter:
```fsharp
// wit/ephemeral/spin-redis-trigger.wit
// The message payload.
type payload = list<u8>
// The entrypoint for a Redis handler.
handler: function(msg: payload) -> expected<_, error>
```
> The interface might change in the future to add the Redis instance and
> message channel as arguments to the function.
This is the function that all Redis components must implement, and which is
used by the Spin Redis executor when instantiating and invoking the component.
This interface (`spin-redis-trigger.wit`) can be directly used together with the
[Bytecode Alliance `wit-bindgen` project](https://github.com/bytecodealliance/wit-bindgen)
to build a component that the Spin HTTP executor can invoke.
This is exactly how [the Rust SDK for Spin](/rust-components) is built, and,
as more languages add support for the component model, how we plan to add
support for them as well.
> We are [exploring a compatibility layer (similar to Wagi)](https://github.com/fermyon/spin/issues/193)
> so languages that do not have support for the component model can be used to
> build Redis components for Spin.

View File

@ -1,13 +1,8 @@
title = "Creating a New Spin Release"
title = "Creating a new Spin release"
template = "main"
date = "2022-03-14T00:22:56Z"
[extra]
author = "Fermyon"
---
# Creating a new Spin release
To cut a release of Spin, you will need to do the following:
1. Create a pull request that changes the version number for your new version

View File

@ -0,0 +1,212 @@
title = "Building Spin components in Rust"
template = "main"
date = "2022-03-14T00:22:56Z"
---
Spin aims to have best in class support for building components in Rust, and
writing such components should be familiar for Rust developers.
> This guide assumes you are familiar with the Rust programming language,
> but if you are just getting started, be sure to check [the
official resources for learning Rust](https://www.rust-lang.org/learn).
> All examples from this page can be found in [the Spin repository on GitHub](https://github.com/fermyon/spin/tree/main/examples).
## HTTP components
In Spin, HTTP components are triggered by the occurrence of an HTTP request, and
must return an HTTP response at the end of their execution. Components can be
built in any language that compiles to WASI, and Rust has improved support
for writing applications, through its SDK.
Building a Spin HTTP component using the Rust SDK means writing a single function
that takes an HTTP request as a parameter, and returns an HTTP response — below
is a complete implementation for such a component:
```rust
use spin_sdk::{
http::{Request, Response},
http_component,
};
/// A simple Spin HTTP component.
#[http_component]
fn hello_world(req: Request) -> anyhow::Result<Response> {
println!("{:?}", req);
Ok(http::Response::builder()
.status(200)
.header("foo", "bar")
.body(Some("Hello, Fermyon!".into()))?)
}
```
The important things to note in the implementation above:
- the `spin_sdk::http_component` macro marks the function as the
entrypoint for the Spin component
- the function signature — `fn hello_world(req: Request) -> Result<Response>`
the Spin HTTP component uses the HTTP objects from the popular Rust crate
[`http`](https://crates.io/crates/http), and the request and response bodies
are optionally using [`bytes::Bytes`](https://crates.io/crates/bytes)
(`spin_sdk::http::Request` is a type alias for `http::Request<Option<Bytes>>`)
- the component returns a Rust `anyhow::Result`, so if there is an error processing the request, it returns an `anyhow::Error`.
## Sending outbound HTTP requests
If allowed, Spin components can send outbound HTTP requests using the [DeisLabs
WASI experimental HTTP library](https://github.com/deislabs/wasi-experimental-http).
Let's see an example of a component that makes a request to
[an API that returns random dog facts](https://some-random-api.ml/facts/dog) and
inserts a custom header into the response before returning:
```rust
#[http_component]
fn hello_world(_req: Request) -> Result<Response> {
let mut res = spin_sdk::http::send(
http::Request::builder()
.method("GET")
.uri("https://some-random-api.ml/facts/dog")
.body(None)?,
)?;
res.headers_mut()
.insert(http::header::SERVER, "spin/0.1.0".try_into()?);
Ok(res)
}
```
Before we can execute this component, we need to add the `https://some-random-api.ml`
domain to the application configuration list containing the list of
domains the component is allowed to make HTTP requests to:
```toml
# spin.toml
spin_version = "1"
name = "spin-hello-world"
trigger = { type = "http", base = "/" }
version = "1.0.0"
[[component]]
id = "hello"
source = "target/wasm32-wasi/release/spinhelloworld.wasm"
allowed_http_hosts = [ "https://some-random-api.ml" ]
[component.trigger]
route = "/hello"
```
Running the application using `spin up --file spin.toml` will start the HTTP
listener locally (by default on `localhost:3000`), and our component can
now receive requests in route `/hello`:
```bash
$ curl -i localhost:3000/hello
HTTP/1.1 200 OK
date: Fri, 18 Mar 2022 03:54:36 GMT
content-type: application/json; charset=utf-8
content-length: 185
server: spin/0.1.0
{"fact":"It's rumored that, at the end of the Beatles song,
\"A Day in the Life,\" Paul McCartney recorded an ultrasonic whistle,
audible only to dogs, just for his Shetland sheepdog."}
```
> Without the `allowed_http_hosts` field populated properly in `spin.toml`,
> the component would not be allowed to send HTTP requests, and sending the
> request would generate in a "Destination not allowed" error.
We just built a WebAssembly component that sends an HTTP request to another
service, manipulates that result, then responds to the original request.
This can be the basis for building components that communicate with external
databases or storage accounts, or even more specialized components like HTTP
proxies or URL shorteners.
## Redis components
Besides the HTTP trigger, Spin has built-in support for a Redis trigger —
which will connect to a Redis instance and will execute Spin components for
new messages on the configured channels.
> See the [Redis trigger](/redis-rigger) for details about the Redis trigger.
Writing a Redis component in Rust also takes advantage of the SDK:
```rust
/// A simple Spin Redis component.
#[redis_component]
fn on_message(msg: Bytes) -> Result<()> {
println!("{}", from_utf8(&msg)?);
Ok(())
}
```
- the `spin_sdk::redis_component` macro marks the function as the
entrypoint for the Spin component
- in the function signature — `fn on_message(msg: Bytes) -> anyhow::Result<()>`
`msg` contains the payload from the Redis channel
- the component returns a Rust `anyhow::Result`, so if there is an error
processing the request, it returns an `anyhow::Error`.
The component can be built with Cargo by executing:
```bash
$ cargo build --target wasm32-wasi --release
```
The configuration for a Redis application must contain the address of the Redis
instance the trigger must connect to:
```toml
spin_version = "1"
name = "spin-redis"
trigger = { type = "redis", address = "redis://localhost:6379" }
version = "0.1.0"
[[component]]
id = "echo-message"
source = "target/wasm32-wasi/release/spinredis.wasm"
[component.trigger]
channel = "messages"
```
This application will connect to `redis://localhost:6379`, and for every new
message on the `messages` channel, the `echo-message` component will be executed.
```bash
# first, start redis-server on the default prot 6379
$ redis-server --port 6379
# then, start the Spin application
$ spin up --file spin.toml
INFO spin_redis_engine: Connecting to Redis server at redis://localhost:6379
INFO spin_redis_engine: Subscribed component 0 (echo-message) to channel: messages
```
For every new message on the `messages` channel:
```bash
$ redis-cli
127.0.0.1:6379> publish messages "Hello, there!"
```
Spin will instantiate and execute the component we just built:
```
INFO spin_redis_engine: Received message on channel "messages"
Hello, there!
```
> We are also evaluating adding
> [host support for connecting to Redis databases](https://github.com/fermyon/spin/issues/181),
> which would allow using the key/value store and publishing messages to channels.
## Using external crates in Rust components
In Rust, Spin components are regular libraries that contain a function
annotated using the `http_component` macro, compiled to the
[`wasm32-wasi` target](https://doc.rust-lang.org/stable/nightly-rustc/rustc_target/spec/wasm32_wasi/index.html).
This means that any [crate](https://crates.io) that compiles to `wasm32-wasi` can
be used when implementing the component.
> Make sure to read [the page describing the HTTP trigger](/http-trigger) for more
> details about building HTTP applications.

View File

@ -1,228 +0,0 @@
title = "Building HTTP applications using Spin"
template = "main"
date = "2022-03-14T00:22:56Z"
[extra]
author = "Fermyon"
---
# Building HTTP applications using Spin
Currently, the only applications that can be built with Spin are web based, or
applications that are invoked as the result of an HTTP request, and which return
an HTTP response. This is because HTTP workloads appear to be the most important
for event-driven Functions-as-a-Service workloads, and we think initially serve
the most popular use cases.
> The extensible nature of Spin allows anyone to extend it by building more
> triggers (see the [architecture](./architecture.md) and
> [contributing](./contributing.md) documents), and we are experimenting with a
> new trigger that invokes components for new payloads on a Redis message queue
> (see [#59](https://github.com/fermyon/spin/issues/59)).
Spin is built on top of the
[WebAssembly component model](https://github.com/WebAssembly/component-model).
We _strongly_ believe it represents the future of WebAssembly, and that it will
enable scenarios that are simply not possible today (for example dynamic linking
and transitive dependencies). As a result, the Spin HTTP trigger (and executor)
is defined using [WebAssembly interfaces](../wit/ephemeral), and the
[SDK for building Rust components](../sdk/rust) is built on top of the Rust
implementation and bindings generator for WebAssembly components.
But the WebAssembly component model is currently in its early stages. This means
only a few languages fully implement it. While language communities implement
the component model, we want to allow developers to use
[any language that compiles to WASI](https://www.fermyon.com/wasm-languages/webassembly-language-support)
to build Spin HTTP applications. This is why we currently implement a Wagi
executor which supports [Wagi](https://github.com/deislabs/wagi)-based
components that expect the HTTP request using the module's standard input, and
return the HTTP response using the module's standard output, following
[the CGI specification](https://tools.ietf.org/html/rfc3875). As a programming
language adds support for the component model, we plan to enable better support
for it in Spin, and eventually only support Spin applications that implement the
WebAssembly component model.
## Building HTTP components in Rust
We believe the Rust SDK offers the best experience for building Spin HTTP
components, and this is the recommended way of writing Spin components in Rust.
Building such a component in Rust requires writing a function that takes an HTTP
`Request` and returns an HTTP `Response`, annotated with a special Spin
procedural macro. Below is a complete component implementation:
```rust
use anyhow::Result;
use spin_sdk::{
http::{Request, Response},
http_component,
};
/// A simple Spin HTTP component.
#[http_component]
fn hello_world(req: Request) -> Result<Response> {
println!("{:?}", req.headers());
Ok(http::Response::builder()
.status(200)
.header("foo", "bar")
.body(Some("Hello, Fermyon!".into()))?)
}
```
The important things to note in the function above:
- the `spin_sdk::http_component` macro — this marks the function as the
entrypoint for the Spin component
- the function signature — `fn hello_world(req: Request) -> Result<Response>`
the Spin HTTP component uses the HTTP objects from the popular Rust crate
[`http`](https://crates.io/crates/http), and the request and response bodies
are optionally using [`bytes::Bytes`](https://crates.io/crates/bytes)
### Making outbound HTTP requests
This SDK includes the ability to send outbound HTTP requests using the
[DeisLabs WASI experimental HTTP library](https://github.com/deislabs/wasi-experimental-http).
Let's see an example where the component makes an outbound HTTP request to a
server, modifies the result, then returns it:
```rust
#[http_component]
fn hello_world(_req: Request) -> Result<Response> {
let mut res = spin_sdk::http::send(
http::Request::builder()
.method("GET")
.uri("https://fermyon.com")
.body(None)?,
)?;
res.headers_mut()
.insert(http::header::SERVER, "spin/0.1.0".try_into()?);
Ok(res)
}
```
In order for the component above to be allowed to make the outbound HTTP
request, the destination host must be declared in the Spin application
configuration:
```toml
[[component]]
id = "hello"
source = "target/wasm32-wasi/release/spinhelloworld.wasm"
allowed_http_hosts = [ "https://fermyon.com" ]
[component.trigger]
route = "/hello"
```
Making a request to this component, we can see the appended header, and that the
response contains the expected body:
```shell
$ curl -I localhost:3000/hello
HTTP/1.1 200 OK
content-length: 29350
content-type: text/html; charset=utf-8
server: spin/0.1.0 # the header added by our component
```
Any Rust crate that compiles to `wasm32-wasi` can be used as dependency in Rust
components.
As the Spin framework evolves, the Spin SDK will continue adding functionality
that improves the experience for building Spin components (such as implementing
interfaces for popular functionality such as
[object storage](https://github.com/fermyon/spin/issues/48),
[key/value stores](https://github.com/fermyon/spin/issues/47), or
[neural networks](https://github.com/fermyon/spin/issues/50)).
As more languages support the WebAssembly component model, our goal is to
develop language SDKs for such popular languages.
## Building HTTP components using the Wagi executor
You can use any language that compiles to WASI to build an HTTP component using
the [Wagi](https://github.com/deislabs/wagi) executor.
Wagi is a project that lets you write HTTP handlers using nothing but a
language's standard library, following
[the CGI specification](https://tools.ietf.org/html/rfc3875).
For example, here is a complete Wagi component written in Swift:
```swift
print("content-type: text/html; charset=UTF-8\n\n");
print("hello world\n");
```
Here is another example, this time written in [Grain](https://grain-lang.org/),
a new programming language that natively targets WebAssembly:
```js
import Process from "sys/process";
import Array from "array";
print("content-type: text/plain\n");
// This will print all the Wagi env variable
print("==== Environment: ====");
Array.forEach(print, Process.env());
// This will print the route path followed by each query
// param. So /foo?bar=baz will be ["/foo", "bar=baz"].
print("==== Args: ====");
Array.forEach(print, Process.argv());
```
> You can find examples on how to build Wagi applications in
> [the DeisLabs GitHub organization](https://github.com/deislabs?q=wagi&type=public&language=&sort=).
In short, read HTTP headers from environment variables and the HTTP body from
standard input, and return the response to standard output. You can follow the
[Wagi guide](https://github.com/deislabs/wagi/blob/main/docs/writing_modules.md)
on writing modules (note that a module declaring its subroutes will not be
implemented in Spin).
## Writing HTTP components in (Tiny)Go
Below is a complete implementation for a Spin HTTP component in Go:
```go
package main
import (
"io"
"net/http"
spin_http "github.com/fermyon/spin-sdk"
)
func main() {
spin_http.HandleRequest(func(w http.ResponseWriter, r *http.Request) {
io.WriteString(w, "Hello, Fermyon!")
})
}
```
## The default headers set in Spin HTTP components
Spin sets a few default headers on the request based on the base path, component
route, and request URI, which will always be available when writing a module:
- `X_FULL_URL` - the full URL of the request —
`http://localhost:3000/test/wagi/abc/def?foo=bar`
- `PATH_INFO` - the path info, relative to both the base application path _and_
component route — in our example, where the base path is `/test`, and the
component route is `/hello`, this is `/abc/def`.
- `X_MATCHED_ROUTE` - the base path and route pattern matched (including the
wildcard pattern, if applicable) (this updates the header set in Wagi to
include the base path) — in our case `"/test/hello/..."`.
- `X_RAW_COMPONENT_ROUTE` - the route pattern matched (including the wildcard
pattern, if applicable) — in our case `/hello/...`.
- `X_COMPONENT_ROUTE` - the route path matched (stripped of the wildcard
pattern) — in our case `/hello`
- `X_BASE_PATH` - the application base path — in our case `/test`.
Besides the headers above, components that use the Wagi executor also have
available
[all headers set by Wagi, following the CGI spec](https://github.com/deislabs/wagi/blob/main/docs/environment_variables.md).

Binary file not shown.

4007
docs/package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -31,7 +31,7 @@
"sass": "^1.49.9"
},
"scripts": {
"spin": "nodemon --watch content --watch static --watch templates --ext md,rhai,hbs,css --verbose --legacy-watch --exec 'RUST_LOG=spin=trace spin up --file spin.toml'",
"styles": "npx parcel static/sass/styles.scss --dist-dir static/css"
"spin": "nodemon --watch content --watch static --watch templates --ext md,rhai,hbs,css,js --verbose --legacy-watch --signal SIGINT --exec 'RUST_LOG=spin=trace spin up --file spin.toml'",
"styles": "npx parcel build static/sass/styles.scss --dist-dir static/css --no-optimize"
}
}

View File

@ -1019,18 +1019,12 @@ aside.menu.is-sticky {
.content pre code {
margin-bottom: 0 !important;
}
.content p pre,
.content p code, .content li pre,
.content li code, .content blockquote pre,
.content blockquote code {
font-size: 1rem !important;
}
.content h1 code, .content h2 code, .content h3 code, .content h4 code, .content h5 code {
font-weight: bold;
font-size: 1em !important;
}
.content blockquote, .content dl, .content ol, .content p, .content ul {
max-width: 70vw;
.content blockquote, .content dl, .content ol, .content p, .content ul, .content pre {
max-width: 65vw;
}
.content .hljs {
background-color: transparent !important;
@ -1060,13 +1054,19 @@ aside.menu.is-sticky {
}
.content blockquote p {
font-size: 1rem;
background-color: #1b2c4f;
line-height: 1.4;
}
html {
background-color: #0D203F;
}
body.dark-theme .content a:hover {
background: #1b2c4f !important;
}
body.dark-theme .content blockquote p {
background-color: #1b2c4f !important;
line-height: 1.4;
}
body.dark-theme aside.menu a.button svg,
body.dark-theme aside.menu a.button path {

File diff suppressed because one or more lines are too long

View File

@ -13,7 +13,5 @@
"type": "image/png"
}
],
"theme_color": "#ffffff",
"background_color": "#ffffff",
"display": "standalone"
}

View File

@ -23,37 +23,47 @@ document.querySelectorAll('.modal-button').forEach(function(el) {
});
const systemTheme = window.matchMedia("(prefers-color-scheme: dark)").matches
? "dark"
: "light";
const prefersDarkScheme = window.matchMedia('(prefers-color-scheme: dark)');
if (prefersDarkScheme.matches) {
document.body.classList.add('dark-theme');
} else {
document.body.classList.remove('dark-theme');
}
// the default theme is the system theme, unless the user has
// explicitly overriden it.
var savedTheme = localStorage.getItem("theme") || systemTheme;
setTheme(savedTheme);
// toggle light and dark mode
const btn = document.querySelector(".dark-mode");
const currentTheme = localStorage.getItem("theme");
if (currentTheme == "dark") {
document.body.classList.add("dark-theme");
}
btn.addEventListener("click", function() {
document.body.classList.toggle("dark-theme");
let theme = "light";
if (document.body.classList.contains("dark-theme")) {
theme = "dark";
btn.addEventListener("click", () => {
if(savedTheme === "dark") {
setTheme("light");
} else if(savedTheme === "light") {
setTheme("dark");
}
// save theme to localstorage
localStorage.setItem("theme", theme);
});
// change the website theme when the system theme changes.
window
.matchMedia("(prefers-color-scheme: dark)")
.addEventListener("change", (event) => {
if (event.matches) {
setTheme("dark");
} else {
setTheme("light");
}
});
function setTheme(mode) {
localStorage.setItem("theme", mode);
savedTheme = mode;
if (mode === "dark") {
document.body.classList.add('dark-theme');
} else if (mode === "light") {
document.body.classList.remove('dark-theme');
}
}
document.addEventListener("DOMContentLoaded", function(){
// init after dom
(function () {
var burger = document.querySelector('.burger');
var menu = document.querySelector('#' + burger.dataset.target);

View File

@ -31,7 +31,6 @@ aside.menu.is-sticky {
min-height: 100vh;
}
.content {
pre,
code {
@ -44,13 +43,6 @@ aside.menu.is-sticky {
}
}
p, li, blockquote {
pre,
code {
font-size: 1rem !important;
}
}
h1, h2, h3, h4, h5 {
code {
font-weight: bold;
@ -58,8 +50,8 @@ aside.menu.is-sticky {
}
}
blockquote, dl, ol, p, ul {
max-width: 70vw;
blockquote, dl, ol, p, ul, pre {
max-width: 65vw;
}
.hljs {
@ -93,20 +85,16 @@ aside.menu.is-sticky {
blockquote {
p {
font-size: 1rem;
background-color: darken($darkspace, 5%);
}
blockquote {
p {
}
line-height: 1.4;
}
}
}
html {
background-color: #0D203F;
}
body.dark-theme {
.content {
a {
&:hover {
@ -117,6 +105,7 @@ body.dark-theme {
blockquote {
p {
background-color: darken($darkspace, 5%) !important;
line-height: 1.4;
}
}
}
@ -125,5 +114,4 @@ body.dark-theme {
aside.menu a.button path {
fill: white !important;
}
}

View File

@ -1,6 +1,6 @@
<header class="navbar is-transparent is-wide" id="topbar">
<div class="navbar-brand">
<a class="navbar-item" href="{{site.info.base_url}}">
<a class="navbar-item" href="/">
<div class="logo">
<svg width="170" height="64" viewBox="0 0 685 87" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M84.5571 18.574H16.4092V36.0591H63.5585V52.5029H16.4092V85H0V2.13016H1.86173H16.4092H84.5571V18.574Z" fill="#0D203F" />
@ -22,7 +22,7 @@
</a>
<div class="logo-project">
<a href="#">
<a href="https://github.com/fermyon/spin" target="_blank">
Spin
</a>
</div>
@ -59,4 +59,4 @@
<div class="navbar-burger burger" aria-label="menu" aria-expanded="false" data-target="fermyonMenu"><span></span> <span></span> <span></span></div>
</div>
</div>
</header>
</header>

View File

@ -7,14 +7,26 @@
<li><a href="{{site.info.base_url}}/index/">Introduction</a></li>
<li><a href="{{site.info.base_url}}/quickstart/">Quickstart</a></li>
<li><a href="{{site.info.base_url}}/configuration/">Configuration</a></li>
<li><a href="{{site.info.base_url}}/writing-http-apps/">Writing HTTP applications</a></li>
</ul>
<p class="menu-label">
Development
Language guides
</p>
<ul class="menu-list">
<li><a href="{{site.info.base_url}}/architecture/">Architecture</a></li>
<li><a href="{{site.info.base_url}}/contributing/">Contributing</a></li>
<li><a href="{{site.info.base_url}}/rust-components/">Rust</a></li>
<li><a href="{{site.info.base_url}}/go-components/">Go</a></li>
</ul>
<p class="menu-label">
Advanced
</p>
<ul class="menu-list">
<li><a href="{{site.info.base_url}}/http-trigger/">The HTTP trigger</a></li>
<li><a href="{{site.info.base_url}}/redis-trigger/">The Redis trigger</a></li>
<li><a href="{{site.info.base_url}}/distributing-apps/">Packaging and distribution</a></li>
<li><a href="{{site.info.base_url}}/extending-and-embedding/">Extending and embedding Spin</a></li>
{{!-- <li><a href="{{site.info.base_url}}/architecture/">Architecture</a></li> --}}
<li><a href="{{site.info.base_url}}/contributing/">Contribution guide</a></li>
<li>&nbsp;</li>
<li><a href="https://github.com/fermyon/spin" class="button is-round is-outlined is-size-6 is-medium">
<svg width="0.825em" height="0.825em" viewBox="0 0 32 32" fill="none" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" clip-rule="evenodd" d="M16 0C7.16 0 0 7.16 0 16C0 23.08 4.58 29.06 10.94 31.18C11.74 31.32 12.04 30.84 12.04 30.42C12.04 30.04 12.02 28.78 12.02 27.44C8 28.18 6.96 26.46 6.64 25.56C6.46 25.1 5.68 23.68 5 23.3C4.44 23 3.64 22.26 4.98 22.24C6.24 22.22 7.14 23.4 7.44 23.88C8.88 26.3 11.18 25.62 12.1 25.2C12.24 24.16 12.66 23.46 13.12 23.06C9.56 22.66 5.84 21.28 5.84 15.16C5.84 13.42 6.46 11.98 7.48 10.86C7.32 10.46 6.76 8.82 7.64 6.62C7.64 6.62 8.98 6.2 12.04 8.26C13.32 7.9 14.68 7.72 16.04 7.72C17.4 7.72 18.76 7.9 20.04 8.26C23.1 6.18 24.44 6.62 24.44 6.62C25.32 8.82 24.76 10.46 24.6 10.86C25.62 11.98 26.24 13.4 26.24 15.16C26.24 21.3 22.5 22.66 18.94 23.06C19.52 23.56 20.02 24.52 20.02 26.02C20.02 28.16 20 29.88 20 30.42C20 30.84 20.3 31.34 21.1 31.18C27.42 29.06 32 23.06 32 16C32 7.16 24.84 0 16 0V0Z" fill="#24292E"/></svg>

View File

@ -63,7 +63,6 @@
<link rel="manifest" href="{{site.info.base_url}}/static/image/icon/site.webmanifest">
<link rel="mask-icon" href="{{site.info.base_url}}/static/image/icon/safari-pinned-tab.svg" color="#0d203f">
<meta name="msapplication-TileColor" content="#0d203f">
<meta name="theme-color" content="#ffffff">
<link rel="alternate" type="application/rss+xml" title="{{site.info.title}}" href="{{site.info.base_url}}/atom.xml">
<link rel="canonical" href="{{site.info.base_url}}{{env.PATH_INFO}}">

View File

@ -9,12 +9,7 @@
<article class="column content content-docs content-docs-wide">
<section id="type">
{{#with page.frontmatter}}
<h1 class="blog-post-title border-bottom">{{title}}</h1>
<hr class="page-break" />
<p class="blog-post-meta">{{#if extra.date}}Published: {{extra.date}}{{/if}}</p>
{{/with}}
<h1 class="blog-post-title border-bottom">{{page.head.title}}</h1>
{{{page.body}}}
@ -28,4 +23,4 @@
{{> content_bottom }}
</body>
</html>
</html>

View File

@ -8,6 +8,5 @@ version = "1.0.0"
[[component]]
id = "hello"
source = "target/wasm32-wasi/release/spinhelloworld.wasm"
allowed_http_hosts = [ "https://fermyon.com" ]
[component.trigger]
route = "/hello"

View File

@ -6,7 +6,7 @@ trigger = {type = "redis", address = "redis://localhost:6379"}
version = "0.1.0"
[[component]]
id = "example"
id = "echo-message"
source = "target/wasm32-wasi/release/spinredis.wasm"
[component.trigger]
channel="messages"

2682
examples/spin-timer-echo/Cargo.lock generated Normal file

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,26 @@
[package]
name = "spin-timer-echo"
version = "0.1.0"
authors = ["Fermyon Engineering <engineering@fermyon.com>"]
edition = "2021"
[lib]
doctest = false
[dependencies]
anyhow = "1.0"
async-trait = "0.1"
chrono = "0.4"
env_logger = "0.9"
futures = "0.3"
log = { version = "0.4", default-features = false }
spin-engine = { path = "../../crates/engine" }
spin-config = { path = "../../crates/config" }
tokio = { version = "1.14", features = [ "full" ] }
tracing = { version = "0.1", features = [ "log" ] }
tracing-futures = "0.2"
tracing-subscriber = { version = "0.3.7", features = [ "env-filter" ] }
wasi-common = "0.34"
wasmtime = "0.34"
wasmtime-wasi = "0.34"
wit-bindgen-wasmtime = { git = "https://github.com/bytecodealliance/wit-bindgen", rev = "2f46ce4cc072107153da0cefe15bdc69aa5b84d0" }

View File

@ -0,0 +1 @@
echo: function(msg: string) -> string

View File

@ -0,0 +1,206 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 3
[[package]]
name = "anyhow"
version = "1.0.48"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "62e1f47f7dc0422027a4e370dd4548d4d66b26782e513e98dca1e689e058a80e"
[[package]]
name = "async-trait"
version = "0.1.51"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "44318e776df68115a881de9a8fd1b9e53368d7a4a5ce4cc48517da3393233a5e"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "bitflags"
version = "1.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
[[package]]
name = "heck"
version = "0.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6d621efb26863f0e9924c6ac577e8275e5e6b77455db64ffa6c65c904e9e132c"
dependencies = [
"unicode-segmentation",
]
[[package]]
name = "id-arena"
version = "2.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "25a2bc672d1148e28034f176e01fffebb08b35768468cc954630da77a1449005"
[[package]]
name = "memchr"
version = "2.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "308cc39be01b73d0d18f82a0e7b2a3df85245f84af96fdddc5d202d27e47b86a"
[[package]]
name = "proc-macro2"
version = "1.0.32"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ba508cc11742c0dc5c1659771673afbab7a0efab23aa17e854cbab0837ed0b43"
dependencies = [
"unicode-xid",
]
[[package]]
name = "pulldown-cmark"
version = "0.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ffade02495f22453cd593159ea2f59827aae7f53fa8323f756799b670881dcf8"
dependencies = [
"bitflags",
"memchr",
"unicase",
]
[[package]]
name = "quote"
version = "1.0.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "38bc8cc6a5f2e3655e0899c1b848643b2562f853f114bfec7be120678e3ace05"
dependencies = [
"proc-macro2",
]
[[package]]
name = "rust-echo-test"
version = "0.1.0"
dependencies = [
"wit-bindgen-rust",
]
[[package]]
name = "syn"
version = "1.0.81"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f2afee18b8beb5a596ecb4a2dce128c719b4ba399d34126b9e4396e3f9860966"
dependencies = [
"proc-macro2",
"quote",
"unicode-xid",
]
[[package]]
name = "tinyvec"
version = "1.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2c1c1d5a42b6245520c249549ec267180beaffcc0615401ac8e31853d4b6d8d2"
dependencies = [
"tinyvec_macros",
]
[[package]]
name = "tinyvec_macros"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cda74da7e1a664f795bb1f8a87ec406fb89a02522cf6e50620d016add6dbbf5c"
[[package]]
name = "unicase"
version = "2.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "50f37be617794602aabbeee0be4f259dc1778fabe05e2d67ee8f79326d5cb4f6"
dependencies = [
"version_check",
]
[[package]]
name = "unicode-normalization"
version = "0.1.19"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d54590932941a9e9266f0832deed84ebe1bf2e4c9e4a3554d393d18f5e854bf9"
dependencies = [
"tinyvec",
]
[[package]]
name = "unicode-segmentation"
version = "1.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8895849a949e7845e06bd6dc1aa51731a103c42707010a5b591c0038fb73385b"
[[package]]
name = "unicode-xid"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3"
[[package]]
name = "version_check"
version = "0.9.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5fecdca9a5291cc2b8dcf7dc02453fee791a280f3743cb0905f8822ae463b3fe"
[[package]]
name = "wit-bindgen-gen-core"
version = "0.1.0"
source = "git+https://github.com/bytecodealliance/wit-bindgen?rev=2f46ce4cc072107153da0cefe15bdc69aa5b84d0#2f46ce4cc072107153da0cefe15bdc69aa5b84d0"
dependencies = [
"anyhow",
"wit-parser",
]
[[package]]
name = "wit-bindgen-gen-rust"
version = "0.1.0"
source = "git+https://github.com/bytecodealliance/wit-bindgen?rev=2f46ce4cc072107153da0cefe15bdc69aa5b84d0#2f46ce4cc072107153da0cefe15bdc69aa5b84d0"
dependencies = [
"heck",
"wit-bindgen-gen-core",
]
[[package]]
name = "wit-bindgen-gen-rust-wasm"
version = "0.1.0"
source = "git+https://github.com/bytecodealliance/wit-bindgen?rev=2f46ce4cc072107153da0cefe15bdc69aa5b84d0#2f46ce4cc072107153da0cefe15bdc69aa5b84d0"
dependencies = [
"heck",
"wit-bindgen-gen-core",
"wit-bindgen-gen-rust",
]
[[package]]
name = "wit-bindgen-rust"
version = "0.1.0"
source = "git+https://github.com/bytecodealliance/wit-bindgen?rev=2f46ce4cc072107153da0cefe15bdc69aa5b84d0#2f46ce4cc072107153da0cefe15bdc69aa5b84d0"
dependencies = [
"async-trait",
"bitflags",
"wit-bindgen-rust-impl",
]
[[package]]
name = "wit-bindgen-rust-impl"
version = "0.1.0"
source = "git+https://github.com/bytecodealliance/wit-bindgen?rev=2f46ce4cc072107153da0cefe15bdc69aa5b84d0#2f46ce4cc072107153da0cefe15bdc69aa5b84d0"
dependencies = [
"proc-macro2",
"syn",
"wit-bindgen-gen-core",
"wit-bindgen-gen-rust-wasm",
]
[[package]]
name = "wit-parser"
version = "0.1.0"
source = "git+https://github.com/bytecodealliance/wit-bindgen?rev=2f46ce4cc072107153da0cefe15bdc69aa5b84d0#2f46ce4cc072107153da0cefe15bdc69aa5b84d0"
dependencies = [
"anyhow",
"id-arena",
"pulldown-cmark",
"unicode-normalization",
"unicode-xid",
]

View File

@ -0,0 +1,12 @@
[package]
name = "rust-echo-test"
version = "0.1.0"
edition = "2021"
[lib]
crate-type = [ "cdylib" ]
[dependencies]
wit-bindgen-rust = { git = "https://github.com/bytecodealliance/wit-bindgen", rev = "2f46ce4cc072107153da0cefe15bdc69aa5b84d0" }
[workspace]

View File

@ -0,0 +1,8 @@
wit_bindgen_rust::export!("../echo.wit");
struct Echo;
impl echo::Echo for Echo {
fn echo(msg: String) -> String {
format!("ECHO: {}", msg)
}
}

View File

@ -0,0 +1,3 @@
# Extending Spin with a trigger and application
<https://spin.fermyon.dev/extending-and-embedding>

View File

@ -0,0 +1,66 @@
use anyhow::Result;
use spin_config::{Configuration, CoreComponent};
use spin_engine::{Builder, ExecutionContextConfiguration};
use std::{sync::Arc, time::Duration};
use tokio::task::spawn_blocking;
wit_bindgen_wasmtime::import!("echo.wit");
type ExecutionContext = spin_engine::ExecutionContext<echo::EchoData>;
/// A custom timer trigger that executes the
/// first component of an application on every interval.
#[derive(Clone)]
pub struct TimerTrigger {
/// The interval at which the component is executed.
pub interval: Duration,
/// The application configuration.
app: Configuration<CoreComponent>,
/// The Spin execution context.
engine: Arc<ExecutionContext>,
}
impl TimerTrigger {
/// Creates a new trigger.
pub async fn new(interval: Duration, app: Configuration<CoreComponent>) -> Result<Self> {
let config = ExecutionContextConfiguration::new(app.clone(), None);
let engine = Arc::new(Builder::build_default(config).await?);
log::debug!("Created new Timer trigger.");
Ok(Self {
interval,
app,
engine,
})
}
/// Runs the trigger at every interval.
pub async fn run(&self) -> Result<()> {
let mut interval = tokio::time::interval(self.interval);
loop {
interval.tick().await;
self.handle(
chrono::Local::now()
.format("%Y-%m-%d][%H:%M:%S")
.to_string(),
)
.await?;
}
}
/// Execute the first component in the application configuration.
async fn handle(&self, msg: String) -> Result<()> {
let (mut store, instance) =
self.engine
.prepare_component(&self.app.components[0].id, None, None, None, None)?;
let res = spawn_blocking(move || -> Result<String> {
let e = echo::Echo::new(&mut store, &instance, |host| host.data.as_mut().unwrap())?;
Ok(e.echo(&mut store, &msg)?)
})
.await??;
log::info!("{}\n", res);
Ok(())
}
}

View File

@ -0,0 +1,42 @@
use anyhow::Result;
use spin_config::{
ApplicationInformation, ApplicationOrigin, Configuration, CoreComponent, ModuleSource,
TriggerConfig, WasmConfig,
};
use spin_timer_echo::TimerTrigger;
use std::time::Duration;
#[tokio::main]
async fn main() -> Result<()> {
tracing_subscriber::fmt()
.with_env_filter(tracing_subscriber::EnvFilter::from_default_env())
.init();
let trigger = TimerTrigger::new(Duration::from_secs(1), app()).await?;
trigger.run().await
}
fn app() -> Configuration<CoreComponent> {
let info = ApplicationInformation {
spin_version: spin_config::SpinVersion::V1,
name: "test-app".to_string(),
version: "1.0.0".to_string(),
description: None,
authors: vec![],
trigger: spin_config::ApplicationTrigger::Http(spin_config::HttpTriggerConfiguration {
base: "/".to_owned(),
}),
namespace: None,
origin: ApplicationOrigin::File("".into()),
};
let component = CoreComponent {
source: ModuleSource::FileReference("target/test-programs/echo.wasm".into()),
id: "test".to_string(),
trigger: TriggerConfig::default(),
wasm: WasmConfig::default(),
};
let components = vec![component];
Configuration::<CoreComponent> { info, components }
}

View File

@ -19,7 +19,7 @@ applications and how to get started.
## Getting started
See the [quickstart document](./docs/content/docs/quickstart.md) for a detailed
See the [quickstart document](./docs/content/quickstart.md) for a detailed
guide on configuring Spin and writing your first Spin application, but in short:
```
@ -28,11 +28,11 @@ $ tar xfv spin-canary-<os-arch>.tar.gz
$ ./spin --help
```
After you follow the [quickstart document](./docs/content/docs/quickstart.md),
After you follow the [quickstart document](./docs/content/quickstart.md),
you can follow the
[guide on writing HTTP applications with Spin](./docs/content/docs/writing-http-apps.md)
[guide on writing HTTP applications with Spin](./docs/content/writing-http-apps.md)
and the
[guide on configuring Spin applications](./docs/content/docs/configuration.md).
[guide on configuring Spin applications](./docs/content/configuration.md).
After you built your application, run it using Spin, pointing to the Spin
application configuration file:
@ -44,4 +44,4 @@ $ spin up --file spin.toml
## Contributing
We are delighted that you are interested in making Spin better! Thank you!
Please follow the [contributing guide](./docs/content/docs/contributing.md).
Please follow the [contributing guide](./docs/content/contributing.md).