openwhisk-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From mrutkow...@apache.org
Subject [incubator-openwhisk-wskdeploy] branch master updated: wskdeploy runtime overhaul (#669)
Date Fri, 15 Dec 2017 14:24:54 GMT
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 5c87981  wskdeploy runtime overhaul (#669)
5c87981 is described below

commit 5c8798190a72163bf63539edc72075767af8dcf3
Author: Priti Desai <pdesai@us.ibm.com>
AuthorDate: Fri Dec 15 06:24:51 2017 -0800

    wskdeploy runtime overhaul (#669)
    
    * wskdeploy runtime overhaul
    
    * fixing syntax error in wski18n
    
    * fixing syntax erro
    
    * fixing debug messages
    
    * wskdeploy runtime overhaul
    
    * fixing syntax error in wski18n
    
    * fixing syntax erro
    
    * fixing debug messages
    
    * Adding more tests
    
    * adding more tests
    
    * more tests
    
    * fixing build err
    
    * Adding detailed error message
---
 cmd/root.go                                        |   6 +-
 parsers/manifest_parser.go                         | 102 ++++--
 parsers/manifest_parser_test.go                    | 155 ++++++----
 tests/src/integration/common/wskdeploy.go          |   6 +-
 tests/src/integration/runtimetests/manifest.yaml   | 341 +++++++++++++--------
 .../runtimetests/src/helloworld/helloworld.zip     | Bin 0 -> 5764 bytes
 .../runtimetests/src/helloworld/index.js           |   8 +
 .../node_modules/string-format/README.md           | 192 ++++++++++++
 .../string-format/lib/string-format.js             | 104 +++++++
 .../node_modules/string-format/package.json        | 101 ++++++
 .../runtimetests/src/helloworld/package.json       |  14 +
 utils/misc.go                                      | 293 +++---------------
 utils/runtimes.go                                  | 293 ++++++++++++++++++
 wskderrors/wskdeployerror.go                       |  30 ++
 wski18n/i18n_resources.go                          |   5 +-
 wski18n/resources/en_US.all.json                   |  12 +-
 16 files changed, 1194 insertions(+), 468 deletions(-)

diff --git a/cmd/root.go b/cmd/root.go
index fd0642d..eab75d2 100644
--- a/cmd/root.go
+++ b/cmd/root.go
@@ -146,9 +146,9 @@ func initConfig() {
 func setSupportedRuntimes(apiHost string) {
 	op, error := utils.ParseOpenWhisk(apiHost)
 	if error == nil {
-		utils.Rts = utils.ConvertToMap(op)
-	} else {
-		utils.Rts = utils.DefaultRts
+		utils.SupportedRunTimes = utils.ConvertToMap(op)
+		utils.DefaultRunTimes = utils.DefaultRuntimes(op)
+		utils.FileExtensionRuntimeKindMap = utils.FileExtensionRuntimes(op)
 	}
 }
 
diff --git a/parsers/manifest_parser.go b/parsers/manifest_parser.go
index 4a651c5..a7497b0 100644
--- a/parsers/manifest_parser.go
+++ b/parsers/manifest_parser.go
@@ -398,12 +398,13 @@ func (dm *YAMLParser) ComposeActionsFromAllPackages(manifest *YAML, filePath str
 
 func (dm *YAMLParser) ComposeActions(filePath string, actions map[string]Action, packageName string, ma whisk.KeyValue) ([]utils.ActionRecord, error) {
 
+	const RUNTIME_ERR_MESSAGE = "Please specify any of the supported runtime for zip actions in manifest YAML."
 	var errorParser error
+	var ext string
 	var s1 []utils.ActionRecord = make([]utils.ActionRecord, 0)
 
 	for key, action := range actions {
 		splitFilePath := strings.Split(filePath, string(os.PathSeparator))
-
 		// set the name of the action (which is the key)
 		action.Name = key
 
@@ -422,6 +423,7 @@ func (dm *YAMLParser) ComposeActions(filePath string, actions map[string]Action,
 
 		//bind action, and exposed URL
 		if action.Function != "" {
+
 			filePath := strings.TrimRight(filePath, splitFilePath[len(splitFilePath)-1]) + action.Function
 
 			if utils.IsDirectory(filePath) {
@@ -433,28 +435,27 @@ func (dm *YAMLParser) ComposeActions(filePath string, actions map[string]Action,
 				defer os.Remove(zipName)
 				// To do: support docker and main entry as did by go cli?
 				wskaction.Exec, err = utils.GetExec(zipName, action.Runtime, false, "")
+				if err != nil {
+					return nil, err
+				}
 			} else {
-				ext := path.Ext(filePath)
-				var kind string
+				ext = path.Ext(filePath)
+				// drop the "." from file extension
+				if len(ext) > 0 && ext[0] == '.' {
+					ext = ext[1:]
+				}
 
-				switch ext {
-				case ".swift":
-					kind = "swift:3.1.1"
-				case ".js":
-					kind = "nodejs:6"
-				case ".py":
-					kind = "python"
-				case ".java":
-					kind = "java"
-				case ".php":
-					kind = "php:7.1"
-				case ".jar":
-					kind = "java"
-				default:
-					kind = "nodejs:6"
-					errStr := wski18n.T("Unsupported runtime type, set to nodejs")
-					whisk.Debug(whisk.DbgWarn, errStr)
-				// TODO() add the user input kind here if interactive
+				// determine default runtime for the given file extension
+				var kind string
+				r := utils.FileExtensionRuntimeKindMap[ext]
+				kind = utils.DefaultRunTimes[r]
+
+				// produce an error when a runtime could not be derived from the action file extension
+				// and its not explicitly specified in the manifest YAML file
+				// and action source is not a zip file
+				if len(kind) == 0 && len(action.Runtime) == 0 && ext != utils.ZIP_FILE_EXTENSION {
+					errMessage := "ERROR: Failed to discover runtime from the action source files. " + RUNTIME_ERR_MESSAGE
+					return nil, wskderrors.NewInvalidRuntimeError(errMessage, splitFilePath[len(splitFilePath)-1], action.Name, "Not Specified in Manifest YAML", utils.ListOfSupportedRuntimes(utils.SupportedRunTimes))
 				}
 
 				wskaction.Exec.Kind = kind
@@ -465,11 +466,12 @@ func (dm *YAMLParser) ComposeActions(filePath string, actions map[string]Action,
 					return s1, err
 				}
 				code := string(dat)
-				if ext == ".zip" || ext == ".jar" {
+				if ext == utils.ZIP_FILE_EXTENSION || ext == utils.JAR_FILE_EXTENSION {
 					code = base64.StdEncoding.EncodeToString([]byte(dat))
 				}
-				if ext == ".zip" && action.Runtime == "" {
-					utils.PrintOpenWhiskOutputln("need explicit action Runtime value")
+				if ext == utils.ZIP_FILE_EXTENSION && len(action.Runtime) == 0 {
+					errMessage := "ERROR: Runtime is missing for zip action. " + RUNTIME_ERR_MESSAGE
+					return nil, wskderrors.NewInvalidRuntimeError(errMessage, splitFilePath[len(splitFilePath)-1], action.Name, "Not Specified in Manifest YAML", utils.ListOfSupportedRuntimes(utils.SupportedRunTimes))
 				}
 				wskaction.Exec.Code = &code
 			}
@@ -478,16 +480,56 @@ func (dm *YAMLParser) ComposeActions(filePath string, actions map[string]Action,
 
 		/*
  		 *  Action.Runtime
+		 *  Perform few checks if action runtime is specified in manifest YAML file
+		 *  (1) Check if specified runtime is one of the supported runtimes by OpenWhisk server
+		 *  (2) Check if specified runtime is consistent with action source file extensions
+		 *  Set the action runtime to match with the source file extension, if wskdeploy is not invoked in strict mode
  		 */
 		if action.Runtime != "" {
-			if utils.CheckExistRuntime(action.Runtime, utils.Rts) {
-				wskaction.Exec.Kind = action.Runtime
-
-			} else if utils.Flags.Strict {
-				wskaction.Exec.Kind = action.Runtime
+			if utils.CheckExistRuntime(action.Runtime, utils.SupportedRunTimes) {
+				// for zip actions, rely on the runtimes from the manifest file as it can not be derived from the action source file extension
+				// pick runtime from manifest file if its supported by OpenWhisk server
+				if ext == utils.ZIP_FILE_EXTENSION {
+					wskaction.Exec.Kind = action.Runtime
+				} else {
+					if utils.CheckRuntimeConsistencyWithFileExtension(ext, action.Runtime) {
+						wskaction.Exec.Kind = action.Runtime
+					} else {
+						errStr := wski18n.T("WARNING: Runtime specified in manifest " +
+							"YAML {{.runtime}} does not match with action source " +
+							"file extension {{.ext}} for action {{.action}}.\n",
+							map[string]interface{}{"runtime": action.Runtime, "ext": ext, "action": action.Name})
+						whisk.Debug(whisk.DbgWarn, errStr)
+						// even if runtime is not consistent with file extension, deploy action with specified runtime in strict mode
+						if utils.Flags.Strict {
+							wskaction.Exec.Kind = action.Runtime
+						} else {
+							errStr := wski18n.T("WARNING: Whisk Deploy has chosen appropriate " +
+								"runtime {{.runtime}} based on the action source file " +
+								"extension for that action {{.action}}.\n",
+								map[string]interface{}{"runtime": wskaction.Exec.Kind, "action": action.Name})
+							whisk.Debug(whisk.DbgWarn, errStr)
+						}
+					}
+				}
 			} else {
-				errStr := wski18n.T("wskdeploy has chosen a particular runtime for the action.\n")
+				errStr := wski18n.T("WARNING: Runtime specified in manifest " +
+					"YAML {{.runtime}} is not supported by OpenWhisk server " +
+					"for the action {{.action}}.\n",
+					map[string]interface{}{"runtime": action.Runtime, "action": action.Name})
 				whisk.Debug(whisk.DbgWarn, errStr)
+				if ext == utils.ZIP_FILE_EXTENSION {
+					// for zip action, error out if specified runtime is not supported by OpenWhisk server
+					errMessage := "ERROR: Given runtime for a zip action is not supported by OpenWhisk server. " + RUNTIME_ERR_MESSAGE
+					return nil, wskderrors.NewInvalidRuntimeError(errMessage, splitFilePath[len(splitFilePath)-1], action.Name, action.Runtime, utils.ListOfSupportedRuntimes(utils.SupportedRunTimes))
+				} else {
+					errStr = wski18n.T("WARNING: Whisk Deploy has chosen appropriate " +
+						"runtime {{.runtime}} based on the action source file " +
+						"extension for that action {{.action}}.\n",
+						map[string]interface{}{"runtime": wskaction.Exec.Kind, "action": action.Name})
+					whisk.Debug(whisk.DbgWarn, errStr)
+				}
+
 			}
 		}
 
diff --git a/parsers/manifest_parser_test.go b/parsers/manifest_parser_test.go
index 54d0a0d..9731630 100644
--- a/parsers/manifest_parser_test.go
+++ b/parsers/manifest_parser_test.go
@@ -31,6 +31,7 @@ import (
     "strings"
     "github.com/apache/incubator-openwhisk-client-go/whisk"
     "github.com/apache/incubator-openwhisk-wskdeploy/wskderrors"
+    "github.com/apache/incubator-openwhisk-wskdeploy/utils"
 )
 
 const (
@@ -46,6 +47,7 @@ const (
     TEST_MSG_ACTION_PARAMETER_VALUE_MISMATCH = "Action parameter [%s] had a value mismatch."
     TEST_MSG_PARAMETER_NUMBER_MISMATCH = "Number of Paramaters mismatched."
     TEST_MSG_MANIFEST_UNMARSHALL_ERROR_EXPECTED = "Manifest [%s]: Expected Unmarshal error."
+    TEST_MSG_ACTION_FUNCTION_RUNTIME_ERROR_EXPECTED = "Manifest [%s]: Expected runtime error."
 
     // local error messages
     TEST_ERROR_MANIFEST_PARSE_FAILURE = "Manifest [%s]: Failed to parse."
@@ -53,6 +55,14 @@ const (
     TEST_ERROR_MANIFEST_DATA_UNMARSHALL = "Manifest [%s]: Failed to Unmarshall manifest."
 )
 
+func init() {
+    op, error := utils.ParseOpenWhisk("")
+    if error == nil {
+        utils.SupportedRunTimes = utils.ConvertToMap(op)
+        utils.DefaultRunTimes = utils.DefaultRuntimes(op)
+        utils.FileExtensionRuntimeKindMap = utils.FileExtensionRuntimes(op)
+    }
+}
 
 func testReadAndUnmarshalManifest(t *testing.T, pathManifest string)(YAML, error){
     // Init YAML struct and attempt to Unmarshal YAML byte[] data
@@ -133,6 +143,22 @@ func testUnmarshalManifestAndActionBasic(t *testing.T,
     return m, nil
 }
 
+func testUnmarshalTemporaryFile (data []byte, filename string) (p *YAMLParser, m *YAML, t string) {
+    dir, _ := os.Getwd()
+    tmpfile, err := ioutil.TempFile(dir, filename)
+    if err == nil {
+        defer os.Remove(tmpfile.Name()) // clean up
+        if _, err := tmpfile.Write(data); err == nil {
+            // read and parse manifest.yaml file
+            p = NewYAMLParser()
+            m, _ = p.ParseManifest(tmpfile.Name())
+        }
+    }
+    t = tmpfile.Name()
+    tmpfile.Close()
+    return
+}
+
 // Test 1: validate manifest_parser:Unmarshal() method with a sample manifest in NodeJS
 // validate that manifest_parser is able to read and parse the manifest data
 func TestUnmarshalForHelloNodeJS(t *testing.T) {
@@ -478,66 +504,87 @@ func TestComposeActionsForImplicitRuntimes(t *testing.T) {
       function: ../tests/src/integration/helloworld/actions/hello.py
     helloSwift:
       function: ../tests/src/integration/helloworld/actions/hello.swift`
-
-    dir, _ := os.Getwd()
-    tmpfile, err := ioutil.TempFile(dir, "manifest_parser_validate_runtimes_")
+    p, m, tmpfile := testUnmarshalTemporaryFile([]byte(data), "manifest_parser_validate_runtime_")
+    actions, err := p.ComposeActionsFromAllPackages(m, tmpfile, whisk.KeyValue{})
+    var expectedResult string
     if err == nil {
-        defer os.Remove(tmpfile.Name()) // clean up
-        if _, err := tmpfile.Write([]byte(data)); err == nil {
-            // read and parse manifest.yaml file
-            p := NewYAMLParser()
-            m, _ := p.ParseManifest(tmpfile.Name())
-            actions, err := p.ComposeActionsFromAllPackages(m, tmpfile.Name(), whisk.KeyValue{})
-            var expectedResult string
-            if err == nil {
-                for i := 0; i < len(actions); i++ {
-                    if actions[i].Action.Name == "helloNodejs" {
-                        expectedResult = "nodejs:6"
-                    } else if actions[i].Action.Name == "helloJava" {
-                        expectedResult = "java"
-                    } else if actions[i].Action.Name == "helloPython" {
-                        expectedResult = "python"
-                    } else if actions[i].Action.Name == "helloSwift" {
-                        expectedResult = "swift:3.1.1"
-                    }
-                    actualResult := actions[i].Action.Exec.Kind
-                    assert.Equal(t, expectedResult, actualResult, TEST_MSG_ACTION_FUNCTION_RUNTIME_MISMATCH)
-                }
-            }
-
-        }
-        tmpfile.Close()
+	for i := 0; i < len(actions); i++ {
+	    if actions[i].Action.Name == "helloNodejs" {
+		expectedResult = utils.DefaultRunTimes[utils.FileExtensionRuntimeKindMap["js"]]
+	    } else if actions[i].Action.Name == "helloJava" {
+		expectedResult = utils.DefaultRunTimes[utils.FileExtensionRuntimeKindMap["jar"]]
+	    } else if actions[i].Action.Name == "helloPython" {
+		expectedResult = utils.DefaultRunTimes[utils.FileExtensionRuntimeKindMap["py"]]
+	    } else if actions[i].Action.Name == "helloSwift" {
+		expectedResult = utils.DefaultRunTimes[utils.FileExtensionRuntimeKindMap["swift"]]
+	    }
+	    actualResult := actions[i].Action.Exec.Kind
+	    assert.Equal(t, expectedResult, actualResult, TEST_MSG_ACTION_FUNCTION_RUNTIME_MISMATCH)
+	}
     }
+
 }
 
-// Test 10: validate manifest_parser.ComposeActions() method for invalid runtimes
-// when a runtime of an action is set to some garbage, manifest_parser should
+
+// Test 10(1): validate manifest_parser.ComposeActions() method for invalid runtimes
+// when the action has a source file written in unsupported runtimes, manifest_parser should
 // report an error for that action
-func TestComposeActionsForInvalidRuntime(t *testing.T) {
-    data :=
-        `package:
-   name: helloworld
-   actions:
-     helloInvalidRuntime:
-       function: ../tests/src/integration/helloworld/actions/hello.js
-       runtime: invalid`
-    dir, _ := os.Getwd()
-    tmpfile, err := ioutil.TempFile(dir, "manifest_parser_validate_runtime_")
-    if err == nil {
-        defer os.Remove(tmpfile.Name()) // clean up
-        if _, err := tmpfile.Write([]byte(data)); err == nil {
-            // read and parse manifest.yaml file
-            p := NewYAMLParser()
-            m, _ := p.ParseManifest(tmpfile.Name())
-            _, err := p.ComposeActionsFromAllPackages(m, tmpfile.Name(), whisk.KeyValue{})
-            // (TODO) uncomment the following test case after issue #307 is fixed
-            // (TODO) its failing right now as we are lacking check on invalid runtime
-            // TODO() https://github.com/apache/incubator-openwhisk-wskdeploy/issues/608
-            //assert.NotNil(t, err, "Invalid runtime, ComposeActions should report an error")
-            // (TODO) remove this print statement after uncommenting above test case
-            fmt.Println(fmt.Sprintf("!!! TODO(): Fix this testcase: error=[%v]", err))
+func TestComposeActionsForInvalidRuntime_1(t *testing.T) {
+    data := `packages:
+    helloworld:
+        actions:
+            helloInvalidRuntime:
+                function: ../tests/src/integration/common/wskdeploy.go`
+    p, m, tmpfile := testUnmarshalTemporaryFile([]byte(data), "manifest_parser_validate_runtime_")
+    _, err := p.ComposeActionsFromAllPackages(m, tmpfile, whisk.KeyValue{})
+    assert.NotNil(t, err, fmt.Sprintf(TEST_MSG_ACTION_FUNCTION_RUNTIME_ERROR_EXPECTED, tmpfile))
+}
+
+// Test 10(2): validate manifest_parser.ComposeActions() method for invalid runtimes
+// when a runtime of an action is missing for zip action, manifest_parser should
+// report an error for that action
+func TestComposeActionsForInvalidRuntime_2(t *testing.T) {
+    data := `packages:
+    helloworld:
+        actions:
+            helloInvalidRuntime:
+                function: ../tests/src/integration/runtimetests/src/helloworld/`
+    p, m, tmpfile := testUnmarshalTemporaryFile([]byte(data), "manifest_parser_validate_runtime_")
+    _, err := p.ComposeActionsFromAllPackages(m, tmpfile, whisk.KeyValue{})
+    assert.NotNil(t, err, fmt.Sprintf(TEST_MSG_ACTION_FUNCTION_RUNTIME_ERROR_EXPECTED, tmpfile))
+}
+
+// Test 10(3): validate manifest_parser.ComposeActions() method for invalid runtimes
+// when a runtime of an action is missing for zip action, manifest_parser should
+// report an error for that action
+func TestComposeActionsForInvalidRuntime_3(t *testing.T) {
+    data := `packages:
+    helloworld:
+        actions:
+            helloInvalidRuntime:
+                function: ../tests/src/integration/runtimetests/src/helloworld/helloworld.zip`
+    p, m, tmpfile := testUnmarshalTemporaryFile([]byte(data), "manifest_parser_validate_runtime_")
+    _, err := p.ComposeActionsFromAllPackages(m, tmpfile, whisk.KeyValue{})
+    assert.NotNil(t, err, fmt.Sprintf(TEST_MSG_ACTION_FUNCTION_RUNTIME_ERROR_EXPECTED, tmpfile))
+}
+
+// Test 10(3): validate manifest_parser.ComposeActions() method for valid runtimes with zip action
+// when a runtime of a zip action is set to one of the supported runtimes, manifest_parser should
+// return a valid actionRecord with specified runtime
+func TestComposeActionsForValidRuntime_ZipAction(t *testing.T) {
+    data := `packages:
+    helloworld:
+        actions:
+            hello:
+                function: ../tests/src/integration/runtimetests/src/helloworld/helloworld.zip
+                runtime: nodejs:6`
+    p, m, tmpfile := testUnmarshalTemporaryFile([]byte(data), "manifest_parser_validate_runtime_")
+    actions, _ := p.ComposeActionsFromAllPackages(m, tmpfile, whisk.KeyValue{})
+    for _, action := range actions {
+        if action.Action.Name == "hello" {
+            assert.Equal(t, action.Action.Exec.Kind, "nodejs:6", fmt.Sprintf(TEST_MSG_ACTION_FUNCTION_RUNTIME_MISMATCH, action))
         }
-        tmpfile.Close()
+
     }
 }
 
diff --git a/tests/src/integration/common/wskdeploy.go b/tests/src/integration/common/wskdeploy.go
index 7f9651b..7551c27 100644
--- a/tests/src/integration/common/wskdeploy.go
+++ b/tests/src/integration/common/wskdeploy.go
@@ -197,9 +197,9 @@ func (wskdeploy *Wskdeploy) GetDeploymentObjects(manifestPath string, deployment
 	//only for testing, mock values
 	op, err := utils.ParseOpenWhisk(clientConfig.Host)
 	if err == nil {
-		utils.Rts = utils.ConvertToMap(op)
-	} else {
-		utils.Rts = utils.DefaultRts
+		utils.SupportedRunTimes = utils.ConvertToMap(op)
+		utils.DefaultRunTimes = utils.DefaultRuntimes(op)
+		utils.FileExtensionRuntimeKindMap = utils.FileExtensionRuntimes(op)
 	}
 
 	//invoke ConstructDeploymentPlan to create the in memory objects for deployment
diff --git a/tests/src/integration/runtimetests/manifest.yaml b/tests/src/integration/runtimetests/manifest.yaml
index 922e7c4..3a8d12c 100644
--- a/tests/src/integration/runtimetests/manifest.yaml
+++ b/tests/src/integration/runtimetests/manifest.yaml
@@ -1,125 +1,218 @@
 packages:
-  TestExplicitRuntimes:
-      version: 1.0
-      license: Apache-2.0
-      actions:
-        greetingnodejs-with-explicit-runtime:
-          web-export: true
-          version: 1.0
-          function: src/greeting.js
-          runtime: nodejs:6
-          inputs:
-            name: string
-            place: string
-          outputs:
-            payload: string
-        greetingnodejs-without-explicit-runtime:
-          web-export: true
-          version: 1.0
-          function: src/greeting.js
-          inputs:
-            name: string
-            place: string
-          outputs:
-            payload: string
-        greetingphp-with-explicit-runtime:
-          web-export: true
-          version: 1.0
-          function: src/hello.php
-          runtime: php:7.1
-          inputs:
-            name: string
-            place: string
-          outputs:
-            payload: string
-        greetingphp-without-explicit-runtime:
-          web-export: true
-          version: 1.0
-          function: src/hello.php
-          inputs:
-            name: string
-            place: string
-          outputs:
-            payload: string
-        greetingpython-with-explicit-runtime:
-          web-export: true
-          version: 1.0
-          function: src/hello.py
-          runtime: python
-          inputs:
-            name: string
-            place: string
-          outputs:
-            payload: string
-        greetingpython-without-explicit-runtime:
-          web-export: true
-          version: 1.0
-          function: src/hello.py
-          inputs:
-            name: string
-            place: string
-          outputs:
-            payload: string
-        greetingpython2-with-explicit-runtime:
-          web-export: true
-          version: 1.0
-          function: src/hello.py
-          runtime: python:2
-          inputs:
-            name: string
-            place: string
-          outputs:
-            payload: string
-        greetingpython3-with-explicit-runtime:
-          web-export: true
-          version: 1.0
-          function: src/hello.py
-          runtime: python:3
-          inputs:
-            name: string
-            place: string
-          outputs:
-            payload: string
-        greetingswift311-with-explicit-runtime:
-          web-export: true
-          version: 1.0
-          function: src/hello.swift
-          runtime: swift:3.1.1
-          inputs:
-            name: string
-            place: string
-          outputs:
-            payload: string
-        greetingswift3-with-explicit-runtime:
-          web-export: true
-          version: 1.0
-          function: src/hello.swift
-          runtime: swift:3.1.1
-          inputs:
-            name: string
-            place: string
-          outputs:
-            payload: string
-        greetingswift-without-explicit-runtime:
-          web-export: true
-          version: 1.0
-          function: src/hello.swift
-          inputs:
-            name: string
-            place: string
-          outputs:
-            payload: string
-        helloworldjava-with-explicit-runtime:
-          function: src/hello.jar
-          runtime: java
-          main: Hello
-        helloworldjava-without-explicit-runtime:
-          function: src/hello.jar
-          main: Hello
-      triggers:
-        locationUpdateRuntime:
-      rules:
-        myRuleRuntime:
-          trigger: locationUpdateRuntime
-          action:  greetingnodejs-with-explicit-runtime
-
+    TestExplicitRuntimes:
+        version: 1.0
+        license: Apache-1.0
+        actions:
+            greetingnodejs6-with-explicit-runtime:
+                web-export: true
+                version: 1.0
+                function: src/greeting.js
+                runtime: nodejs:6
+                inputs:
+                    name: string
+                    place: string
+                outputs:
+                    payload: string
+            greetingnodejs8-with-explicit-runtime:
+                web-export: true
+                version: 1.0
+                function: src/greeting.js
+                runtime: nodejs:8
+                inputs:
+                    name: string
+                    place: string
+                outputs:
+                    payload: string
+            greetingphp-with-explicit-runtime:
+                web-export: true
+                version: 1.0
+                function: src/hello.php
+                runtime: php:7.1
+                inputs:
+                    name: string
+                    place: string
+                outputs:
+                    payload: string
+            greetingpython-with-explicit-runtime:
+                web-export: true
+                version: 1.0
+                function: src/hello.py
+                runtime: python
+                inputs:
+                    name: string
+                    place: string
+                outputs:
+                    payload: string
+            greetingpython2-with-explicit-runtime:
+                web-export: true
+                version: 1.0
+                function: src/hello.py
+                runtime: python:2
+                inputs:
+                    name: string
+                    place: string
+                outputs:
+                    payload: string
+            greetingpython3-with-explicit-runtime:
+                web-export: true
+                version: 1.0
+                function: src/hello.py
+                runtime: python:3
+                inputs:
+                    name: string
+                    place: string
+                outputs:
+                    payload: string
+            greetingswift311-with-explicit-runtime:
+                web-export: true
+                version: 1.0
+                function: src/hello.swift
+                runtime: swift:3.1.1
+                inputs:
+                    name: string
+                    place: string
+                outputs:
+                    payload: string
+            helloworldjava-with-explicit-runtime:
+                function: src/hello.jar
+                runtime: java
+                main: Hello
+            zipaction-with-explicit-runtime:
+                function: src/helloworld
+                runtime: nodejs:6
+                inputs:
+                    name: string
+                    place: string
+                outputs:
+                    payload: string
+    TestImplicitRuntimes:
+        actions:
+            greetingnodejs-without-explicit-runtime:
+                web-export: true
+                version: 1.0
+                function: src/greeting.js
+                inputs:
+                    name: string
+                    place: string
+                outputs:
+                    payload: string
+            greetingphp-without-explicit-runtime:
+                web-export: true
+                version: 1.0
+                function: src/hello.php
+                inputs:
+                    name: string
+                    place: string
+                outputs:
+                    payload: string
+            greetingpython-without-explicit-runtime:
+                web-export: true
+                version: 1.0
+                function: src/hello.py
+                inputs:
+                    name: string
+                    place: string
+                outputs:
+                    payload: string
+            greetingswift-without-explicit-runtime:
+                web-export: true
+                version: 1.0
+                function: src/hello.swift
+                inputs:
+                    name: string
+                    place: string
+                outputs:
+                    payload: string
+            helloworldjava-without-explicit-runtime:
+                function: src/hello.jar
+                main: Hello
+    TestInvalidExplicitRuntimes:
+        actions:
+            greetingnodejs-with-java-explicit-runtime:
+                web-export: true
+                version: 1.0
+                function: src/greeting.js
+                runtime: java
+                inputs:
+                    name: string
+                    place: string
+                outputs:
+                    payload: string
+            greetingnodejs-with-random-explicit-runtime:
+                web-export: true
+                version: 1.0
+                function: src/greeting.js
+                runtime: random
+                inputs:
+                    name: string
+                    place: string
+                outputs:
+                    payload: string
+            greetingphp-with-nodejs-explicit-runtime:
+                web-export: true
+                version: 1.0
+                function: src/hello.php
+                runtime: nodejs:6
+                inputs:
+                    name: string
+                    place: string
+                outputs:
+                    payload: string
+            greetingphp-with-random-explicit-runtime:
+                web-export: true
+                version: 1.0
+                function: src/hello.php
+                runtime: random
+                inputs:
+                    name: string
+                    place: string
+                outputs:
+                    payload: string
+            greetingpython-with-php-explicit-runtime:
+                web-export: true
+                version: 1.0
+                function: src/hello.py
+                runtime: php
+                inputs:
+                    name: string
+                    place: string
+                outputs:
+                    payload: string
+            greetingpython-with-random-explicit-runtime:
+                web-export: true
+                version: 1.0
+                function: src/hello.py
+                runtime: random
+                inputs:
+                    name: string
+                    place: string
+                outputs:
+                    payload: string
+            greetingswift-with-nodejs-explicit-runtime:
+                web-export: true
+                version: 1.0
+                function: src/hello.swift
+                runtime: nodejs:6
+                inputs:
+                    name: string
+                    place: string
+                outputs:
+                    payload: string
+            greetingswift-with-random-explicit-runtime:
+                web-export: true
+                version: 1.0
+                function: src/hello.swift
+                runtime: random
+                inputs:
+                    name: string
+                    place: string
+                outputs:
+                    payload: string
+            helloworldjava-with-swift-explicit-runtime:
+                function: src/hello.jar
+                main: Hello
+                runtime: swift:3.1.1
+            helloworldjava-with-random-explicit-runtime:
+                function: src/hello.jar
+                main: Hello
+                runtime: random
diff --git a/tests/src/integration/runtimetests/src/helloworld/helloworld.zip b/tests/src/integration/runtimetests/src/helloworld/helloworld.zip
new file mode 100644
index 0000000..190f0ef
Binary files /dev/null and b/tests/src/integration/runtimetests/src/helloworld/helloworld.zip differ
diff --git a/tests/src/integration/runtimetests/src/helloworld/index.js b/tests/src/integration/runtimetests/src/helloworld/index.js
new file mode 100644
index 0000000..7e2dffb
--- /dev/null
+++ b/tests/src/integration/runtimetests/src/helloworld/index.js
@@ -0,0 +1,8 @@
+function helloworld(params) {
+    var format = require('string-format');
+    var name = params.name || 'Stranger';
+    payload = format('Hello, {}!', name)
+    return { message: payload };
+}
+
+exports.main = helloworld;
diff --git a/tests/src/integration/runtimetests/src/helloworld/node_modules/string-format/README.md b/tests/src/integration/runtimetests/src/helloworld/node_modules/string-format/README.md
new file mode 100644
index 0000000..8f5aeb5
--- /dev/null
+++ b/tests/src/integration/runtimetests/src/helloworld/node_modules/string-format/README.md
@@ -0,0 +1,192 @@
+# String::format
+
+String::format is a small JavaScript library for formatting strings, based on
+Python's [`str.format()`][1]. For example:
+
+```javascript
+'"{firstName} {lastName}" <{email}>'.format(user)
+// => '"Jane Smith" <jsmith@example.com>'
+```
+
+The equivalent concatenation:
+
+```javascript
+'"' + user.firstName + ' ' + user.lastName + '" <' + user.email + '>'
+// => '"Jane Smith" <jsmith@example.com>'
+```
+
+### Installation
+
+#### Node
+
+1.  Install:
+
+        $ npm install string-format
+
+2.  Require:
+
+        var format = require('string-format')
+
+#### Browser
+
+1.  Define `window.format`:
+
+        <script src="path/to/string-format.js"></script>
+
+### Modes
+
+String::format can be used in two modes: [function mode](#function-mode) and
+[method mode](#method-mode).
+
+#### Function mode
+
+```javascript
+format('Hello, {}!', 'Alice')
+// => 'Hello, Alice!'
+```
+
+In this mode the first argument is a template string and the remaining
+arguments are values to be interpolated.
+
+#### Method mode
+
+```javascript
+'Hello, {}!'.format('Alice')
+// => 'Hello, Alice!'
+```
+
+In this mode values to be interpolated are supplied to the `format` method
+of a template string. This mode is not enabled by default. The method must
+first be defined via [`format.extend`](#formatextendprototype-transformers):
+
+```javascript
+format.extend(String.prototype)
+```
+
+`format(template, $0, $1, …, $N)` and `template.format($0, $1, …, $N)` can then
+be used interchangeably.
+
+### `format(template, $0, $1, …, $N)`
+
+Returns the result of replacing each `{…}` placeholder in the template string
+with its corresponding replacement.
+
+Placeholders may contain numbers which refer to positional arguments:
+
+```javascript
+'{0}, you have {1} unread message{2}'.format('Holly', 2, 's')
+// => 'Holly, you have 2 unread messages'
+```
+
+Unmatched placeholders produce no output:
+
+```javascript
+'{0}, you have {1} unread message{2}'.format('Steve', 1)
+// => 'Steve, you have 1 unread message'
+```
+
+A format string may reference a positional argument multiple times:
+
+```javascript
+"The name's {1}. {0} {1}.".format('James', 'Bond')
+// => "The name's Bond. James Bond."
+```
+
+Positional arguments may be referenced implicitly:
+
+```javascript
+'{}, you have {} unread message{}'.format('Steve', 1)
+// => 'Steve, you have 1 unread message'
+```
+
+A format string must not contain both implicit and explicit references:
+
+```javascript
+'My name is {} {}. Do you like the name {0}?'.format('Lemony', 'Snicket')
+// => ValueError: cannot switch from implicit to explicit numbering
+```
+
+`{{` and `}}` in format strings produce `{` and `}`:
+
+```javascript
+'{{}} creates an empty {} in {}'.format('dictionary', 'Python')
+// => '{} creates an empty dictionary in Python'
+```
+
+Dot notation may be used to reference object properties:
+
+```javascript
+var bobby = {firstName: 'Bobby', lastName: 'Fischer'}
+var garry = {firstName: 'Garry', lastName: 'Kasparov'}
+
+'{0.firstName} {0.lastName} vs. {1.firstName} {1.lastName}'.format(bobby, garry)
+// => 'Bobby Fischer vs. Garry Kasparov'
+```
+
+`0.` may be omitted when referencing a property of `{0}`:
+
+```javascript
+var repo = {owner: 'davidchambers', slug: 'string-format'}
+
+'https://github.com/{owner}/{slug}'.format(repo)
+// => 'https://github.com/davidchambers/string-format'
+```
+
+If the referenced property is a method, it is invoked with no arguments to
+determine the replacement:
+
+```javascript
+var sheldon = {
+  firstName:  'Sheldon',
+  lastName:   'Cooper',
+  dob:        new Date('1970-01-01'),
+  fullName:   function() { return '{firstName} {lastName}'.format(this) },
+  quip:       function() { return 'Bazinga!' }
+}
+
+'{fullName} was born at precisely {dob.toISOString}'.format(sheldon)
+// => 'Sheldon Cooper was born at precisely 1970-01-01T00:00:00.000Z'
+
+"I've always wanted to go to a goth club. {quip.toUpperCase}".format(sheldon)
+// => "I've always wanted to go to a goth club. BAZINGA!"
+```
+
+### `format.extend(prototype[, transformers])`
+
+This function defines a `format` method on the provided prototype (presumably
+`String.prototype`). One may provide an object mapping names to transformers.
+A transformer is applied if its name appears, prefixed with `!`, after a field
+name in a template string.
+
+```javascript
+format.extend(String.prototype, {
+  escape: function(s) {
+    return s.replace(/[&<>"'`]/g, function(c) {
+      return '&#' + c.charCodeAt(0) + ';'
+    })
+  },
+  upper: function(s) { return s.toUpperCase() }
+})
+
+'Hello, {!upper}!'.format('Alice')
+// => 'Hello, ALICE!'
+
+var restaurant = {
+  name: 'Anchor & Hope',
+  url: 'http://anchorandhopesf.com/'
+}
+
+'<a href="{url!escape}">{name!escape}</a>'.format(restaurant)
+// => '<a href="http://anchorandhopesf.com/">Anchor &#38; Hope</a>'
+```
+
+### Running the test suite
+
+```console
+$ npm install
+$ npm test
+```
+
+
+[1]: http://docs.python.org/library/stdtypes.html#str.format
+[2]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/bind
diff --git a/tests/src/integration/runtimetests/src/helloworld/node_modules/string-format/lib/string-format.js b/tests/src/integration/runtimetests/src/helloworld/node_modules/string-format/lib/string-format.js
new file mode 100644
index 0000000..e15b77b
--- /dev/null
+++ b/tests/src/integration/runtimetests/src/helloworld/node_modules/string-format/lib/string-format.js
@@ -0,0 +1,104 @@
+// Generated by CoffeeScript 1.8.0
+(function() {
+  var ValueError, create, explicitToImplicit, format, implicitToExplicit, lookup, resolve,
+    __hasProp = {}.hasOwnProperty,
+    __extends = function(child, parent) { for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; },
+    __slice = [].slice;
+
+  ValueError = (function(_super) {
+    __extends(ValueError, _super);
+
+    function ValueError(message) {
+      this.message = message;
+    }
+
+    ValueError.prototype.name = 'ValueError';
+
+    return ValueError;
+
+  })(Error);
+
+  implicitToExplicit = 'cannot switch from implicit to explicit numbering';
+
+  explicitToImplicit = 'cannot switch from explicit to implicit numbering';
+
+  create = function(transformers) {
+    if (transformers == null) {
+      transformers = {};
+    }
+    return function() {
+      var args, explicit, idx, implicit, message, template;
+      template = arguments[0], args = 2 <= arguments.length ? __slice.call(arguments, 1) : [];
+      idx = 0;
+      explicit = implicit = false;
+      message = 'cannot switch from {} to {} numbering';
+      return template.replace(/([{}])\1|[{](.*?)(?:!(.+?))?[}]/g, function(match, literal, key, transformer) {
+        var value, _ref, _ref1;
+        if (literal) {
+          return literal;
+        }
+        if (key.length) {
+          if (implicit) {
+            throw new ValueError(implicitToExplicit);
+          }
+          explicit = true;
+          value = (_ref = lookup(args, key)) != null ? _ref : '';
+        } else {
+          if (explicit) {
+            throw new ValueError(explicitToImplicit);
+          }
+          implicit = true;
+          value = (_ref1 = args[idx++]) != null ? _ref1 : '';
+        }
+        if (Object.prototype.hasOwnProperty.call(transformers, transformer)) {
+          return transformers[transformer](value);
+        } else {
+          return value;
+        }
+      });
+    };
+  };
+
+  lookup = function(object, key) {
+    var match;
+    if (!/^(\d+)([.]|$)/.test(key)) {
+      key = '0.' + key;
+    }
+    while (match = /(.+?)[.](.+)/.exec(key)) {
+      object = resolve(object, match[1]);
+      key = match[2];
+    }
+    return resolve(object, key);
+  };
+
+  resolve = function(object, key) {
+    var value;
+    value = object[key];
+    if (typeof value === 'function') {
+      return value.call(object);
+    } else {
+      return value;
+    }
+  };
+
+  format = create({});
+
+  format.create = create;
+
+  format.extend = function(prototype, transformers) {
+    var $format;
+    $format = create(transformers);
+    prototype.format = function() {
+      return $format.apply(null, [this].concat(__slice.call(arguments)));
+    };
+  };
+
+  if (typeof module !== 'undefined') {
+    module.exports = format;
+  } else if (typeof define === 'function' && define.amd) {
+    define(format);
+  } else {
+    window.format = format;
+  }
+
+}).call(this);
diff --git a/tests/src/integration/runtimetests/src/helloworld/node_modules/string-format/package.json b/tests/src/integration/runtimetests/src/helloworld/node_modules/string-format/package.json
new file mode 100644
index 0000000..d6524b6
--- /dev/null
+++ b/tests/src/integration/runtimetests/src/helloworld/node_modules/string-format/package.json
@@ -0,0 +1,101 @@
+{
+  "_args": [
+    [
+      {
+        "raw": "string-format@0.5.0",
+        "scope": null,
+        "escapedName": "string-format",
+        "name": "string-format",
+        "rawSpec": "0.5.0",
+        "spec": "0.5.0",
+        "type": "version"
+      },
+      ""
+    ]
+  ],
+  "_from": "string-format@0.5.0",
+  "_id": "string-format@0.5.0",
+  "_inCache": true,
+  "_location": "/string-format",
+  "_npmUser": {
+    "name": "davidchambers",
+    "email": "dc@davidchambers.me"
+  },
+  "_npmVersion": "1.4.28",
+  "_phantomChildren": {},
+  "_requested": {
+    "raw": "string-format@0.5.0",
+    "scope": null,
+    "escapedName": "string-format",
+    "name": "string-format",
+    "rawSpec": "0.5.0",
+    "spec": "0.5.0",
+    "type": "version"
+  },
+  "_requiredBy": [
+    "/"
+  ],
+  "_resolved": "https://registry.npmjs.org/string-format/-/string-format-0.5.0.tgz",
+  "_shasum": "bfc4a69a250f17f273d97336797daf5dca6ecf30",
+  "_shrinkwrap": null,
+  "_spec": "string-format@0.5.0",
+  "_where": "",
+  "author": {
+    "name": "David Chambers",
+    "email": "dc@davidchambers.me"
+  },
+  "bugs": {
+    "url": "https://github.com/davidchambers/string-format/issues"
+  },
+  "dependencies": {},
+  "description": "Adds a `format` method to `String.prototype`. Inspired by Python's `str.format()`.",
+  "devDependencies": {
+    "coffee-script": "1.8.x",
+    "mocha": "2.x.x",
+    "ramda": "0.8.x",
+    "xyz": "0.5.x"
+  },
+  "directories": {},
+  "dist": {
+    "shasum": "bfc4a69a250f17f273d97336797daf5dca6ecf30",
+    "tarball": "https://registry.npmjs.org/string-format/-/string-format-0.5.0.tgz"
+  },
+  "files": [
+    "README.md",
+    "lib/string-format.js",
+    "package.json"
+  ],
+  "gitHead": "e98595d385a460edb8fe9bd384fe1af3da307a31",
+  "homepage": "https://github.com/davidchambers/string-format",
+  "keywords": [
+    "string",
+    "formatting",
+    "language",
+    "util"
+  ],
+  "licenses": [
+    {
+      "type": "WTFPL",
+      "url": "https://raw.github.com/davidchambers/string-format/master/LICENSE"
+    }
+  ],
+  "main": "./lib/string-format",
+  "maintainers": [
+    {
+      "name": "davidchambers",
+      "email": "dc@hashify.me"
+    }
+  ],
+  "name": "string-format",
+  "optionalDependencies": {},
+  "readme": "ERROR: No README data found!",
+  "repository": {
+    "type": "git",
+    "url": "git://github.com/davidchambers/string-format.git"
+  },
+  "scripts": {
+    "prepublish": "make clean && make",
+    "test": "make test"
+  },
+  "version": "0.5.0"
+}
diff --git a/tests/src/integration/runtimetests/src/helloworld/package.json b/tests/src/integration/runtimetests/src/helloworld/package.json
new file mode 100644
index 0000000..94de3f9
--- /dev/null
+++ b/tests/src/integration/runtimetests/src/helloworld/package.json
@@ -0,0 +1,14 @@
+{
+	"name": "hello-world-sample",
+	"description": "",
+	"license": "Apache-2.0",
+	"repository": {
+		"type": "git",
+		"url": "https://github.com/apache/incubator-openwhisk-wskdeploy"
+	},
+	"version": "1.0.0",
+	"main": "index.js",
+	"dependencies": {
+		"string-format": "0.5.0"
+	}
+}
diff --git a/utils/misc.go b/utils/misc.go
index 246656e..5026c1d 100644
--- a/utils/misc.go
+++ b/utils/misc.go
@@ -21,7 +21,6 @@ import (
 	"archive/zip"
 	"bufio"
 	"encoding/base64"
-	"encoding/json"
 	"errors"
 	"fmt"
 	"io"
@@ -31,25 +30,24 @@ import (
 	"reflect"
 	"strings"
 
-	"crypto/tls"
 	"github.com/apache/incubator-openwhisk-client-go/whisk"
 	"github.com/apache/incubator-openwhisk-wskdeploy/wski18n"
 	"github.com/hokaccha/go-prettyjson"
 	"io/ioutil"
 	"net/http"
 	"path"
-    "time"
 )
 
 const (
-    DEFAULT_HTTP_TIMEOUT = 30
-    DEFAULT_PROJECT_PATH = "."
-    // name of manifest and deployment files
-    ManifestFileNameYaml = "manifest.yaml"
-    ManifestFileNameYml = "manifest.yml"
-    DeploymentFileNameYaml = "deployment.yaml"
-    DeploymentFileNameYml = "deployment.yml"
+	DEFAULT_HTTP_TIMEOUT = 30
+	DEFAULT_PROJECT_PATH = "."
+	// name of manifest and deployment files
+	ManifestFileNameYaml   = "manifest.yaml"
+	ManifestFileNameYml    = "manifest.yml"
+	DeploymentFileNameYaml = "deployment.yaml"
+	DeploymentFileNameYml  = "deployment.yml"
 )
+
 // ActionRecord is a container to keep track of
 // a whisk action struct and a location filepath we use to
 // map files and manifest declared actions
@@ -72,8 +70,8 @@ type RuleRecord struct {
 func GetHomeDirectory() string {
 	usr, err := user.Current()
 	if err != nil {
-        return ""
-    }
+		return ""
+	}
 
 	return usr.HomeDir
 }
@@ -100,8 +98,8 @@ func PrettyJSON(j interface{}) (string, error) {
 	formatter := prettyjson.NewFormatter()
 	bytes, err := formatter.Marshal(j)
 	if err != nil {
-        	return "", err
-    	}
+		return "", err
+	}
 	return string(bytes), nil
 }
 
@@ -127,10 +125,10 @@ func isValidEnvironmentVar(value string) bool {
 	// and have at least 1 additional character after it, e.g. $ENV_VAR
 	// If the value is a concatenation of a string and a Env. variable, it should contain '$' (dollar)
 	// and have a string following which is surrounded with '{' and '}', e.g. xxx${ENV_VAR}xxx.
-	if value != "" && strings.HasPrefix(value, "$") && len(value) > 1  {
+	if value != "" && strings.HasPrefix(value, "$") && len(value) > 1 {
 		return true
 	}
-	if value != "" && strings.Contains(value,"${") && strings.Count(value,"{")==strings.Count(value,"}") {
+	if value != "" && strings.Contains(value, "${") && strings.Count(value, "{") == strings.Count(value, "}") {
 		return true
 	}
 	return false
@@ -153,23 +151,23 @@ func GetEnvVar(key interface{}) interface{} {
 			//test if the substr is a environment var
 			//if it is, replace it with the value
 			f := func(c rune) bool {
-				return c=='$' || c=='{' || c=='}'
+				return c == '$' || c == '{' || c == '}'
 			}
-			for _,substr := range strings.FieldsFunc(keystr, f) {
+			for _, substr := range strings.FieldsFunc(keystr, f) {
 				//if the substr is a $ENV_VAR
-				if strings.Contains(keystr,"$"+substr) {
+				if strings.Contains(keystr, "$"+substr) {
 					thisValue = os.Getenv(substr)
 					if thisValue == "" {
-                        PrintOpenWhiskOutputln("WARNING: Missing Environment Variable " + substr + ".")
+						PrintOpenWhiskOutputln("WARNING: Missing Environment Variable " + substr + ".")
 					}
-					keystr = strings.Replace(keystr,"$"+substr,thisValue,-1)
-				//if the substr is a ${ENV_VAR}
-				} else if strings.Contains(keystr,"${"+substr+"}") {
+					keystr = strings.Replace(keystr, "$"+substr, thisValue, -1)
+					//if the substr is a ${ENV_VAR}
+				} else if strings.Contains(keystr, "${"+substr+"}") {
 					thisValue = os.Getenv(substr)
 					if thisValue == "" {
-                        PrintOpenWhiskOutputln("WARNING: Missing Environment Variable " + substr + ".")
+						PrintOpenWhiskOutputln("WARNING: Missing Environment Variable " + substr + ".")
 					}
-					keystr = strings.Replace(keystr,"${"+substr+"}",thisValue,-1)
+					keystr = strings.Replace(keystr, "${"+substr+"}", thisValue, -1)
 				}
 			}
 			return keystr
@@ -254,13 +252,18 @@ func GetExec(artifact string, kind string, isDocker bool, mainEntry string) (*wh
 	var exec *whisk.Exec
 
 	ext := filepath.Ext(artifact)
+	// drop the "." from file extension
+	if len(ext) > 0 && ext[0] == '.' {
+		ext = ext[1:]
+	}
+
 	exec = new(whisk.Exec)
 
-	if !isDocker || ext == ".zip" {
+	if !isDocker || ext == ZIP_FILE_EXTENSION {
 		content, err = new(ContentReader).ReadLocal(artifact)
 		if err != nil {
-            return nil, err
-        }
+			return nil, err
+		}
 		code = string(content)
 		exec.Code = &code
 	}
@@ -269,22 +272,22 @@ func GetExec(artifact string, kind string, isDocker bool, mainEntry string) (*wh
 		exec.Kind = kind
 	} else if isDocker {
 		exec.Kind = "blackbox"
-		if ext != ".zip" {
+		if ext != ZIP_FILE_EXTENSION {
 			exec.Image = artifact
 		} else {
 			exec.Image = "openwhisk/dockerskeleton"
 		}
-	} else if ext == ".swift" {
-		exec.Kind = "swift:default"
-	} else if ext == ".js" {
-		exec.Kind = "nodejs:default"
-	} else if ext == ".py" {
-		exec.Kind = "python:default"
-	} else if ext == ".jar" {
-		exec.Kind = "java:default"
-		exec.Code = nil
 	} else {
-		if ext == ".zip" {
+		r := FileExtensionRuntimeKindMap[ext]
+		exec.Kind = DefaultRunTimes[r]
+	}
+
+	if ext == JAR_FILE_EXTENSION {
+		exec.Code = nil
+	}
+
+	if len(exec.Kind) == 0 {
+		if ext == ZIP_FILE_EXTENSION {
 			return nil, zipKindError()
 		} else {
 			return nil, extensionError(ext)
@@ -301,7 +304,7 @@ func GetExec(artifact string, kind string, isDocker bool, mainEntry string) (*wh
 	}
 
 	// Base64 encode the zip file content
-	if ext == ".zip" {
+	if ext == ZIP_FILE_EXTENSION {
 		code = base64.StdEncoding.EncodeToString([]byte(code))
 		exec.Code = &code
 	}
@@ -351,216 +354,6 @@ func addKeyValue(key string, value interface{}, keyValueArr whisk.KeyValueArr) w
 	return append(keyValueArr, keyValue)
 }
 
-// Structs used to denote the OpenWhisk Runtime information
-type Limit struct {
-	Apm       uint16 `json:"actions_per_minute"`
-	Tpm       uint16 `json:"triggers_per_minute"`
-	ConAction uint16 `json:"concurrent_actions"`
-}
-
-type Runtime struct {
-	Image      string `json:"image"`
-	Deprecated bool   `json:"deprecated"`
-	ReMain     bool   `json:"requireMain"`
-	Default    bool   `json:"default"`
-	Attach     bool   `json:"attached"`
-	Kind       string `json:"kind"`
-}
-
-type SupportInfo struct {
-	Github string `json:"github"`
-	Slack  string `json:"slack"`
-}
-
-type OpenWhiskInfo struct {
-	Support  SupportInfo          `json:"support"`
-	Desc     string               `json:"description"`
-	ApiPath  []string             `json:"api_paths"`
-	Runtimes map[string][]Runtime `json:"runtimes"`
-	Limits   Limit                `json:"limits"`
-}
-
-// We could get the openwhisk info from bluemix through running the command
-// `curl -k https://openwhisk.ng.bluemix.net`
-// hard coding it here in case of network unavailable or failure.
-func ParseOpenWhisk(apiHost string) (op OpenWhiskInfo, err error) {
-	ct := "application/json; charset=UTF-8"
-	req, _ := http.NewRequest("GET", "https://"+apiHost, nil)
-	req.Header.Set("Content-Type", ct)
-	tlsConfig := &tls.Config{
-		InsecureSkipVerify: true,
-	}
-
-    var netTransport = &http.Transport{
-        TLSClientConfig: tlsConfig,
-    }
-
-    var netClient = &http.Client{
-        Timeout: time.Second * DEFAULT_HTTP_TIMEOUT,
-        Transport: netTransport,
-    }
-
-	res, err := netClient.Do(req)
-    if err != nil {
-        errString := wski18n.T("Failed to get the supported runtimes from OpenWhisk service: {{.err}}.\n",
-            map[string]interface{}{"err": err.Error()})
-        whisk.Debug(whisk.DbgWarn, errString)
-    }
-
-    if res != nil {
-        defer res.Body.Close()
-    }
-
-	// Local openwhisk deployment sometimes only returns "application/json" as the content type
-	if err != nil || !strings.Contains(ct, res.Header.Get("Content-Type")) {
-        stdout := wski18n.T("Start to unmarshal Openwhisk info from local values.\n")
-        whisk.Debug(whisk.DbgInfo, stdout)
-		err = json.Unmarshal(runtimeInfo, &op)
-	} else {
-		b, _ := ioutil.ReadAll(res.Body)
-		if b != nil && len(b) > 0 {
-            stdout := wski18n.T("Unmarshal Openwhisk info from internet.\n")
-            whisk.Debug(whisk.DbgInfo, stdout)
-			err = json.Unmarshal(b, &op)
-		}
-	}
-	return
-}
-
-func ConvertToMap(op OpenWhiskInfo) (rt map[string][]string) {
-	rt = make(map[string][]string)
-	for k, v := range op.Runtimes {
-		rt[k] = make([]string, 0, len(v))
-		for i := range v {
-			if (!v[i].Deprecated) {
-				rt[k] = append(rt[k], v[i].Kind)
-			}
-		}
-	}
-	return
-}
-
-var runtimeInfo = []byte(`{
-	"support":{
-		"github":"https://github.com/apache/incubator-openwhisk/issues",
-		"slack":"http://slack.openwhisk.org"
-	},
-	"description":"OpenWhisk",
-	"api_paths":["/api/v1"],
-	"runtimes":{
-		"nodejs":[{
-			"image":"openwhisk/nodejsaction:latest",
-			"deprecated":true,
-			"requireMain":false,
-			"default":false,
-			"attached":false,
-			"kind":"nodejs"
-		},{
-			"image":"openwhisk/nodejs6action:latest",
-			"deprecated":false,
-			"requireMain":false,
-			"default":true,
-			"attached":false,
-			"kind":"nodejs:6"
-		},{
-			"image":"openwhisk/action-nodejs-v8:latest",
-			"deprecated":false,
-			"requireMain":false,
-			"default":false,
-			"attached":false,
-			"kind":"nodejs:8"
-		}],
-		"java":[{
-			"image":"openwhisk/java8action:latest",
-			"deprecated":false,
-			"requireMain":true,
-			"default":true,
-			"attached":true,
-			"kind":"java"
-		}],
-		"php":[{
-			"image":"openwhisk/action-php-v7.1:latest",
-			"deprecated":false,
-			"requireMain":false,
-			"default":true,
-			"attached":false,
-			"kind":"php:7.1"
-		}],
-		"python":[{
-			"image":"openwhisk/python2action:latest",
-			"deprecated":false,
-			"requireMain":false,
-			"default":false,
-			"attached":false,
-			"kind":"python"
-		},{
-			"image":"openwhisk/python2action:latest",
-			"deprecated":false,
-			"requireMain":false,
-			"default":true,
-			"attached":false,
-			"kind":"python:2"
-		},{
-			"image":"openwhisk/python3action:latest",
-			"deprecated":false,
-			"requireMain":false,
-			"default":false,
-			"attached":false,
-			"kind":"python:3"
-		}],
-		"swift":[{
-			"image":"openwhisk/swiftaction:latest",
-			"deprecated":true,
-			"requireMain":false,
-			"default":false,
-			"attached":false,
-			"kind":"swift"
-		},{
-			"image":"openwhisk/swift3action:latest",
-			"deprecated":true,
-			"requireMain":false,
-			"default":false,
-			"attached":false,
-			"kind":"swift:3"
-		},{
-			"image":"openwhisk/action-swift-v3.1.1:latest",
-			"deprecated":false,
-			"requireMain":false,
-			"default":true,
-			"attached":false,
-			"kind":"swift:3.1.1"
-		}]
-	},
-	"limits":{
-		"actions_per_minute":5000,
-		"triggers_per_minute":5000,
-		"concurrent_actions":1000
-	}
-	}
-`)
-
-
-var Rts map[string][]string
-
-var DefaultRts = map[string][]string{
-	"nodejs": {"nodejs:6"},
-	"java":   {"java"},
-	"php":    {"php:7.1"},
-	"python": {"python:2"},
-	"swift":  {"swift:3.1.1"},
-}
-
-func CheckExistRuntime(rtname string, rts map[string][]string) bool {
-	for _, v := range rts {
-		for i := range v {
-			if rtname == v[i] {
-				return true
-			}
-		}
-	}
-	return false
-}
-
 func GetManifestFilePath(projectPath string) string {
 	if _, err := os.Stat(path.Join(projectPath, ManifestFileNameYaml)); err == nil {
 		return path.Join(projectPath, ManifestFileNameYaml)
diff --git a/utils/runtimes.go b/utils/runtimes.go
new file mode 100644
index 0000000..e39d027
--- /dev/null
+++ b/utils/runtimes.go
@@ -0,0 +1,293 @@
+/*
+ * 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 utils
+
+import (
+	"crypto/tls"
+	"encoding/json"
+	"github.com/apache/incubator-openwhisk-client-go/whisk"
+	"github.com/apache/incubator-openwhisk-wskdeploy/wski18n"
+	"io/ioutil"
+	"net/http"
+	"strings"
+	"time"
+)
+
+const NODEJS_FILE_EXTENSION = "js"
+const SWIFT_FILE_EXTENSION = "swift"
+const PYTHON_FILE_EXTENSION = "py"
+const JAVA_FILE_EXTENSION = "java"
+const JAR_FILE_EXTENSION = "jar"
+const PHP_FILE_EXTENSION = "php"
+const ZIP_FILE_EXTENSION = "zip"
+
+// Structs used to denote the OpenWhisk Runtime information
+type Limit struct {
+	Apm       uint16 `json:"actions_per_minute"`
+	Tpm       uint16 `json:"triggers_per_minute"`
+	ConAction uint16 `json:"concurrent_actions"`
+}
+
+type Runtime struct {
+	Image      string `json:"image"`
+	Deprecated bool   `json:"deprecated"`
+	ReMain     bool   `json:"requireMain"`
+	Default    bool   `json:"default"`
+	Attach     bool   `json:"attached"`
+	Kind       string `json:"kind"`
+}
+
+type SupportInfo struct {
+	Github string `json:"github"`
+	Slack  string `json:"slack"`
+}
+
+type OpenWhiskInfo struct {
+	Support  SupportInfo          `json:"support"`
+	Desc     string               `json:"description"`
+	ApiPath  []string             `json:"api_paths"`
+	Runtimes map[string][]Runtime `json:"runtimes"`
+	Limits   Limit                `json:"limits"`
+}
+
+var FileExtensionRuntimeKindMap map[string]string
+var SupportedRunTimes map[string][]string
+var DefaultRunTimes map[string]string
+
+
+// We could get the openwhisk info from bluemix through running the command
+// `curl -k https://openwhisk.ng.bluemix.net`
+// hard coding it here in case of network unavailable or failure.
+func ParseOpenWhisk(apiHost string) (op OpenWhiskInfo, err error) {
+	ct := "application/json; charset=UTF-8"
+	req, _ := http.NewRequest("GET", "https://"+apiHost, nil)
+	req.Header.Set("Content-Type", ct)
+	tlsConfig := &tls.Config{
+		InsecureSkipVerify: true,
+	}
+
+	var netTransport = &http.Transport{
+		TLSClientConfig: tlsConfig,
+	}
+
+	var netClient = &http.Client{
+		Timeout:   time.Second * DEFAULT_HTTP_TIMEOUT,
+		Transport: netTransport,
+	}
+
+	res, err := netClient.Do(req)
+	if err != nil {
+		errString := wski18n.T("Failed to get the supported runtimes from OpenWhisk service: {{.err}}.\n",
+			map[string]interface{}{"err": err.Error()})
+		whisk.Debug(whisk.DbgWarn, errString)
+	}
+
+	if res != nil {
+		defer res.Body.Close()
+	}
+
+	// Local openwhisk deployment sometimes only returns "application/json" as the content type
+	if err != nil || !strings.Contains(ct, res.Header.Get("Content-Type")) {
+		stdout := wski18n.T("Start to unmarshal Openwhisk info from local values.\n")
+		whisk.Debug(whisk.DbgInfo, stdout)
+		err = json.Unmarshal(RUNTIME_DETAILS, &op)
+	} else {
+		b, _ := ioutil.ReadAll(res.Body)
+		if b != nil && len(b) > 0 {
+			stdout := wski18n.T("Unmarshal Openwhisk info from internet.\n")
+			whisk.Debug(whisk.DbgInfo, stdout)
+			err = json.Unmarshal(b, &op)
+		}
+	}
+	return
+}
+
+func ConvertToMap(op OpenWhiskInfo) (rt map[string][]string) {
+	rt = make(map[string][]string)
+	for k, v := range op.Runtimes {
+		rt[k] = make([]string, 0, len(v))
+		for i := range v {
+			if !v[i].Deprecated {
+				rt[k] = append(rt[k], v[i].Kind)
+			}
+		}
+	}
+	return
+}
+
+func DefaultRuntimes(op OpenWhiskInfo) (rt map[string]string) {
+	rt = make(map[string]string)
+	for k, v := range op.Runtimes {
+		for i := range v {
+			if !v[i].Deprecated {
+				if v[i].Default {
+					rt[k] = v[i].Kind
+				}
+			}
+		}
+	}
+	return
+}
+
+func FileExtensionRuntimes(op OpenWhiskInfo) (ext map[string]string) {
+	ext = make(map[string]string)
+	for k := range op.Runtimes {
+		if strings.Contains(k, NODEJS_FILE_EXTENSION) {
+			ext[NODEJS_FILE_EXTENSION] = k
+		} else if strings.Contains(k, PYTHON_FILE_EXTENSION) {
+			ext[PYTHON_FILE_EXTENSION] = k
+		} else if strings.Contains(k, SWIFT_FILE_EXTENSION) {
+			ext[SWIFT_FILE_EXTENSION] = k
+		} else if strings.Contains(k, PHP_FILE_EXTENSION) {
+			ext[PHP_FILE_EXTENSION] = k
+		} else if strings.Contains(k, JAVA_FILE_EXTENSION) {
+			ext[JAVA_FILE_EXTENSION] = k
+			ext[JAR_FILE_EXTENSION] = k
+		}
+	}
+	return
+}
+
+func CheckRuntimeConsistencyWithFileExtension (ext string, runtime string) bool {
+	rt := FileExtensionRuntimeKindMap[ext]
+	for _, v := range SupportedRunTimes[rt] {
+		if (runtime == v) {
+			return true
+		}
+	}
+	return false
+}
+
+func CheckExistRuntime(rtname string, runtimes map[string][]string) bool {
+	for _, v := range runtimes {
+		for i := range v {
+			if rtname == v[i] {
+				return true
+			}
+		}
+	}
+	return false
+}
+
+func ListOfSupportedRuntimes(runtimes map[string][]string) (rt []string) {
+	for _, v := range runtimes {
+		for _, t := range v {
+			rt = append(rt, t)
+		}
+	}
+	return
+}
+
+var RUNTIME_DETAILS = []byte(`{
+	"support":{
+		"github":"https://github.com/apache/incubator-openwhisk/issues",
+		"slack":"http://slack.openwhisk.org"
+	},
+	"description":"OpenWhisk",
+	"api_paths":["/api/v1"],
+	"runtimes":{
+		"nodejs":[{
+			"image":"openwhisk/nodejsaction:latest",
+			"deprecated":true,
+			"requireMain":false,
+			"default":false,
+			"attached":false,
+			"kind":"nodejs"
+		},{
+			"image":"openwhisk/nodejs6action:latest",
+			"deprecated":false,
+			"requireMain":false,
+			"default":true,
+			"attached":false,
+			"kind":"nodejs:6"
+		},{
+			"image":"openwhisk/action-nodejs-v8:latest",
+			"deprecated":false,
+			"requireMain":false,
+			"default":false,
+			"attached":false,
+			"kind":"nodejs:8"
+		}],
+		"java":[{
+			"image":"openwhisk/java8action:latest",
+			"deprecated":false,
+			"requireMain":true,
+			"default":true,
+			"attached":true,
+			"kind":"java"
+		}],
+		"php":[{
+			"image":"openwhisk/action-php-v7.1:latest",
+			"deprecated":false,
+			"requireMain":false,
+			"default":true,
+			"attached":false,
+			"kind":"php:7.1"
+		}],
+		"python":[{
+			"image":"openwhisk/python2action:latest",
+			"deprecated":false,
+			"requireMain":false,
+			"default":false,
+			"attached":false,
+			"kind":"python"
+		},{
+			"image":"openwhisk/python2action:latest",
+			"deprecated":false,
+			"requireMain":false,
+			"default":true,
+			"attached":false,
+			"kind":"python:2"
+		},{
+			"image":"openwhisk/python3action:latest",
+			"deprecated":false,
+			"requireMain":false,
+			"default":false,
+			"attached":false,
+			"kind":"python:3"
+		}],
+		"swift":[{
+			"image":"openwhisk/swiftaction:latest",
+			"deprecated":true,
+			"requireMain":false,
+			"default":false,
+			"attached":false,
+			"kind":"swift"
+		},{
+			"image":"openwhisk/swift3action:latest",
+			"deprecated":true,
+			"requireMain":false,
+			"default":false,
+			"attached":false,
+			"kind":"swift:3"
+		},{
+			"image":"openwhisk/action-swift-v3.1.1:latest",
+			"deprecated":false,
+			"requireMain":false,
+			"default":true,
+			"attached":false,
+			"kind":"swift:3.1.1"
+		}]
+	},
+	"limits":{
+		"actions_per_minute":5000,
+		"triggers_per_minute":5000,
+		"concurrent_actions":1000
+	}
+	}
+`)
diff --git a/wskderrors/wskdeployerror.go b/wskderrors/wskdeployerror.go
index b1935f4..61881d1 100644
--- a/wskderrors/wskdeployerror.go
+++ b/wskderrors/wskdeployerror.go
@@ -35,6 +35,9 @@ const (
 	STR_EXPECTED = "Expected"
 	STR_ACTUAL = "Actual"
 	STR_NEWLINE = "\n"
+	STR_ACTION = "Action"
+	STR_RUNTIME = "Runtime"
+	STR_SUPPORTED_RUNTIMES = "Supported Runtimes"
 
 	// Formatting
 	STR_INDENT_1 = "==>"
@@ -49,6 +52,7 @@ const (
 	ERROR_YAML_PARSER_ERROR = "ERROR_YAML_PARSER_ERROR"
 	ERROR_YAML_PARAMETER_TYPE_MISMATCH = "ERROR_YAML_PARAMETER_TYPE_MISMATCH"
 	ERROR_YAML_INVALID_PARAMETER_TYPE = "ERROR_YAML_INVALID_PARAMETER_TYPE"
+	ERROR_YAML_INVALID_RUNTIME = "ERROR_YAML_INVALID_RUNTIME"
 )
 
 /*
@@ -346,6 +350,32 @@ func NewYAMLParserErr(fpath string, msg interface{}) *YAMLParserError {
 	return err
 }
 
+
+/*
+ * InvalidRuntime
+ */
+type InvalidRuntimeError struct {
+	FileError
+	Runtime    		string
+	SupportedRuntimes	[]string
+}
+
+func NewInvalidRuntimeError(errMessage string, fpath string, action string, runtime string, supportedRuntimes []string) *InvalidRuntimeError {
+	var err = &InvalidRuntimeError{
+		SupportedRuntimes: supportedRuntimes,
+	}
+	err.SetErrorFilePath(fpath)
+	err.SetErrorType(ERROR_YAML_INVALID_RUNTIME)
+	err.SetCallerByStackFrameSkip(2)
+	str := fmt.Sprintf("%s %s [%s]: %s [%s]: %s [%s]",
+		errMessage,
+		STR_ACTION, action,
+		STR_RUNTIME, runtime,
+		STR_SUPPORTED_RUNTIMES, strings.Join(supportedRuntimes, ", "))
+	err.SetMessage(str)
+	return err
+}
+
 func IsCustomError( err error ) bool {
 
 	switch err.(type) {
diff --git a/wski18n/i18n_resources.go b/wski18n/i18n_resources.go
index 53ecfca..15db0ca 100644
--- a/wski18n/i18n_resources.go
+++ b/wski18n/i18n_resources.go
@@ -15,6 +15,7 @@
  * limitations under the License.
  */
 
+
 // Code generated by go-bindata.
 // sources:
 // wski18n/resources/de_DE.all.json
@@ -114,7 +115,7 @@ func wski18nResourcesDe_deAllJson() (*asset, error) {
 	return a, nil
 }
 
-var _wski18nResourcesEn_usAllJson = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\xec\x5b\x4d\x6f\xdb\x38\x13\xbe\xe7\x57\x0c\x72\xf1\x25\xd0\xdb\xf6\xc5\x02\x8b\xde\x82\xed\x57\xd0\x36\x09\x92\x6c\x8b\xa2\x5b\x20\xb4\x38\xb6\x58\x53\xa4\x40\x52\x0e\x5c\xc3\xff\x7d\x41\x51\xb2\x9d\x44\xa2\x28\x59\x76\x82\x62\x73\x72\x64\xce\xf3\x3c\x33\xfc\x1c\x6a\xfc\xfd\x08\x60\x79\x04\x00\x70\xcc\xe8\xf1\x6b\x38\xfe\x80\x9c\xcb\xe3\x13\xf7\xc8\x28\x22\x34\x27\x86\x49\x61\xbf\x3b\x15\x70\x7a\x79\x06 [...]
+var _wski18nResourcesEn_usAllJson = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\xec\x5b\x5b\x6f\xe3\x36\x16\x7e\xcf\xaf\x38\xc8\x8b\x5f\x02\xed\x4c\x17\x0b\x2c\xe6\x2d\xd8\xe9\x25\x68\xe7\x82\x99\xd9\x16\x45\x77\x80\xd0\xe2\xb1\xc5\x9a\x22\x05\x92\x72\xea\x0a\xfe\xef\x0b\x92\x92\x2f\x89\x44\x51\xb2\xec\x04\x45\xf3\xe4\xd8\x3a\xdf\xf7\x9d\xc3\xdb\x21\x79\xf4\xdb\x15\x40\x75\x05\x00\x70\xcd\xe8\xf5\x1b\xb8\xfe\x01\x39\x97\xd7\x37\xfe\x2b\xa3\x88\xd0\x9c\x18\x26\x85\xfd\xed\x56\xc0\xed [...]
 
 func wski18nResourcesEn_usAllJsonBytes() ([]byte, error) {
 	return bindataRead(
@@ -129,7 +130,7 @@ func wski18nResourcesEn_usAllJson() (*asset, error) {
 		return nil, err
 	}
 
-	info := bindataFileInfo{name: "wski18n/resources/en_US.all.json", size: 15072, mode: os.FileMode(420), modTime: time.Unix(1510298069, 0)}
+	info := bindataFileInfo{name: "wski18n/resources/en_US.all.json", size: 15847, mode: os.FileMode(420), modTime: time.Unix(1513194493, 0)}
 	a := &asset{bytes: bytes, info: info}
 	return a, nil
 }
diff --git a/wski18n/resources/en_US.all.json b/wski18n/resources/en_US.all.json
index b5cf545..cb49feb 100644
--- a/wski18n/resources/en_US.all.json
+++ b/wski18n/resources/en_US.all.json
@@ -48,8 +48,16 @@
     "translation": "the runtime is not supported by Openwhisk platform.\n"
   },
   {
-    "id": "wskdeploy has chosen a particular runtime for the action.\n",
-    "translation": "wskdeploy has chosen a particular runtime for the action.\n"
+    "id": "WARNING: Runtime specified in manifest YAML {{.runtime}} does not match with action source file extension {{.ext}} for action {{.action}}.\n",
+    "translation": "WARNING: Runtime specified in manifest YAML {{.runtime}} does not match with action source file extension {{.ext}} for action {{.action}}.\n"
+  },
+  {
+    "id": "WARNING: Whisk Deploy has chosen appropriate runtime {{.runtime}} based on the action source file extension for that action {{.action}}.\n",
+    "translation": "WARNING: Whisk Deploy has chosen appropriate runtime {{.runtime}} based on the action source file extension for that action {{.action}}.\n"
+  },
+  {
+    "id": "WARNING: Runtime specified in manifest YAML {{.runtime}} is not supported by OpenWhisk server for the action {{.action}}.\n",
+    "translation": "WARNING: Runtime specified in manifest YAML {{.runtime}} is not supported by OpenWhisk server for the action {{.action}}.\n"
   },
   {
     "id": "Unsupported runtime type, set to nodejs",

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

Mime
View raw message