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:
parent
491e5557eb
commit
127907b313
|
@ -4,3 +4,4 @@ cache
|
|||
node_modules
|
||||
ignored-assets
|
||||
main.wasm
|
||||
.parcel-cache
|
||||
|
|
|
@ -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",
|
||||
]
|
||||
|
|
13
Cargo.toml
13
Cargo.toml
|
@ -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"
|
||||
|
|
1
build.rs
1
build.rs
|
@ -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.
|
@ -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.
|
||||
|
||||
|
|
|
@ -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"
|
||||
```
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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`.
|
|
@ -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#.
|
|
@ -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.
|
|
@ -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).
|
|
@ -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).
|
||||
|
|
|
@ -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).
|
|
@ -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.
|
||||
|
|
|
@ -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.
|
|
@ -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
|
||||
|
|
|
@ -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.
|
|
@ -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.
File diff suppressed because it is too large
Load Diff
|
@ -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"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
@ -13,7 +13,5 @@
|
|||
"type": "image/png"
|
||||
}
|
||||
],
|
||||
"theme_color": "#ffffff",
|
||||
"background_color": "#ffffff",
|
||||
"display": "standalone"
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
|
@ -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>
|
||||
|
|
|
@ -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> </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>
|
||||
|
|
|
@ -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}}">
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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"
|
File diff suppressed because it is too large
Load Diff
|
@ -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" }
|
|
@ -0,0 +1 @@
|
|||
echo: function(msg: string) -> string
|
|
@ -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",
|
||||
]
|
|
@ -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]
|
|
@ -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)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,3 @@
|
|||
# Extending Spin with a trigger and application
|
||||
|
||||
<https://spin.fermyon.dev/extending-and-embedding>
|
|
@ -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(())
|
||||
}
|
||||
}
|
|
@ -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 }
|
||||
}
|
10
readme.md
10
readme.md
|
@ -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).
|
||||
|
|
Loading…
Reference in New Issue