commands/package.go (429 lines of code) (raw):

/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package commands import ( "errors" "fmt" "net/http" "github.com/apache/openwhisk-cli/wski18n" "github.com/apache/openwhisk-client-go/whisk" "github.com/fatih/color" "github.com/spf13/cobra" ) var packageCmd = &cobra.Command{ Use: "package", Short: wski18n.T("work with packages"), } var packageBindCmd = &cobra.Command{ Use: "bind PACKAGE_NAME BOUND_PACKAGE_NAME", Short: wski18n.T("bind parameters to a package"), SilenceUsage: true, SilenceErrors: true, PreRunE: SetupClientConfig, RunE: func(cmd *cobra.Command, args []string) error { var err error var pkgQualifiedName = new(QualifiedName) var bindQualifiedName = new(QualifiedName) if whiskErr := CheckArgs(args, 2, 2, "Package bind", wski18n.T("A package name and binding name are required.")); whiskErr != nil { return whiskErr } packageName := args[0] if pkgQualifiedName, err = NewQualifiedName(packageName); err != nil { return NewQualifiedNameError(packageName, err) } bindingName := args[1] if bindQualifiedName, err = NewQualifiedName(bindingName); err != nil { return NewQualifiedNameError(bindingName, err) } Client.Namespace = bindQualifiedName.GetNamespace() // Convert the binding's list of default parameters from a string into []KeyValue // The 1 or more --param arguments have all been combined into a single []string // e.g. --p arg1,arg2 --p arg3,arg4 -> [arg1, arg2, arg3, arg4] whisk.Debug(whisk.DbgInfo, "Parsing parameters: %#v\n", Flags.common.param) parameters, err := getJSONFromStrings(Flags.common.param, true) if err != nil { whisk.Debug(whisk.DbgError, "getJSONFromStrings(%#v, true) failed: %s\n", Flags.common.param, err) errStr := wski18n.T("Invalid parameter argument '{{.param}}': {{.err}}", map[string]interface{}{"param": fmt.Sprintf("%#v", Flags.common.param), "err": err}) werr := whisk.MakeWskErrorFromWskError(errors.New(errStr), err, whisk.EXIT_CODE_ERR_GENERAL, whisk.DISPLAY_MSG, whisk.DISPLAY_USAGE) return werr } // Convert the binding's list of default annotations from a string into []KeyValue // The 1 or more --annotation arguments have all been combined into a single []string // e.g. --a arg1,arg2 --a arg3,arg4 -> [arg1, arg2, arg3, arg4] whisk.Debug(whisk.DbgInfo, "Parsing annotations: %#v\n", Flags.common.annotation) annotations, err := getJSONFromStrings(Flags.common.annotation, true) if err != nil { whisk.Debug(whisk.DbgError, "getJSONFromStrings(%#v, true) failed: %s\n", Flags.common.annotation, err) errStr := wski18n.T("Invalid annotation argument '{{.annotation}}': {{.err}}", map[string]interface{}{"annotation": fmt.Sprintf("%#v", Flags.common.annotation), "err": err}) werr := whisk.MakeWskErrorFromWskError(errors.New(errStr), err, whisk.EXIT_CODE_ERR_GENERAL, whisk.DISPLAY_MSG, whisk.DISPLAY_USAGE) return werr } binding := whisk.Binding{ Name: pkgQualifiedName.GetEntityName(), Namespace: pkgQualifiedName.GetNamespace(), } p := &whisk.BindingPackage{ Name: bindQualifiedName.GetEntityName(), Annotations: annotations.(whisk.KeyValueArr), Parameters: parameters.(whisk.KeyValueArr), Binding: binding, } overwrite := Flags.common.overwrite _, _, err = Client.Packages.Insert(p, overwrite) if err != nil { whisk.Debug(whisk.DbgError, "Client.Packages.Insert(%#v, false) failed: %s\n", p, err) errStr := wski18n.T("Binding creation failed: {{.err}}", map[string]interface{}{"err": err}) werr := whisk.MakeWskErrorFromWskError(errors.New(errStr), err, whisk.EXIT_CODE_ERR_GENERAL, whisk.DISPLAY_MSG, whisk.NO_DISPLAY_USAGE) return werr } fmt.Fprintf(color.Output, wski18n.T("{{.ok}} created binding {{.name}}\n", map[string]interface{}{"ok": color.GreenString(wski18n.T("ok:")), "name": boldString(bindingName)})) return nil }, } var packageCreateCmd = &cobra.Command{ Use: "create PACKAGE_NAME", Short: wski18n.T("create a new package"), SilenceUsage: true, SilenceErrors: true, PreRunE: SetupClientConfig, RunE: func(cmd *cobra.Command, args []string) error { var err error var shared, sharedSet bool var qualifiedName = new(QualifiedName) if whiskErr := CheckArgs(args, 1, 1, "Package create", wski18n.T("A package name is required.")); whiskErr != nil { return whiskErr } if qualifiedName, err = NewQualifiedName(args[0]); err != nil { return NewQualifiedNameError(args[0], err) } Client.Namespace = qualifiedName.GetNamespace() if shared, sharedSet, err = parseShared(Flags.common.shared); err != nil { whisk.Debug(whisk.DbgError, "parseShared(%s) failed: %s\n", Flags.common.shared, err) return err } whisk.Debug(whisk.DbgInfo, "Parsing parameters: %#v\n", Flags.common.param) parameters, err := getJSONFromStrings(Flags.common.param, true) if err != nil { whisk.Debug(whisk.DbgError, "getJSONFromStrings(%#v, true) failed: %s\n", Flags.common.param, err) errStr := wski18n.T("Invalid parameter argument '{{.param}}': {{.err}}", map[string]interface{}{"param": fmt.Sprintf("%#v", Flags.common.param), "err": err}) werr := whisk.MakeWskErrorFromWskError(errors.New(errStr), err, whisk.EXIT_CODE_ERR_GENERAL, whisk.DISPLAY_MSG, whisk.DISPLAY_USAGE) return werr } whisk.Debug(whisk.DbgInfo, "Parsing annotations: %#v\n", Flags.common.annotation) annotations, err := getJSONFromStrings(Flags.common.annotation, true) if err != nil { whisk.Debug(whisk.DbgError, "getJSONFromStrings(%#v, true) failed: %s\n", Flags.common.annotation, err) errStr := wski18n.T("Invalid annotation argument '{{.annotation}}': {{.err}}", map[string]interface{}{"annotation": fmt.Sprintf("%#v", Flags.common.annotation), "err": err}) werr := whisk.MakeWskErrorFromWskError(errors.New(errStr), err, whisk.EXIT_CODE_ERR_GENERAL, whisk.DISPLAY_MSG, whisk.DISPLAY_USAGE) return werr } p := &whisk.Package{ Name: qualifiedName.GetEntityName(), Namespace: qualifiedName.GetNamespace(), Annotations: annotations.(whisk.KeyValueArr), Parameters: parameters.(whisk.KeyValueArr), } if sharedSet { p.Publish = &shared } p, _, err = Client.Packages.Insert(p, false) if err != nil { whisk.Debug(whisk.DbgError, "Client.Packages.Insert(%#v, false) failed: %s\n", p, err) errStr := wski18n.T( "Unable to create package '{{.name}}': {{.err}}", map[string]interface{}{ "name": qualifiedName.GetEntityName(), "err": err, }) werr := whisk.MakeWskErrorFromWskError(errors.New(errStr), err, whisk.EXIT_CODE_ERR_GENERAL, whisk.DISPLAY_MSG, whisk.NO_DISPLAY_USAGE) return werr } fmt.Fprintf(color.Output, wski18n.T("{{.ok}} created package {{.name}}\n", map[string]interface{}{"ok": color.GreenString(wski18n.T("ok:")), "name": boldString(qualifiedName.GetEntityName())})) return nil }, } var packageUpdateCmd = &cobra.Command{ Use: "update PACKAGE_NAME", Short: wski18n.T("update an existing package, or create a package if it does not exist"), SilenceUsage: true, SilenceErrors: true, PreRunE: SetupClientConfig, RunE: func(cmd *cobra.Command, args []string) error { var err error var shared, sharedSet bool var qualifiedName = new(QualifiedName) if whiskErr := CheckArgs(args, 1, 1, "Package update", wski18n.T("A package name is required.")); whiskErr != nil { return whiskErr } if qualifiedName, err = NewQualifiedName(args[0]); err != nil { return NewQualifiedNameError(args[0], err) } Client.Namespace = qualifiedName.GetNamespace() if shared, sharedSet, err = parseShared(Flags.common.shared); err != nil { whisk.Debug(whisk.DbgError, "parseShared(%s) failed: %s\n", Flags.common.shared, err) return err } whisk.Debug(whisk.DbgInfo, "Parsing parameters: %#v\n", Flags.common.param) parameters, err := getJSONFromStrings(Flags.common.param, true) if err != nil { whisk.Debug(whisk.DbgError, "getJSONFromStrings(%#v, true) failed: %s\n", Flags.common.param, err) errStr := wski18n.T("Invalid parameter argument '{{.param}}': {{.err}}", map[string]interface{}{"param": fmt.Sprintf("%#v", Flags.common.param), "err": err}) werr := whisk.MakeWskErrorFromWskError(errors.New(errStr), err, whisk.EXIT_CODE_ERR_GENERAL, whisk.DISPLAY_MSG, whisk.DISPLAY_USAGE) return werr } whisk.Debug(whisk.DbgInfo, "Parsing annotations: %#v\n", Flags.common.annotation) annotations, err := getJSONFromStrings(Flags.common.annotation, true) if err != nil { whisk.Debug(whisk.DbgError, "getJSONFromStrings(%#v, true) failed: %s\n", Flags.common.annotation, err) errStr := wski18n.T("Invalid annotation argument '{{.annotation}}': {{.err}}", map[string]interface{}{"annotation": fmt.Sprintf("%#v", Flags.common.annotation), "err": err}) werr := whisk.MakeWskErrorFromWskError(errors.New(errStr), err, whisk.EXIT_CODE_ERR_GENERAL, whisk.DISPLAY_MSG, whisk.DISPLAY_USAGE) return werr } p := &whisk.Package{ Name: qualifiedName.GetEntityName(), Namespace: qualifiedName.GetNamespace(), Annotations: annotations.(whisk.KeyValueArr), Parameters: parameters.(whisk.KeyValueArr), } if sharedSet { p.Publish = &shared } p, _, err = Client.Packages.Insert(p, true) if err != nil { whisk.Debug(whisk.DbgError, "Client.Packages.Insert(%#v, true) failed: %s\n", p, err) errStr := wski18n.T("Package update failed: {{.err}}", map[string]interface{}{"err": err}) werr := whisk.MakeWskErrorFromWskError(errors.New(errStr), err, whisk.EXIT_CODE_ERR_GENERAL, whisk.DISPLAY_MSG, whisk.NO_DISPLAY_USAGE) return werr } fmt.Fprintf(color.Output, wski18n.T("{{.ok}} updated package {{.name}}\n", map[string]interface{}{"ok": color.GreenString(wski18n.T("ok:")), "name": boldString(qualifiedName.GetEntityName())})) return nil }, } var packageGetCmd = &cobra.Command{ Use: "get PACKAGE_NAME [FIELD_FILTER]", Short: wski18n.T("get package"), SilenceUsage: true, SilenceErrors: true, PreRunE: SetupClientConfig, RunE: func(cmd *cobra.Command, args []string) error { var err error var field string var qualifiedName = new(QualifiedName) if whiskErr := CheckArgs(args, 1, 2, "Package get", wski18n.T("A package name is required.")); whiskErr != nil { return whiskErr } if len(args) > 1 { field = args[1] if !fieldExists(&whisk.Package{}, field) { errMsg := wski18n.T("Invalid field filter '{{.arg}}'.", map[string]interface{}{"arg": field}) whiskErr := whisk.MakeWskError(errors.New(errMsg), whisk.EXIT_CODE_ERR_GENERAL, whisk.DISPLAY_MSG, whisk.NO_DISPLAY_USAGE) return whiskErr } } if qualifiedName, err = NewQualifiedName(args[0]); err != nil { return NewQualifiedNameError(args[0], err) } Client.Namespace = qualifiedName.GetNamespace() xPackage, _, err := Client.Packages.Get(qualifiedName.GetEntityName()) if err != nil { whisk.Debug(whisk.DbgError, "Client.Packages.Get(%s) failed: %s\n", qualifiedName.GetEntityName(), err) errStr := wski18n.T( "Unable to get package '{{.name}}': {{.err}}", map[string]interface{}{ "name": qualifiedName.GetEntityName(), "err": err, }) werr := whisk.MakeWskErrorFromWskError(errors.New(errStr), err, whisk.EXIT_CODE_ERR_GENERAL, whisk.DISPLAY_MSG, whisk.NO_DISPLAY_USAGE) return werr } if Flags.common.summary { printSummary(xPackage) } else { if len(field) > 0 { fmt.Fprintf(color.Output, wski18n.T("{{.ok}} got package {{.name}}, displaying field {{.field}}\n", map[string]interface{}{"ok": color.GreenString("ok:"), "name": boldString(qualifiedName.GetEntityName()), "field": boldString(field)})) printField(xPackage, field) } else { fmt.Fprintf(color.Output, wski18n.T("{{.ok}} got package {{.name}}\n", map[string]interface{}{"ok": color.GreenString("ok:"), "name": boldString(qualifiedName.GetEntityName())})) printJSON(xPackage) } } return nil }, } var packageDeleteCmd = &cobra.Command{ Use: "delete PACKAGE_NAME", Short: wski18n.T("delete package"), SilenceUsage: true, SilenceErrors: true, PreRunE: SetupClientConfig, RunE: func(cmd *cobra.Command, args []string) error { var err error var qualifiedName = new(QualifiedName) if whiskErr := CheckArgs(args, 1, 1, "Package delete", wski18n.T("A package name is required.")); whiskErr != nil { return whiskErr } if qualifiedName, err = NewQualifiedName(args[0]); err != nil { return NewQualifiedNameError(args[0], err) } Client.Namespace = qualifiedName.GetNamespace() _, err = Client.Packages.Delete(qualifiedName.GetEntityName()) if err != nil { whisk.Debug(whisk.DbgError, "Client.Packages.Delete(%s) failed: %s\n", qualifiedName.GetEntityName(), err) errStr := wski18n.T( "Unable to delete package '{{.name}}': {{.err}}", map[string]interface{}{ "name": qualifiedName.GetEntityName(), "err": err, }) werr := whisk.MakeWskErrorFromWskError(errors.New(errStr), err, whisk.EXIT_CODE_ERR_GENERAL, whisk.DISPLAY_MSG, whisk.NO_DISPLAY_USAGE) return werr } fmt.Fprintf(color.Output, wski18n.T("{{.ok}} deleted package {{.name}}\n", map[string]interface{}{"ok": color.GreenString("ok:"), "name": boldString(qualifiedName.GetEntityName())})) return nil }, } var packageListCmd = &cobra.Command{ Use: "list [NAMESPACE]", Short: wski18n.T("list all packages"), SilenceUsage: true, SilenceErrors: true, PreRunE: SetupClientConfig, RunE: func(cmd *cobra.Command, args []string) error { var err error var qualifiedName = new(QualifiedName) if whiskErr := CheckArgs(args, 0, 1, "Package list", wski18n.T("An optional namespace is the only valid argument.")); whiskErr != nil { return whiskErr } if len(args) == 1 { if qualifiedName, err = NewQualifiedName(args[0]); err != nil { return NewQualifiedNameError(args[0], err) } if len(qualifiedName.GetEntityName()) > 0 { return entityNameError(qualifiedName.GetEntityName()) } Client.Namespace = qualifiedName.GetNamespace() } options := &whisk.PackageListOptions{ Skip: Flags.common.skip, Limit: Flags.common.limit, } packages, _, err := Client.Packages.List(options) if err != nil { whisk.Debug(whisk.DbgError, "Client.Packages.List(%+v) failed: %s\n", options, err) errStr := wski18n.T("Unable to obtain the list of packages for namespace '{{.name}}': {{.err}}", map[string]interface{}{"name": getClientNamespace(), "err": err}) werr := whisk.MakeWskErrorFromWskError(errors.New(errStr), err, whisk.EXIT_CODE_ERR_GENERAL, whisk.DISPLAY_MSG, whisk.NO_DISPLAY_USAGE) return werr } sortByName := Flags.common.nameSort printList(packages, sortByName) return nil }, } var packageRefreshCmd = &cobra.Command{ Use: "refresh [NAMESPACE]", Short: wski18n.T("refresh package bindings"), SilenceUsage: true, SilenceErrors: true, PreRunE: SetupClientConfig, RunE: func(cmd *cobra.Command, args []string) error { var err error var qualifiedName = new(QualifiedName) if whiskErr := CheckArgs(args, 0, 1, "Package refresh", wski18n.T("An optional namespace is the only valid argument.")); whiskErr != nil { return whiskErr } if len(args) == 0 { qualifiedName.namespace = getNamespaceFromProp() } else { if qualifiedName, err = NewQualifiedName(args[0]); err != nil { return NewQualifiedNameError(args[0], err) } if len(qualifiedName.GetEntityName()) > 0 { return entityNameError(qualifiedName.GetEntityName()) } } currentNamespace := Client.Config.Namespace Client.Config.Namespace = qualifiedName.GetNamespace() defer func() { Client.Config.Namespace = currentNamespace }() updates, resp, err := Client.Packages.Refresh() if err != nil { whisk.Debug(whisk.DbgError, "Client.Packages.Refresh() of namespace '%s' failed: %s\n", Client.Config.Namespace, err) errStr := wski18n.T("Package refresh for namespace '{{.name}}' failed: {{.err}}", map[string]interface{}{"name": Client.Config.Namespace, "err": err}) werr := whisk.MakeWskErrorFromWskError(errors.New(errStr), err, whisk.EXIT_CODE_ERR_GENERAL, whisk.DISPLAY_MSG, whisk.NO_DISPLAY_USAGE) return werr } whisk.Debug(whisk.DbgInfo, "Refresh updates received: %#v\n", updates) switch resp.StatusCode { case http.StatusOK: fmt.Printf(wski18n.T("'{{.name}}' refreshed successfully\n", map[string]interface{}{"name": Client.Config.Namespace})) fmt.Println(wski18n.T("created bindings:")) if len(updates.Added) > 0 { printArrayContents(updates.Added) } fmt.Println(wski18n.T("updated bindings:")) if len(updates.Updated) > 0 { printArrayContents(updates.Updated) } fmt.Println(wski18n.T("deleted bindings:")) if len(updates.Deleted) > 0 { printArrayContents(updates.Deleted) } case http.StatusNotImplemented: whisk.Debug(whisk.DbgError, "Client.Packages.Refresh() for namespace '%s' returned 'Not Implemented' HTTP status code: %d\n", Client.Config.Namespace, resp.StatusCode) errStr := wski18n.T("The package refresh feature is not implemented in the target deployment") werr := whisk.MakeWskErrorFromWskError(errors.New(errStr), err, whisk.EXIT_CODE_ERR_NETWORK, whisk.DISPLAY_MSG, whisk.NO_DISPLAY_USAGE) return werr default: whisk.Debug(whisk.DbgError, "Client.Packages.Refresh() for namespace '%s' returned an unexpected HTTP status code: %d\n", Client.Config.Namespace, resp.StatusCode) errStr := wski18n.T("Package refresh for namespace '{{.name}}' failed due to unexpected HTTP status code: {{.code}}", map[string]interface{}{"name": Client.Config.Namespace, "code": resp.StatusCode}) werr := whisk.MakeWskErrorFromWskError(errors.New(errStr), err, whisk.EXIT_CODE_ERR_NETWORK, whisk.DISPLAY_MSG, whisk.NO_DISPLAY_USAGE) return werr } return nil }, } func init() { packageCreateCmd.Flags().StringSliceVarP(&Flags.common.annotation, "annotation", "a", []string{}, wski18n.T("annotation values in `KEY VALUE` format")) packageCreateCmd.Flags().StringVarP(&Flags.common.annotFile, "annotation-file", "A", "", wski18n.T("`FILE` containing annotation values in JSON format")) packageCreateCmd.Flags().StringSliceVarP(&Flags.common.param, "param", "p", []string{}, wski18n.T("parameter values in `KEY VALUE` format")) packageCreateCmd.Flags().StringVarP(&Flags.common.paramFile, "param-file", "P", "", wski18n.T("`FILE` containing parameter values in JSON format")) packageCreateCmd.Flags().StringVar(&Flags.common.shared, "shared", "", wski18n.T("package visibility `SCOPE`; yes = shared, no = private")) packageUpdateCmd.Flags().StringSliceVarP(&Flags.common.annotation, "annotation", "a", []string{}, wski18n.T("annotation values in `KEY VALUE` format")) packageUpdateCmd.Flags().StringVarP(&Flags.common.annotFile, "annotation-file", "A", "", wski18n.T("`FILE` containing annotation values in JSON format")) packageUpdateCmd.Flags().StringSliceVarP(&Flags.common.param, "param", "p", []string{}, wski18n.T("parameter values in `KEY VALUE` format")) packageUpdateCmd.Flags().StringVarP(&Flags.common.paramFile, "param-file", "P", "", wski18n.T("`FILE` containing parameter values in JSON format")) packageUpdateCmd.Flags().StringVar(&Flags.common.shared, "shared", "", wski18n.T("package visibility `SCOPE`; yes = shared, no = private")) packageGetCmd.Flags().BoolVarP(&Flags.common.summary, "summary", "s", false, wski18n.T("summarize package details; parameters with prefix \"*\" are bound")) packageBindCmd.Flags().StringSliceVarP(&Flags.common.annotation, "annotation", "a", []string{}, wski18n.T("annotation values in `KEY VALUE` format")) packageBindCmd.Flags().StringVarP(&Flags.common.annotFile, "annotation-file", "A", "", wski18n.T("`FILE` containing annotation values in JSON format")) packageBindCmd.Flags().StringSliceVarP(&Flags.common.param, "param", "p", []string{}, wski18n.T("parameter values in `KEY VALUE` format")) packageBindCmd.Flags().StringVarP(&Flags.common.paramFile, "param-file", "P", "", wski18n.T("`FILE` containing parameter values in JSON format")) packageBindCmd.Flags().BoolVarP(&Flags.common.overwrite, "overwrite", "o", false, wski18n.T("overwrite bind")) packageListCmd.Flags().IntVarP(&Flags.common.skip, "skip", "s", 0, wski18n.T("exclude the first `SKIP` number of packages from the result")) packageListCmd.Flags().IntVarP(&Flags.common.limit, "limit", "l", 30, wski18n.T("only return `LIMIT` number of packages from the collection")) packageListCmd.Flags().BoolVarP(&Flags.common.nameSort, "name-sort", "n", false, wski18n.T("sorts a list alphabetically by entity name; only applicable within the limit/skip returned entity block")) packageCmd.AddCommand( packageBindCmd, packageCreateCmd, packageUpdateCmd, packageGetCmd, packageDeleteCmd, packageListCmd, packageRefreshCmd, ) }