Skip to content

Commit 411493f

Browse files
ereslibrejprendes
andcommitted
feat: add docker+wasm examples
Co-Authored-By: Jorge Prendes <[email protected]>
1 parent 3b4858d commit 411493f

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

62 files changed

+8822
-0
lines changed

examples/containers/.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
/dist

examples/containers/Dockerfile

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
# syntax=docker/dockerfile:labs
2+
3+
# Prepare a base layer for building the rust endpoint
4+
FROM --platform=$BUILDPLATFORM rust:1.72.0-alpine AS base
5+
RUN apk add sudo curl musl-dev ca-certificates && \
6+
curl -fsSL https://workers.wasmlabs.dev/install | sh
7+
8+
# Rust endpoint: first build, then generate the release layer
9+
FROM base AS build-rust
10+
WORKDIR /src
11+
RUN --mount=type=bind,target=/src,source=./apps-src/user-generation-rust \
12+
cargo build --release --target-dir /output
13+
14+
FROM scratch AS release-rust
15+
COPY --from=build-rust /output/wasm32-wasi/release/user-generation-rust.wasm /
16+
COPY ./apps-src/user-generation-rust/user-generation-rust.toml /
17+
18+
# JS endpoint: no build needed, just generate the release layer
19+
FROM scratch AS release-js
20+
COPY ./apps-src/user-generation-js/ /
21+
22+
# Ruby endpoint: no build needed, just generate the release layer
23+
FROM scratch AS release-ruby
24+
COPY ./apps-src/user-generation-ruby /user-generation-ruby
25+
26+
# Python endpoint: no build needed, just generate the release layer
27+
FROM scratch AS release-python
28+
COPY ./apps-src/user-generation-python/ /user-generation-python
29+
30+
# Go endpoint: first build, then generate the release layer
31+
FROM --platform=$BUILDPLATFORM tinygo/tinygo:0.28.1 AS build-go
32+
WORKDIR /src
33+
RUN --mount=type=bind,target=/src,source=./apps-src/user-generation-go \
34+
tinygo build \
35+
-o /home/tinygo/user-generation-go.wasm \
36+
-no-debug -panic=trap -scheduler=none -gc=leaking \
37+
-target=wasi .
38+
39+
FROM scratch AS release-go
40+
COPY --from=build-go /home/tinygo/user-generation-go.wasm /
41+
COPY ./apps-src/user-generation-go/user-generation-go.toml /
42+
43+
# Wws root: install the required runtimes, then generate the release layer
44+
FROM base AS build-root
45+
WORKDIR /output
46+
RUN wws runtimes install ruby latest
47+
RUN wws runtimes install python latest
48+
COPY ./apps-src/tmp /output/tmp
49+
50+
FROM scratch AS release-root
51+
COPY --from=build-root /output /
52+
53+
# Merge all the release layers into one
54+
FROM scratch AS release
55+
COPY --from=release-root / /
56+
COPY --from=release-rust / /
57+
COPY --from=release-js / /
58+
COPY --from=release-ruby / /
59+
COPY --from=release-python / /
60+
COPY --from=release-go / /
61+
62+
# Copy over te SSL certificates
63+
COPY --from=base /etc/ssl /etc/ssl
64+
65+
ENTRYPOINT ["/"]

examples/containers/Makefile

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
# Workaround for https://github.com/moby/buildkit/issues/3891
2+
export BUILDX_NO_DEFAULT_ATTESTATIONS = 1
3+
4+
# Build a container image for the demo
5+
.PHONY: image
6+
image:
7+
docker build --platform wasi/wasm --tag=wws-apps:latest .
8+
9+
# Export the content of the demo image into the ./dist folder
10+
.PHONY: dist
11+
dist: clean
12+
docker build --platform wasi/wasm --output=dist .
13+
14+
# Run the demo container
15+
.PHONY: run
16+
run: stop image
17+
docker run --rm -d --name docker-wws \
18+
-p 3000:3000 \
19+
--runtime=io.containerd.wws.v1 \
20+
--platform=wasi/wasm \
21+
wws-apps:latest
22+
@echo "Now you can reach the Wasm Workers Server functions, such as:"
23+
@echo " - curl http://localhost:3000/user-generation-rust"
24+
@echo " - curl http://localhost:3000/user-generation-go"
25+
@echo " - curl http://localhost:3000/user-generation-js"
26+
@echo " - curl http://localhost:3000/user-generation-python"
27+
@echo " - curl http://localhost:3000/user-generation-ruby"
28+
29+
# Run the demo container using a host mount
30+
.PHONY: run-with-mount
31+
run-with-mount: stop image
32+
docker run --rm -d --name docker-wws \
33+
-p 3000:3000 \
34+
--runtime=io.containerd.wws.v1 \
35+
--platform=wasi/wasm \
36+
-v $(PWD)/tmp:/tmp \
37+
wws-apps:latest
38+
@echo "Now you can reach the Wasm Workers Server functions, such as:"
39+
@echo " - curl http://localhost:3000/user-generation-rust"
40+
@echo " - curl http://localhost:3000/user-generation-go"
41+
@echo " - curl http://localhost:3000/user-generation-js"
42+
@echo " - curl http://localhost:3000/user-generation-python"
43+
@echo " - curl http://localhost:3000/user-generation-ruby"
44+
45+
# Stop the demo contianer
46+
.PHONY: stop
47+
stop:
48+
docker rm -f docker-wws
49+
50+
# Same as dist
51+
.PHONY: build
52+
build: dist;
53+
54+
.PHONY: clean
55+
clean:
56+
rm -Rf ./dist

examples/containers/README.md

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
# Docker + Wasm + Wasm Workers Server (wws)
2+
3+
This repo showcases some functions you can write, taking advantage of
4+
Wasm Workers Server, on top of Docker Desktop, thanks to the
5+
[`containerd-wasm-shims`](https://github.com/deislabs/containerd-wasm-shims) project.
6+
7+
## Build
8+
9+
Prerequisites for building this project:
10+
11+
- Docker, with [Docker + Wasm support](https://docs.docker.com/desktop/wasm/)
12+
13+
In order to build this example, you just have to run on the root of
14+
this project:
15+
16+
```shell-session
17+
$ make build
18+
```
19+
20+
## Running
21+
22+
Prerequisites for running this project: Docker Desktop 4.23.0 or later.
23+
24+
You can run the example:
25+
26+
```shell-session
27+
$ make run
28+
```
29+
30+
After that, you can target the different endpoints exposed by the Wasm
31+
Workers Server:
32+
33+
```shell-session
34+
$ curl -s http://localhost:3000/user-generation-rust | jq
35+
$ curl -s http://localhost:3000/user-generation-go | jq
36+
$ curl -s http://localhost:3000/user-generation-js | jq
37+
$ curl -s http://localhost:3000/user-generation-python | jq
38+
$ curl -s http://localhost:3000/user-generation-ruby | jq
39+
```
40+
41+
This example also showcases exposing a directory in the host to the WebAssembly guest. This example can be executed with:
42+
43+
```shell-session
44+
$ make run-with-mount
45+
```
46+
47+
You can reach the same endpoints, but you will notice that the
48+
attribute `.some_file_contents` of the produced JSON in all examples
49+
now is the content of
50+
[tmp/file.txt](tmp/file.txt)
51+
from the host.
52+
53+
The only worker that is not able to read contents from the filesystem
54+
is the JS one, so you can only check it with the rest:
55+
56+
```shell-session
57+
$ curl -s http://localhost:3000/user-generation-rust | jq
58+
$ curl -s http://localhost:3000/user-generation-go | jq
59+
$ curl -s http://localhost:3000/user-generation-python | jq
60+
$ curl -s http://localhost:3000/user-generation-ruby | jq
61+
```
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Some contents
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
*.wasm
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
module github.com/vmware-labs/docker-wasm
2+
3+
go 1.20
4+
5+
require github.com/vmware-labs/wasm-workers-server v1.4.0
6+
7+
require (
8+
github.com/tidwall/gjson v1.14.4 // indirect
9+
github.com/tidwall/match v1.1.1 // indirect
10+
github.com/tidwall/pretty v1.2.1 // indirect
11+
github.com/tidwall/sjson v1.2.5 // indirect
12+
)
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
github.com/tidwall/gjson v1.14.2/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk=
2+
github.com/tidwall/gjson v1.14.4 h1:uo0p8EbA09J7RQaflQ1aBRffTR7xedD2bcIVSYxLnkM=
3+
github.com/tidwall/gjson v1.14.4/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk=
4+
github.com/tidwall/match v1.1.1 h1:+Ho715JplO36QYgwN9PGYNhgZvoUSc9X2c80KVTi+GA=
5+
github.com/tidwall/match v1.1.1/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM=
6+
github.com/tidwall/pretty v1.2.0/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU=
7+
github.com/tidwall/pretty v1.2.1 h1:qjsOFOWWQl+N3RsoF5/ssm1pHmJJwhjlSbZ51I6wMl4=
8+
github.com/tidwall/pretty v1.2.1/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU=
9+
github.com/tidwall/sjson v1.2.5 h1:kLy8mja+1c9jlljvWTlSazM7cKDRfJuR/bOJhcY5NcY=
10+
github.com/tidwall/sjson v1.2.5/go.mod h1:Fvgq9kS/6ociJEDnK0Fk1cpYF4FIW6ZF7LAe+6jwd28=
11+
github.com/vmware-labs/wasm-workers-server v1.4.0 h1:dlc0eWc3TdijQ6RxX3Mt5OLPazO/Trmk4mqDt68eDkI=
12+
github.com/vmware-labs/wasm-workers-server v1.4.0/go.mod h1:cigUhoitjUTLsUzR4+q0cz2FymdvJtfrfIS2hYAj69c=
Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
package main
2+
3+
import (
4+
"encoding/json"
5+
"fmt"
6+
"io"
7+
"io/ioutil"
8+
"net/http"
9+
"strconv"
10+
11+
"github.com/vmware-labs/wasm-workers-server/kits/go/worker"
12+
)
13+
14+
type User struct {
15+
FirstName string `json:"first_name"`
16+
LastName string `json:"last_name"`
17+
Username string `json:"username"`
18+
Email string `json:"email"`
19+
}
20+
21+
type ResponseData struct {
22+
User User `json:"user"`
23+
SomeFileContents string `json:"some_file_contents"`
24+
GeneratedUsers uint32 `json:"generated_users"`
25+
}
26+
27+
func main() {
28+
worker.ServeFunc(func(w http.ResponseWriter, r *http.Request) {
29+
cache, _ := r.Context().Value(worker.CacheKey).(map[string]string)
30+
31+
// Create the request
32+
req, err := http.NewRequest(http.MethodGet, "https://random-data-api.com/api/v2/users", nil)
33+
if err != nil {
34+
panic(err)
35+
}
36+
37+
res, err := worker.SendHttpRequest(req)
38+
if err != nil {
39+
panic(err)
40+
}
41+
42+
// Read the response
43+
resBody, err := io.ReadAll(res.Body)
44+
if err != nil {
45+
panic(err)
46+
}
47+
res.Body.Close()
48+
49+
user := User{}
50+
err = json.Unmarshal([]byte(resBody), &user)
51+
if err != nil {
52+
panic(err)
53+
}
54+
55+
fileContents_, err := ioutil.ReadFile("/tmp/file.txt")
56+
if err != nil {
57+
panic(err)
58+
}
59+
fileContents := string(fileContents_)
60+
61+
generatedUserCount := uint32(1)
62+
if count, ok := cache["generated_users_counter"]; ok {
63+
n, _ := strconv.ParseUint(count, 10, 32)
64+
generatedUserCount = uint32(n) + 1
65+
}
66+
cache["generated_users_counter"] = fmt.Sprintf("%d", generatedUserCount)
67+
68+
responseData := ResponseData{
69+
User: user,
70+
SomeFileContents: fileContents,
71+
GeneratedUsers: generatedUserCount,
72+
}
73+
74+
marshaledResponseData, err := json.Marshal(responseData)
75+
if err != nil {
76+
panic(err)
77+
}
78+
79+
w.Header().Set("x-generated-by", "wasm-workers-server")
80+
w.Write([]byte(marshaledResponseData))
81+
})
82+
}
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
name = "user-generation-go"
2+
version = "1"
3+
4+
[data]
5+
[data.kv]
6+
namespace = "generated_users_counter"
7+
8+
[[folders]]
9+
from = "./tmp"
10+
to = "/tmp"
11+
12+
[features]
13+
[features.http_requests]
14+
allowed_hosts = ["random-data-api.com"]

examples/containers/apps-src/user-generation-go/vendor/github.com/tidwall/gjson/LICENSE

Lines changed: 20 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)