commands/property.go (461 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" "os" "strings" "github.com/fatih/color" homedir "github.com/mitchellh/go-homedir" "github.com/spf13/cobra" "github.com/apache/openwhisk-cli/wski18n" "github.com/apache/openwhisk-client-go/whisk" ) var Properties struct { Cert string Key string Auth string APIHost string APIVersion string APIBuild string APIBuildNo string CLIVersion string Namespace string PropsFile string } const DefaultCert string = "" const DefaultKey string = "" const DefaultAuth string = "" const DefaultAPIHost string = "" const DefaultAPIVersion string = "v1" const DefaultAPIBuild string = "" const DefaultAPIBuildNo string = "" const DefaultNamespace string = "_" const DefaultPropsFile string = "~/.wskprops" const ( propDisplayCert = "client cert" propDisplayKey = "Client key" propDisplayAuth = "whisk auth" propDisplayAPIHost = "whisk API host" propDisplayAPIVersion = "whisk API version" propDisplayNamespace = "whisk namespace" propDisplayCLIVersion = "whisk CLI version" propDisplayAPIBuild = "whisk API build" propDisplayAPIBuildNo = "whisk API build number" ) var propertyCmd = &cobra.Command{ Use: "property", Short: wski18n.T("work with whisk properties"), } // // Set one or more openwhisk property values // var propertySetCmd = &cobra.Command{ Use: "set", Short: wski18n.T("set property"), SilenceUsage: true, SilenceErrors: true, PreRunE: SetupClientConfig, RunE: func(cmd *cobra.Command, args []string) error { var okMsg string = "" var werr *whisk.WskError = nil // get current props props, err := ReadProps(Properties.PropsFile) if err != nil { whisk.Debug(whisk.DbgError, "readProps(%s) failed: %s\n", Properties.PropsFile, err) errStr := wski18n.T("Unable to set the property value: {{.err}}", map[string]interface{}{"err": err}) werr = whisk.MakeWskError(errors.New(errStr), whisk.EXIT_CODE_ERR_GENERAL, whisk.DISPLAY_MSG, whisk.NO_DISPLAY_USAGE) return werr } // read in each flag, update if necessary if cert := Flags.Global.Cert; len(cert) > 0 { props["CERT"] = cert Client.Config.Cert = cert okMsg += fmt.Sprintf( wski18n.T("{{.ok}} client cert set. Run 'wsk property get --cert' to see the new value.\n", map[string]interface{}{"ok": color.GreenString("ok:")})) } if key := Flags.Global.Key; len(key) > 0 { props["KEY"] = key Client.Config.Key = key okMsg += fmt.Sprintf( wski18n.T("{{.ok}} client key set. Run 'wsk property get --key' to see the new value.\n", map[string]interface{}{"ok": color.GreenString("ok:")})) } if auth := Flags.Global.Auth; len(auth) > 0 { props["AUTH"] = auth Client.Config.AuthToken = auth okMsg += fmt.Sprintf( wski18n.T("{{.ok}} whisk auth set. Run 'wsk property get --auth' to see the new value.\n", map[string]interface{}{"ok": color.GreenString("ok:")})) } if apiHost := Flags.property.apihostSet; len(apiHost) > 0 { baseURL, err := whisk.GetURLBase(apiHost, DefaultOpenWhiskApiPath) if err != nil { // Not aborting now. Subsequent commands will result in error whisk.Debug(whisk.DbgError, "whisk.GetURLBase(%s, %s) error: %s", apiHost, DefaultOpenWhiskApiPath, err) errStr := fmt.Sprintf( wski18n.T("Unable to set API host value; the API host value '{{.apihost}}' is invalid: {{.err}}", map[string]interface{}{"apihost": apiHost, "err": err})) werr = whisk.MakeWskErrorFromWskError(errors.New(errStr), err, whisk.EXIT_CODE_ERR_GENERAL, whisk.DISPLAY_MSG, whisk.DISPLAY_USAGE) } else { props["APIHOST"] = apiHost Client.Config.BaseURL = baseURL okMsg += fmt.Sprintf( wski18n.T("{{.ok}} whisk API host set to {{.host}}\n", map[string]interface{}{"ok": color.GreenString("ok:"), "host": boldString(apiHost)})) } } if apiVersion := Flags.property.apiversionSet; len(apiVersion) > 0 { props["APIVERSION"] = apiVersion Client.Config.Version = apiVersion okMsg += fmt.Sprintf( wski18n.T("{{.ok}} whisk API version set to {{.version}}\n", map[string]interface{}{"ok": color.GreenString("ok:"), "version": boldString(apiVersion)})) } err = WriteProps(Properties.PropsFile, props) if err != nil { whisk.Debug(whisk.DbgError, "writeProps(%s, %#v) failed: %s\n", Properties.PropsFile, props, err) errStr := fmt.Sprintf( wski18n.T("Unable to set the property value(s): {{.err}}", map[string]interface{}{"err": err})) werr = whisk.MakeWskError(errors.New(errStr), whisk.EXIT_CODE_ERR_GENERAL, whisk.DISPLAY_MSG, whisk.NO_DISPLAY_USAGE) } else { fmt.Fprintf(color.Output, okMsg) } if werr != nil { return werr } return nil }, } var propertyUnsetCmd = &cobra.Command{ Use: "unset", Short: wski18n.T("unset property"), SilenceUsage: true, SilenceErrors: true, RunE: func(cmd *cobra.Command, args []string) error { var okMsg string = "" props, err := ReadProps(Properties.PropsFile) if err != nil { whisk.Debug(whisk.DbgError, "readProps(%s) failed: %s\n", Properties.PropsFile, err) errStr := fmt.Sprintf( wski18n.T("Unable to unset the property value: {{.err}}", map[string]interface{}{"err": err})) werr := whisk.MakeWskError(errors.New(errStr), whisk.EXIT_CODE_ERR_GENERAL, whisk.DISPLAY_MSG, whisk.NO_DISPLAY_USAGE) return werr } // read in each flag, update if necessary if Flags.property.cert { delete(props, "CERT") okMsg += fmt.Sprintf( wski18n.T("{{.ok}} client cert unset.\n", map[string]interface{}{"ok": color.GreenString("ok:")})) } if Flags.property.key { delete(props, "KEY") okMsg += fmt.Sprintf( wski18n.T("{{.ok}} client key unset.\n", map[string]interface{}{"ok": color.GreenString("ok:")})) } if Flags.property.auth { delete(props, "AUTH") okMsg += fmt.Sprintf( wski18n.T("{{.ok}} whisk auth unset.\n", map[string]interface{}{"ok": color.GreenString("ok:")})) } if Flags.property.apihost { delete(props, "APIHOST") okMsg += fmt.Sprintf( wski18n.T("{{.ok}} whisk API host unset.\n", map[string]interface{}{"ok": color.GreenString("ok:")})) } if Flags.property.apiversion { delete(props, "APIVERSION") okMsg += fmt.Sprintf( wski18n.T("{{.ok}} whisk API version unset", map[string]interface{}{"ok": color.GreenString("ok:")})) if len(DefaultAPIVersion) > 0 { okMsg += fmt.Sprintf( wski18n.T("; the default value of {{.default}} will be used.\n", map[string]interface{}{"default": boldString(DefaultAPIVersion)})) } else { okMsg += fmt.Sprint( wski18n.T("; there is no default value that can be used.\n")) } } err = WriteProps(Properties.PropsFile, props) if err != nil { whisk.Debug(whisk.DbgError, "writeProps(%s, %#v) failed: %s\n", Properties.PropsFile, props, err) errStr := fmt.Sprintf( wski18n.T("Unable to unset the property value: {{.err}}", map[string]interface{}{"err": err})) werr := whisk.MakeWskError(errors.New(errStr), whisk.EXIT_CODE_ERR_GENERAL, whisk.DISPLAY_MSG, whisk.NO_DISPLAY_USAGE) return werr } fmt.Fprintf(color.Output, okMsg) if err = loadProperties(); err != nil { whisk.Debug(whisk.DbgError, "loadProperties() failed: %s\n", err) } return nil }, } var propertyGetCmd = &cobra.Command{ Use: "get", Short: wski18n.T("get property"), SilenceUsage: true, SilenceErrors: true, PreRunE: SetupClientConfig, RunE: func(cmd *cobra.Command, args []string) error { var outputFormat string = "std" if Flags.property.output != "std" { switch Flags.property.output { case "raw": outputFormat = "raw" break //case "json": For future implementation //case "yaml": For future implementation default: errStr := fmt.Sprintf( wski18n.T("Supported output format are std|raw")) werr := whisk.MakeWskErrorFromWskError(errors.New(errStr), nil, whisk.EXIT_CODE_ERR_GENERAL, whisk.DISPLAY_MSG, whisk.NO_DISPLAY_USAGE) return werr } } // If no property is explicitly specified, default to all properties if !(Flags.property.all || Flags.property.cert || Flags.property.key || Flags.property.auth || Flags.property.apiversion || Flags.property.cliversion || Flags.property.namespace || Flags.property.apibuild || Flags.property.apihost || Flags.property.apibuildno) { Flags.property.all = true } if Flags.property.all { // Currently with all only standard output format is supported. if outputFormat != "std" { errStr := fmt.Sprintf( wski18n.T("--output|-o raw only supported with specific property type")) werr := whisk.MakeWskErrorFromWskError(errors.New(errStr), nil, whisk.EXIT_CODE_ERR_GENERAL, whisk.DISPLAY_MSG, whisk.NO_DISPLAY_USAGE) return werr } fmt.Fprintf(color.Output, "%s\t\t%s\n", wski18n.T(propDisplayAPIHost), boldString(Properties.APIHost)) fmt.Fprintf(color.Output, "%s\t\t%s\n", wski18n.T(propDisplayAuth), boldString(Properties.Auth)) fmt.Fprintf(color.Output, "%s\t\t%s\n", wski18n.T(propDisplayNamespace), boldString(getNamespace())) fmt.Fprintf(color.Output, "%s\t\t%s\n", wski18n.T(propDisplayCert), boldString(Properties.Cert)) fmt.Fprintf(color.Output, "%s\t\t%s\n", wski18n.T(propDisplayKey), boldString(Properties.Key)) fmt.Fprintf(color.Output, "%s\t%s\n", wski18n.T(propDisplayAPIVersion), boldString(Properties.APIVersion)) fmt.Fprintf(color.Output, "%s\t%s\n", wski18n.T(propDisplayCLIVersion), boldString(Properties.CLIVersion)) } else { if Flags.property.apihost { printProperty(Properties.APIHost, propDisplayAPIHost, outputFormat) } if Flags.property.auth { printProperty(Properties.Auth, propDisplayAuth, outputFormat) } if Flags.property.namespace { printProperty(getNamespace(), propDisplayNamespace, outputFormat) } if Flags.property.cert { printProperty(Properties.Cert, propDisplayCert, outputFormat) } if Flags.property.key { printProperty(Properties.Key, propDisplayKey, outputFormat) } if Flags.property.cliversion { printProperty(Properties.CLIVersion, propDisplayCLIVersion, outputFormat, "%s\t%s\n") } if Flags.property.apiversion { printProperty(Properties.APIVersion, propDisplayAPIVersion, outputFormat, "%s\t%s\n") } } if Flags.property.all || Flags.property.apibuild || Flags.property.apibuildno { info, _, err := Client.Info.Get() if err != nil { whisk.Debug(whisk.DbgError, "Client.Info.Get() failed: %s\n", err) info = &whisk.Info{} info.Build = wski18n.T("Unknown") info.BuildNo = wski18n.T("Unknown") } if Flags.property.all { fmt.Fprintf(color.Output, "%s\t\t%s\n", wski18n.T(propDisplayAPIBuild), boldString(info.Build)) fmt.Fprintf(color.Output, "%s\t%s\n", wski18n.T(propDisplayAPIBuildNo), boldString(info.BuildNo)) } if Flags.property.apibuild && !Flags.property.all { printProperty(info.Build, propDisplayAPIBuild, outputFormat) } if Flags.property.apibuildno && !Flags.property.all { printProperty(info.BuildNo, propDisplayAPIBuildNo, outputFormat, "%s\t%s\n") } if err != nil { errStr := fmt.Sprintf( wski18n.T("Unable to obtain API build information: {{.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 } } return nil }, } func init() { propertyCmd.AddCommand( propertySetCmd, propertyUnsetCmd, propertyGetCmd, ) // need to set property flags as booleans instead of strings... perhaps with boolApihost... propertyGetCmd.Flags().BoolVar(&Flags.property.cert, "cert", false, wski18n.T(propDisplayCert)) propertyGetCmd.Flags().BoolVar(&Flags.property.key, "key", false, wski18n.T(propDisplayKey)) propertyGetCmd.Flags().BoolVar(&Flags.property.auth, "auth", false, wski18n.T("authorization key")) propertyGetCmd.Flags().BoolVar(&Flags.property.apihost, "apihost", false, wski18n.T(propDisplayAPIHost)) propertyGetCmd.Flags().BoolVar(&Flags.property.apiversion, "apiversion", false, wski18n.T(propDisplayAPIVersion)) propertyGetCmd.Flags().BoolVar(&Flags.property.apibuild, "apibuild", false, wski18n.T("whisk API build version")) propertyGetCmd.Flags().BoolVar(&Flags.property.apibuildno, "apibuildno", false, wski18n.T(propDisplayAPIBuildNo)) propertyGetCmd.Flags().BoolVar(&Flags.property.cliversion, "cliversion", false, wski18n.T(propDisplayCLIVersion)) propertyGetCmd.Flags().BoolVar(&Flags.property.namespace, "namespace", false, wski18n.T(propDisplayNamespace)) propertyGetCmd.Flags().BoolVar(&Flags.property.all, "all", false, wski18n.T("all properties")) propertyGetCmd.Flags().StringVarP(&Flags.property.output, "output", "o", "std", wski18n.T("Output format in std|raw")) propertySetCmd.Flags().StringVarP(&Flags.Global.Auth, "auth", "u", "", wski18n.T("authorization `KEY`")) propertySetCmd.Flags().StringVar(&Flags.Global.Cert, "cert", "", wski18n.T(propDisplayCert)) propertySetCmd.Flags().StringVar(&Flags.Global.Key, "key", "", wski18n.T(propDisplayKey)) propertySetCmd.Flags().StringVar(&Flags.property.apihostSet, "apihost", "", wski18n.T("whisk API `HOST`")) propertySetCmd.Flags().StringVar(&Flags.property.apiversionSet, "apiversion", "", wski18n.T("whisk API `VERSION`")) propertyUnsetCmd.Flags().BoolVar(&Flags.property.cert, "cert", false, wski18n.T(propDisplayCert)) propertyUnsetCmd.Flags().BoolVar(&Flags.property.key, "key", false, wski18n.T(propDisplayKey)) propertyUnsetCmd.Flags().BoolVar(&Flags.property.auth, "auth", false, wski18n.T("authorization key")) propertyUnsetCmd.Flags().BoolVar(&Flags.property.apihost, "apihost", false, wski18n.T(propDisplayAPIHost)) propertyUnsetCmd.Flags().BoolVar(&Flags.property.apiversion, "apiversion", false, wski18n.T(propDisplayAPIVersion)) } func SetDefaultProperties() { Properties.Key = DefaultCert Properties.Cert = DefaultKey Properties.Auth = DefaultAuth Properties.Namespace = DefaultNamespace Properties.APIHost = DefaultAPIHost Properties.APIBuild = DefaultAPIBuild Properties.APIBuildNo = DefaultAPIBuildNo Properties.APIVersion = DefaultAPIVersion Properties.PropsFile = DefaultPropsFile // Properties.CLIVersion value is set from main's init() } func GetPropertiesFilePath() (propsFilePath string, werr error) { var envExists bool // WSK_CONFIG_FILE environment variable overrides the default properties file path // NOTE: If this variable is set to an empty string or non-existent/unreadable file // - any existing $HOME/.wskprops is ignored // - a default configuration is used if propsFilePath, envExists = os.LookupEnv("WSK_CONFIG_FILE"); envExists { whisk.Debug(whisk.DbgInfo, "Using properties file '%s' from WSK_CONFIG_FILE environment variable\n", propsFilePath) return propsFilePath, nil } else { var err error propsFilePath, err = homedir.Expand(Properties.PropsFile) if err != nil { whisk.Debug(whisk.DbgError, "homedir.Expand(%s) failed: %s\n", Properties.PropsFile, err) errStr := fmt.Sprintf( wski18n.T("Unable to locate properties file '{{.filename}}': {{.err}}", map[string]interface{}{"filename": Properties.PropsFile, "err": err})) werr = whisk.MakeWskError(errors.New(errStr), whisk.EXIT_CODE_ERR_GENERAL, whisk.DISPLAY_MSG, whisk.NO_DISPLAY_USAGE) return propsFilePath, werr } whisk.Debug(whisk.DbgInfo, "Using properties file home dir '%s'\n", propsFilePath) } return propsFilePath, nil } func loadProperties() error { var err error SetDefaultProperties() Properties.PropsFile, err = GetPropertiesFilePath() if err != nil { return nil //whisk.Debug(whisk.DbgError, "GetPropertiesFilePath() failed: %s\n", err) //errStr := fmt.Sprintf("Unable to load the properties file: %s", err) //werr := whisk.MakeWskError(errors.New(errStr), whisk.EXIT_CODE_ERR_GENERAL, whisk.DISPLAY_MSG, whisk.NO_DISPLAY_USAGE) //return werr } props, err := ReadProps(Properties.PropsFile) if err != nil { whisk.Debug(whisk.DbgError, "readProps(%s) failed: %s\n", Properties.PropsFile, err) errStr := wski18n.T("Unable to read the properties file '{{.filename}}': {{.err}}", map[string]interface{}{"filename": Properties.PropsFile, "err": err}) werr := whisk.MakeWskError(errors.New(errStr), whisk.EXIT_CODE_ERR_GENERAL, whisk.DISPLAY_MSG, whisk.NO_DISPLAY_USAGE) return werr } if cert, hasProp := props["CERT"]; hasProp { Properties.Cert = cert } if key, hasProp := props["KEY"]; hasProp { Properties.Key = key } if authToken, hasProp := props["AUTH"]; hasProp { Properties.Auth = authToken } if authToken := os.Getenv("WHISK_AUTH"); len(authToken) > 0 { Properties.Auth = authToken } if apiVersion, hasProp := props["APIVERSION"]; hasProp { Properties.APIVersion = apiVersion } if apiVersion := os.Getenv("WHISK_APIVERSION"); len(apiVersion) > 0 { Properties.APIVersion = apiVersion } if apiHost, hasProp := props["APIHOST"]; hasProp { Properties.APIHost = strings.TrimRight(apiHost, "/") } if apiHost := os.Getenv("WHISK_APIHOST"); len(apiHost) > 0 { Properties.APIHost = apiHost } return nil } func parseConfigFlags(cmd *cobra.Command, args []string) error { if cert := Flags.Global.Cert; len(cert) > 0 { Properties.Cert = cert if Client != nil { Client.Config.Cert = cert } } if key := Flags.Global.Key; len(key) > 0 { Properties.Key = key if Client != nil { Client.Config.Key = key } } if auth := Flags.Global.Auth; len(auth) > 0 { Properties.Auth = auth if Client != nil { Client.Config.AuthToken = auth } } if apiVersion := Flags.Global.Apiversion; len(apiVersion) > 0 { Properties.APIVersion = apiVersion if Client != nil { Client.Config.Version = apiVersion } } if apiHost := Flags.Global.Apihost; len(apiHost) > 0 { Properties.APIHost = apiHost if Client != nil { Client.Config.Host = apiHost baseURL, err := whisk.GetURLBase(apiHost, DefaultOpenWhiskApiPath) if err != nil { whisk.Debug(whisk.DbgError, "whisk.GetURLBase(%s, %s) failed: %s\n", apiHost, DefaultOpenWhiskApiPath, err) errStr := wski18n.T("Invalid host address '{{.host}}': {{.err}}", map[string]interface{}{"host": Properties.APIHost, "err": err}) werr := whisk.MakeWskError(errors.New(errStr), whisk.EXIT_CODE_ERR_GENERAL, whisk.DISPLAY_MSG, whisk.NO_DISPLAY_USAGE) return werr } Client.Config.BaseURL = baseURL } } if Flags.Global.Debug { whisk.SetDebug(true) } if Flags.Global.Verbose { whisk.SetVerbose(true) } return nil } func printProperty(propertyName string, displayText string, formatType string, format ...string) { switch formatType { case "std": if len(format) > 0 { fmt.Fprintf(color.Output, format[0], wski18n.T(displayText), boldString(propertyName)) break } fmt.Fprintf(color.Output, "%s\t\t%s\n", wski18n.T(displayText), boldString(propertyName)) break case "raw": fmt.Fprintf(color.Output, "%s\n", boldString(propertyName)) break default: // In case of any other type for now print in std format. fmt.Fprintf(color.Output, "%s\t\t%s\n", wski18n.T(displayText), boldString(propertyName)) } } func getNamespace() string { var namespaces, _, err = Client.Namespaces.List() whisk.Debug(whisk.DbgError, "Client.Namespaces.List() failed: %s\n", err) if err != nil { return "_" } else { return namespaces[0].Name } }