Skip to content

Commit

Permalink
feat: add docker+wasm examples (#222)
Browse files Browse the repository at this point in the history
Co-authored-by: Jorge Prendes <[email protected]>
  • Loading branch information
ereslibre and jprendes authored Oct 3, 2023
1 parent c7e1017 commit 6710bf4
Show file tree
Hide file tree
Showing 62 changed files with 8,824 additions and 0 deletions.
1 change: 1 addition & 0 deletions examples/containers/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
/dist
67 changes: 67 additions & 0 deletions examples/containers/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
# syntax=docker/dockerfile:labs

# Prepare a base layer for building the rust endpoint
FROM --platform=$BUILDPLATFORM rust:1.72.0-alpine AS base
RUN apk add sudo curl musl-dev ca-certificates && \
curl -fsSL https://workers.wasmlabs.dev/install | sh

# Rust endpoint: first build, then generate the release layer
FROM base AS build-rust
WORKDIR /src
RUN --mount=type=bind,target=/src,source=./apps-src/user-generation-rust \
cargo build --release --target-dir /output

FROM scratch AS release-rust
COPY --from=build-rust /output/wasm32-wasi/release/user-generation-rust.wasm /
COPY ./apps-src/user-generation-rust/user-generation-rust.toml /

# JS endpoint: no build needed, just generate the release layer
FROM scratch AS release-js
COPY ./apps-src/user-generation-js/ /

# Ruby endpoint: no build needed, just generate the release layer
FROM scratch AS release-ruby
COPY ./apps-src/user-generation-ruby /user-generation-ruby

# Python endpoint: no build needed, just generate the release layer
FROM scratch AS release-python
COPY ./apps-src/user-generation-python/ /user-generation-python

# Go endpoint: first build, then generate the release layer
FROM --platform=$BUILDPLATFORM tinygo/tinygo:0.28.1 AS build-go
WORKDIR /src
RUN --mount=type=bind,target=/src,source=./apps-src/user-generation-go \
tinygo build \
-o /home/tinygo/user-generation-go.wasm \
-no-debug -panic=trap -scheduler=none -gc=leaking \
-target=wasi .

FROM scratch AS release-go
COPY --from=build-go /home/tinygo/user-generation-go.wasm /
COPY ./apps-src/user-generation-go/user-generation-go.toml /

# Wws root: install the required runtimes, then generate the release layer
FROM base AS build-root
WORKDIR /output
RUN wws runtimes install ruby latest
RUN wws runtimes install python latest
COPY ./apps-src/tmp /output/tmp
RUN mkdir /public

FROM scratch AS release-root
COPY --from=build-root /public /public
COPY --from=build-root /output /

# Merge all the release layers into one
FROM scratch AS release
COPY --from=release-root / /
COPY --from=release-rust / /
COPY --from=release-js / /
COPY --from=release-ruby / /
COPY --from=release-python / /
COPY --from=release-go / /

# Copy over te SSL certificates
COPY --from=base /etc/ssl /etc/ssl

ENTRYPOINT ["/"]
56 changes: 56 additions & 0 deletions examples/containers/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
# Workaround for https://github.com/moby/buildkit/issues/3891
export BUILDX_NO_DEFAULT_ATTESTATIONS = 1

# Build a container image for the demo
.PHONY: image
image:
docker build --platform wasi/wasm --tag=ghcr.io/vmware-labs/wws-apps:latest .

# Export the content of the demo image into the ./dist folder
.PHONY: dist
dist: clean
docker build --platform wasi/wasm --output=dist .

# Run the demo container
.PHONY: run
run: stop image
docker run --rm -d --name docker-wws \
-p 3000:3000 \
--runtime=io.containerd.wws.v1 \
--platform=wasi/wasm \
ghcr.io/vmware-labs/wws-apps:latest
@echo "Now you can reach the Wasm Workers Server functions, such as:"
@echo " - curl http://localhost:3000/user-generation-rust"
@echo " - curl http://localhost:3000/user-generation-go"
@echo " - curl http://localhost:3000/user-generation-js"
@echo " - curl http://localhost:3000/user-generation-python"
@echo " - curl http://localhost:3000/user-generation-ruby"

# Run the demo container using a host mount
.PHONY: run-with-mount
run-with-mount: stop image
docker run --rm -d --name docker-wws \
-p 3000:3000 \
--runtime=io.containerd.wws.v1 \
--platform=wasi/wasm \
-v $(PWD)/tmp:/tmp \
ghcr.io/vmware-labs/wws-apps:latest
@echo "Now you can reach the Wasm Workers Server functions, such as:"
@echo " - curl http://localhost:3000/user-generation-rust"
@echo " - curl http://localhost:3000/user-generation-go"
@echo " - curl http://localhost:3000/user-generation-js"
@echo " - curl http://localhost:3000/user-generation-python"
@echo " - curl http://localhost:3000/user-generation-ruby"

# Stop the demo contianer
.PHONY: stop
stop:
docker rm -f docker-wws

# Same as dist
.PHONY: build
build: dist;

.PHONY: clean
clean:
rm -Rf ./dist
61 changes: 61 additions & 0 deletions examples/containers/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
# Docker + Wasm + Wasm Workers Server (wws)

This repo showcases some functions you can write, taking advantage of
Wasm Workers Server, on top of Docker Desktop, thanks to the
[`containerd-wasm-shims`](https://github.com/deislabs/containerd-wasm-shims) project.

## Build

Prerequisites for building this project:

- Docker, with [Docker + Wasm support](https://docs.docker.com/desktop/wasm/)

In order to build this example, you just have to run on the root of
this project:

```shell-session
$ make build
```

## Running

Prerequisites for running this project: Docker Desktop 4.23.0 or later.

You can run the example:

```shell-session
$ make run
```

After that, you can target the different endpoints exposed by the Wasm
Workers Server:

```shell-session
$ curl -s http://localhost:3000/user-generation-rust | jq
$ curl -s http://localhost:3000/user-generation-go | jq
$ curl -s http://localhost:3000/user-generation-js | jq
$ curl -s http://localhost:3000/user-generation-python | jq
$ curl -s http://localhost:3000/user-generation-ruby | jq
```

This example also showcases exposing a directory in the host to the WebAssembly guest. This example can be executed with:

```shell-session
$ make run-with-mount
```

You can reach the same endpoints, but you will notice that the
attribute `.some_file_contents` of the produced JSON in all examples
now is the content of
[tmp/file.txt](tmp/file.txt)
from the host.

The only worker that is not able to read contents from the filesystem
is the JS one, so you can only check it with the rest:

```shell-session
$ curl -s http://localhost:3000/user-generation-rust | jq
$ curl -s http://localhost:3000/user-generation-go | jq
$ curl -s http://localhost:3000/user-generation-python | jq
$ curl -s http://localhost:3000/user-generation-ruby | jq
```
1 change: 1 addition & 0 deletions examples/containers/apps-src/tmp/file.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Some contents
1 change: 1 addition & 0 deletions examples/containers/apps-src/user-generation-go/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
*.wasm
12 changes: 12 additions & 0 deletions examples/containers/apps-src/user-generation-go/go.mod
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
module github.com/vmware-labs/docker-wasm

go 1.20

require github.com/vmware-labs/wasm-workers-server v1.4.0

require (
github.com/tidwall/gjson v1.14.4 // indirect
github.com/tidwall/match v1.1.1 // indirect
github.com/tidwall/pretty v1.2.1 // indirect
github.com/tidwall/sjson v1.2.5 // indirect
)
12 changes: 12 additions & 0 deletions examples/containers/apps-src/user-generation-go/go.sum
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
github.com/tidwall/gjson v1.14.2/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk=
github.com/tidwall/gjson v1.14.4 h1:uo0p8EbA09J7RQaflQ1aBRffTR7xedD2bcIVSYxLnkM=
github.com/tidwall/gjson v1.14.4/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk=
github.com/tidwall/match v1.1.1 h1:+Ho715JplO36QYgwN9PGYNhgZvoUSc9X2c80KVTi+GA=
github.com/tidwall/match v1.1.1/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM=
github.com/tidwall/pretty v1.2.0/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU=
github.com/tidwall/pretty v1.2.1 h1:qjsOFOWWQl+N3RsoF5/ssm1pHmJJwhjlSbZ51I6wMl4=
github.com/tidwall/pretty v1.2.1/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU=
github.com/tidwall/sjson v1.2.5 h1:kLy8mja+1c9jlljvWTlSazM7cKDRfJuR/bOJhcY5NcY=
github.com/tidwall/sjson v1.2.5/go.mod h1:Fvgq9kS/6ociJEDnK0Fk1cpYF4FIW6ZF7LAe+6jwd28=
github.com/vmware-labs/wasm-workers-server v1.4.0 h1:dlc0eWc3TdijQ6RxX3Mt5OLPazO/Trmk4mqDt68eDkI=
github.com/vmware-labs/wasm-workers-server v1.4.0/go.mod h1:cigUhoitjUTLsUzR4+q0cz2FymdvJtfrfIS2hYAj69c=
82 changes: 82 additions & 0 deletions examples/containers/apps-src/user-generation-go/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
package main

import (
"encoding/json"
"fmt"
"io"
"io/ioutil"
"net/http"
"strconv"

"github.com/vmware-labs/wasm-workers-server/kits/go/worker"
)

type User struct {
FirstName string `json:"first_name"`
LastName string `json:"last_name"`
Username string `json:"username"`
Email string `json:"email"`
}

type ResponseData struct {
User User `json:"user"`
SomeFileContents string `json:"some_file_contents"`
GeneratedUsers uint32 `json:"generated_users"`
}

func main() {
worker.ServeFunc(func(w http.ResponseWriter, r *http.Request) {
cache, _ := r.Context().Value(worker.CacheKey).(map[string]string)

// Create the request
req, err := http.NewRequest(http.MethodGet, "https://random-data-api.com/api/v2/users", nil)
if err != nil {
panic(err)
}

res, err := worker.SendHttpRequest(req)
if err != nil {
panic(err)
}

// Read the response
resBody, err := io.ReadAll(res.Body)
if err != nil {
panic(err)
}
res.Body.Close()

user := User{}
err = json.Unmarshal([]byte(resBody), &user)
if err != nil {
panic(err)
}

fileContents_, err := ioutil.ReadFile("/tmp/file.txt")
if err != nil {
panic(err)
}
fileContents := string(fileContents_)

generatedUserCount := uint32(1)
if count, ok := cache["generated_users_counter"]; ok {
n, _ := strconv.ParseUint(count, 10, 32)
generatedUserCount = uint32(n) + 1
}
cache["generated_users_counter"] = fmt.Sprintf("%d", generatedUserCount)

responseData := ResponseData{
User: user,
SomeFileContents: fileContents,
GeneratedUsers: generatedUserCount,
}

marshaledResponseData, err := json.Marshal(responseData)
if err != nil {
panic(err)
}

w.Header().Set("x-generated-by", "wasm-workers-server")
w.Write([]byte(marshaledResponseData))
})
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
name = "user-generation-go"
version = "1"

[data]
[data.kv]
namespace = "generated_users_counter"

[[folders]]
from = "./tmp"
to = "/tmp"

[features]
[features.http_requests]
allowed_hosts = ["random-data-api.com"]

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading

0 comments on commit 6710bf4

Please sign in to comment.