128 lines
2.8 KiB
Go
128 lines
2.8 KiB
Go
package main
|
|
|
|
import (
|
|
"bytes"
|
|
"context"
|
|
"flag"
|
|
"fmt"
|
|
"io/ioutil"
|
|
"log"
|
|
"os"
|
|
"path/filepath"
|
|
"strings"
|
|
"time"
|
|
|
|
"github.com/google/subcommands"
|
|
"github.com/perlin-network/life/exec"
|
|
"github.com/rogpeppe/go-internal/txtar"
|
|
"tulpa.dev/within/wasmcloud/executor"
|
|
)
|
|
|
|
type runCmd struct {
|
|
funcName, abi string
|
|
gasLimit uint64
|
|
ramLimit int
|
|
}
|
|
|
|
func (runCmd) Name() string { return "run" }
|
|
func (runCmd) Synopsis() string {
|
|
return "run a webassembly file with the same environment as production servers"
|
|
}
|
|
func (runCmd) Usage() string {
|
|
return `wasmcloud run [options] <file.wasm>
|
|
|
|
$ wasmcloud run olinfetch.wasm
|
|
$ wasmcloud run -abi dagger -func-name dagger_main hello_dagger.wasm
|
|
|
|
Run a given webassembly binary and return the output.
|
|
|
|
Flags:
|
|
`
|
|
}
|
|
|
|
func (r *runCmd) SetFlags(fs *flag.FlagSet) {
|
|
fs.StringVar(&r.funcName, "func-name", "cwa_main", "entrypoint function to call")
|
|
fs.StringVar(&r.abi, "abi", "cwa", "ABI to use")
|
|
fs.Uint64Var(&r.gasLimit, "gas-limit", 1048576, "number of wasm instructions per execution")
|
|
fs.IntVar(&r.ramLimit, "ram-limit", 128, "number of wasm pages that can be used")
|
|
}
|
|
|
|
func (r runCmd) Execute(ctx context.Context, fs *flag.FlagSet, _ ...interface{}) subcommands.ExitStatus {
|
|
if fs.NArg() != 1 {
|
|
fmt.Println(r.Usage())
|
|
return subcommands.ExitUsageError
|
|
}
|
|
|
|
fname := fs.Arg(0)
|
|
|
|
stdout := bytes.NewBuffer(nil)
|
|
stderr := bytes.NewBuffer(nil)
|
|
logBuf := bytes.NewBuffer(nil)
|
|
|
|
wasmBin, err := ioutil.ReadFile(fname)
|
|
if err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
|
|
getenvironment := func(data []string, getkeyval func(item string) (key, val string)) map[string]string {
|
|
items := make(map[string]string)
|
|
for _, item := range data {
|
|
key, val := getkeyval(item)
|
|
items[key] = val
|
|
}
|
|
return items
|
|
}
|
|
environment := getenvironment(os.Environ(), func(item string) (key, val string) {
|
|
splits := strings.Split(item, "=")
|
|
key = splits[0]
|
|
val = splits[1]
|
|
return
|
|
})
|
|
|
|
c := executor.Config{
|
|
VMConfig: exec.VMConfig{
|
|
GasLimit: r.gasLimit,
|
|
ReturnOnGasLimitExceeded: true,
|
|
MaxMemoryPages: r.ramLimit,
|
|
},
|
|
|
|
Name: filepath.Base(fname),
|
|
FuncName: r.funcName,
|
|
Env: environment,
|
|
Binary: wasmBin,
|
|
Stdin: os.Stdin,
|
|
Stdout: stdout,
|
|
Stderr: stderr,
|
|
LogSink: logBuf,
|
|
}
|
|
|
|
result, err := executor.Run(c)
|
|
if err != nil {
|
|
log.Printf("can't run binary: %v", err)
|
|
return subcommands.ExitFailure
|
|
}
|
|
|
|
arc := txtar.Archive{
|
|
Comment: []byte(fmt.Sprintf("execution of %s at %s", filepath.Base(fname), result.StartTime.Format(time.RFC3339))),
|
|
Files: []txtar.File{
|
|
{
|
|
Name: "logs.txt",
|
|
Data: logBuf.Bytes(),
|
|
},
|
|
{
|
|
Name: "stdout.txt",
|
|
Data: stdout.Bytes(),
|
|
},
|
|
{
|
|
Name: "stderr.txt",
|
|
Data: stderr.Bytes(),
|
|
},
|
|
result.StatsFile(),
|
|
},
|
|
}
|
|
|
|
fmt.Println(string(txtar.Format(&arc)))
|
|
|
|
return subcommands.ExitSuccess
|
|
}
|