blob: b87bd4605c4ac85e76d42f3223400b1774b50aa9 [file] [log] [blame]
/*
* 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"
"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 = 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
}
}