diff --git a/cmd/wasmcloud/handler_invoke.go b/cmd/wasmcloud/handler_invoke.go new file mode 100644 index 0000000..c5055b5 --- /dev/null +++ b/cmd/wasmcloud/handler_invoke.go @@ -0,0 +1,76 @@ +package main + +import ( + "bytes" + "context" + "flag" + "fmt" + "io" + "io/ioutil" + "log" + "net/http" + "os" + + "github.com/google/subcommands" +) + +type handlerInvokeCmd struct { + fin string +} + +func (hic *handlerInvokeCmd) SetFlags(fs *flag.FlagSet) { + fs.StringVar(&hic.fin, "i", "", "input file to pipe to wasmcloud") +} + +func (handlerInvokeCmd) Name() string { return "invoke" } +func (handlerInvokeCmd) Synopsis() string { return "synchronously invoke a handler on wasmcloud" } +func (handlerInvokeCmd) Usage() string { + return `wasmcloud invoke + +$ wasmcloud invoke princess-of-wands-4018 + +Invokes a given handler with input read from the given file. Use /dev/stdin +if you want to pipe things. + +Flags: +` +} + +func (hic handlerInvokeCmd) Execute(ctx context.Context, fs *flag.FlagSet, _ ...interface{}) subcommands.ExitStatus { + if fs.NArg() != 1 { + fmt.Println("usage: wasmcloud invoke ") + return subcommands.ExitUsageError + } + + hname := fs.Arg(0) + + var buf = bytes.NewBuffer(nil) + + if hic.fin != "" { + data, err := ioutil.ReadFile(hic.fin) + if err != nil { + log.Fatal(err) + } + buf = bytes.NewBuffer(data) + } + + req, err := http.NewRequestWithContext(ctx, http.MethodPost, *apiServer+"/invoke/sync?name="+hname+"&whole-bundle=true", buf) + if err != nil { + log.Fatal(err) + } + withAPI(req) + + resp, err := http.DefaultClient.Do(req) + if err != nil { + log.Fatal(err) + } + defer resp.Body.Close() + + if resp.StatusCode != http.StatusOK { + io.Copy(os.Stdout, resp.Body) + return subcommands.ExitFailure + } + + io.Copy(os.Stdout, resp.Body) + return subcommands.ExitSuccess +} diff --git a/cmd/wasmcloud/handler_list.go b/cmd/wasmcloud/handler_list.go new file mode 100644 index 0000000..db678e8 --- /dev/null +++ b/cmd/wasmcloud/handler_list.go @@ -0,0 +1,90 @@ +package main + +import ( + "context" + "encoding/json" + "flag" + "fmt" + "io" + "log" + "net/http" + "os" + "time" + + "github.com/google/subcommands" + "github.com/gosuri/uitable" +) + +type handlerListCmd struct { + format string +} + +func (h *handlerListCmd) SetFlags(fs *flag.FlagSet) { + fs.StringVar(&h.format, "format", "table", "what format to present output in (table|json)") +} + +func (handlerListCmd) Name() string { return "list" } +func (handlerListCmd) Synopsis() string { return "lists handlers you've created" } +func (handlerListCmd) Usage() string { + return `wasmcloud list [options] + +$ wasmcloud list +$ wasmcloud list -format json + +Shows all of the handlers you have registered with wasmcloud. + +Flags:` +} + +func (h handlerListCmd) Execute(ctx context.Context, fs *flag.FlagSet, _ ...interface{}) subcommands.ExitStatus { + req, err := http.NewRequestWithContext(ctx, http.MethodGet, *apiServer+"/api/handler", nil) + if err != nil { + log.Fatal(err) + } + withAPI(req) + + resp, err := http.DefaultClient.Do(req) + if err != nil { + log.Fatal(err) + } + defer resp.Body.Close() + + if resp.StatusCode != http.StatusOK { + io.Copy(os.Stdout, resp.Body) + return subcommands.ExitFailure + } + + switch h.format { + case "json": + io.Copy(os.Stdout, resp.Body) + return subcommands.ExitSuccess + case "table": + type apiResp struct { + CreatedAt time.Time `json:"CreatedAt"` + UpdatedAt time.Time `json:"UpdatedAt"` + Name string `json:"Name"` + CID string `json:"Path"` + } + var result []apiResp + err = json.NewDecoder(resp.Body).Decode(&result) + if err != nil { + log.Fatal(err) + } + + table := uitable.New() + table.AddRow("Name", "Created at", "Updated at", "CID") + + for _, hdlr := range result { + table.AddRow( + hdlr.Name, + hdlr.CreatedAt.Format(time.RFC3339), + hdlr.UpdatedAt.Format(time.RFC3339), + hdlr.CID, + ) + } + + fmt.Println(table.String()) + } + + return subcommands.ExitSuccess +} diff --git a/cmd/wasmcloud/handler_logs.go b/cmd/wasmcloud/handler_logs.go index c540b36..b458e75 100644 --- a/cmd/wasmcloud/handler_logs.go +++ b/cmd/wasmcloud/handler_logs.go @@ -31,12 +31,14 @@ func (handlerLogsCmd) Usage() string { $ wasmcloud logs four-of-aether-60037 Returns all of the logs for a handler. + +Flags: ` } func (h handlerLogsCmd) Execute(ctx context.Context, fs *flag.FlagSet, _ ...interface{}) subcommands.ExitStatus { if fs.NArg() != 1 { - fmt.Println("usage: wasmcloud create [options] ") + fmt.Println("usage: wasmcloud logs [options] ") return subcommands.ExitUsageError } diff --git a/cmd/wasmcloud/main.go b/cmd/wasmcloud/main.go index 6a9e07c..8260cc6 100644 --- a/cmd/wasmcloud/main.go +++ b/cmd/wasmcloud/main.go @@ -18,12 +18,18 @@ func main() { subcommands.Register(subcommands.HelpCommand(), "") subcommands.Register(subcommands.FlagsCommand(), "") subcommands.Register(subcommands.CommandsCommand(), "") + subcommands.Register(&loginCmd{}, "api") subcommands.Register(&whoamiCmd{}, "api") + subcommands.Register(&handlerCreateCmd{}, "handlers") + subcommands.Register(&handlerListCmd{}, "handlers") subcommands.Register(&handlerLogsCmd{}, "handlers") + subcommands.Register(&handlerInvokeCmd{}, "handlers") + subcommands.Register(namegenCmd{}, "utils") subcommands.Register(&runCmd{}, "utils") + subcommands.ImportantFlag("api-server") subcommands.ImportantFlag("config") diff --git a/cmd/wasmcloud/run.go b/cmd/wasmcloud/run.go index 77f0894..1d6cec7 100644 --- a/cmd/wasmcloud/run.go +++ b/cmd/wasmcloud/run.go @@ -103,7 +103,7 @@ func (r runCmd) Execute(ctx context.Context, fs *flag.FlagSet, _ ...interface{}) } arc := txtar.Archive{ - Comment: []byte(fmt.Sprintf("execution of %s at %s", fname, result.StartTime.Format(time.RFC3339))), + Comment: []byte(fmt.Sprintf("execution of %s at %s", filepath.Base(fname), result.StartTime.Format(time.RFC3339))), Files: []txtar.File{ { Name: "logs.txt", @@ -117,7 +117,7 @@ func (r runCmd) Execute(ctx context.Context, fs *flag.FlagSet, _ ...interface{}) Name: "stderr.txt", Data: stderr.Bytes(), }, - result.ToFile(), + result.StatsFile(), }, } diff --git a/cmd/wasmcloud/whoami.go b/cmd/wasmcloud/whoami.go index a875935..84e74e2 100644 --- a/cmd/wasmcloud/whoami.go +++ b/cmd/wasmcloud/whoami.go @@ -12,6 +12,7 @@ import ( "time" "github.com/google/subcommands" + "github.com/gosuri/uitable" ) type whoamiCmd struct { @@ -71,6 +72,13 @@ func (w *whoamiCmd) Execute(ctx context.Context, fs *flag.FlagSet, _ ...interfac panic(err) } - fmt.Printf("Username: %s\nEmail: %s\nIs Admin: %v\nCan Create Handlers: %v\n", result.Username, result.Email, result.IsAdmin, result.CanCreateHandlers) + table := uitable.New() + + table.AddRow("Username", result.Username) + table.AddRow("Email", result.Email) + table.AddRow("Is Admin", fmt.Sprint(result.IsAdmin)) + table.AddRow("Can Create Handlers", fmt.Sprint(result.CanCreateHandlers)) + + fmt.Println(table.String()) return subcommands.ExitSuccess } diff --git a/cmd/wasmcloudd/handler.go b/cmd/wasmcloudd/handler.go index eba775a..c111a59 100644 --- a/cmd/wasmcloudd/handler.go +++ b/cmd/wasmcloudd/handler.go @@ -12,6 +12,7 @@ import ( "github.com/rogpeppe/go-internal/txtar" "tulpa.dev/within/wasmcloud/cmd/internal" "within.website/ln" + "within.website/ln/opname" "within.website/olin/namegen" ) @@ -80,9 +81,10 @@ func createHandler(w http.ResponseWriter, r *http.Request, u *User) { func listHandlers(w http.ResponseWriter, r *http.Request, u *User) { ctx := r.Context() + ctx = opname.With(ctx, "read-handlers") var hdlrs []Handler - err := db.Where("user_id = ?", u.ID).Scan(&hdlrs).Error + err := db.Where("user_id = ?", u.ID).Find(&hdlrs).Error if err != nil { ln.Error(ctx, err) http.Error(w, "can't read handlers", http.StatusInternalServerError) @@ -150,6 +152,7 @@ func invokeHandlerSync(w http.ResponseWriter, r *http.Request) { http.NotFound(w, r) return } + wholeBundle := q.Get("whole-bundle") == "true" var hdlr Handler err := db.Where("name = ?", name).First(&hdlr).Error @@ -186,6 +189,12 @@ func invokeHandlerSync(w http.ResponseWriter, r *http.Request) { ln.Log(ctx, ln.Action("saving-logs")) + if wholeBundle { + w.Header().Set("Content-Type", "application/txtar") + w.Write(logData) + return + } + for _, file := range resp.Logs.Files { if file.Name == "stdout.txt" { w.Write(file.Data) diff --git a/cmd/wasmcloudd/models.go b/cmd/wasmcloudd/models.go index a2fb1d1..e65db86 100644 --- a/cmd/wasmcloudd/models.go +++ b/cmd/wasmcloudd/models.go @@ -37,8 +37,8 @@ type Handler struct { gorm.Model Name string `gorm:"unique;not null"` Path string `gorm:"unique;not null"` - UserID uint - User User + UserID uint `json:"-"` + User User `json:"-"` } func (t Token) ToCookie() *http.Cookie {