You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@openwhisk.apache.org by mr...@apache.org on 2017/09/14 21:33:22 UTC

[incubator-openwhisk-wskdeploy] branch master updated: Fixing the precedence order of retreiving credentials and adding unit tests (#485)

This is an automated email from the ASF dual-hosted git repository.

mrutkowski pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/incubator-openwhisk-wskdeploy.git


The following commit(s) were added to refs/heads/master by this push:
     new 71d2ec6  Fixing the precedence order of retreiving credentials and adding unit tests (#485)
71d2ec6 is described below

commit 71d2ec6d13fb71ae3cf4d2181df215456542d25b
Author: Priti Desai <pd...@us.ibm.com>
AuthorDate: Thu Sep 14 14:33:20 2017 -0700

    Fixing the precedence order of retreiving credentials and adding unit tests (#485)
    
    * Fixing the precedence order of retreiving credentials and adding unit tests
    
    Closes #380
    Closes #471
    
    * adding newline
---
 deployers/whiskclient.go                       | 321 +++++++++++++------------
 deployers/whiskclient_test.go                  | 218 +++++++++++++++++
 tests/dat/deployment_validate_credentials.yaml |  11 +
 tests/dat/manifest_validate_credentials.yaml   |  21 ++
 4 files changed, 422 insertions(+), 149 deletions(-)

diff --git a/deployers/whiskclient.go b/deployers/whiskclient.go
index d136aca..0277380 100644
--- a/deployers/whiskclient.go
+++ b/deployers/whiskclient.go
@@ -20,31 +20,31 @@ package deployers
 import (
 	"bufio"
 	"fmt"
-	"net/http"
-	"os"
-	"strings"
 	"github.com/apache/incubator-openwhisk-client-go/whisk"
 	"github.com/apache/incubator-openwhisk-wskdeploy/parsers"
-    "github.com/apache/incubator-openwhisk-wskdeploy/wski18n"
 	"github.com/apache/incubator-openwhisk-wskdeploy/utils"
-    "path"
-    "time"
+	"github.com/apache/incubator-openwhisk-wskdeploy/wski18n"
+	"net/http"
+	"os"
+	"path"
+	"strings"
+	"time"
 )
 
 const (
-	COMMANDLINE = "wskdeploy command line"
+	COMMANDLINE  = "wskdeploy command line"
 	DEFAULTVALUE = "default value"
-	WSKPROPS = ".wskprops"
+	WSKPROPS     = ".wskprops"
 	WHISKPROPERTY = "whisk.properties"
-	INTERINPUT = "interactve input"
+	INTERINPUT   = "interactve input"
 )
 
 type PropertyValue struct {
-	Value string
+	Value  string
 	Source string
 }
 
-var GetPropertyValue = func (prop PropertyValue, newValue string, source string) PropertyValue {
+var GetPropertyValue = func(prop PropertyValue, newValue string, source string) PropertyValue {
 	if len(prop.Value) == 0 && len(newValue) > 0 {
 		prop.Value = newValue
 		prop.Source = source
@@ -52,160 +52,183 @@ var GetPropertyValue = func (prop PropertyValue, newValue string, source string)
 	return prop
 }
 
-var GetWskPropFromWskprops = func (pi whisk.Properties, proppath string) (*whisk.Wskprops, error) {
+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) {
+var GetWskPropFromWhiskProperty = func(pi whisk.Properties) (*whisk.Wskprops, error) {
 	return whisk.GetWskPropFromWhiskProperty(pi)
 }
 
-var GetCommandLineFlags = func () (string, string, string) {
+var GetCommandLineFlags = func() (string, string, string) {
 	return utils.Flags.ApiHost, utils.Flags.Auth, utils.Flags.Namespace
 }
 
 var CreateNewClient = func (config_input *whisk.Config) (*whisk.Client, error) {
-    var netClient = &http.Client{
-        Timeout: time.Second * utils.DEFAULT_HTTP_TIMEOUT,
-    }
+	var netClient = &http.Client{
+		Timeout: time.Second * utils.DEFAULT_HTTP_TIMEOUT,
+	}
 	return whisk.NewClient(netClient, clientConfig)
 }
 
+// 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) {
-    credential := PropertyValue {}
-    namespace := PropertyValue {}
-    apiHost := PropertyValue {}
-
-    // First, we look up the above variables in the deployment file.
-    if utils.FileExists(deploymentPath) {
-        mm := parsers.NewYAMLParser()
-        deployment, err := mm.ParseDeployment(deploymentPath)
-        if err == nil {
-            credential.Value = deployment.Application.Credential
-            credential.Source = path.Base(deploymentPath)
-            namespace.Value = deployment.Application.Namespace
-            namespace.Source = path.Base(deploymentPath)
-            apiHost.Value = deployment.Application.ApiHost
-            apiHost.Source = path.Base(deploymentPath)
-        }
-    }
-
-    if len(credential.Value) == 0 || len(namespace.Value) == 0 || len(apiHost.Value) == 0 {
-        if utils.FileExists(manifestPath) {
-            mm := parsers.NewYAMLParser()
-            manifest, err := mm.ParseManifest(manifestPath)
-            if err == nil {
-                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))
-            }
-        }
-    }
-
-    // If the variables are not correctly assigned, we look up auth key and api host in the command line. The namespace
-    // is currently not available in command line, which can be added later.
-    apihost, auth, ns := GetCommandLineFlags()
-    credential = GetPropertyValue(credential, auth, COMMANDLINE)
-    namespace = GetPropertyValue(namespace, ns, COMMANDLINE)
-    apiHost = GetPropertyValue(apiHost, apihost, COMMANDLINE)
-
-    // 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)
-
-    // Fourth, we look up the variables in whisk.properties on a local openwhisk deployment.
-    if len(credential.Value) == 0 || len(apiHost.Value) == 0 {
-        // No need to keep the default value for namespace, since both of auth and apihost are not set after .wskprops.
-        // whisk.property will set the default value as well.
-        apiHost.Value = ""
-        apiHost.Source = DEFAULTVALUE
-    }
-
-    // The error raised here can be neglected, because we will handle it in the end of this function.
-    whiskproperty, _ := GetWskPropFromWhiskProperty(pi)
-    credential = GetPropertyValue(credential, whiskproperty.AuthKey, WHISKPROPERTY)
-    namespace = GetPropertyValue(namespace, whiskproperty.Namespace, WHISKPROPERTY)
-    apiHost = GetPropertyValue(apiHost, whiskproperty.APIHost, WHISKPROPERTY)
-
-    // If we still can not find the variables we need, check if it is interactive mode. If so, we accept the input
-    // from the user. The namespace will be set to a default value, when the code reaches this line, because WSKPROPS
-    // has a default value for namespace.
-    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
-        }
-    }
-
-    clientConfig = &whisk.Config{
-        AuthToken: credential.Value, //Authtoken
-        Namespace: namespace.Value,  //Namespace
-        Host: apiHost.Value,
-        Version:   "v1",
-        Insecure:  true, // true if you want to ignore certificate signing
-    }
-
-    if len(credential.Value) == 0 {
-        errStr := wski18n.T("The authentication key is not configured.\n")
-        whisk.Debug(whisk.DbgError, errStr)
-        return clientConfig, utils.NewInvalidWskpropsError(errStr)
-    }
-
-    if len(apiHost.Value) == 0 {
-        errStr := wski18n.T("The API host is not configured.\n")
-        whisk.Debug(whisk.DbgError, errStr)
-        return clientConfig, utils.NewInvalidWskpropsError(errStr)
-    }
-
-    if len(namespace.Value) == 0 {
-        errStr := wski18n.T("The namespace is not configured.\n")
-        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
+	// struct to store credential, namespace, and host with their respective source
+	credential := PropertyValue{}
+	namespace := PropertyValue{}
+	apiHost := PropertyValue{}
+
+	// read credentials from command line
+	apihost, auth, ns := GetCommandLineFlags()
+	credential = GetPropertyValue(credential, auth, COMMANDLINE)
+	namespace = GetPropertyValue(namespace, ns, COMMANDLINE)
+	apiHost = GetPropertyValue(apiHost, apihost, 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.Application.Credential, path.Base(deploymentPath))
+			namespace = GetPropertyValue(namespace, deployment.Application.Namespace, path.Base(deploymentPath))
+			apiHost = GetPropertyValue(apiHost, deployment.Application.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)
+			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))
+		}
+	}
+
+	// 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)
+
+	// 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 {
+		fmt.Println("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 {
+		fmt.Println("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 {
+		fmt.Println("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
+		}
+	}
+
+	clientConfig = &whisk.Config{
+		AuthToken: credential.Value, //Authtoken
+		Namespace: namespace.Value,  //Namespace
+		Host:      apiHost.Value,
+		Version:   "v1",
+		Insecure:  true, // 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) {
+var promptForValue = func(msg string) (string) {
 	reader := bufio.NewReader(os.Stdin)
 	fmt.Print(msg)
 
diff --git a/deployers/whiskclient_test.go b/deployers/whiskclient_test.go
new file mode 100644
index 0000000..0c5798b
--- /dev/null
+++ b/deployers/whiskclient_test.go
@@ -0,0 +1,218 @@
+// +build unit
+
+/*
+ * 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 (
+	"testing"
+	"github.com/stretchr/testify/assert"
+	"github.com/apache/incubator-openwhisk-wskdeploy/utils"
+	"github.com/apache/incubator-openwhisk-client-go/whisk"
+)
+
+const (
+	DEPLOYMENT_HOST = "sample.deployment.openwhisk.org"
+	DEPLOYMENT_AUTH = "sample-deployment-credential"
+	DEPLOYMENT_NAMESPACE = "sample-deployment-namespace"
+
+	MANIFEST_HOST = "sample.manifest.openwhisk.org"
+	MANIFEST_AUTH = "sample-manifest-credential"
+	MANIFEST_NAMESPACE = "sample-manifest-namespace"
+
+	CLI_HOST = "sample.cli.openwhisk.org"
+	CLI_AUTH = "sample-cli-credential"
+	CLI_NAMESPACE = "sample-cli-namespace"
+
+	WSKPROPS_HOST = "openwhisk.ng.bluemix.net"
+	WSKPROPS_AUTH = "a4f8c502:123zO3xZCLrMN6v2BKK"
+	WSKPROPS_NAMESPACE = "guest"
+)
+
+func TestNewWhiskConfig(t *testing.T) {
+	propPath := ""
+	manifestPath := ""
+	deploymentPath := ""
+	config, err := NewWhiskConfig(propPath, deploymentPath, manifestPath, false)
+	if err == nil {
+		pi := whisk.PropertiesImp {
+			OsPackage: whisk.OSPackageImp{},
+		}
+		wskprops, err := whisk.GetDefaultWskProp(pi)
+		if err == nil {
+			assert.Equal(t, config.Namespace, wskprops.Namespace, "")
+			assert.Equal(t, config.Host, wskprops.APIHost, "")
+			assert.Equal(t, config.AuthToken, wskprops.AuthKey, "")
+		}
+		return
+	}
+	assert.NotNil(t, err, "Failed to produce an error when credentials could not be retrieved")
+}
+
+func TestNewWhiskConfigCommandLine(t *testing.T) {
+	propPath := ""
+	manifestPath := ""
+	deploymentPath := ""
+	utils.Flags.ApiHost = CLI_HOST
+	utils.Flags.Auth = CLI_AUTH
+	utils.Flags.Namespace = CLI_NAMESPACE
+
+	config, err := NewWhiskConfig(propPath, deploymentPath, manifestPath, false)
+	assert.Nil(t, err, "Failed to read credentials from wskdeploy command line")
+	assert.Equal(t, config.Host, CLI_HOST, "Failed to get host name from wskdeploy command line")
+	assert.Equal(t, config.AuthToken, CLI_AUTH, "Failed to get auth token from wskdeploy command line")
+	assert.Equal(t, config.Namespace, CLI_NAMESPACE, "Failed to get namespace from wskdeploy command line")
+
+	utils.Flags.Auth = ""
+	utils.Flags.Namespace = ""
+	utils.Flags.ApiHost = ""
+}
+
+func TestNewWhiskConfigDeploymentFile(t *testing.T) {
+	propPath := ""
+	manifestPath := ""
+	deploymentPath := "../tests/dat/deployment_validate_credentials.yaml"
+	config, err := NewWhiskConfig(propPath, deploymentPath, manifestPath, false)
+	assert.Nil(t, err, "Failed to read credentials from deployment file")
+	assert.Equal(t, config.Host, DEPLOYMENT_HOST, "Failed to get host name from deployment file")
+	assert.Equal(t, config.AuthToken, DEPLOYMENT_AUTH, "Failed to get auth token from deployment file")
+	assert.Equal(t, config.Namespace, DEPLOYMENT_NAMESPACE, "Failed to get namespace from deployment file")
+}
+
+func TestNewWhiskConfigManifestFile(t *testing.T) {
+	propPath := ""
+	manifestPath := "../tests/dat/manifest_validate_credentials.yaml"
+	deploymentPath := ""
+	config, err := NewWhiskConfig(propPath, deploymentPath, manifestPath, false)
+	assert.Nil(t, err, "Failed to read credentials from manifest file")
+	assert.Equal(t, config.Host, MANIFEST_HOST, "Failed to get host name from manifest file")
+	assert.Equal(t, config.AuthToken, MANIFEST_AUTH, "Failed to get auth token from manifest file")
+	assert.Equal(t, config.Namespace, MANIFEST_NAMESPACE, "Failed to get namespace from manifest file")
+}
+
+
+func TestNewWhiskConfigWithWskProps(t *testing.T) {
+	propPath := "../tests/dat/wskprops"
+	manifestPath := ""
+	deploymentPath := ""
+	config, err := NewWhiskConfig(propPath, deploymentPath, manifestPath, false)
+	assert.Nil(t, err, "Failed to read credentials from wskprops")
+	assert.Equal(t, config.Host, WSKPROPS_HOST, "Failed to get host name from wskprops")
+	assert.Equal(t, config.AuthToken, WSKPROPS_AUTH, "Failed to get auth token from wskprops")
+	assert.Equal(t, config.Namespace, WSKPROPS_NAMESPACE, "Failed to get namespace from wskprops")
+}
+
+// (TODO) add the following test
+/*func TestNewWhiskConfigInteractiveMode(t *testing.T) {
+	propPath := ""
+	manifestPath := ""
+	deploymentPath := ""
+	config, err := NewWhiskConfig(propPath, deploymentPath, manifestPath, true)
+	assert.Nil(t, err, "Failed to read credentials in interactive mode")
+}*/
+
+func TestNewWhiskConfigWithCLIDeploymentAndManifestFile(t *testing.T) {
+	propPath := ""
+	manifestPath := "../tests/dat/manifest_validate_credentials.yaml"
+	deploymentPath := "../tests/dat/deployment_validate_credentials.yaml"
+
+	utils.Flags.ApiHost = CLI_HOST
+	utils.Flags.Auth = CLI_AUTH
+	utils.Flags.Namespace = CLI_NAMESPACE
+
+	config, err := NewWhiskConfig(propPath, deploymentPath, manifestPath, false)
+	assert.Nil(t, err, "Failed to read credentials from CLI or deployment or manifest file")
+	assert.Equal(t, config.Host, CLI_HOST, "Failed to get host name from wskdeploy CLI")
+	assert.Equal(t, config.AuthToken, CLI_AUTH, "Failed to get auth token from wskdeploy CLI")
+	assert.Equal(t, config.Namespace, CLI_NAMESPACE, "Failed to get namespace from wskdeploy CLI")
+
+	utils.Flags.Auth = ""
+	utils.Flags.Namespace = ""
+	utils.Flags.ApiHost = ""
+}
+
+
+func TestNewWhiskConfigWithCLIAndDeployment(t *testing.T) {
+	propPath := ""
+	manifestPath := "../tests/dat/deployment_validate_credentials.yaml"
+	deploymentPath := ""
+
+	utils.Flags.ApiHost = CLI_HOST
+	utils.Flags.Auth = CLI_AUTH
+	utils.Flags.Namespace = CLI_NAMESPACE
+
+	config, err := NewWhiskConfig(propPath, deploymentPath, manifestPath, false)
+	assert.Nil(t, err, "Failed to read credentials from wskdeploy CLI")
+	assert.Equal(t, config.Host, CLI_HOST, "Failed to get host name from wskdeploy CLI")
+	assert.Equal(t, config.AuthToken, CLI_AUTH, "Failed to get auth token from wskdeploy CLI")
+	assert.Equal(t, config.Namespace, CLI_NAMESPACE, "Failed to get namespace from wskdeploy CLI")
+
+	utils.Flags.Auth = ""
+	utils.Flags.Namespace = ""
+	utils.Flags.ApiHost = ""
+}
+
+func TestNewWhiskConfigWithCLIAndManifest(t *testing.T) {
+	propPath := ""
+	manifestPath := "../tests/dat/manifest_validate_credentials.yaml"
+	deploymentPath := ""
+
+	utils.Flags.ApiHost = CLI_HOST
+	utils.Flags.Auth = CLI_AUTH
+	utils.Flags.Namespace = CLI_NAMESPACE
+
+	config, err := NewWhiskConfig(propPath, deploymentPath, manifestPath, false)
+	assert.Nil(t, err, "Failed to read credentials from manifest file")
+	assert.Equal(t, config.Host, CLI_HOST, "Failed to get host name from wskdeploy CLI")
+	assert.Equal(t, config.AuthToken, CLI_AUTH, "Failed to get auth token from wskdeploy CLI")
+	assert.Equal(t, config.Namespace, CLI_NAMESPACE, "Failed to get namespace from wskdeploy CLI")
+
+	utils.Flags.Auth = ""
+	utils.Flags.Namespace = ""
+	utils.Flags.ApiHost = ""
+}
+
+func TestNewWhiskConfigWithCLIAndWskProps(t *testing.T) {
+	propPath := "../tests/dat/wskprops"
+	manifestPath := ""
+	deploymentPath := ""
+
+	utils.Flags.ApiHost = CLI_HOST
+	utils.Flags.Auth = CLI_AUTH
+	utils.Flags.Namespace = CLI_NAMESPACE
+	config, err := NewWhiskConfig(propPath, deploymentPath, manifestPath, false)
+	assert.Nil(t, err, "Failed to read credentials from wskdeploy command line")
+	assert.Equal(t, config.Host, CLI_HOST, "Failed to get host name from wskdeploy command line")
+	assert.Equal(t, config.AuthToken, CLI_AUTH, "Failed to get auth token from wskdeploy command line")
+	assert.Equal(t, config.Namespace, CLI_NAMESPACE, "Failed to get namespace from wskdeploy command line")
+
+	utils.Flags.Auth = ""
+	utils.Flags.Namespace = ""
+	utils.Flags.ApiHost = ""
+}
+
+func TestNewWhiskConfigWithDeploymentAndManifestFile(t *testing.T) {
+	propPath := ""
+	manifestPath := "../tests/dat/manifest_validate_credentials.yaml"
+	deploymentPath := "../tests/dat/deployment_validate_credentials.yaml"
+	config, err := NewWhiskConfig(propPath, deploymentPath, manifestPath, false)
+	assert.Nil(t, err, "Failed to read credentials from manifest or deployment file")
+	assert.Equal(t, config.Host, DEPLOYMENT_HOST, "Failed to get host name from deployment file")
+	assert.Equal(t, config.AuthToken, DEPLOYMENT_AUTH, "Failed to get auth token from deployment file")
+	assert.Equal(t, config.Namespace, DEPLOYMENT_NAMESPACE, "Failed to get namespace from deployment file")
+}
diff --git a/tests/dat/deployment_validate_credentials.yaml b/tests/dat/deployment_validate_credentials.yaml
new file mode 100644
index 0000000..6026572
--- /dev/null
+++ b/tests/dat/deployment_validate_credentials.yaml
@@ -0,0 +1,11 @@
+# do not change or delete this file without changing deployers/whiskclient_test.go
+# this is used for testing whiskclient functionality
+application:
+  name: UnitTestCredentials
+  apiHost: sample.deployment.openwhisk.org
+  credential: sample-deployment-credential
+  namespace: sample-deployment-namespace
+  packages:
+    ValidateWhiskConfig:
+      actions:
+        helloworld:
diff --git a/tests/dat/manifest_validate_credentials.yaml b/tests/dat/manifest_validate_credentials.yaml
new file mode 100644
index 0000000..f8d3ce4
--- /dev/null
+++ b/tests/dat/manifest_validate_credentials.yaml
@@ -0,0 +1,21 @@
+package:
+   name: ValidateWhiskConfig
+   apiHost: sample.manifest.openwhisk.org
+   credential: sample-manifest-credential
+   namespace: sample-manifest-namespace
+   actions:
+     # helloworld action in NodeJS
+     helloworld:
+       function: actions/hello.js
+       runtime: nodejs:6
+       inputs:
+         name:
+           type: string
+           description: name of a person
+         place:
+           type: string
+           description: location of a person
+       outputs:
+         payload:
+           type: string
+           description: a simple greeting message, Hello World!

-- 
To stop receiving notification emails like this one, please contact
['"commits@openwhisk.apache.org" <co...@openwhisk.apache.org>'].