wasmcloud/cmd/wasmcloud/run.go

149 lines
3.1 KiB
Go

package main
import (
"bytes"
"context"
"flag"
"fmt"
"io"
"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"
"within.website/olin/policy"
)
type runCmd struct {
funcName, abi string
policyFile string
}
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
Run a given webassembly binary and return the output.
Please keep in mind that the performance reports may differ from how programs
behave on production servers.
Flags:
`
}
func (r *runCmd) SetFlags(fs *flag.FlagSet) {
fs.StringVar(&r.funcName, "func-name", "_start", "entrypoint function to call")
fs.StringVar(&r.abi, "abi", "cwa", "ABI to use")
fs.StringVar(&r.policyFile, "policy", "", "if set, the policy file to use")
}
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
})
var stdin bytes.Buffer
c := executor.Config{
VMConfig: exec.VMConfig{
ReturnOnGasLimitExceeded: true,
},
Name: filepath.Base(fname),
FuncName: r.funcName,
Env: environment,
Binary: wasmBin,
Stdin: io.TeeReader(os.Stdin, &stdin),
Stdout: stdout,
Stderr: stderr,
LogSink: logBuf,
}
if r.policyFile != "" {
data, err := ioutil.ReadFile(r.policyFile)
if err != nil {
log.Fatal(err)
}
pol, err := policy.Parse(r.policyFile, data)
if err != nil {
log.Fatal(err)
}
c.VMConfig.MaxMemoryPages = int(pol.RamPageLimit)
c.VMConfig.GasLimit = uint64(pol.GasLimit)
c.Policy = &pol
}
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: "stdin.txt",
Data: stdin.Bytes(),
},
{
Name: "stdout.txt",
Data: stdout.Bytes(),
},
{
Name: "stderr.txt",
Data: stderr.Bytes(),
},
result.StatsFile(),
},
}
fmt.Println(string(txtar.Format(&arc)))
return subcommands.ExitSuccess
}