2019-12-08 16:07:31 +00:00
|
|
|
package main
|
|
|
|
|
|
|
|
import (
|
|
|
|
"bytes"
|
|
|
|
"encoding/json"
|
|
|
|
"io/ioutil"
|
|
|
|
"net/http"
|
2019-12-08 19:13:36 +00:00
|
|
|
"time"
|
2019-12-08 16:07:31 +00:00
|
|
|
|
2019-12-08 19:13:36 +00:00
|
|
|
"github.com/go-interpreter/wagon/wasm"
|
2019-12-08 16:07:31 +00:00
|
|
|
shell "github.com/ipfs/go-ipfs-api"
|
2019-12-08 19:13:36 +00:00
|
|
|
"github.com/rogpeppe/go-internal/txtar"
|
2019-12-08 16:07:31 +00:00
|
|
|
"tulpa.dev/within/wasmcloud/cmd/internal"
|
|
|
|
"within.website/ln"
|
2019-12-10 23:57:07 +00:00
|
|
|
"within.website/ln/opname"
|
2019-12-08 16:07:31 +00:00
|
|
|
"within.website/olin/namegen"
|
|
|
|
)
|
|
|
|
|
|
|
|
func createHandler(w http.ResponseWriter, r *http.Request, u *User) {
|
|
|
|
if !u.CanCreateHandlers {
|
|
|
|
http.Error(w, "you can't create handlers, contact support", http.StatusForbidden)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
ctx := r.Context()
|
|
|
|
sh := shell.NewShell(*ipfsURL)
|
2019-12-08 19:13:36 +00:00
|
|
|
data, err := ioutil.ReadAll(http.MaxBytesReader(w, r.Body, 16*1024*1024))
|
2019-12-08 16:07:31 +00:00
|
|
|
if err != nil {
|
|
|
|
ln.Error(ctx, err)
|
|
|
|
http.Error(w, "invalid data", http.StatusBadRequest)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
r.Body.Close()
|
|
|
|
|
|
|
|
var hdlr internal.Handler
|
|
|
|
err = json.Unmarshal(data, &hdlr)
|
|
|
|
if err != nil {
|
|
|
|
ln.Error(ctx, err)
|
|
|
|
http.Error(w, "not json", http.StatusBadRequest)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
switch string(hdlr.ABI) {
|
|
|
|
case string(internal.CWA), string(internal.Dagger):
|
|
|
|
default:
|
|
|
|
ln.Log(ctx, ln.Info("unknown ABI"), ln.F{"abi": hdlr.ABI})
|
|
|
|
http.Error(w, "unknown ABI", http.StatusBadRequest)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2019-12-08 19:13:36 +00:00
|
|
|
buf := bytes.NewBuffer(hdlr.WASM)
|
|
|
|
_, err = wasm.DecodeModule(buf)
|
|
|
|
if err != nil {
|
|
|
|
ln.Error(ctx, err)
|
|
|
|
http.Error(w, "not webassembly", http.StatusBadRequest)
|
|
|
|
}
|
|
|
|
|
2019-12-08 16:07:31 +00:00
|
|
|
cid, err := sh.Add(bytes.NewBuffer(hdlr.WASM))
|
|
|
|
if err != nil {
|
|
|
|
ln.Error(ctx, err)
|
|
|
|
http.Error(w, "can't upload wasm to storage", http.StatusInternalServerError)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
n := namegen.Next()
|
|
|
|
h := Handler{
|
|
|
|
Name: n,
|
|
|
|
User: *u,
|
|
|
|
UserID: u.ID,
|
|
|
|
Path: cid,
|
|
|
|
}
|
|
|
|
|
|
|
|
if err := db.Save(&h).Error; err != nil {
|
|
|
|
ln.Error(ctx, err)
|
|
|
|
http.Error(w, "can't save record", http.StatusInternalServerError)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
json.NewEncoder(w).Encode(h)
|
|
|
|
}
|
2019-12-08 19:13:36 +00:00
|
|
|
|
|
|
|
func listHandlers(w http.ResponseWriter, r *http.Request, u *User) {
|
|
|
|
ctx := r.Context()
|
2019-12-10 23:57:07 +00:00
|
|
|
ctx = opname.With(ctx, "read-handlers")
|
2019-12-08 19:13:36 +00:00
|
|
|
var hdlrs []Handler
|
|
|
|
|
2019-12-10 23:57:07 +00:00
|
|
|
err := db.Where("user_id = ?", u.ID).Find(&hdlrs).Error
|
2019-12-08 19:13:36 +00:00
|
|
|
if err != nil {
|
|
|
|
ln.Error(ctx, err)
|
|
|
|
http.Error(w, "can't read handlers", http.StatusInternalServerError)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
json.NewEncoder(w).Encode(hdlrs)
|
|
|
|
}
|
|
|
|
|
|
|
|
func getLogs(w http.ResponseWriter, r *http.Request, u *User) {
|
|
|
|
ctx := r.Context()
|
|
|
|
q := r.URL.Query()
|
|
|
|
name := q.Get("name")
|
|
|
|
if name == "" {
|
|
|
|
http.NotFound(w, r)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
var hdlr Handler
|
|
|
|
err := db.Where("name = ? AND user_id = ?", name, u.ID).First(&hdlr).Error
|
|
|
|
if err != nil {
|
|
|
|
ln.Error(ctx, err)
|
|
|
|
http.NotFound(w, r)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
var elogs []ExecutionLog
|
|
|
|
err = db.Where("handler_id = ?", hdlr.ID).Find(&elogs).Error
|
|
|
|
if err != nil {
|
|
|
|
ln.Error(ctx, err)
|
|
|
|
http.Error(w, "handler id not found", http.StatusInternalServerError)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
type resultLine struct {
|
|
|
|
ExecID string `json:"exec_id"`
|
|
|
|
Comment string `json:"comment"`
|
|
|
|
Logs map[string]string `json:"logs"`
|
|
|
|
}
|
|
|
|
var result []resultLine
|
|
|
|
|
|
|
|
for _, elog := range elogs {
|
|
|
|
arc := txtar.Parse(elog.Data)
|
|
|
|
logs := map[string]string{}
|
|
|
|
|
|
|
|
for _, file := range arc.Files {
|
|
|
|
logs[file.Name] = string(file.Data)
|
|
|
|
}
|
|
|
|
|
|
|
|
result = append(result, resultLine{
|
|
|
|
ExecID: elog.RunID,
|
|
|
|
Comment: string(arc.Comment),
|
|
|
|
Logs: logs,
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
json.NewEncoder(w).Encode(result)
|
|
|
|
}
|
|
|
|
|
2019-12-09 23:01:42 +00:00
|
|
|
func invokeHandlerSync(w http.ResponseWriter, r *http.Request) {
|
2019-12-08 19:13:36 +00:00
|
|
|
ctx := r.Context()
|
|
|
|
q := r.URL.Query()
|
|
|
|
name := q.Get("name")
|
|
|
|
if name == "" {
|
|
|
|
http.NotFound(w, r)
|
|
|
|
return
|
|
|
|
}
|
2019-12-10 23:57:07 +00:00
|
|
|
wholeBundle := q.Get("whole-bundle") == "true"
|
2019-12-08 19:13:36 +00:00
|
|
|
|
|
|
|
var hdlr Handler
|
|
|
|
err := db.Where("name = ?", name).First(&hdlr).Error
|
|
|
|
if err != nil {
|
|
|
|
ln.Error(ctx, err)
|
|
|
|
http.NotFound(w, r)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
data, err := ioutil.ReadAll(http.MaxBytesReader(w, r.Body, 1*1024*1024))
|
|
|
|
if err != nil {
|
|
|
|
ln.Error(ctx, err)
|
|
|
|
http.NotFound(w, r)
|
|
|
|
}
|
|
|
|
|
2019-12-09 23:01:42 +00:00
|
|
|
resp, err := runHandler(ctx, hdlr, 5*time.Minute, data)
|
|
|
|
if err != nil {
|
|
|
|
ln.Error(ctx, err)
|
|
|
|
return
|
|
|
|
}
|
2019-12-08 19:13:36 +00:00
|
|
|
|
2019-12-09 23:01:42 +00:00
|
|
|
logData := txtar.Format(&resp.Logs)
|
|
|
|
entry := ExecutionLog{
|
|
|
|
HandlerID: hdlr.ID,
|
|
|
|
RunID: resp.UUID,
|
|
|
|
Data: logData,
|
2019-12-08 19:13:36 +00:00
|
|
|
}
|
|
|
|
|
2019-12-09 23:01:42 +00:00
|
|
|
err = db.Save(&entry).Error
|
2019-12-08 19:13:36 +00:00
|
|
|
if err != nil {
|
|
|
|
ln.Error(ctx, err)
|
2019-12-09 23:01:42 +00:00
|
|
|
return
|
2019-12-08 19:13:36 +00:00
|
|
|
}
|
|
|
|
|
2019-12-09 23:01:42 +00:00
|
|
|
ln.Log(ctx, ln.Action("saving-logs"))
|
|
|
|
|
2019-12-10 23:57:07 +00:00
|
|
|
if wholeBundle {
|
|
|
|
w.Header().Set("Content-Type", "application/txtar")
|
|
|
|
w.Write(logData)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2019-12-09 23:01:42 +00:00
|
|
|
for _, file := range resp.Logs.Files {
|
|
|
|
if file.Name == "stdout.txt" {
|
|
|
|
w.Write(file.Data)
|
2019-12-08 19:13:36 +00:00
|
|
|
return
|
|
|
|
}
|
2019-12-09 23:01:42 +00:00
|
|
|
}
|
|
|
|
}
|
2019-12-08 19:13:36 +00:00
|
|
|
|
2019-12-09 23:01:42 +00:00
|
|
|
func invokeHandler(w http.ResponseWriter, r *http.Request) {
|
|
|
|
ctx := r.Context()
|
|
|
|
q := r.URL.Query()
|
|
|
|
name := q.Get("name")
|
|
|
|
if name == "" {
|
|
|
|
http.NotFound(w, r)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
var hdlr Handler
|
|
|
|
err := db.Where("name = ?", name).First(&hdlr).Error
|
|
|
|
if err != nil {
|
|
|
|
ln.Error(ctx, err)
|
|
|
|
http.NotFound(w, r)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
data, err := ioutil.ReadAll(http.MaxBytesReader(w, r.Body, 1*1024*1024))
|
|
|
|
if err != nil {
|
|
|
|
ln.Error(ctx, err)
|
|
|
|
http.NotFound(w, r)
|
|
|
|
}
|
|
|
|
|
|
|
|
go func() {
|
|
|
|
resp, err := runHandler(ctx, hdlr, 5*time.Minute, data)
|
2019-12-08 19:13:36 +00:00
|
|
|
if err != nil {
|
|
|
|
ln.Error(ctx, err)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
data := txtar.Format(&resp.Logs)
|
|
|
|
entry := ExecutionLog{
|
|
|
|
HandlerID: hdlr.ID,
|
2019-12-09 23:01:42 +00:00
|
|
|
RunID: resp.UUID,
|
2019-12-08 19:13:36 +00:00
|
|
|
Data: data,
|
|
|
|
}
|
|
|
|
|
|
|
|
err = db.Save(&entry).Error
|
|
|
|
if err != nil {
|
|
|
|
ln.Error(ctx, err)
|
|
|
|
return
|
|
|
|
}
|
2019-12-08 21:44:12 +00:00
|
|
|
|
2019-12-09 23:01:42 +00:00
|
|
|
ln.Log(ctx, ln.Action("saving-logs"))
|
2019-12-08 19:13:36 +00:00
|
|
|
}()
|
|
|
|
}
|