blob: 61e1274dd10a51ad06cae4617d2b20e4618f11fe [file] [log] [blame]
package commands
import (
"archive/tar"
"compress/gzip"
"encoding/base64"
"encoding/json"
"errors"
"fmt"
"io"
"io/ioutil"
"os"
"path/filepath"
"regexp"
"strings"
"github.ibm.com/BlueMix-Fabric/go-whisk/whisk"
"github.com/fatih/color"
"github.com/spf13/cobra"
)
//////////////
// Commands //
//////////////
var actionCmd = &cobra.Command{
Use: "action",
Short: "work with actions",
}
var actionCreateCmd = &cobra.Command{
Use: "create <name string> <artifact string>",
Short: "create a new action",
Run: func(cmd *cobra.Command, args []string) {
action, err := parseAction(cmd, args)
if err != nil {
fmt.Println(err)
return
}
action, _, err = client.Actions.Insert(action, false)
if err != nil {
fmt.Println(err)
return
}
fmt.Printf("%s created action %s", color.GreenString("ok:"), boldString(action.Name))
},
}
var actionUpdateCmd = &cobra.Command{
Use: "update <name string> <artifact string>",
Short: "update an existing action",
Run: func(cmd *cobra.Command, args []string) {
action, err := parseAction(cmd, args)
if err != nil {
fmt.Println(err)
return
}
action, _, err = client.Actions.Insert(action, true)
if err != nil {
fmt.Println(err)
return
}
fmt.Printf("%s updated action %s", color.GreenString("ok:"), boldString(action.Name))
},
}
var actionInvokeCmd = &cobra.Command{
Use: "invoke <name string> <payload string>",
Short: "invoke action",
Run: func(cmd *cobra.Command, args []string) {
var err error
var actionName, payloadArg string
if len(args) < 1 || len(args) > 2 {
err = errors.New("Invalid argument list")
fmt.Println(err)
return
}
actionName = args[0]
payload := map[string]interface{}{}
if len(flags.common.param) > 0 {
parameters, err := parseParameters(flags.common.param)
if err != nil {
fmt.Printf("error: %s", err)
return
}
for _, param := range parameters {
payload[param.Key] = param.Value
}
}
if len(args) == 2 {
payloadArg = args[1]
reader := strings.NewReader(payloadArg)
err = json.NewDecoder(reader).Decode(&payload)
if err != nil {
payload["payload"] = payloadArg
}
}
activation, _, err := client.Actions.Invoke(actionName, payload, flags.common.blocking)
if err != nil {
fmt.Printf("error: %s", err)
return
}
if flags.common.blocking && flags.action.result {
printJSON(activation.Response.Result)
} else if flags.common.blocking {
fmt.Printf("%s invoked %s with id %s\n", color.GreenString("ok:"), boldString(actionName), boldString(activation.ActivationID))
boldPrintf("response:\n")
printJSON(activation.Response)
} else {
fmt.Printf("%s invoked %s with id %s\n", color.GreenString("ok:"), boldString(actionName), boldString(activation.ActivationID))
}
},
}
var actionGetCmd = &cobra.Command{
Use: "get <name string>",
Short: "get action",
Run: func(cmd *cobra.Command, args []string) {
var err error
if len(args) != 1 {
err = errors.New("Invalid argument")
fmt.Println(err)
return
}
actionName := args[0]
action, _, err := client.Actions.Get(actionName)
if err != nil {
fmt.Printf("error: %s", err)
return
}
// print out response
if flags.common.summary {
fmt.Printf("%s /%s/%s\n", boldString("action"), action.Namespace, action.Name)
} else {
fmt.Printf("%s got action %s\n", color.GreenString("ok:"), boldString(actionName))
printJSON(action)
}
},
}
var actionDeleteCmd = &cobra.Command{
Use: "delete <name string>",
Short: "delete action",
Run: func(cmd *cobra.Command, args []string) {
actionName := args[0]
_, err := client.Actions.Delete(actionName)
if err != nil {
fmt.Printf("error: %s", err)
return
}
// print out response
fmt.Printf("%s deleted action %s\n", color.GreenString("ok:"), boldString(actionName))
},
}
var actionListCmd = &cobra.Command{
Use: "list <namespace string>",
Short: "list all actions",
Run: func(cmd *cobra.Command, args []string) {
var err error
qName := qualifiedName{}
if len(args) == 1 {
qName, err = parseQualifiedName(args[0])
if err != nil {
fmt.Printf("error: %s", err)
return
}
ns := qName.namespace
if len(ns) == 0 {
err = errors.New("No valid namespace detected. Make sure that namespace argument is preceded by a \"/\"")
fmt.Printf("error: %s\n", err)
return
}
client.Namespace = ns
if pkg := qName.packageName; len(pkg) > 0 {
// todo :: scope call to package
}
}
options := &whisk.ActionListOptions{
Skip: flags.common.skip,
Limit: flags.common.limit,
}
actions, _, err := client.Actions.List(options)
if err != nil {
fmt.Printf("error: %s", err)
return
}
printList(actions)
},
}
func parseAction(cmd *cobra.Command, args []string) (*whisk.Action, error) {
var err error
var actionName, artifact string
if len(args) < 1 || len(args) > 2 {
err = errors.New("Invalid argument list")
return nil, err
}
actionName = args[0]
if len(args) == 2 {
artifact = args[1]
}
exec := whisk.Exec{}
parameters, err := parseParameters(flags.common.param)
if err != nil {
return nil, err
}
annotations, err := parseAnnotations(flags.common.annotation)
if err != nil {
return nil, err
}
limits := whisk.Limits{
Timeout: flags.action.timeout,
Memory: flags.action.memory,
}
if flags.action.docker {
exec.Image = artifact
} else if flags.action.copy {
existingAction, _, err := client.Actions.Get(actionName)
if err != nil {
return nil, err
}
exec = existingAction.Exec
} else if flags.action.sequence {
currentNamespace := client.Config.Namespace
client.Config.Namespace = "whisk.system"
pipeAction, _, err := client.Actions.Get("system/pipe")
if err != nil {
return nil, err
}
exec = pipeAction.Exec
client.Config.Namespace = currentNamespace
} else if artifact != "" {
stat, err := os.Stat(artifact)
if err != nil {
// file does not exist
return nil, err
}
file, err := ioutil.ReadFile(artifact)
if err != nil {
return nil, err
}
exec.Code = string(file)
if matched, _ := regexp.MatchString(".swift$", stat.Name()); matched {
exec.Kind = "swift"
} else {
exec.Kind = "nodejs"
}
}
if flags.action.lib != "" {
file, err := os.Open(flags.action.lib)
if err != nil {
return nil, err
}
var r io.Reader
switch ext := filepath.Ext(file.Name()); ext {
case "tar":
r = tar.NewReader(file)
case "gzip":
r, err = gzip.NewReader(file)
default:
err = fmt.Errorf("Unrecognized file compression %s", ext)
}
if err != nil {
return nil, err
}
lib, err := ioutil.ReadAll(r)
if err != nil {
return nil, err
}
exec.Init = base64.StdEncoding.EncodeToString(lib)
}
action := &whisk.Action{
Name: actionName,
Publish: flags.action.shared,
Exec: exec,
Annotations: annotations,
Parameters: parameters,
Limits: limits,
}
return action, nil
}
///////////
// Flags //
///////////
func init() {
actionCreateCmd.Flags().BoolVar(&flags.action.docker, "docker", false, "treat artifact as docker image path on dockerhub")
actionCreateCmd.Flags().BoolVar(&flags.action.copy, "copy", false, "treat artifact as the name of an existing action")
actionCreateCmd.Flags().BoolVar(&flags.action.sequence, "sequence", false, "treat artifact as comma separated sequence of actions to invoke")
actionCreateCmd.Flags().BoolVar(&flags.action.shared, "shared", false, "shared action (default: private)")
actionCreateCmd.Flags().StringVar(&flags.action.lib, "lib", "", "add library to artifact (must be a gzipped tar file)")
actionCreateCmd.Flags().StringVar(&flags.action.xPackage, "package", "", "package")
actionCreateCmd.Flags().IntVarP(&flags.action.timeout, "timeout", "t", 0, "the timeout limit in miliseconds when the action will be terminated")
actionCreateCmd.Flags().IntVarP(&flags.action.memory, "memory", "m", 0, "the memory limit in MB of the container that runs the action")
actionCreateCmd.Flags().StringSliceVarP(&flags.common.annotation, "annotation", "a", []string{}, "annotations")
actionCreateCmd.Flags().StringSliceVarP(&flags.common.param, "param", "p", []string{}, "default parameters")
actionUpdateCmd.Flags().BoolVar(&flags.action.docker, "docker", false, "treat artifact as docker image path on dockerhub")
actionUpdateCmd.Flags().BoolVar(&flags.action.copy, "copy", false, "treat artifact as the name of an existing action")
actionUpdateCmd.Flags().BoolVar(&flags.action.sequence, "sequence", false, "treat artifact as comma separated sequence of actions to invoke")
actionUpdateCmd.Flags().BoolVar(&flags.action.shared, "shared", false, "shared action (default: private)")
actionUpdateCmd.Flags().StringVar(&flags.action.lib, "lib", "", "add library to artifact (must be a gzipped tar file)")
actionUpdateCmd.Flags().StringVar(&flags.action.xPackage, "package", "", "package")
actionUpdateCmd.Flags().IntVarP(&flags.action.timeout, "timeout", "t", 0, "the timeout limit in miliseconds when the action will be terminated")
actionUpdateCmd.Flags().IntVarP(&flags.action.memory, "memory", "m", 0, "the memory limit in MB of the container that runs the action")
actionUpdateCmd.Flags().StringSliceVarP(&flags.common.annotation, "annotation", "a", []string{}, "annotations")
actionUpdateCmd.Flags().StringSliceVarP(&flags.common.param, "param", "p", []string{}, "default parameters")
actionInvokeCmd.Flags().StringSliceVarP(&flags.common.param, "param", "p", []string{}, "parameters")
actionInvokeCmd.Flags().BoolVarP(&flags.common.blocking, "blocking", "b", false, "blocking invoke")
actionInvokeCmd.Flags().BoolVarP(&flags.action.result, "result", "r", false, "show only activation result if a blocking activation (unless there is a failure)")
actionGetCmd.Flags().BoolVarP(&flags.common.summary, "summary", "s", false, "summarize entity details")
actionListCmd.Flags().IntVarP(&flags.common.skip, "skip", "s", 0, "skip this many entitites from the head of the collection")
actionListCmd.Flags().IntVarP(&flags.common.limit, "limit", "l", 30, "only return this many entities from the collection")
actionListCmd.Flags().BoolVar(&flags.common.full, "full", false, "include full entity description")
actionCmd.AddCommand(
actionCreateCmd,
actionUpdateCmd,
actionInvokeCmd,
actionGetCmd,
actionDeleteCmd,
actionListCmd,
)
}