blob: b0eac7c36d5d590a034ec9374722887afff3f3d4 [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 deployers
import (
"bufio"
"fmt"
"github.com/apache/incubator-openwhisk-client-go/whisk"
"github.com/apache/incubator-openwhisk-wskdeploy/parsers"
"github.com/apache/incubator-openwhisk-wskdeploy/utils"
"github.com/apache/incubator-openwhisk-wskdeploy/wski18n"
"net/http"
"os"
"path"
"strings"
"time"
)
const (
COMMANDLINE = "wskdeploy command line"
DEFAULTVALUE = "default value"
WSKPROPS = ".wskprops"
WHISKPROPERTY = "whisk.properties"
INTERINPUT = "interactve input"
)
type PropertyValue struct {
Value string
Source string
}
var GetPropertyValue = func(prop PropertyValue, newValue string, source string) PropertyValue {
if len(prop.Value) == 0 && len(newValue) > 0 {
prop.Value = newValue
prop.Source = source
}
return prop
}
var GetWskPropFromWskprops = func(pi whisk.Properties, proppath string) (*whisk.Wskprops, error) {
return whisk.GetWskPropFromWskprops(pi, proppath)
}
var GetWskPropFromWhiskProperty = func(pi whisk.Properties) (*whisk.Wskprops, error) {
return whisk.GetWskPropFromWhiskProperty(pi)
}
var GetCommandLineFlags = func() (string, string, string, string, string) {
return utils.Flags.ApiHost, utils.Flags.Auth, utils.Flags.Namespace, utils.Flags.Key, utils.Flags.Cert
}
var CreateNewClient = func (config_input *whisk.Config) (*whisk.Client, error) {
var netClient = &http.Client{
Timeout: time.Second * utils.DEFAULT_HTTP_TIMEOUT,
}
return whisk.NewClient(netClient, config_input)
}
// we are reading openwhisk credentials (apihost, namespace, and auth) in the following precedence order:
// (1) wskdeploy command line `wskdeploy --apihost --namespace --auth`
// (2) deployment file
// (3) manifest file
// (4) .wskprops
// (5) prompt for values in interactive mode if any of them are missing
func NewWhiskConfig(proppath string, deploymentPath string, manifestPath string, isInteractive bool) (*whisk.Config, error) {
// struct to store credential, namespace, and host with their respective source
credential := PropertyValue{}
namespace := PropertyValue{}
apiHost := PropertyValue{}
key := PropertyValue{}
cert := PropertyValue{}
// read credentials from command line
apihost, auth, ns, keyfile, certfile := GetCommandLineFlags()
credential = GetPropertyValue(credential, auth, COMMANDLINE)
namespace = GetPropertyValue(namespace, ns, COMMANDLINE)
apiHost = GetPropertyValue(apiHost, apihost, COMMANDLINE)
key = GetPropertyValue(key, keyfile, COMMANDLINE)
cert = GetPropertyValue(cert, certfile, COMMANDLINE)
// now, read them from deployment file if not found on command line
if len(credential.Value) == 0 || len(namespace.Value) == 0 || len(apiHost.Value) == 0 {
if utils.FileExists(deploymentPath) {
mm := parsers.NewYAMLParser()
deployment, _ := mm.ParseDeployment(deploymentPath)
credential = GetPropertyValue(credential, deployment.GetProject().Credential, path.Base(deploymentPath))
namespace = GetPropertyValue(namespace, deployment.GetProject().Namespace, path.Base(deploymentPath))
apiHost = GetPropertyValue(apiHost, deployment.GetProject().ApiHost, path.Base(deploymentPath))
}
}
// read credentials from manifest file as didn't find them on command line and in deployment file
if len(credential.Value) == 0 || len(namespace.Value) == 0 || len(apiHost.Value) == 0 {
if utils.FileExists(manifestPath) {
mm := parsers.NewYAMLParser()
manifest, _ := mm.ParseManifest(manifestPath)
if manifest.Package.Packagename != "" {
credential = GetPropertyValue(credential, manifest.Package.Credential, path.Base(manifestPath))
namespace = GetPropertyValue(namespace, manifest.Package.Namespace, path.Base(manifestPath))
apiHost = GetPropertyValue(apiHost, manifest.Package.ApiHost, path.Base(manifestPath))
} else if manifest.Packages != nil {
if len(manifest.Packages) == 1 {
for _, pkg := range manifest.Packages {
credential = GetPropertyValue(credential, pkg.Credential, path.Base(manifestPath))
namespace = GetPropertyValue(namespace, pkg.Namespace, path.Base(manifestPath))
apiHost = GetPropertyValue(apiHost, pkg.ApiHost, path.Base(manifestPath))
}
}
}
}
}
// Third, we need to look up the variables in .wskprops file.
pi := whisk.PropertiesImp{
OsPackage: whisk.OSPackageImp{},
}
// The error raised here can be neglected, because we will handle it in the end of this function.
wskprops, _ := GetWskPropFromWskprops(pi, proppath)
credential = GetPropertyValue(credential, wskprops.AuthKey, WSKPROPS)
namespace = GetPropertyValue(namespace, wskprops.Namespace, WSKPROPS)
apiHost = GetPropertyValue(apiHost, wskprops.APIHost, WSKPROPS)
key = GetPropertyValue(key, wskprops.Key, WSKPROPS)
cert = GetPropertyValue(cert, wskprops.Cert, WSKPROPS)
// now, read credentials from whisk.properties but this is only acceptable within Travis
// whisk.properties will soon be deprecated and should not be used for any production deployment
whiskproperty, _ := GetWskPropFromWhiskProperty(pi)
credential = GetPropertyValue(credential, whiskproperty.AuthKey, WHISKPROPERTY)
if credential.Source == WHISKPROPERTY {
utils.PrintOpenWhiskOutputln("WARNING: The authentication key was retrieved from whisk.properties " +
"which will soon be deprecated please do not use it outside of Travis builds.")
}
namespace = GetPropertyValue(namespace, whiskproperty.Namespace, WHISKPROPERTY)
if namespace.Source == WHISKPROPERTY {
utils.PrintOpenWhiskOutputln("WARNING: The namespace was retrieved from whisk.properties " +
"which will soon be deprecated please do not use it outside of Travis builds.")
}
apiHost = GetPropertyValue(apiHost, whiskproperty.APIHost, WHISKPROPERTY)
if apiHost.Source == WHISKPROPERTY {
utils.PrintOpenWhiskOutputln("WARNING: The API host was retrieved from whisk.properties " +
"which will soon be deprecated please do not use it outside of Travis builds.")
}
// set namespace to default namespace if not yet found
if len(apiHost.Value) != 0 && len(credential.Value) != 0 && len(namespace.Value) == 0 {
namespace.Value = whisk.DEFAULT_NAMESPACE
namespace.Source = DEFAULTVALUE
}
// If we still can not find the values we need, check if it is interactive mode.
// If so, we prompt users for the input.
// The namespace is set to a default value at this point if not provided.
if len(apiHost.Value) == 0 && isInteractive == true {
host := promptForValue("\nPlease provide the hostname for OpenWhisk [default value is openwhisk.ng.bluemix.net]: ")
if host == "" {
host = "openwhisk.ng.bluemix.net"
}
apiHost.Value = host
apiHost.Source = INTERINPUT
}
if len(credential.Value) == 0 && isInteractive == true {
cred := promptForValue("\nPlease provide an authentication token: ")
credential.Value = cred
credential.Source = INTERINPUT
// The namespace is always associated with the credential. Both of them should be picked up from the same source.
if len(namespace.Value) == 0 || namespace.Value == whisk.DEFAULT_NAMESPACE {
ns := promptForValue("\nPlease provide a namespace [default value is guest]: ")
source := INTERINPUT
if ns == "" {
ns = whisk.DEFAULT_NAMESPACE
source = DEFAULTVALUE
}
namespace.Value = ns
namespace.Source = source
}
}
mode := true
if (len(cert.Value) != 0 && len(key.Value) != 0) {
mode = false
}
clientConfig = &whisk.Config {
AuthToken: credential.Value, //Authtoken
Namespace: namespace.Value, //Namespace
Host: apiHost.Value,
Version: "v1",
Cert: cert.Value,
Key: key.Value,
Insecure: mode, // true if you want to ignore certificate signing
}
if len(credential.Value) == 0 || len(apiHost.Value) == 0 || len(namespace.Value) == 0 {
var errStr string
if len(credential.Value) == 0 {
errStr += wski18n.T("The authentication key is not configured.\n")
} else {
errStr += wski18n.T("The authenitcation key is set from {{.authsource}}.\n",
map[string]interface{}{"authsource": credential.Source})
}
if len(apiHost.Value) == 0 {
errStr += wski18n.T("The API host is not configured.\n")
} else {
errStr += wski18n.T("The API host is {{.apihost}}, from {{.apisource}}.\n",
map[string]interface{}{"apihost": apiHost.Value, "apisource": apiHost.Source})
}
if len(namespace.Value) == 0 {
errStr += wski18n.T("The namespace is not configured.\n")
} else {
errStr += wski18n.T("The namespace is {{.namespace}}, from {{.namespacesource}}.\n",
map[string]interface{}{"namespace": namespace.Value, "namespacesource": namespace.Source})
}
whisk.Debug(whisk.DbgError, errStr)
return clientConfig, utils.NewInvalidWskpropsError(errStr)
}
stdout := wski18n.T("The API host is {{.apihost}}, from {{.apisource}}.\n",
map[string]interface{}{"apihost": apiHost.Value, "apisource": apiHost.Source})
whisk.Debug(whisk.DbgInfo, stdout)
stdout = wski18n.T("The auth key is set, from {{.authsource}}.\n",
map[string]interface{}{"authsource": credential.Source})
whisk.Debug(whisk.DbgInfo, stdout)
stdout = wski18n.T("The namespace is {{.namespace}}, from {{.namespacesource}}.\n",
map[string]interface{}{"namespace": namespace.Value, "namespacesource": namespace.Source})
whisk.Debug(whisk.DbgInfo, stdout)
return clientConfig, nil
}
var promptForValue = func(msg string) (string) {
reader := bufio.NewReader(os.Stdin)
fmt.Print(msg)
text, _ := reader.ReadString('\n')
text = strings.TrimSpace(text)
return text
}