blob: ccabcd16eede174cca8556cc596533ddb8b27e63 [file] [log] [blame]
/*
* Copyright 2015-2016 IBM Corporation
*
* Licensed 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 (
"bufio"
"errors"
"fmt"
"strings"
"github.com/openwhisk/openwhisk-client-go/whisk"
"github.com/openwhisk/openwhisk-cli/wski18n"
"github.com/fatih/color"
//prettyjson "github.com/hokaccha/go-prettyjson" // See prettyjson comment below
"archive/tar"
"io"
"os"
"compress/gzip"
"archive/zip"
"encoding/json"
"net/url"
"io/ioutil"
"sort"
"reflect"
"bytes"
)
type QualifiedName struct {
namespace string // namespace. does not include leading '/'. may be "" (i.e. default namespace)
packageName string // package. may be "". does not include leading/trailing '/'
entity string // entity. should not be ""
entityName string // pkg+entity
}
func (qName QualifiedName) String() string {
output := []string{}
if len(qName.namespace) > 0 {
output = append(output, "/", qName.namespace, "/")
}
if len(qName.packageName) > 0 {
output = append(output, qName.packageName, "/")
}
output = append(output, qName.entityName)
return strings.Join(output, "")
}
/*
Parse a (possibly fully qualified) resource name into namespace and name components. If the given qualified name isNone,
then this is a default qualified name and it is resolved from properties. If the namespace is missing from the qualified
name, the namespace is also resolved from the property file.
Return a qualifiedName struct
Examples:
foo => qName {namespace: "_", entityName: foo}
pkg/foo => qName {namespace: "_", entityName: pkg/foo}
/ns/foo => qName {namespace: ns, entityName: foo}
/ns/pkg/foo => qName {namespace: ns, entityName: pkg/foo}
*/
func parseQualifiedName(name string) (QualifiedName, error) {
var qualifiedName QualifiedName
// If name has a preceding delimiter (/), it contains a namespace. Otherwise the name does not specify a namespace,
// so default the namespace to the namespace value set in the properties file; if that is not set, use "_"
if strings.HasPrefix(name, "/") {
parts := strings.Split(name, "/")
qualifiedName.namespace = parts[1]
if len(parts) < 2 || len(parts) > 4 {
whisk.Debug(whisk.DbgError, "A valid qualified name was not detected\n")
errStr := wski18n.T("A valid qualified name must be specified.")
err := whisk.MakeWskError(errors.New(errStr), whisk.NOT_ALLOWED, whisk.DISPLAY_MSG, whisk.NO_DISPLAY_USAGE)
return qualifiedName, err
}
for i := 1; i < len(parts); i++ {
if len(parts[i]) == 0 || parts[i] == "." {
whisk.Debug(whisk.DbgError, "A valid qualified name was not detected\n")
errStr := wski18n.T("A valid qualified name must be specified.")
err := whisk.MakeWskError(errors.New(errStr), whisk.NOT_ALLOWED, whisk.DISPLAY_MSG, whisk.NO_DISPLAY_USAGE)
return qualifiedName, err
}
}
qualifiedName.entityName = strings.Join(parts[2:], "/")
if len(parts) == 4 {
qualifiedName.packageName = parts[2]
}
qualifiedName.entity = parts[len(parts)-1]
} else {
if len(name) == 0 || name == "." {
whisk.Debug(whisk.DbgError, "A valid qualified name was not detected\n")
errStr := wski18n.T("A valid qualified name must be specified.")
err := whisk.MakeWskError(errors.New(errStr), whisk.NOT_ALLOWED, whisk.DISPLAY_MSG, whisk.NO_DISPLAY_USAGE)
return qualifiedName, err
}
parts := strings.Split(name, "/")
qualifiedName.entity = parts[len(parts)-1]
if len(parts) == 2 {
qualifiedName.packageName = parts[0]
}
qualifiedName.entityName = name
qualifiedName.namespace = getNamespace()
}
whisk.Debug(whisk.DbgInfo, "Qualified pkg+entity (EntityName): %s\n", qualifiedName.entityName)
whisk.Debug(whisk.DbgInfo, "Qualified namespace: %s\n", qualifiedName.namespace)
whisk.Debug(whisk.DbgInfo, "Qualified package: %s\n", qualifiedName.packageName)
whisk.Debug(whisk.DbgInfo, "Qualified entity: %s\n", qualifiedName.entity)
return qualifiedName, nil
}
func getNamespace() (string) {
namespace := "_"
if Properties.Namespace != "" {
namespace = Properties.Namespace
}
return namespace
}
/*
Return a fully qualified name given a (possibly fully qualified) resource name and optional namespace.
Examples:
(foo, None) => /_/foo
(pkg/foo, None) => /_/pkg/foo
(foo, ns) => /ns/foo
(/ns/pkg/foo, None) => /ns/pkg/foo
(/ns/pkg/foo, otherns) => /ns/pkg/foo
*/
func getQualifiedName(name string, namespace string) (string) {
if strings.HasPrefix(name, "/") {
return name
} else if strings.HasPrefix(namespace, "/") {
return fmt.Sprintf("%s/%s", namespace, name)
} else {
if len(namespace) == 0 {
namespace = Properties.Namespace
}
return fmt.Sprintf("/%s/%s", namespace, name)
}
}
func csvToQualifiedActions(artifacts string) ([]string) {
var res []string
actions := strings.Split(artifacts, ",")
for i := 0; i < len(actions); i++ {
res = append(res, getQualifiedName(actions[i], Properties.Namespace))
}
return res
}
func getJSONFromStrings(content []string, keyValueFormat bool) (interface{}, error) {
var data map[string]interface{}
var res interface{}
whisk.Debug(whisk.DbgInfo, "Convert content to JSON: %#v\n", content)
for i := 0; i < len(content); i++ {
dc := json.NewDecoder(strings.NewReader(content[i]))
dc.UseNumber()
if err := dc.Decode(&data); err!=nil {
whisk.Debug(whisk.DbgError, "Invalid JSON detected for '%s' \n", content[i])
return whisk.KeyValueArr{} , err
}
whisk.Debug(whisk.DbgInfo, "Created map '%v' from '%v'\n", data, content[i])
}
if keyValueFormat {
res = getKeyValueFormattedJSON(data)
} else {
res = data
}
return res, nil
}
func getKeyValueFormattedJSON(data map[string]interface{}) (whisk.KeyValueArr) {
var keyValueArr whisk.KeyValueArr
for key, value := range data {
keyValue := whisk.KeyValue{
Key: key,
Value: value,
}
keyValueArr = append(keyValueArr, keyValue)
}
whisk.Debug(whisk.DbgInfo, "Created key/value format '%v' from '%v'\n", keyValueArr, data)
return keyValueArr
}
func getFormattedJSON(key string, value string) (string) {
var res string
key = getEscapedJSON(key)
if isValidJSON(value) {
whisk.Debug(whisk.DbgInfo, "Value '%s' is valid JSON.\n", value)
res = fmt.Sprintf("{\"%s\": %s}", key, value)
} else {
whisk.Debug(whisk.DbgInfo, "Converting value '%s' to a string as it is not valid JSON.\n", value)
res = fmt.Sprintf("{\"%s\": \"%s\"}", key, value)
}
whisk.Debug(whisk.DbgInfo, "Formatted JSON '%s'\n", res)
return res
}
func getEscapedJSON(value string) (string) {
value = strings.Replace(value, "\\", "\\\\", -1)
value = strings.Replace(value, "\"", "\\\"", -1)
return value
}
func isValidJSON(value string) (bool) {
var jsonInterface interface{}
err := json.Unmarshal([]byte(value), &jsonInterface)
return err == nil
}
var boldString = color.New(color.Bold).SprintFunc()
func printList(collection interface{}) {
switch collection := collection.(type) {
case []whisk.Action:
printActionList(collection)
case []whisk.Trigger:
printTriggerList(collection)
case []whisk.Package:
printPackageList(collection)
case []whisk.Rule:
printRuleList(collection)
case []whisk.Namespace:
printNamespaceList(collection)
case []whisk.Activation:
printActivationList(collection)
case []whisk.Api:
printApiList(collection)
}
}
func printFullList(collection interface{}) {
switch collection := collection.(type) {
case []whisk.Action:
case []whisk.Trigger:
case []whisk.Package:
case []whisk.Rule:
case []whisk.Namespace:
case []whisk.Activation:
printFullActivationList(collection)
}
}
func printSummary(collection interface{}) {
switch collection := collection.(type) {
case *whisk.Action:
printActionSummary(collection)
case *whisk.Trigger:
printTriggerSummary(collection)
case *whisk.Package:
printPackageSummary(collection)
case *whisk.Rule:
case *whisk.Namespace:
case *whisk.Activation:
}
}
func printActionList(actions []whisk.Action) {
fmt.Fprintf(color.Output, "%s\n", boldString("actions"))
for _, action := range actions {
publishState := wski18n.T("private")
kind := getValueString(action.Annotations, "exec")
fmt.Printf("%-70s %s %s\n", fmt.Sprintf("/%s/%s", action.Namespace, action.Name), publishState, kind)
}
}
func printTriggerList(triggers []whisk.Trigger) {
fmt.Fprintf(color.Output, "%s\n", boldString("triggers"))
for _, trigger := range triggers {
publishState := wski18n.T("private")
fmt.Printf("%-70s %s\n", fmt.Sprintf("/%s/%s", trigger.Namespace, trigger.Name), publishState)
}
}
func printPackageList(packages []whisk.Package) {
fmt.Fprintf(color.Output, "%s\n", boldString("packages"))
for _, xPackage := range packages {
publishState := wski18n.T("private")
if xPackage.Publish != nil && *xPackage.Publish {
publishState = wski18n.T("shared")
}
fmt.Printf("%-70s %s\n", fmt.Sprintf("/%s/%s", xPackage.Namespace, xPackage.Name), publishState)
}
}
func printRuleList(rules []whisk.Rule) {
fmt.Fprintf(color.Output, "%s\n", boldString("rules"))
for _, rule := range rules {
publishState := wski18n.T("private")
fmt.Printf("%-70s %s\n", fmt.Sprintf("/%s/%s", rule.Namespace, rule.Name), publishState)
}
}
func printNamespaceList(namespaces []whisk.Namespace) {
fmt.Fprintf(color.Output, "%s\n", boldString("namespaces"))
for _, namespace := range namespaces {
fmt.Printf("%s\n", namespace.Name)
}
}
func printActivationList(activations []whisk.Activation) {
fmt.Fprintf(color.Output, "%s\n", boldString("activations"))
for _, activation := range activations {
fmt.Printf("%s %-20s\n", activation.ActivationID, activation.Name)
}
}
func printFullActivationList(activations []whisk.Activation) {
fmt.Fprintf(color.Output, "%s\n", boldString("activations"))
for _, activation := range activations {
printJSON(activation)
}
}
func printApiList(apis []whisk.Api) {
fmt.Fprintf(color.Output, "%s\n", boldString("apis"))
for _, api := range apis {
fmt.Printf("%s %20s %20s\n", api.ApiName, api.GatewayBasePath, api.GatewayFullPath)
}
}
func printFullApiList(apis []whisk.Api) {
fmt.Fprintf(color.Output, "%s\n", boldString("apis"))
for _, api := range apis {
printJSON(api)
}
}
func printActivationLogs(logs []string) {
for _, log := range logs {
fmt.Printf("%s\n", log)
}
}
func printArrayContents(arrStr []string) {
for _, str := range arrStr {
fmt.Printf("%s\n", str)
}
}
func printPackageSummary(pkg *whisk.Package) {
printEntitySummary(fmt.Sprintf("%7s", "package"), getFullName(pkg.Namespace, pkg.Name, ""),
getValueString(pkg.Annotations, "description"),
strings.Join(getChildValueStrings(pkg.Annotations, "parameters", "name"), ", "))
if pkg.Actions != nil {
for _, action := range pkg.Actions {
printEntitySummary(fmt.Sprintf("%7s", "action"), getFullName(pkg.Namespace, pkg.Name, action.Name),
getValueString(action.Annotations, "description"),
strings.Join(getChildValueStrings(action.Annotations, "parameters", "name"), ", "))
}
}
if pkg.Feeds != nil {
for _, feed := range pkg.Feeds {
printEntitySummary(fmt.Sprintf("%7s", "feed "), getFullName(pkg.Namespace, pkg.Name, feed.Name),
getValueString(feed.Annotations, "description"),
strings.Join(getChildValueStrings(feed.Annotations, "parameters", "name"), ", "))
}
}
}
func printActionSummary(action *whisk.Action) {
printEntitySummary(fmt.Sprintf("%6s", "action"),
getFullName(action.Namespace, "", action.Name),
getValueString(action.Annotations, "description"),
strings.Join(getChildValueStrings(action.Annotations, "parameters", "name"), ", "))
}
func printTriggerSummary(trigger *whisk.Trigger) {
printEntitySummary(fmt.Sprintf("%7s", "trigger"),
getFullName(trigger.Namespace, "", trigger.Name),
getValueString(trigger.Annotations, "description"),
strings.Join(getChildValueStrings(trigger.Annotations, "parameters", "name"), ", "))
}
func printRuleSummary(rule *whisk.Rule) {
fmt.Fprintf(color.Output, "%s %s\n", boldString(fmt.Sprintf("%4s", "rule")),
getFullName(rule.Namespace, "", rule.Name))
fmt.Fprintf(color.Output, " (%s: %s)\n", boldString(wski18n.T("status")), rule.Status)
}
func printEntitySummary(entityType string, fullName string, description string, params string) {
if len(description) > 0 {
fmt.Fprintf(color.Output, "%s %s: %s\n", boldString(entityType), fullName, description)
} else {
fmt.Fprintf(color.Output, "%s %s\n", boldString(entityType), fullName)
}
if len(params) > 0 {
fmt.Fprintf(color.Output, " (%s: %s)\n", boldString(wski18n.T("parameters")), params)
}
}
func getFullName(namespace string, packageName string, entityName string) (string) {
var fullName string
if len(namespace) > 0 && len(packageName) > 0 && len(entityName) > 0 {
fullName = fmt.Sprintf("/%s/%s/%s", namespace, packageName, entityName)
} else if len(namespace) > 0 && len(packageName) > 0 {
fullName = fmt.Sprintf("/%s/%s", namespace, packageName)
} else if len(namespace) > 0 && len(entityName) > 0 {
fullName = fmt.Sprintf("/%s/%s", namespace, entityName)
} else if len(namespace) > 0 {
fullName = fmt.Sprintf("/%s", namespace)
}
return fullName
}
func deleteKey(key string, keyValueArr whisk.KeyValueArr) (whisk.KeyValueArr) {
for i := 0; i < len(keyValueArr); i++ {
if keyValueArr[i].Key == key {
keyValueArr = append(keyValueArr[:i], keyValueArr[i + 1:]...)
break
}
}
return keyValueArr
}
func addKeyValue(key string, value interface{}, keyValueArr whisk.KeyValueArr) (whisk.KeyValueArr) {
keyValue := whisk.KeyValue{
Key: key,
Value: value,
}
return append(keyValueArr, keyValue)
}
func getKeys(keyValueArr whisk.KeyValueArr) ([]string) {
var res []string
for i := 0; i < len(keyValueArr); i++ {
res = append(res, keyValueArr[i].Key)
}
sort.Strings(res)
whisk.Debug(whisk.DbgInfo, "Got keys '%v' from '%v'\n", res, keyValueArr)
return res
}
func getValue(keyValueArr whisk.KeyValueArr, key string) (interface{}) {
var res interface{}
for i := 0; i < len(keyValueArr); i++ {
if keyValueArr[i].Key == key {
res = keyValueArr[i].Value
break;
}
}
whisk.Debug(whisk.DbgInfo, "Got value '%v' from '%v' for key '%s'\n", res, keyValueArr, key)
return res
}
func getValueString(keyValueArr whisk.KeyValueArr, key string) (string) {
var value interface{}
var res string
value = getValue(keyValueArr, key)
castedValue, canCast := value.(string)
if (canCast) {
res = castedValue
}
whisk.Debug(whisk.DbgInfo, "Got string value '%v' for key '%s'\n", res, key)
return res
}
func getChildValues(keyValueArr whisk.KeyValueArr, key string, childKey string) ([]interface{}) {
var value interface{}
var res []interface{}
value = getValue(keyValueArr, key)
castedValue, canCast := value.([]interface{})
if canCast {
for i := 0; i < len(castedValue); i++ {
castedValue, canCast := castedValue[i].(map[string]interface{})
if canCast {
for subKey, subValue := range castedValue {
if subKey == childKey {
res = append(res, subValue)
}
}
}
}
}
whisk.Debug(whisk.DbgInfo, "Got values '%s' from '%v' for key '%s' and child key '%s'\n", res, keyValueArr, key,
childKey)
return res
}
func getChildValueStrings(keyValueArr whisk.KeyValueArr, key string, childKey string) ([]string) {
var keys []interface{}
var res []string
keys = getChildValues(keyValueArr, key, childKey)
for i := 0; i < len(keys); i++ {
castedValue, canCast := keys[i].(string)
if (canCast) {
res = append(res, castedValue)
}
}
sort.Strings(res)
whisk.Debug(whisk.DbgInfo, "Got values '%s' from '%v' for key '%s' and child key '%s'\n", res, keyValueArr, key,
childKey)
return res
}
func getValueFromJSONResponse(field string, response map[string]interface {}) (interface{}) {
var res interface{}
for key, value := range response {
if key == field {
res = value
break
}
}
return res
}
func logoText() string {
logo := `
____ ___ _ _ _ _ _
/\ \ / _ \ _ __ ___ _ __ | | | | |__ (_)___| | __
/\ /__\ \ | | | | '_ \ / _ \ '_ \| | | | '_ \| / __| |/ /
/ \____ \ / | |_| | |_) | __/ | | | |/\| | | | | \__ \ <
\ \ / \/ \___/| .__/ \___|_| |_|__/\__|_| |_|_|___/_|\_\
\___\/ tm |_|
`
return logo
}
func printJSON(v interface{}, stream ...io.Writer) {
// Can't use prettyjson util issue https://github.com/hokaccha/go-prettyjson/issues/1 is fixed
//output, _ := prettyjson.Marshal(v)
//
//if len(stream) > 0 {
// fmt.Fprintf(stream[0], string(output))
//} else {
// fmt.Fprintf(color.Output, string(output))
//}
printJsonNoColor(v, stream...)
}
func printJsonNoColor(decoded interface{}, stream ...io.Writer) {
var output bytes.Buffer
buffer := new(bytes.Buffer)
encoder := json.NewEncoder(buffer)
encoder.SetEscapeHTML(false)
encoder.Encode(&decoded)
json.Indent(&output, buffer.Bytes(), "", " ")
if len(stream) > 0 {
fmt.Fprintf(stream[0], "%s", string(output.Bytes()))
} else {
fmt.Fprintf(os.Stdout, "%s", string(output.Bytes()))
}
}
func unpackGzip(inpath string, outpath string) error {
// Make sure the target file does not exist
if _, err := os.Stat(outpath); err == nil {
whisk.Debug(whisk.DbgError, "os.Stat reports file '%s' exists\n", outpath)
errStr := wski18n.T("The file {{.name}} already exists. Delete it and retry.",
map[string]interface{}{"name": outpath})
werr := whisk.MakeWskError(errors.New(errStr), whisk.EXITCODE_ERR_GENERAL, whisk.DISPLAY_MSG, whisk.NO_DISPLAY_USAGE)
return werr
}
// Make sure the input file exists
if _, err := os.Stat(inpath); err != nil {
whisk.Debug(whisk.DbgError, "os.Stat reports file '%s' does not exist\n", inpath)
errStr := wski18n.T("The file '{{.name}}' does not exist.", map[string]interface{}{"name": inpath})
werr := whisk.MakeWskError(errors.New(errStr), whisk.EXITCODE_ERR_GENERAL, whisk.DISPLAY_MSG, whisk.NO_DISPLAY_USAGE)
return werr
}
unGzFile, err := os.Create(outpath)
if err != nil {
whisk.Debug(whisk.DbgError, "os.Create(%s) failed: %s\n", outpath, err)
errStr := wski18n.T("Error creating unGzip file '{{.name}}': {{.err}}",
map[string]interface{}{"name": outpath, "err": err})
werr := whisk.MakeWskError(errors.New(errStr), whisk.EXITCODE_ERR_GENERAL, whisk.DISPLAY_MSG, whisk.NO_DISPLAY_USAGE)
return werr
}
defer unGzFile.Close()
gzFile, err := os.Open(inpath)
if err != nil {
whisk.Debug(whisk.DbgError, "os.Open(%s) failed: %s\n", inpath, err)
errStr := wski18n.T("Error opening Gzip file '{{.name}}': {{.err}}",
map[string]interface{}{"name": inpath, "err": err})
werr := whisk.MakeWskError(errors.New(errStr), whisk.EXITCODE_ERR_GENERAL, whisk.DISPLAY_MSG, whisk.NO_DISPLAY_USAGE)
return werr
}
defer gzFile.Close()
gzReader, err := gzip.NewReader(gzFile)
if err != nil {
whisk.Debug(whisk.DbgError, "gzip.NewReader() failed: %s\n", err)
errStr := wski18n.T("Unable to unzip file '{{.name}}': {{.err}}",
map[string]interface{}{"name": inpath, "err": err})
werr := whisk.MakeWskError(errors.New(errStr), whisk.EXITCODE_ERR_GENERAL, whisk.DISPLAY_MSG, whisk.NO_DISPLAY_USAGE)
return werr
}
_, err = io.Copy(unGzFile, gzReader)
if err != nil {
whisk.Debug(whisk.DbgError, "io.Copy() failed: %s\n", err)
errStr := wski18n.T("Unable to unzip file '{{.name}}': {{.err}}",
map[string]interface{}{"name": inpath, "err": err})
werr := whisk.MakeWskError(errors.New(errStr), whisk.EXITCODE_ERR_GENERAL, whisk.DISPLAY_MSG, whisk.NO_DISPLAY_USAGE)
return werr
}
return nil
}
func unpackZip(inpath string) error {
// Make sure the input file exists
if _, err := os.Stat(inpath); err != nil {
whisk.Debug(whisk.DbgError, "os.Stat reports file '%s' does not exist\n", inpath)
errStr := wski18n.T("The file '{{.name}}' does not exist.", map[string]interface{}{"name": inpath})
werr := whisk.MakeWskError(errors.New(errStr), whisk.EXITCODE_ERR_GENERAL, whisk.DISPLAY_MSG, whisk.NO_DISPLAY_USAGE)
return werr
}
zipFileReader, err := zip.OpenReader(inpath)
if err != nil {
whisk.Debug(whisk.DbgError, "zip.OpenReader(%s) failed: %s\n", inpath, err)
errStr := wski18n.T("Unable to opens '{{.name}}' for unzipping: {{.err}}",
map[string]interface{}{"name": inpath, "err": err})
werr := whisk.MakeWskError(errors.New(errStr), whisk.EXITCODE_ERR_GENERAL, whisk.DISPLAY_MSG, whisk.NO_DISPLAY_USAGE)
return werr
}
defer zipFileReader.Close()
// Loop through the files in the zipfile
for _, item := range zipFileReader.File {
itemName := item.Name
itemType := item.Mode()
whisk.Debug(whisk.DbgInfo, "file item - %#v\n", item)
if itemType.IsDir() {
if err := os.MkdirAll(item.Name, item.Mode()); err != nil {
whisk.Debug(whisk.DbgError, "os.MkdirAll(%s, %d) failed: %s\n", item.Name, item.Mode(), err)
errStr := wski18n.T("Unable to create directory '{{.dir}}' while unzipping '{{.name}}': {{.err}}",
map[string]interface{}{"dir": item.Name, "name": inpath, "err": err})
werr := whisk.MakeWskError(errors.New(errStr), whisk.EXITCODE_ERR_GENERAL, whisk.DISPLAY_MSG, whisk.NO_DISPLAY_USAGE)
return werr
}
}
if itemType.IsRegular() {
unzipFile, err := item.Open()
defer unzipFile.Close()
if err != nil {
whisk.Debug(whisk.DbgError, "'%s' Open() failed: %s\n", item.Name, err)
errStr := wski18n.T("Unable to open zipped file '{{.file}}' while unzipping '{{.name}}': {{.err}}",
map[string]interface{}{"file": item.Name, "name": inpath, "err": err})
werr := whisk.MakeWskError(errors.New(errStr), whisk.EXITCODE_ERR_GENERAL, whisk.DISPLAY_MSG, whisk.NO_DISPLAY_USAGE)
return werr
}
targetFile, err := os.Create(itemName)
if err != nil {
whisk.Debug(whisk.DbgError, "os.Create(%s) failed: %s\n", itemName, err)
errStr := wski18n.T("Unable to create file '{{.file}}' while unzipping '{{.name}}': {{.err}}",
map[string]interface{}{"file": item.Name, "name": inpath, "err": err})
werr := whisk.MakeWskError(errors.New(errStr), whisk.EXITCODE_ERR_GENERAL, whisk.DISPLAY_MSG, whisk.NO_DISPLAY_USAGE)
return werr
}
if _, err := io.Copy(targetFile, unzipFile); err != nil {
whisk.Debug(whisk.DbgError, "io.Copy() of '%s' failed: %s\n", itemName, err)
errStr := wski18n.T("Unable to unzip file '{{.name}}': {{.err}}",
map[string]interface{}{"name": itemName, "err": err})
werr := whisk.MakeWskError(errors.New(errStr), whisk.EXITCODE_ERR_GENERAL, whisk.DISPLAY_MSG, whisk.NO_DISPLAY_USAGE)
return werr
}
}
}
return nil
}
func unpackTar(inpath string) error {
// Make sure the input file exists
if _, err := os.Stat(inpath); err != nil {
whisk.Debug(whisk.DbgError, "os.Stat reports file '%s' does not exist\n", inpath)
errStr := wski18n.T("The file '{{.name}}' does not exist.", map[string]interface{}{"name": inpath})
werr := whisk.MakeWskError(errors.New(errStr), whisk.EXITCODE_ERR_GENERAL, whisk.DISPLAY_MSG, whisk.NO_DISPLAY_USAGE)
return werr
}
tarFileReader, err := os.Open(inpath)
if err != nil {
whisk.Debug(whisk.DbgError, "os.Open(%s) failed: %s\n", inpath, err)
errStr := wski18n.T("Error opening tar file '{{.name}}': {{.err}}",
map[string]interface{}{"name": inpath, "err": err})
werr := whisk.MakeWskError(errors.New(errStr), whisk.EXITCODE_ERR_GENERAL, whisk.DISPLAY_MSG, whisk.NO_DISPLAY_USAGE)
return werr
}
defer tarFileReader.Close()
// Loop through the files in the tarfile
tReader := tar.NewReader(tarFileReader)
for {
item, err := tReader.Next()
if err == io.EOF {
whisk.Debug(whisk.DbgError, "EOF reach during untar\n")
break // end of tar
}
if err != nil {
whisk.Debug(whisk.DbgError, "tReader.Next() failed: %s\n", err)
errStr := wski18n.T("Error reading tar file '{{.name}}': {{.err}}",
map[string]interface{}{"name": inpath, "err": err})
werr := whisk.MakeWskError(errors.New(errStr), whisk.EXITCODE_ERR_GENERAL, whisk.DISPLAY_MSG, whisk.NO_DISPLAY_USAGE)
return werr
}
whisk.Debug(whisk.DbgInfo, "tar file item - %#v\n", item)
switch item.Typeflag {
case tar.TypeDir:
if err := os.MkdirAll(item.Name, os.FileMode(item.Mode)); err != nil {
whisk.Debug(whisk.DbgError, "os.MkdirAll(%s, %d) failed: %s\n", item.Name, item.Mode, err)
errStr := wski18n.T("Unable to create directory '{{.dir}}' while untarring '{{.name}}': {{.err}}",
map[string]interface{}{"dir": item.Name, "name": inpath, "err": err})
werr := whisk.MakeWskError(errors.New(errStr), whisk.EXITCODE_ERR_GENERAL, whisk.DISPLAY_MSG, whisk.NO_DISPLAY_USAGE)
return werr
}
case tar.TypeReg:
untarFile, err:= os.OpenFile(item.Name, os.O_CREATE|os.O_TRUNC|os.O_WRONLY, os.FileMode(item.Mode))
defer untarFile.Close()
if err != nil {
whisk.Debug(whisk.DbgError, "os.Create(%s) failed: %s\n", item.Name, err)
errStr := wski18n.T("Unable to create file '{{.file}}' while untarring '{{.name}}': {{.err}}",
map[string]interface{}{"file": item.Name, "name": inpath, "err": err})
werr := whisk.MakeWskError(errors.New(errStr), whisk.EXITCODE_ERR_GENERAL, whisk.DISPLAY_MSG, whisk.NO_DISPLAY_USAGE)
return werr
}
if _, err := io.Copy(untarFile, tReader); err != nil {
whisk.Debug(whisk.DbgError, "io.Copy() of '%s' failed: %s\n", item.Name, err)
errStr := wski18n.T("Unable to untar file '{{.name}}': {{.err}}",
map[string]interface{}{"name": item.Name, "err": err})
werr := whisk.MakeWskError(errors.New(errStr), whisk.EXITCODE_ERR_GENERAL, whisk.DISPLAY_MSG, whisk.NO_DISPLAY_USAGE)
return werr
}
default:
whisk.Debug(whisk.DbgError, "Unexpected tar file type of %q\n", item.Typeflag)
errStr := wski18n.T("Unable to untar '{{.name}}' due to unexpected tar file type\n",
map[string]interface{}{"name": item.Name})
werr := whisk.MakeWskError(errors.New(errStr), whisk.EXITCODE_ERR_GENERAL, whisk.DISPLAY_MSG, whisk.NO_DISPLAY_USAGE)
return werr
}
}
return nil
}
func checkArgs(args []string, minimumArgNumber int, maximumArgNumber int, commandName string,
requiredArgMsg string) (*whisk.WskError) {
exactlyOrAtLeast := wski18n.T("exactly")
exactlyOrNoMoreThan := wski18n.T("exactly")
if (minimumArgNumber != maximumArgNumber) {
exactlyOrAtLeast = wski18n.T("at least")
exactlyOrNoMoreThan = wski18n.T("no more than")
}
if len(args) < minimumArgNumber {
whisk.Debug(whisk.DbgError, fmt.Sprintf("%s command must have %s %d argument(s)\n", commandName,
exactlyOrAtLeast, minimumArgNumber))
errMsg := wski18n.T("Invalid argument(s). {{.required}}", map[string]interface{}{"required": requiredArgMsg})
whiskErr := whisk.MakeWskError(errors.New(errMsg), whisk.EXITCODE_ERR_GENERAL,
whisk.DISPLAY_MSG, whisk.DISPLAY_USAGE)
return whiskErr
} else if len(args) > maximumArgNumber {
whisk.Debug(whisk.DbgError, fmt.Sprintf("%s command must have %s %d argument(s)\n", commandName,
exactlyOrNoMoreThan, maximumArgNumber))
errMsg := wski18n.T("Invalid argument(s): {{.args}}. {{.required}}",
map[string]interface{}{"args": strings.Join(args[maximumArgNumber:], ", "), "required": requiredArgMsg})
whiskErr := whisk.MakeWskError(errors.New(errMsg), whisk.EXITCODE_ERR_GENERAL,
whisk.DISPLAY_MSG, whisk.DISPLAY_USAGE)
return whiskErr
} else {
return nil
}
}
func getURLBase(host string, path string) (*url.URL, error) {
if len(host) == 0 {
errMsg := wski18n.T("An API host must be provided.")
whiskErr := whisk.MakeWskError(errors.New(errMsg), whisk.EXITCODE_ERR_GENERAL,
whisk.DISPLAY_MSG, whisk.DISPLAY_USAGE)
return nil, whiskErr
}
urlBase := fmt.Sprintf("%s%s", host, path)
url, err := url.Parse(urlBase)
if len(url.Scheme) == 0 || len(url.Host) == 0 {
urlBase = fmt.Sprintf("https://%s%s", host, path)
url, err = url.Parse(urlBase)
}
return url, err
}
func normalizeNamespace(namespace string) (string) {
if (namespace == "_") {
namespace = wski18n.T("default")
}
return namespace
}
func getClientNamespace() (string) {
return normalizeNamespace(client.Config.Namespace)
}
func readFile(filename string) (string, error) {
_, err := os.Stat(filename)
if err != nil {
whisk.Debug(whisk.DbgError, "os.Stat(%s) error: %s\n", filename, err)
errMsg := wski18n.T("File '{{.name}}' is not a valid file or it does not exist: {{.err}}",
map[string]interface{}{"name": filename, "err": err})
whiskErr := whisk.MakeWskErrorFromWskError(errors.New(errMsg), err, whisk.EXITCODE_ERR_USAGE,
whisk.DISPLAY_MSG, whisk.DISPLAY_USAGE)
return "", whiskErr
}
file, err := ioutil.ReadFile(filename)
if err != nil {
whisk.Debug(whisk.DbgError, "os.ioutil.ReadFile(%s) error: %s\n", filename, err)
errMsg := wski18n.T("Unable to read '{{.name}}': {{.err}}",
map[string]interface{}{"name": filename, "err": err})
whiskErr := whisk.MakeWskErrorFromWskError(errors.New(errMsg), err, whisk.EXITCODE_ERR_GENERAL,
whisk.DISPLAY_MSG, whisk.DISPLAY_USAGE)
return "", whiskErr
}
return string(file), nil
}
func fieldExists(value interface{}, field string) (bool) {
element := reflect.ValueOf(value).Elem()
for i := 0; i < element.NumField(); i++ {
if strings.ToLower(element.Type().Field(i).Name) == strings.ToLower(field) {
return true
}
}
return false
}
func printField(value interface{}, field string) {
var matchFunc = func(structField string) bool {
return strings.ToLower(structField) == strings.ToLower(field)
}
structValue := reflect.ValueOf(value)
fieldValue := reflect.Indirect(structValue).FieldByNameFunc(matchFunc)
printJSON(fieldValue.Interface())
}
func parseShared(shared string) (bool, bool, error) {
var isShared, isSet bool
if strings.ToLower(shared) == "yes" {
isShared = true
isSet = true
} else if strings.ToLower(shared) == "no" {
isShared = false
isSet = true
} else if len(shared) == 0 {
isSet = false
} else {
whisk.Debug(whisk.DbgError, "Cannot use value '%s' for shared.\n", shared)
errMsg := wski18n.T("Cannot use value '{{.arg}}' for shared.", map[string]interface{}{"arg": shared})
whiskErr := whisk.MakeWskError(errors.New(errMsg), whisk.EXITCODE_ERR_GENERAL, whisk.DISPLAY_MSG,
whisk.DISPLAY_USAGE)
return false, false, whiskErr
}
whisk.Debug(whisk.DbgError, "Sharing is '%t'\n", isShared)
return isShared, isSet, nil
}
func max(a int, b int) int {
if (a > b) {
return a
}
return b
}
func min (a int, b int) int {
if (a < b) {
return a
}
return b
}
func readProps(path string) (map[string]string, error) {
props := map[string]string{}
file, err := os.Open(path)
if err != nil {
// If file does not exist, just return props
whisk.Debug(whisk.DbgWarn, "Unable to read whisk properties file '%s' (file open error: %s); falling back to default properties\n" ,path, err)
return props, nil
}
defer file.Close()
lines := []string{}
scanner := bufio.NewScanner(file)
for scanner.Scan() {
lines = append(lines, scanner.Text())
}
props = map[string]string{}
for _, line := range lines {
kv := strings.Split(line, "=")
if len(kv) != 2 {
// Invalid format; skip
continue
}
props[kv[0]] = kv[1]
}
return props, nil
}
func writeProps(path string, props map[string]string) error {
file, err := os.Create(path)
if err != nil {
whisk.Debug(whisk.DbgError, "os.Create(%s) failed: %s\n", path, err)
errStr := wski18n.T("Whisk properties file write failure: {{.err}}", map[string]interface{}{"err": err})
werr := whisk.MakeWskError(errors.New(errStr), whisk.EXITCODE_ERR_GENERAL, whisk.DISPLAY_MSG, whisk.NO_DISPLAY_USAGE)
return werr
}
defer file.Close()
writer := bufio.NewWriter(file)
defer writer.Flush()
for key, value := range props {
line := fmt.Sprintf("%s=%s", strings.ToUpper(key), value)
_, err = fmt.Fprintln(writer, line)
if err != nil {
whisk.Debug(whisk.DbgError, "fmt.Fprintln() write to '%s' failed: %s\n", path, err)
errStr := wski18n.T("Whisk properties file write failure: {{.err}}", map[string]interface{}{"err": err})
werr := whisk.MakeWskError(errors.New(errStr), whisk.EXITCODE_ERR_GENERAL, whisk.DISPLAY_MSG, whisk.NO_DISPLAY_USAGE)
return werr
}
}
return nil
}
func getSpaceGuid() (string, error) {
// 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 obtain the `auth` property value: {{.err}}", map[string]interface{}{"err": err})
werr := whisk.MakeWskError(errors.New(errStr), whisk.EXITCODE_ERR_GENERAL, whisk.DISPLAY_MSG, whisk.NO_DISPLAY_USAGE)
return "", werr
}
// get the auth key and parse out the space guid
if authToken, hasProp := props["AUTH"]; hasProp {
spaceGuid := strings.Split(authToken, ":")[0]
return spaceGuid, nil
}
whisk.Debug(whisk.DbgError, "auth not found in properties: %#q\n", props)
errStr := wski18n.T("Auth key property value is not set")
werr := whisk.MakeWskError(errors.New(errStr), whisk.EXITCODE_ERR_GENERAL, whisk.DISPLAY_MSG, whisk.NO_DISPLAY_USAGE)
return "", werr
}
func isBlockingTimeout(err error) (bool) {
var blockingTimeout bool
whiskErr, isWhiskErr := err.(*whisk.WskError)
if isWhiskErr && whiskErr.TimedOut {
blockingTimeout = true
}
return blockingTimeout
}
func isApplicationError(err error) (bool) {
var applicationError bool
whiskErr, isWhiskErr := err.(*whisk.WskError)
if isWhiskErr && whiskErr.ApplicationError {
applicationError = true
}
return applicationError
}