Giter Site home page Giter Site logo

fermyon / spin Goto Github PK

View Code? Open in Web Editor NEW
4.9K 41.0 230.0 27.26 MB

Spin is the open source developer tool for building and running serverless applications powered by WebAssembly.

Home Page: https://developer.fermyon.com/spin

License: Apache License 2.0

Rust 98.74% Makefile 0.26% Dockerfile 0.23% Go 0.12% C 0.06% Swift 0.02% Zig 0.01% Shell 0.07% PHP 0.01% HCL 0.21% Python 0.22% Nix 0.07%
webassembly fermyon spin serverless

spin's Introduction

Fermyon Spin

spin logo

Spin is a framework for building, deploying, and running fast, secure, and composable cloud microservices with WebAssembly.

build status Discord

What is 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 and Wasmtime 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.

Getting started

See the Install Spin page of the Spin documentation for a detailed guide on installing and configuring Spin, but in short run the following commands:

curl -fsSL https://developer.fermyon.com/downloads/install.sh | bash
sudo mv ./spin /usr/local/bin/spin

Alternatively, you could build Spin from source.

To get started writing apps, follow the quickstart guide, and then follow the Rust, JavaScript, Python, or Go language guides, and the guide on writing Spin applications.

Usage

Below is an example of using the spin CLI to create a new Spin application. To run the example you will need to install the wasm32-wasi target for Rust.

$ rustup target add wasm32-wasi

First, run the spin new command to create a Spin application from a template.

# Create a new Spin application named 'hello-rust' based on the Rust http template, accepting all defaults
$ spin new --accept-defaults -t http-rust hello-rust

Running the spin new command created a hello-rust directory with all the necessary files for your application. Change to the hello-rust directory and build the application with spin build, then run it locally with spin up:

# Compile to Wasm by executing the `build` command.
$ spin build
Executing the build command for component hello-rust: cargo build --target wasm32-wasi --release
    Finished release [optimized] target(s) in 0.03s
Successfully ran the build command for the Spin components.

# Run the application locally.
$ spin up
Logging component stdio to ".spin/logs/"

Serving http://127.0.0.1:3000
Available Routes:
  hello-rust: http://127.0.0.1:3000 (wildcard)

That's it! Now that the application is running, use your browser or cURL in another shell to try it out:

# Send a request to the application.
$ curl -i 127.0.0.1:3000
HTTP/1.1 200 OK
foo: bar
content-length: 14
date: Thu, 13 Apr 2023 17:47:24 GMT

Hello, Fermyon         

You can make the app do more by editting the src/lib.rs file in the hello-rust directory using your favorite editor or IDE. To learn more about writing Spin applications see Writing Applications in the Spin documentation. To learn how to publish and distribute your application see the Publishing and Distribution guide in the Spin documentation.

For more information on the cli commands and subcommands see the CLI Reference.

Language Support for Spin Features

The table below summarizes the feature support in each of the language SDKs.

Feature Rust SDK Supported? TypeScript SDK Supported? Python SDK Supported? Tiny Go SDK Supported? C# SDK Supported?
Triggers
HTTP Supported Supported Supported Supported Supported
Redis Supported Not Supported Supported Supported Not Supported
APIs
Outbound HTTP Supported Supported Supported Supported Supported
Configuration Variables Supported Supported Supported Supported Supported
Key Value Storage Supported Supported Supported Supported Not Supported
SQLite Storage Supported Supported Supported Supported Not Supported
MySQL Supported Supported Not Supported Supported Not Supported
PostgreSQL Supported Supported Not Supported Supported Supported
Outbound Redis Supported Supported Supported Supported Supported
Serverless AI Supported Supported Supported Supported Not Supported
Extensibility
Authoring Custom Triggers Supported Not Supported Not Supported Not Supported Not Supported

Contributing

We are delighted that you are interested in making Spin better! Thank you! Please follow the contributing guide. And join our Discord server.

Stay in Touch

Follow us on Twitter: @spinframework

You can join the Spin community in our Discord server where you can ask questions, get help, and show off the cool things you are doing with Spin!

spin's People

Contributors

adamreese avatar alexcrichton avatar bacongobbler avatar calebschoepp avatar dependabot[bot] avatar dicej avatar endocrimes avatar etehtsea avatar fermybot avatar fibonacci1729 avatar flynnduism avatar frankyang0529 avatar itowlson avatar karthik2804 avatar kate-goldenring avatar lann avatar melissaklein24 avatar michellen avatar miketang84 avatar mikkelhegn avatar mooori avatar mossaka avatar radu-matei avatar rajatjindal avatar rylev avatar suneetnangia avatar technosophos avatar tpmccallum avatar tschneidereit avatar vdice avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

spin's Issues

Verify and document the case sensitivity of HTTP headers

The HTTP headers passed into Spin HTTP components are represented as list<tuple<string, string>>.
When consuming the headers from a component, there is no information about the case sensitivity of the keys or of the values.

What is the expected behavior in other environments?

Add GitHub token as secret to pull private crate

The action currently fails because it cannot pull the private Wact repository.
I need to add a GH token that can pull repositories I have access to, and export the CARGO_NET_GIT_FETCH_WITH_CLI=true environment variable.

Handle versioned interfaces

There should be a clean way for users to target a specific Spin trigger interface, and for the runtime to select the versioned interface to execute a guest module.

Run Bartholomew on top of Spin

Once #55 and #56 get merged, we should be able to run Bartholomew on top of Spin.

Depending on the status of #54, most likely we will have to implement the fn handler(req: Request) -> Response entrypoint for Bartholomew as opposed to the current way of getting the information.

This will help fermyon.com, as well as serve as a smoke test for the status of Spin.

HTTP types are currently blocking and non-streaming

#14 adds common types between the outbound HTTP interface and the HTTP trigger.
However, those types do not contain streams, and are currently blocking, both due to limitations in the bindings generator.

This issue tracks updating them to use streams and asynchronous functions once they are implemented upstream.

Encapsulate configuration

In the first iterations of Spin, for speed of development, the internal configuration object was a direct representation of the TOML configuration file. As we've seen in projects like WAGI and the Hippo CLI, this tends not to be sustainable: the application code ends up having to repeatedly perform the same validation/massage on the raw deserialised object, and you get alternative config flows that end up being awkwardly crobarred into that first serialisation format because so much app code is coupled to it.

It's hard to predict future configuration needs so this may be premature, but if we can make a reasonable stab at abstracting the configuration surfaced to the application from the raw serialised format, we will hopefully mitigate the effort of transition when it comes.

As an initial stab, I'd suggest:

  • Public interface of config crate should be
    • Configuration objects designed around the needs of the application as best we understand them
    • Associated functions for parsing those configuration objects from sources such as files and strings
  • Private (hopefully) config::serialisation module that deals with things like the raw on-disk TOML format

Related: https://lexi-lambda.github.io/blog/2019/11/05/parse-don-t-validate/

Spin up silently fails when interface mismatch between trigger & handler

TL;DR: Define behavior for interface mismatch between handler and trigger.

If I build a handler using the bindings generated from the following wit file

use * from http-types

// NOTE: the missing argument
handler: function() -> response

Assuming the HTTP trigger,

$ spin up --local path/to/handler.wasm

When executing curl localhost:3000/

spin will do nothing (i.e. no logs or error).

Configuration for Spin applications

Configuration could be split into:

  • language / toolchain specific configuration — (Cargo.toml, package.json)
  • Spin configuration

We currently don't have any configuration defined for applications. This could include (the list below does not represent a single configuration file, but potentially information aggregated from multiple sources):

  • static assets to include in the component
  • runtime configuration (volumes, allowed HTTP domains)
  • routes
  • overrides for dependencies
  • Hippo configuration

We need to learn from the current configuration splits in Hippo, Wagi, and yo-wasm and surface a coherent user experience for handling configuration.

Handle route patterns for the HTTP trigger

A component will only be executed if the request's URI path matches the route defined in the trigger configuration exactly:

match req.uri().path() {
"/healthz" => Ok(Response::new(Body::from("OK"))),
route => match self.router.routes.get(&route.to_string()) {
Some(c) => return SpinHttpExecutor::execute(&self.engine, c.id.clone(), req).await,
None => return Ok(Self::not_found()),
},
}
}

This needs to handle route patterns like route = /hello/....

Add per-environment dependencies

spin.toml could define optional, per-environment dependencies which should be selected at runtime based on some constraint (for example an environment variable or flag).

Use a templating engine for Spin templates

The current implementation for templates is rather simple:

❯ spin templates add --help
spin-templates-add 0.1.0
Add a template repository locally

USAGE:
    spin templates add [OPTIONS] --name <name>

OPTIONS:
        --branch <branch>    The optional branch of the git repository
        --git <git>          The URL of the templates git repository. The templates must be in a git repository in a
                             "templates" directory
        --local <local>      Local directory to add as a template
        --name <name>        The name of the templates repository

Templates can added from:

  • a git repository with a templates/ directory in the root, containing individual templates as directories
  • a single local directory

While they are named "templates", there is no templating engine configured, and creating a new application based on a template using spin new will simply copy the contents of the selected "template".
There are a few options for selecting a templating engine - for example, yo-wasm uses EJS, while cargo generate uses the Liquid template language.

(One very interesting approach of cargo generate is allowing template-defined placeholders, which allow for template authors to define custom variables and either get their values through user interaction, or use a supplied default value.)

Ideally, we would pick a templating language and implement support for it with Spin templates.

Base path for HTTP applications

Currently, there is no way for an application to define a top-level base path.

For example, there should be a way to define the base path such that for the following Spin configuration:

name = "test-name"
trigger = "http"
base_path = "/base"

[[component]]
id = "component-foo"
[component.trigger]
route = "/foo"

[[component]]
id = "component-bar"
[component.trigger]
route = "/bar"

The behavior should be:

  • component foo would handle /base/foo
  • component bar would handle /base/bar

ref #40

Authorization for Spin components

Currently, there is no concept of authorization in Spin.

A component is invoked whenever its trigger receives a request, and at no point is checked where the event is coming from, or who generated it.

How should this work?

cc @fibonacci1729

Middleware

Support using components as "middleware" that can intercept/modify/replace requests and/or responses.

A few use cases (suggest others!):

  • Auth
  • Caching
  • Tracing

Document the contributor guidelines

There should be a document that explains:

  • setting up the development environment for building and testing the project
  • how to cryptographically sign commits
  • writing informative commit messages

Default HTTP headers

What are the default HTTP headers an HTTP executor should set when handling a request?
Should they be different between different executors?

Monitor performance in CI

Very early tests for #54 show approximately 1ms latency for requests for the same application running in Wagi, which for multiple concurrent requests, can be significant.

We should explore where that extra latency is coming from.

Storing `spin.toml` vs. reconstructing it from an invoice

A local application configuration is currently defined as a TOML file on disk, which contains, among others, paths for the application components and local files that should be mapped inside the Wasm module at runtime.

We want to distribute such an application (components + their configuration) using Bindle (ref #37).
The first obvious step is creating a Bindle invoice with parcels for all assets (defined in the source, files, and potentially dependencies sections).

However, there is the question of the configuration for the application:

  1. we could store a modified version of spin.toml (which, for example, no longer references local files as the sources for the components' Wasm modules) as a Bindle parcel.
  2. we could deconstruct it and store data for each components as a combination of groups, features, and annotations.

I'm currently leaning towards option 1, as it would make it easier at each step to directly pass already formed configuration, rather than reconstructing from invoices.

I can also see the appeal in option 2, which is the approach taken with HIPPOFACTS — which ties the file format rather closely with a Bindle invoice.

Any strong opinions in favor of either?

Push a local application configuration to Bindle

A local Spin application configuration is a file that contains information about the application and its components:

name        = "spin-hello-world"
version     = "1.0.0"
description = "A simple application that returns hello and goodbye."
authors     = [ "Radu Matei <[email protected]>" ]
trigger     = "http"

[[component]]
    source = "target/wasm32-wasi/release/spinhelloworld.wasm"
    id     = "hello"
    files = [ ...]
    env = [ ...]
[component.trigger]
    route = "/hello"

See https://github.com/fermyon/spin/blob/main/docs/configuration.md#configuration-for-spin-applications.

There should be a way to package the application defined by the configuration as a bindle, distribute it, then run it based on its remote reference.

Handling dependencies

spin.toml allows defining a list of dependencies that the main component requires in order to be instantiated:

    [component.dependencies]
        cache    = { type = "host" }
        markdown = { type = "component", reference = "github/octo-markdown/1.0.0" }

However, the behavior of how this section should be used is not currently implemented (or specified).

Proposal: at startup, there should be a step that links the entrypoint module using the components specified in this section, and generates a "linked" component that can be directly instantiated by the execution context (engine).

This section could be potentially generated from Cargo.toml in the case of Rust (the format and necessary information is the same).

Handling static assets

The HIPPOFACTS format allowed for defining files as static assets, which had two main effects:

  • the files would be included in the resulting bindle
  • the files would be mapped into the running Wasm module by Wagi at runtime

This enables a wide range of scenarios for applications, and there should be a way to do this in Spin.

ref #17

Timing out

Should there be a default timeout for components?

Initializing new applications

Currently, Spin has no concept of "initializing" a new application without using a template (re #15).

Is there a need for a spin init command? What would it do?

Spin SDKs for Rust

Right now, the assumption is that users will directly reference a Spin interface (either implementing a handler, or consuming some functionality).

For languages with great bindgen support, would it make sense to have versioned packages that contain all functionality for building an application? This would include:

  • only defining the handler function (without all other boilerplate code)
  • all other functionality (cache, object storage, logging) could be imported using spin_* (or similar) packages.

This means keeping the packages in sync with the versioned interfaces, and choosing which languages would have this type of support.

Request URI passed into a Spin HTTP component should be stripped of the route

Currently, when a component's declared route matches a request, the request's full URI is passed as the component's URI.

To illustrate, take the following component:

[[component]]
id = "foo"
[component.trigger]
route = "/static/..."

Assuming Spin is listening on example.com, a request to example.com/static/images/image.png is currently translated into the component with the full /static/images/image.png URI.

I think the expected behavior should be that the URI that reaches the component to be stripped of the initial mapped route, so in this case it should be /images/image.png.

Integration tests

We should have automated tests that run all applications based on the templates.

The relationship between routes, modules, and entrypoints

Currently, Spin defines its triggers through a WebAssembly interface (through a WIT file).
Implementing a component (Wasm module) for a particular trigger means implementing its interface.

Because a module can only contain at most one implementation of the same interface (the function that implements the trigger is the "entrypoint" function, and is exported by name), it cannot export more than one such "entrypoint".
(the entrypoint can contain logic and call other functions, but directly, a trigger can currently only execute the one entrypoint).

With the HTTP trigger, one common scenario is writing multiple route handlers within the same "project".

How should this be handled with Spin? The two clear options at first are:

  • the entrypoint handles all routes / subroutes, and it should contain logic to call other functions in the module
  • one module corresponds to one route, each with an entrypoint that handles the single route, and it should be the responsibility of a higher level tool + configuration (ref #17) to assemble multiple such routes.

Note that this requires no actual implementation changes to the current HTTP trigger, but has an impact on configuration, documentation, and templates.

[META]: Running fermyon.com using Spin

With the new file server (https://github.com/fermyon/spin-fileserver) and the port of Bartholomew (no PR yet, as it would be public), we can run the fermyon.com website locally using Spin, and I think it is in our best interest to run it in production using Spin as soon as possible.

Before doing that, we need to solve a few issues:

  • #70 (unnoticeable for the website, but very annoying)

  • #37 and #42 (we could it as a local app, but we need to use Bindle anyway)

  • #71 (fix available)

  • #31 (redirecting both stdout and stderr to files should work)

  • #33

  • [ ] #22

EDIT: we decided to move ahead with running spin.fermyon.dev using Spin first, and integrate Hippo there.
So #22 is no longer a hard requirement for now, but it will become again for v0.2.0

[Discussion]: Implement Wagi HTTP executor

The HTTP trigger implementation allows implementing other interfaces (on top of the Spin HTTP interface).
Given our existing Wagi work, and given that the Spin interface can only be implemented by components written in languages that have support for Wasm components (the list is growing, but at the moment of writing this issue only contains mature support for Rust and C++), would it make sense to implement a Wagi executor as well?

Note that this would not require rewriting Wagi from scratch, but rather only passing the request data and getting the response.
Most other concerns (files, environment variables, outbound HTTP) should be handled in the same way for both executors, and mostly handled by the underlying engine, rather than by the HTTP trigger.

(The only potential difference compared to current Wagi would be supporting modules defining routes, which should be a discussion point if we decide implementing a Wagi executor makes sense).

spin/crates/http/src/lib.rs

Lines 266 to 280 in 751cda3

#[derive(Clone)]
pub struct WagiHttpExecutor;
#[async_trait]
impl HttpExecutor for WagiHttpExecutor {
#[instrument(skip(_engine))]
async fn execute(
_engine: &ExecutionContext,
_component: String,
_req: Request<Body>,
) -> Result<Response<Body>> {
log::info!("Executing request for component {}", _component);
todo!("Wagi executor not implemented yet.")
}
}

Templates for Wagi HTTP components

#54 added support for running Wagi HTTP components.

There should be corresponding templates and examples that show how to write such applications (and with mixed components).

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.