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 & 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>'].
|