mrutkows closed pull request #801: Adding Docker and Native support
URL: https://github.com/apache/incubator-openwhisk-wskdeploy/pull/801
This is a PR merged from a forked repository.
As GitHub hides the original diff on merge, it is displayed below for
the sake of provenance:
As this is a foreign pull request (from a fork), the diff is supplied
below (as it won't show otherwise due to GitHub magic):
diff --git a/parsers/manifest_parser.go b/parsers/manifest_parser.go
index 3a626e70..155a8d83 100644
--- a/parsers/manifest_parser.go
+++ b/parsers/manifest_parser.go
@@ -32,16 +32,18 @@ import (
"github.com/apache/incubator-openwhisk-wskdeploy/wskenv"
"github.com/apache/incubator-openwhisk-wskdeploy/wski18n"
"github.com/apache/incubator-openwhisk-wskdeploy/wskprint"
+ "path/filepath"
)
const (
- PATH_SEPERATOR = "/"
- API = "API"
- HTTPS = "https"
- HTTP = "http"
- API_VERSION = "v1"
- WEB = "web"
- DEFAULT_PACKAGE = "default"
+ API = "API"
+ HTTPS = "https://"
+ HTTP = "http://"
+ API_VERSION = "v1"
+ WEB = "web"
+ PATH_SEPARATOR = "/"
+ DEFAULT_PACKAGE = "default"
+ NATIVE_DOCKER_IMAGE = "openwhisk/dockerskeleton"
)
// Read existing manifest file or create new if none exists
@@ -110,6 +112,34 @@ func (dm *YAMLParser) ParseManifest(manifestPath string)
(*YAML, error) {
return manifest, nil
}
+func (dm *YAMLParser) composeInputsOrOutputs(inputs map[string]Parameter,
manifestFilePath string) (whisk.KeyValueArr, error) {
+ var errorParser error
+ keyValArr := make(whisk.KeyValueArr, 0)
+ for name, param := range inputs {
+ var keyVal whisk.KeyValue
+ keyVal.Key = name
+ keyVal.Value, errorParser = ResolveParameter(name, ¶m,
manifestFilePath)
+ if errorParser != nil {
+ return nil, errorParser
+ }
+ if keyVal.Value != nil {
+ keyValArr = append(keyValArr, keyVal)
+ }
+ }
+ return keyValArr, nil
+}
+
+func (dm *YAMLParser) composeAnnotations(annotations map[string]interface{})
whisk.KeyValueArr {
+ listOfAnnotations := make(whisk.KeyValueArr, 0)
+ for name, value := range annotations {
+ var keyVal whisk.KeyValue
+ keyVal.Key = name
+ keyVal.Value = wskenv.InterpolateStringWithEnvVar(value)
+ listOfAnnotations = append(listOfAnnotations, keyVal)
+ }
+ return listOfAnnotations
+}
+
func (dm *YAMLParser) ComposeDependenciesFromAllPackages(manifest *YAML,
projectPath string, filePath string) (map[string]utils.DependencyRecord, error)
{
dependencies := make(map[string]utils.DependencyRecord)
packages := make(map[string]Package)
@@ -135,11 +165,10 @@ func (dm *YAMLParser)
ComposeDependenciesFromAllPackages(manifest *YAML, project
func (dm *YAMLParser) ComposeDependencies(pkg Package, projectPath string,
filePath string, packageName string) (map[string]utils.DependencyRecord, error)
{
- var errorParser error
depMap := make(map[string]utils.DependencyRecord)
for key, dependency := range pkg.Dependencies {
version := dependency.Version
- if version == "" {
+ if len(version) == 0 {
// TODO() interactive ask for branch, AND consider YAML
specification to allow key for branch
version = YAML_VALUE_BRANCH_MASTER
}
@@ -148,54 +177,33 @@ func (dm *YAMLParser) ComposeDependencies(pkg Package,
projectPath string, fileP
isBinding := false
if utils.LocationIsBinding(location) {
-
- if !strings.HasPrefix(location, "/") {
- location = "/" + dependency.Location
+ if !strings.HasPrefix(location, PATH_SEPARATOR) {
+ location = PATH_SEPARATOR + dependency.Location
}
-
isBinding = true
} else if utils.LocationIsGithub(location) {
// TODO() define const for the protocol prefix, etc.
- if !strings.HasPrefix(location, "https://") &&
!strings.HasPrefix(location, "http://") {
- location = "https://" + dependency.Location
+ if !strings.HasPrefix(location, HTTPS) &&
!strings.HasPrefix(location, HTTP) {
+ location = HTTPS + dependency.Location
location =
wskenv.InterpolateStringWithEnvVar(location).(string)
}
-
isBinding = false
} else {
// TODO() create new named error in wskerrors package
return nil,
errors.New(wski18n.T(wski18n.ID_ERR_DEPENDENCY_UNKNOWN_TYPE))
}
- keyValArrParams := make(whisk.KeyValueArr, 0)
- for name, param := range dependency.Inputs {
- var keyVal whisk.KeyValue
- keyVal.Key = name
-
- keyVal.Value, errorParser = ResolveParameter(name,
¶m, filePath)
-
- if errorParser != nil {
- return nil, errorParser
- }
-
- if keyVal.Value != nil {
- keyValArrParams = append(keyValArrParams,
keyVal)
- }
+ inputs, err := dm.composeInputsOrOutputs(dependency.Inputs,
filePath)
+ if err != nil {
+ return nil, err
}
- keyValArrAnot := make(whisk.KeyValueArr, 0)
- for name, value := range dependency.Annotations {
- var keyVal whisk.KeyValue
- keyVal.Key = name
- keyVal.Value = wskenv.InterpolateStringWithEnvVar(value)
-
- keyValArrAnot = append(keyValArrAnot, keyVal)
- }
+ annotations := dm.composeAnnotations(dependency.Annotations)
- packDir := path.Join(projectPath, "Packages")
+ packDir := path.Join(projectPath,
strings.Title(YAML_KEY_PACKAGES))
depName := packageName + ":" + key
- depMap[depName] = utils.NewDependencyRecord(packDir,
packageName, location, version, keyValArrParams, keyValArrAnot, isBinding)
+ depMap[depName] = utils.NewDependencyRecord(packDir,
packageName, location, version, inputs, annotations, isBinding)
}
return depMap, nil
@@ -234,7 +242,6 @@ func (dm *YAMLParser) ComposeAllPackages(manifest *YAML,
filePath string, ma whi
}
func (dm *YAMLParser) ComposePackage(pkg Package, packageName string, filePath
string, ma whisk.KeyValue) (*whisk.Package, error) {
- var errorParser error
pag := &whisk.Package{}
pag.Name = packageName
//The namespace for this package is absent, so we use default guest
here.
@@ -286,34 +293,16 @@ func (dm *YAMLParser) ComposePackage(pkg Package,
packageName string, filePath s
}
//set parameters
- keyValArr := make(whisk.KeyValueArr, 0)
- for name, param := range pkg.Inputs {
- var keyVal whisk.KeyValue
- keyVal.Key = name
-
- keyVal.Value, errorParser = ResolveParameter(name, ¶m,
filePath)
-
- if errorParser != nil {
- return nil, errorParser
- }
-
- if keyVal.Value != nil {
- keyValArr = append(keyValArr, keyVal)
- }
+ inputs, err := dm.composeInputsOrOutputs(pkg.Inputs, filePath)
+ if err != nil {
+ return nil, err
}
-
- if len(keyValArr) > 0 {
- pag.Parameters = keyValArr
+ if len(inputs) > 0 {
+ pag.Parameters = inputs
}
// set Package Annotations
- listOfAnnotations := make(whisk.KeyValueArr, 0)
- for name, value := range pkg.Annotations {
- var keyVal whisk.KeyValue
- keyVal.Key = name
- keyVal.Value = wskenv.InterpolateStringWithEnvVar(value)
- listOfAnnotations = append(listOfAnnotations, keyVal)
- }
+ listOfAnnotations := dm.composeAnnotations(pkg.Annotations)
if len(listOfAnnotations) > 0 {
pag.Annotations = append(pag.Annotations, listOfAnnotations...)
}
@@ -343,8 +332,7 @@ func (dm *YAMLParser) ComposePackage(pkg Package,
packageName string, filePath s
}
func (dm *YAMLParser) ComposeSequencesFromAllPackages(namespace string, mani
*YAML, ma whisk.KeyValue) ([]utils.ActionRecord, error) {
- var s1 []utils.ActionRecord = make([]utils.ActionRecord, 0)
-
+ var sequences []utils.ActionRecord = make([]utils.ActionRecord, 0)
manifestPackages := make(map[string]Package)
if len(mani.Packages) != 0 {
@@ -356,16 +344,16 @@ func (dm *YAMLParser)
ComposeSequencesFromAllPackages(namespace string, mani *YA
for n, p := range manifestPackages {
s, err := dm.ComposeSequences(namespace, p.Sequences, n, ma)
if err == nil {
- s1 = append(s1, s...)
+ sequences = append(sequences, s...)
} else {
return nil, err
}
}
- return s1, nil
+ return sequences, nil
}
func (dm *YAMLParser) ComposeSequences(namespace string, sequences
map[string]Sequence, packageName string, ma whisk.KeyValue)
([]utils.ActionRecord, error) {
- var s1 []utils.ActionRecord = make([]utils.ActionRecord, 0)
+ var listOfSequences []utils.ActionRecord = make([]utils.ActionRecord, 0)
for key, sequence := range sequences {
wskaction := new(whisk.Action)
@@ -376,11 +364,11 @@ func (dm *YAMLParser) ComposeSequences(namespace string,
sequences map[string]Se
var components []string
for _, a := range actionList {
act := strings.TrimSpace(a)
- if !strings.ContainsRune(act, '/') &&
!strings.HasPrefix(act, packageName+"/") &&
+ if !strings.ContainsRune(act,
[]rune(PATH_SEPARATOR)[0]) && !strings.HasPrefix(act,
packageName+PATH_SEPARATOR) &&
strings.ToLower(packageName) != DEFAULT_PACKAGE
{
act = path.Join(packageName, act)
}
- components = append(components,
path.Join("/"+namespace, act))
+ components = append(components,
path.Join(PATH_SEPARATOR+namespace, act))
}
wskaction.Exec.Components = components
@@ -389,17 +377,9 @@ func (dm *YAMLParser) ComposeSequences(namespace string,
sequences map[string]Se
wskaction.Publish = &pub
wskaction.Namespace = namespace
- keyValArr := make(whisk.KeyValueArr, 0)
- for name, value := range sequence.Annotations {
- var keyVal whisk.KeyValue
- keyVal.Key = name
- keyVal.Value = wskenv.InterpolateStringWithEnvVar(value)
-
- keyValArr = append(keyValArr, keyVal)
- }
-
- if len(keyValArr) > 0 {
- wskaction.Annotations = keyValArr
+ annotations := dm.composeAnnotations(sequence.Annotations)
+ if len(annotations) > 0 {
+ wskaction.Annotations = annotations
}
// appending managed annotations if its a managed deployment
@@ -408,13 +388,13 @@ func (dm *YAMLParser) ComposeSequences(namespace string,
sequences map[string]Se
}
record := utils.ActionRecord{Action: wskaction, Packagename:
packageName, Filepath: key}
- s1 = append(s1, record)
+ listOfSequences = append(listOfSequences, record)
}
- return s1, nil
+ return listOfSequences, nil
}
func (dm *YAMLParser) ComposeActionsFromAllPackages(manifest *YAML, filePath
string, ma whisk.KeyValue) ([]utils.ActionRecord, error) {
- var s1 []utils.ActionRecord = make([]utils.ActionRecord, 0)
+ var actions []utils.ActionRecord = make([]utils.ActionRecord, 0)
manifestPackages := make(map[string]Package)
if len(manifest.Packages) != 0 {
@@ -426,356 +406,388 @@ func (dm *YAMLParser)
ComposeActionsFromAllPackages(manifest *YAML, filePath str
for n, p := range manifestPackages {
a, err := dm.ComposeActions(filePath, p.Actions, n, ma)
if err == nil {
- s1 = append(s1, a...)
+ actions = append(actions, a...)
} else {
return nil, err
}
}
- return s1, nil
+ return actions, nil
}
-func (dm *YAMLParser) ComposeActions(filePath string, actions
map[string]Action, packageName string, ma whisk.KeyValue)
([]utils.ActionRecord, error) {
-
- var errorParser error
- var ext string
- var s1 []utils.ActionRecord = make([]utils.ActionRecord, 0)
+func (dm *YAMLParser) validateActionCode(manifestFilePath string, action
Action) error {
+ // Check if action.Function is specified with action.Code
+ // with action.Code, action.Function is not allowed
+ // with action.Code, action.Runtime should be specified
+ if len(action.Function) != 0 {
+ err := wski18n.T(wski18n.ID_ERR_ACTION_INVALID_X_action_X,
+ map[string]interface{}{
+ wski18n.KEY_ACTION: action.Name})
+ return wskderrors.NewYAMLFileFormatError(manifestFilePath, err)
+ }
+ if len(action.Runtime) == 0 {
+ err :=
wski18n.T(wski18n.ID_ERR_ACTION_MISSING_RUNTIME_WITH_CODE_X_action_X,
+ map[string]interface{}{
+ wski18n.KEY_ACTION: action.Name})
+ return wskderrors.NewYAMLFileFormatError(manifestFilePath, err)
+ }
+ return nil
+}
- for key, action := range actions {
- var actionFilePath string
- splitFilePath := strings.Split(filePath,
string(os.PathSeparator))
- // set the name of the action (which is the key)
- action.Name = key
+func (dm *YAMLParser) readActionCode(manifestFilePath string, action Action)
(*whisk.Exec, error) {
+ exec := new(whisk.Exec)
+ if err := dm.validateActionCode(manifestFilePath, action); err != nil {
+ return nil, err
+ }
+ // validate runtime from the manifest file
+ // error out if the specified runtime is not valid or not supported
+ // even if runtime is invalid, deploy action with specified runtime in
strict mode
+ if utils.Flags.Strict {
+ exec.Kind = action.Runtime
+ } else if utils.CheckExistRuntime(action.Runtime,
utils.SupportedRunTimes) {
+ exec.Kind = action.Runtime
+ } else if len(utils.DefaultRunTimes[action.Runtime]) != 0 {
+ exec.Kind = utils.DefaultRunTimes[action.Runtime]
+ } else {
+ err :=
wski18n.T(wski18n.ID_ERR_RUNTIME_INVALID_X_runtime_X_action_X,
+ map[string]interface{}{
+ wski18n.KEY_RUNTIME: action.Runtime,
+ wski18n.KEY_ACTION: action.Name})
+ return nil, wskderrors.NewYAMLFileFormatError(manifestFilePath,
err)
+ }
+ exec.Code = &(action.Code)
+ // we can specify the name of the action entry point using main
+ if len(action.Main) != 0 {
+ exec.Main = action.Main
+ }
+ return exec, nil
+}
- // Create action data object with CLI
- wskaction := new(whisk.Action)
- wskaction.Exec = new(whisk.Exec)
+func (dm *YAMLParser) validateActionFunction(manifestFileName string, action
Action, ext string, kind string) error {
+ // 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(action.Runtime) == 0 && len(action.Docker) == 0 &&
!action.Native {
+ if ext == utils.ZIP_FILE_EXTENSION {
+ errMessage :=
wski18n.T(wski18n.ID_ERR_RUNTIME_INVALID_X_runtime_X_action_X,
+ map[string]interface{}{
+ wski18n.KEY_RUNTIME:
utils.RUNTIME_NOT_SPECIFIED,
+ wski18n.KEY_ACTION: action.Name})
+ return wskderrors.NewInvalidRuntimeError(errMessage,
+ manifestFileName,
+ action.Name,
+ utils.RUNTIME_NOT_SPECIFIED,
+
utils.ListOfSupportedRuntimes(utils.SupportedRunTimes))
+ } else if len(kind) == 0 {
+ errMessage :=
wski18n.T(wski18n.ID_ERR_RUNTIME_ACTION_SOURCE_NOT_SUPPORTED_X_ext_X_action_X,
+ map[string]interface{}{
+ wski18n.KEY_EXTENSION: ext,
+ wski18n.KEY_ACTION: action.Name})
+ return wskderrors.NewInvalidRuntimeError(errMessage,
+ manifestFileName,
+ action.Name,
+ utils.RUNTIME_NOT_SPECIFIED,
+
utils.ListOfSupportedRuntimes(utils.SupportedRunTimes))
+ }
+ }
+ return nil
+}
- /*
- * Action.Function
- */
- //set action.Function to action.Location
- //because Location is deprecated in Action entity
- if action.Function == "" && action.Location != "" {
- action.Function = action.Location
+func (dm *YAMLParser) readActionFunction(manifestFilePath string,
manifestFileName string, action Action) (string, *whisk.Exec, error) {
+ var actionFilePath string
+ var zipFileName string
+ exec := new(whisk.Exec)
+
+ // check if action function is pointing to an URL
+ // we do not support if function is pointing to remote directory
+ // therefore error out if there is a combination of http/https ending
in a directory
+ if strings.HasPrefix(action.Function, HTTP) ||
strings.HasPrefix(action.Function, HTTPS) {
+ if len(path.Ext(action.Function)) == 0 {
+ err :=
wski18n.T(wski18n.ID_ERR_ACTION_FUNCTION_REMOTE_DIR_NOT_SUPPORTED_X_action_X_url_X,
+ map[string]interface{}{
+ wski18n.KEY_ACTION: action.Name,
+ wski18n.KEY_URL: action.Function})
+ return actionFilePath, nil,
wskderrors.NewYAMLFileFormatError(manifestFilePath, err)
}
+ actionFilePath = action.Function
+ } else {
+ actionFilePath = strings.TrimRight(manifestFilePath,
manifestFileName) + action.Function
+ }
- // Check if either one of action.Function and action.Code is
defined
- if len(action.Code) != 0 {
- if len(action.Function) != 0 {
- err :=
wski18n.T(wski18n.ID_ERR_ACTION_INVALID_X_action_X,
- map[string]interface{}{
- wski18n.KEY_ACTION:
action.Name})
- return nil,
wskderrors.NewYAMLFileFormatError(filePath, err)
- }
- if len(action.Runtime) == 0 {
- err :=
wski18n.T(wski18n.ID_ERR_ACTION_MISSING_RUNTIME_WITH_CODE_X_action_X,
- map[string]interface{}{
- wski18n.KEY_ACTION:
action.Name})
- return nil,
wskderrors.NewYAMLFileFormatError(filePath, err)
- }
- code := action.Code
- wskaction.Exec.Code = &code
-
- // validate runtime from the manifest file
- // error out if the specified runtime is not valid or
not supported
- // even if runtime is invalid, deploy action with
specified runtime in strict mode
- if utils.Flags.Strict {
- wskaction.Exec.Kind = action.Runtime
- } else if utils.CheckExistRuntime(action.Runtime,
utils.SupportedRunTimes) {
- wskaction.Exec.Kind = action.Runtime
- } else if len(utils.DefaultRunTimes[action.Runtime]) !=
0 {
- wskaction.Exec.Kind =
utils.DefaultRunTimes[action.Runtime]
- } else {
- err :=
wski18n.T(wski18n.ID_ERR_RUNTIME_INVALID_X_runtime_X_action_X,
- map[string]interface{}{
- wski18n.KEY_RUNTIME:
action.Runtime,
- wski18n.KEY_ACTION:
action.Name})
- return nil,
wskderrors.NewYAMLFileFormatError(filePath, err)
- }
+ if utils.IsDirectory(actionFilePath) {
+ zipFileName = actionFilePath + "." + utils.ZIP_FILE_EXTENSION
+ err := utils.NewZipWritter(actionFilePath, zipFileName).Zip()
+ if err != nil {
+ return actionFilePath, nil, err
}
+ defer os.Remove(zipFileName)
+ actionFilePath = zipFileName
+ }
- //bind action, and exposed URL
- if len(action.Function) != 0 {
- // check if action function is pointing to an URL
- // we do not support if function is pointing to remote
directory
- // therefore error out if there is a combination of
http/https ending in a directory
- if strings.HasPrefix(action.Function, HTTP) ||
strings.HasPrefix(action.Function, HTTPS) {
- if len(path.Ext(action.Function)) == 0 {
- err :=
wski18n.T(wski18n.ID_ERR_ACTION_FUNCTION_REMOTE_DIR_NOT_SUPPORTED_X_action_X_url_X,
- map[string]interface{}{
- wski18n.KEY_ACTION:
action.Name,
- wski18n.KEY_URL:
action.Function})
- return nil,
wskderrors.NewYAMLFileFormatError(filePath, err)
- }
- actionFilePath = action.Function
- } else {
- actionFilePath = strings.TrimRight(filePath,
splitFilePath[len(splitFilePath)-1]) + action.Function
- }
+ action.Function = actionFilePath
- if utils.IsDirectory(actionFilePath) {
- // TODO() define ext as const
- zipName := actionFilePath + ".zip"
- err := utils.NewZipWritter(actionFilePath,
zipName).Zip()
- if err != nil {
- return nil, err
- }
- // TODO() do not use defer in a loop, resource
leaks possible
- defer os.Remove(zipName)
- // TODO(): 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(actionFilePath)
- // drop the "." from file extension
- if len(ext) > 0 && ext[0] == '.' {
- ext = ext[1:]
- }
+ // determine extension of the given action source file
+ ext := filepath.Ext(actionFilePath)
+ // drop the "." from file extension
+ if len(ext) > 0 && ext[0] == '.' {
+ ext = ext[1:]
+ }
- // determine default runtime for the given file
extension
- var kind string
- r := utils.FileExtensionRuntimeKindMap[ext]
- kind = utils.DefaultRunTimes[r]
+ // determine default runtime for the given file extension
+ var kind string
+ r := utils.FileExtensionRuntimeKindMap[ext]
+ kind = utils.DefaultRunTimes[r]
+ if err := dm.validateActionFunction(manifestFileName, action, ext,
kind); err != nil {
+ return actionFilePath, nil, err
+ }
+ exec.Kind = kind
- // 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 :=
wski18n.T(wski18n.ID_ERR_RUNTIME_ACTION_SOURCE_NOT_SUPPORTED_X_ext_X_action_X,
+ dat, err := utils.Read(actionFilePath)
+ if err != nil {
+ return actionFilePath, nil, err
+ }
+ code := string(dat)
+ if ext == utils.ZIP_FILE_EXTENSION || ext == utils.JAR_FILE_EXTENSION {
+ code = base64.StdEncoding.EncodeToString([]byte(dat))
+ }
+ exec.Code = &code
+
+ /*
+ * 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 len(action.Runtime) != 0 {
+ 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 {
+ exec.Kind = action.Runtime
+ } else {
+ if
utils.CheckRuntimeConsistencyWithFileExtension(ext, action.Runtime) {
+ exec.Kind = action.Runtime
+ } else {
+ warnStr :=
wski18n.T(wski18n.ID_ERR_RUNTIME_MISMATCH_X_runtime_X_ext_X_action_X,
map[string]interface{}{
+ wski18n.KEY_RUNTIME:
action.Runtime,
wski18n.KEY_EXTENSION:
ext,
wski18n.KEY_ACTION:
action.Name})
- return nil,
wskderrors.NewInvalidRuntimeError(errMessage,
-
splitFilePath[len(splitFilePath)-1], action.Name,
- utils.RUNTIME_NOT_SPECIFIED,
-
utils.ListOfSupportedRuntimes(utils.SupportedRunTimes))
- }
-
- wskaction.Exec.Kind = kind
-
- action.Function = actionFilePath
- dat, err := utils.Read(actionFilePath)
- if err != nil {
- return s1, err
- }
- code := string(dat)
- if ext == utils.ZIP_FILE_EXTENSION || ext ==
utils.JAR_FILE_EXTENSION {
- code =
base64.StdEncoding.EncodeToString([]byte(dat))
- }
- if ext == utils.ZIP_FILE_EXTENSION &&
len(action.Runtime) == 0 {
- errMessage :=
wski18n.T(wski18n.ID_ERR_RUNTIME_INVALID_X_runtime_X_action_X,
- map[string]interface{}{
- wski18n.KEY_RUNTIME:
utils.RUNTIME_NOT_SPECIFIED,
- wski18n.KEY_ACTION:
action.Name})
- return nil,
wskderrors.NewInvalidRuntimeError(errMessage,
-
splitFilePath[len(splitFilePath)-1],
- action.Name,
- utils.RUNTIME_NOT_SPECIFIED,
-
utils.ListOfSupportedRuntimes(utils.SupportedRunTimes))
- }
- wskaction.Exec.Code = &code
- }
-
- }
+ wskprint.PrintOpenWhiskWarning(warnStr)
- /*
- * 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 len(action.Runtime) != 0 && len(action.Function) != 0 {
- 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
+ // even if runtime is not consistent
with file extension, deploy action with specified runtime in strict mode
+ if utils.Flags.Strict {
+ exec.Kind = action.Runtime
} else {
- warnStr :=
wski18n.T(wski18n.ID_ERR_RUNTIME_MISMATCH_X_runtime_X_ext_X_action_X,
+ warnStr :=
wski18n.T(wski18n.ID_WARN_RUNTIME_CHANGED_X_runtime_X_action_X,
map[string]interface{}{
-
wski18n.KEY_RUNTIME: action.Runtime,
-
wski18n.KEY_EXTENSION: ext,
-
wski18n.KEY_ACTION: action.Name})
+
wski18n.KEY_RUNTIME: exec.Kind,
+
wski18n.KEY_ACTION: action.Name})
wskprint.PrintOpenWhiskWarning(warnStr)
-
- // 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 {
- warnStr :=
wski18n.T(wski18n.ID_WARN_RUNTIME_CHANGED_X_runtime_X_action_X,
-
map[string]interface{}{
-
wski18n.KEY_RUNTIME: wskaction.Exec.Kind,
-
wski18n.KEY_ACTION: action.Name})
-
wskprint.PrintOpenWhiskWarning(warnStr)
- }
}
}
+ }
+ } else {
+ warnStr :=
wski18n.T(wski18n.ID_ERR_RUNTIME_INVALID_X_runtime_X_action_X,
+ map[string]interface{}{
+ wski18n.KEY_RUNTIME: action.Runtime,
+ wski18n.KEY_ACTION: action.Name})
+ wskprint.PrintOpenWhiskWarning(warnStr)
+
+ if ext == utils.ZIP_FILE_EXTENSION {
+ // for zip action, error out if specified
runtime is not supported by
+ // OpenWhisk server
+ return actionFilePath, nil,
wskderrors.NewInvalidRuntimeError(warnStr,
+ manifestFileName,
+ action.Name,
+ action.Runtime,
+
utils.ListOfSupportedRuntimes(utils.SupportedRunTimes))
} else {
- warnStr :=
wski18n.T(wski18n.ID_ERR_RUNTIME_INVALID_X_runtime_X_action_X,
+ warnStr :=
wski18n.T(wski18n.ID_WARN_RUNTIME_CHANGED_X_runtime_X_action_X,
map[string]interface{}{
- wski18n.KEY_RUNTIME:
action.Runtime,
+ wski18n.KEY_RUNTIME: exec.Kind,
wski18n.KEY_ACTION:
action.Name})
wskprint.PrintOpenWhiskWarning(warnStr)
+ }
- if ext == utils.ZIP_FILE_EXTENSION {
- // for zip action, error out if
specified runtime is not supported by
- // OpenWhisk server
- return nil,
wskderrors.NewInvalidRuntimeError(warnStr,
-
splitFilePath[len(splitFilePath)-1],
- action.Name,
- action.Runtime,
-
utils.ListOfSupportedRuntimes(utils.SupportedRunTimes))
- } else {
- warnStr :=
wski18n.T(wski18n.ID_WARN_RUNTIME_CHANGED_X_runtime_X_action_X,
- map[string]interface{}{
- wski18n.KEY_RUNTIME:
wskaction.Exec.Kind,
- wski18n.KEY_ACTION:
action.Name})
- wskprint.PrintOpenWhiskWarning(warnStr)
- }
+ }
+ }
+ // we can specify the name of the action entry point using main
+ if len(action.Main) != 0 {
+ exec.Main = action.Main
+ }
- }
+ return actionFilePath, exec, nil
+}
+
+func (dm *YAMLParser) composeActionExec(manifestFilePath string,
manifestFileName string, action Action) (string, *whisk.Exec, error) {
+ var actionFilePath string
+ exec := new(whisk.Exec)
+ var err error
+
+ if len(action.Code) != 0 {
+ exec, err = dm.readActionCode(manifestFilePath, action)
+ if err != nil {
+ return actionFilePath, nil, err
+ }
+ }
+ if len(action.Function) != 0 {
+ actionFilePath, exec, err =
dm.readActionFunction(manifestFilePath, manifestFileName, action)
+ if err != nil {
+ return actionFilePath, nil, err
}
+ }
- // we can specify the name of the action entry point using main
- if len(action.Main) != 0 {
- wskaction.Exec.Main = action.Main
+ // when an action has Docker image specified,
+ // set exec.Kind to "blackbox" and
+ // set exec.Image to specified image e.g. dockerhub/image
+ // when an action Native is set to true,
+ // set exec.Image to openwhisk/skeleton
+ if len(action.Docker) != 0 || action.Native {
+ exec.Kind = utils.BLACKBOX
+ if action.Native {
+ exec.Image = NATIVE_DOCKER_IMAGE
+ } else {
+ exec.Image = action.Docker
}
+ }
- /*
- * Action.Inputs
- */
- keyValArr := make(whisk.KeyValueArr, 0)
- for name, param := range action.Inputs {
- var keyVal whisk.KeyValue
- keyVal.Key = name
- keyVal.Value, errorParser = ResolveParameter(name,
¶m, filePath)
+ return actionFilePath, exec, err
+}
- if errorParser != nil {
- return nil, errorParser
+func (dm *YAMLParser) validateActionLimits(limits Limits) {
+ // TODO() use LIMITS_UNSUPPORTED in yamlparser to enumerate through
instead of hardcoding
+ // emit warning errors if these limits are not nil
+ utils.NotSupportLimits(limits.ConcurrentActivations,
LIMIT_VALUE_CONCURRENT_ACTIVATIONS)
+ utils.NotSupportLimits(limits.UserInvocationRate,
LIMIT_VALUE_USER_INVOCATION_RATE)
+ utils.NotSupportLimits(limits.CodeSize, LIMIT_VALUE_CODE_SIZE)
+ utils.NotSupportLimits(limits.ParameterSize, LIMIT_VALUE_PARAMETER_SIZE)
+}
+
+func (dm *YAMLParser) composeActionLimits(limits Limits) *whisk.Limits {
+ dm.validateActionLimits(limits)
+ wsklimits := new(whisk.Limits)
+ for _, t := range LIMITS_SUPPORTED {
+ switch t {
+ case LIMIT_VALUE_TIMEOUT:
+ if utils.LimitsTimeoutValidation(limits.Timeout) {
+ wsklimits.Timeout = limits.Timeout
+ } else {
+ warningString :=
wski18n.T(wski18n.ID_WARN_LIMIT_IGNORED_X_limit_X,
+
map[string]interface{}{wski18n.KEY_LIMIT: LIMIT_VALUE_TIMEOUT})
+ wskprint.PrintOpenWhiskWarning(warningString)
}
+ case LIMIT_VALUE_MEMORY_SIZE:
+ if utils.LimitsMemoryValidation(limits.Memory) {
+ wsklimits.Memory = limits.Memory
+ } else {
+ warningString :=
wski18n.T(wski18n.ID_WARN_LIMIT_IGNORED_X_limit_X,
+
map[string]interface{}{wski18n.KEY_LIMIT: LIMIT_VALUE_MEMORY_SIZE})
+ wskprint.PrintOpenWhiskWarning(warningString)
- if keyVal.Value != nil {
- keyValArr = append(keyValArr, keyVal)
}
- }
+ case LIMIT_VALUE_LOG_SIZE:
+ if utils.LimitsLogsizeValidation(limits.Logsize) {
+ wsklimits.Logsize = limits.Logsize
+ } else {
+ warningString :=
wski18n.T(wski18n.ID_WARN_LIMIT_IGNORED_X_limit_X,
+
map[string]interface{}{wski18n.KEY_LIMIT: LIMIT_VALUE_LOG_SIZE})
+ wskprint.PrintOpenWhiskWarning(warningString)
- // if we have successfully parser valid key/value parameters
- if len(keyValArr) > 0 {
- wskaction.Parameters = keyValArr
+ }
}
+ }
+ if wsklimits.Timeout != nil || wsklimits.Memory != nil ||
wsklimits.Logsize != nil {
+ return wsklimits
+ }
+ return nil
+}
- /*
- * Action.Outputs
- */
- keyValArr = make(whisk.KeyValueArr, 0)
- for name, param := range action.Outputs {
- var keyVal whisk.KeyValue
- keyVal.Key = name
- keyVal.Value, errorParser = ResolveParameter(name,
¶m, filePath)
+func (dm *YAMLParser) ComposeActions(manifestFilePath string, actions
map[string]Action, packageName string, ma whisk.KeyValue)
([]utils.ActionRecord, error) {
- // short circuit on error
- if errorParser != nil {
- return nil, errorParser
- }
+ var errorParser error
+ var listOfActions []utils.ActionRecord = make([]utils.ActionRecord, 0)
+ splitManifestFilePath := strings.Split(manifestFilePath,
string(PATH_SEPARATOR))
+ manifestFileName := splitManifestFilePath[len(splitManifestFilePath)-1]
- if keyVal.Value != nil {
- keyValArr = append(keyValArr, keyVal)
- }
+ for actionName, action := range actions {
+ var actionFilePath string
+
+ // update the action (of type Action) to set its name
+ // here key name is the action name
+ action.Name = actionName
+
+ // Create action data object from client library
+ wskaction := new(whisk.Action)
+
+ //set action.Function to action.Location
+ //because Location is deprecated in Action entity
+ if len(action.Function) == 0 && len(action.Location) != 0 {
+ action.Function = action.Location
}
- // TODO{} add outputs as annotations (work to discuss
officially supporting for compositions)
- if len(keyValArr) > 0 {
- // TODO() ?
- //wskaction.Annotations // TBD
+ actionFilePath, wskaction.Exec, errorParser =
dm.composeActionExec(manifestFilePath, manifestFileName, action)
+ if errorParser != nil {
+ return nil, errorParser
}
- /*
- * Action.Annotations
- */
- listOfAnnotations := make(whisk.KeyValueArr, 0)
- for name, value := range action.Annotations {
- var keyVal whisk.KeyValue
- keyVal.Key = name
- keyVal.Value = wskenv.InterpolateStringWithEnvVar(value)
- listOfAnnotations = append(listOfAnnotations, keyVal)
+ // Action.Inputs
+ listOfInputs, err := dm.composeInputsOrOutputs(action.Inputs,
manifestFilePath)
+ if err != nil {
+ return nil, err
}
- if len(listOfAnnotations) > 0 {
+ if len(listOfInputs) > 0 {
+ wskaction.Parameters = listOfInputs
+ }
+
+ // Action.Outputs
+ // TODO{} add outputs as annotations (work to discuss
officially supporting for compositions)
+ listOfOutputs, err := dm.composeInputsOrOutputs(action.Outputs,
manifestFilePath)
+ if err != nil {
+ return nil, err
+ }
+ if len(listOfOutputs) > 0 {
+ //wskaction.Annotations = listOfOutputs
+ }
+
+ // Action.Annotations
+ if listOfAnnotations :=
dm.composeAnnotations(action.Annotations); len(listOfAnnotations) > 0 {
wskaction.Annotations = append(wskaction.Annotations,
listOfAnnotations...)
}
+
// add managed annotations if its marked as managed deployment
if utils.Flags.Managed {
wskaction.Annotations = append(wskaction.Annotations,
ma)
}
- /*
- * Web Export
- */
+ // Web Export
// Treat ACTION as a web action, a raw HTTP web action, or as a
standard action based on web-export;
// when web-export is set to yes | true, treat action as a web
action,
// when web-export is set to raw, treat action as a raw HTTP
web action,
// when web-export is set to no | false, treat action as a
standard action
if len(action.Webexport) != 0 {
- wskaction.Annotations, errorParser =
utils.WebAction(filePath, action.Name, action.Webexport, listOfAnnotations,
false)
+ wskaction.Annotations, errorParser =
utils.WebAction(manifestFilePath, action.Name, action.Webexport,
wskaction.Annotations, false)
if errorParser != nil {
- return s1, errorParser
+ return listOfActions, errorParser
}
}
- /*
- * Action.Limits
- */
+ // Action.Limits
if action.Limits != nil {
- wsklimits := new(whisk.Limits)
-
- // TODO() use LIMITS_SUPPORTED in yamlparser to
enumerata through instead of hardcoding
- // perhaps change into a tuple
- if utils.LimitsTimeoutValidation(action.Limits.Timeout)
{
- wsklimits.Timeout = action.Limits.Timeout
- } else {
- warningString :=
wski18n.T(wski18n.ID_WARN_LIMIT_IGNORED_X_limit_X,
-
map[string]interface{}{wski18n.KEY_LIMIT: LIMIT_VALUE_TIMEOUT})
- wskprint.PrintOpenWhiskWarning(warningString)
- }
- if utils.LimitsMemoryValidation(action.Limits.Memory) {
- wsklimits.Memory = action.Limits.Memory
- } else {
- warningString :=
wski18n.T(wski18n.ID_WARN_LIMIT_IGNORED_X_limit_X,
-
map[string]interface{}{wski18n.KEY_LIMIT: LIMIT_VALUE_MEMORY_SIZE})
- wskprint.PrintOpenWhiskWarning(warningString)
- }
- if utils.LimitsLogsizeValidation(action.Limits.Logsize)
{
- wsklimits.Logsize = action.Limits.Logsize
- } else {
- warningString :=
wski18n.T(wski18n.ID_WARN_LIMIT_IGNORED_X_limit_X,
-
map[string]interface{}{wski18n.KEY_LIMIT: LIMIT_VALUE_LOG_SIZE})
- wskprint.PrintOpenWhiskWarning(warningString)
- }
- if wsklimits.Timeout != nil || wsklimits.Memory != nil
|| wsklimits.Logsize != nil {
+ if wsklimits :=
dm.composeActionLimits(*(action.Limits)); wsklimits != nil {
wskaction.Limits = wsklimits
}
-
- // TODO() use LIMITS_UNSUPPORTED in yamlparser to
enumerata through instead of hardcoding
- // emit warning errors if these limits are not nil
-
utils.NotSupportLimits(action.Limits.ConcurrentActivations,
LIMIT_VALUE_CONCURRENT_ACTIVATIONS)
-
utils.NotSupportLimits(action.Limits.UserInvocationRate,
LIMIT_VALUE_USER_INVOCATION_RATE)
- utils.NotSupportLimits(action.Limits.CodeSize,
LIMIT_VALUE_CODE_SIZE)
- utils.NotSupportLimits(action.Limits.ParameterSize,
LIMIT_VALUE_PARAMETER_SIZE)
}
- wskaction.Name = key
+ wskaction.Name = actionName
pub := false
wskaction.Publish = &pub
- record := utils.ActionRecord{Action: wskaction, Packagename:
packageName, Filepath: action.Function}
- s1 = append(s1, record)
+ record := utils.ActionRecord{Action: wskaction, Packagename:
packageName, Filepath: actionFilePath}
+ listOfActions = append(listOfActions, record)
}
- return s1, nil
+ return listOfActions, nil
}
@@ -802,7 +814,7 @@ func (dm *YAMLParser)
ComposeTriggersFromAllPackages(manifest *YAML, filePath st
func (dm *YAMLParser) ComposeTriggers(filePath string, pkg Package, ma
whisk.KeyValue) ([]*whisk.Trigger, error) {
var errorParser error
- var t1 []*whisk.Trigger = make([]*whisk.Trigger, 0)
+ var listOfTriggers []*whisk.Trigger = make([]*whisk.Trigger, 0)
for _, trigger := range pkg.GetTriggerList() {
wsktrigger := new(whisk.Trigger)
@@ -812,7 +824,7 @@ func (dm *YAMLParser) ComposeTriggers(filePath string, pkg
Package, ma whisk.Key
wsktrigger.Publish = &pub
// print warning information when .Source key's value is not
empty
- if trigger.Source != "" {
+ if len(trigger.Source) != 0 {
warningString := wski18n.T(
wski18n.ID_WARN_KEY_DEPRECATED_X_oldkey_X_filetype_X_newkey_X,
map[string]interface{}{
@@ -821,7 +833,7 @@ func (dm *YAMLParser) ComposeTriggers(filePath string, pkg
Package, ma whisk.Key
wski18n.KEY_FILE_TYPE:
wski18n.MANIFEST_FILE})
wskprint.PrintOpenWhiskWarning(warningString)
}
- if trigger.Feed == "" {
+ if len(trigger.Feed) == 0 {
trigger.Feed = trigger.Source
}
@@ -830,44 +842,23 @@ func (dm *YAMLParser) ComposeTriggers(filePath string,
pkg Package, ma whisk.Key
trigger.Feed =
wskenv.InterpolateStringWithEnvVar(trigger.Feed).(string)
keyValArr := make(whisk.KeyValueArr, 0)
- if trigger.Feed != "" {
+ if len(trigger.Feed) != 0 {
var keyVal whisk.KeyValue
-
keyVal.Key = YAML_KEY_FEED
keyVal.Value = trigger.Feed
-
keyValArr = append(keyValArr, keyVal)
-
wsktrigger.Annotations = keyValArr
}
- keyValArr = make(whisk.KeyValueArr, 0)
- for name, param := range trigger.Inputs {
- var keyVal whisk.KeyValue
- keyVal.Key = name
-
- keyVal.Value, errorParser = ResolveParameter(name,
¶m, filePath)
-
- if errorParser != nil {
- return nil, errorParser
- }
-
- if keyVal.Value != nil {
- keyValArr = append(keyValArr, keyVal)
- }
+ inputs, err := dm.composeInputsOrOutputs(trigger.Inputs,
filePath)
+ if err != nil {
+ return nil, errorParser
}
-
- if len(keyValArr) > 0 {
- wsktrigger.Parameters = keyValArr
+ if len(inputs) > 0 {
+ wsktrigger.Parameters = inputs
}
- listOfAnnotations := make(whisk.KeyValueArr, 0)
- for name, value := range trigger.Annotations {
- var keyVal whisk.KeyValue
- keyVal.Key = name
- keyVal.Value = wskenv.InterpolateStringWithEnvVar(value)
- listOfAnnotations = append(listOfAnnotations, keyVal)
- }
+ listOfAnnotations := dm.composeAnnotations(trigger.Annotations)
if len(listOfAnnotations) > 0 {
wsktrigger.Annotations = append(wsktrigger.Annotations,
listOfAnnotations...)
}
@@ -877,9 +868,9 @@ func (dm *YAMLParser) ComposeTriggers(filePath string, pkg
Package, ma whisk.Key
wsktrigger.Annotations = append(wsktrigger.Annotations,
ma)
}
- t1 = append(t1, wsktrigger)
+ listOfTriggers = append(listOfTriggers, wsktrigger)
}
- return t1, nil
+ return listOfTriggers, nil
}
func (dm *YAMLParser) ComposeRulesFromAllPackages(manifest *YAML, ma
whisk.KeyValue) ([]*whisk.Rule, error) {
@@ -904,7 +895,7 @@ func (dm *YAMLParser) ComposeRulesFromAllPackages(manifest
*YAML, ma whisk.KeyVa
}
func (dm *YAMLParser) ComposeRules(pkg Package, packageName string, ma
whisk.KeyValue) ([]*whisk.Rule, error) {
- var r1 []*whisk.Rule = make([]*whisk.Rule, 0)
+ var rules []*whisk.Rule = make([]*whisk.Rule, 0)
for _, rule := range pkg.GetRuleList() {
wskrule := new(whisk.Rule)
@@ -915,18 +906,12 @@ func (dm *YAMLParser) ComposeRules(pkg Package,
packageName string, ma whisk.Key
wskrule.Trigger = wskenv.ConvertSingleName(rule.Trigger)
wskrule.Action = wskenv.ConvertSingleName(rule.Action)
act := strings.TrimSpace(wskrule.Action.(string))
- if !strings.ContainsRune(act, '/') && !strings.HasPrefix(act,
packageName+"/") &&
+ if !strings.ContainsRune(act, []rune(PATH_SEPARATOR)[0]) &&
!strings.HasPrefix(act, packageName+PATH_SEPARATOR) &&
strings.ToLower(packageName) != DEFAULT_PACKAGE {
act = path.Join(packageName, act)
}
wskrule.Action = act
- listOfAnnotations := make(whisk.KeyValueArr, 0)
- for name, value := range rule.Annotations {
- var keyVal whisk.KeyValue
- keyVal.Key = name
- keyVal.Value = wskenv.InterpolateStringWithEnvVar(value)
- listOfAnnotations = append(listOfAnnotations, keyVal)
- }
+ listOfAnnotations := dm.composeAnnotations(rule.Annotations)
if len(listOfAnnotations) > 0 {
wskrule.Annotations = append(wskrule.Annotations,
listOfAnnotations...)
}
@@ -936,24 +921,20 @@ func (dm *YAMLParser) ComposeRules(pkg Package,
packageName string, ma whisk.Key
wskrule.Annotations = append(wskrule.Annotations, ma)
}
- r1 = append(r1, wskrule)
+ rules = append(rules, wskrule)
}
- return r1, nil
+ return rules, nil
}
func (dm *YAMLParser) ComposeApiRecordsFromAllPackages(client *whisk.Config,
manifest *YAML) ([]*whisk.ApiCreateRequest, error) {
var requests []*whisk.ApiCreateRequest =
make([]*whisk.ApiCreateRequest, 0)
manifestPackages := make(map[string]Package)
- //if manifest.Package.Packagename != "" {
- // return dm.ComposeApiRecords(client,
manifest.Package.Packagename, manifest.Package, manifest.Filepath)
- //} else {
if len(manifest.Packages) != 0 {
manifestPackages = manifest.Packages
} else {
manifestPackages = manifest.GetProject().Packages
}
- //}
for packageName, p := range manifestPackages {
r, err := dm.ComposeApiRecords(client, packageName, p,
manifest.Filepath)
@@ -1005,13 +986,13 @@ func (dm *YAMLParser) ComposeApiRecords(client
*whisk.Config, packageName string
for apiName, apiDoc := range pkg.Apis {
for gatewayBasePath, gatewayBasePathMap := range apiDoc {
// append "/" to the gateway base path if its missing
- if !strings.HasPrefix(gatewayBasePath, PATH_SEPERATOR) {
- gatewayBasePath = PATH_SEPERATOR +
gatewayBasePath
+ if !strings.HasPrefix(gatewayBasePath, PATH_SEPARATOR) {
+ gatewayBasePath = PATH_SEPARATOR +
gatewayBasePath
}
for gatewayRelPath, gatewayRelPathMap := range
gatewayBasePathMap {
// append "/" to the gateway relative path if
its missing
- if !strings.HasPrefix(gatewayRelPath,
PATH_SEPERATOR) {
- gatewayRelPath = PATH_SEPERATOR +
gatewayRelPath
+ if !strings.HasPrefix(gatewayRelPath,
PATH_SEPARATOR) {
+ gatewayRelPath = PATH_SEPARATOR +
gatewayRelPath
}
for actionName, gatewayMethod := range
gatewayRelPathMap {
// verify that the action is defined
under actions sections
@@ -1051,12 +1032,13 @@ func (dm *YAMLParser) ComposeApiRecords(client
*whisk.Config, packageName string
if packageName ==
DEFAULT_PACKAGE {
request.ApiDoc.Action.Name = actionName
} else {
-
request.ApiDoc.Action.Name = packageName + PATH_SEPERATOR + actionName
+
request.ApiDoc.Action.Name = packageName + PATH_SEPARATOR + actionName
}
- url := []string{HTTPS +
":" + PATH_SEPERATOR, client.Host, strings.ToLower(API),
- API_VERSION,
WEB, client.Namespace, packageName, actionName + "." + HTTP}
+ url := []string{HTTPS +
client.Host, strings.ToLower(API),
+ API_VERSION,
WEB, client.Namespace, packageName,
+ actionName +
"." + utils.HTTP_FILE_EXTENSION}
request.ApiDoc.Action.Namespace = client.Namespace
-
request.ApiDoc.Action.BackendUrl = strings.Join(url, PATH_SEPERATOR)
+
request.ApiDoc.Action.BackendUrl = strings.Join(url, PATH_SEPARATOR)
request.ApiDoc.Action.BackendMethod = gatewayMethod
request.ApiDoc.Action.Auth = client.AuthToken
// add a newly created
ApiCreateRequest object to a list of requests
diff --git a/parsers/manifest_parser_test.go b/parsers/manifest_parser_test.go
index 6b3dcca6..bc6948eb 100644
--- a/parsers/manifest_parser_test.go
+++ b/parsers/manifest_parser_test.go
@@ -48,6 +48,9 @@ const (
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."
+ TEST_MSG_ACTION_DOCKER_KIND_MISMATCH = "Docker action kind
is set to [%s] instead of " + utils.BLACKBOX
+ TEST_MSG_ACTION_DOCKER_IMAGE_MISMATCH = "Docker action image
had a value mismatch."
+ TEST_MSG_ACTION_CODE_MISSING = "Action code is
missing."
// local error messages
TEST_ERROR_MANIFEST_PARSE_FAILURE = "Manifest [%s]: Failed to
parse."
@@ -893,6 +896,44 @@ func TestComposeActionsForFunctionWithRemoteDir(t
*testing.T) {
assert.NotNil(t, err, "Compose actions should have exited with error
when code is specified but runtime is missing.")
}
+// validate manifest_parser.ComposeActions() method
+func TestComposeActionsForDocker(t *testing.T) {
+
+ file := "../tests/dat/manifest_data_compose_actions_for_docker.yaml"
+ actionFile := "../tests/src/integration/docker/actions/exec.zip"
+
+ p, m, _ := testLoadParseManifest(t, file)
+
+ actions, err := p.ComposeActionsFromAllPackages(m, m.Filepath,
whisk.KeyValue{})
+ assert.Nil(t, err, fmt.Sprintf(TEST_ERROR_COMPOSE_ACTION_FAILURE, file))
+
+ var expectedResult, actualResult string
+ for _, action := range actions {
+ switch action.Action.Name {
+ case "OpenWhiskSkeleton":
+ case "OpenWhiskSkeletonWithNative":
+ assert.Equal(t, utils.BLACKBOX,
action.Action.Exec.Kind, fmt.Sprintf(TEST_MSG_ACTION_DOCKER_KIND_MISMATCH,
action.Action.Exec.Kind))
+ assert.Equal(t, NATIVE_DOCKER_IMAGE,
action.Action.Exec.Image, TEST_MSG_ACTION_DOCKER_IMAGE_MISMATCH)
+ case "CustomDockerAction1":
+ case "CustomDockerAction2":
+ expectedResult, _ = filepath.Abs(actionFile)
+ actualResult, _ = filepath.Abs(action.Filepath)
+ assert.Equal(t, expectedResult, actualResult,
TEST_MSG_ACTION_FUNCTION_PATH_MISMATCH)
+ assert.Equal(t, utils.BLACKBOX,
action.Action.Exec.Kind, fmt.Sprintf(TEST_MSG_ACTION_DOCKER_KIND_MISMATCH,
action.Action.Exec.Kind))
+ assert.Equal(t, NATIVE_DOCKER_IMAGE,
action.Action.Exec.Image, TEST_MSG_ACTION_DOCKER_IMAGE_MISMATCH)
+ case "CustomDockerAction3":
+ case "CustomDockerAction4":
+ assert.NotNil(t, action.Action.Exec.Code,
TEST_MSG_ACTION_CODE_MISSING)
+ assert.Equal(t, utils.BLACKBOX,
action.Action.Exec.Kind, fmt.Sprintf(TEST_MSG_ACTION_DOCKER_KIND_MISMATCH,
action.Action.Exec.Kind))
+ assert.Equal(t, NATIVE_DOCKER_IMAGE,
action.Action.Exec.Image, TEST_MSG_ACTION_DOCKER_IMAGE_MISMATCH)
+ case "CustomDockerAction5":
+ assert.NotNil(t, action.Action.Exec.Code,
TEST_MSG_ACTION_CODE_MISSING)
+ assert.Equal(t, utils.BLACKBOX,
action.Action.Exec.Kind, fmt.Sprintf(TEST_MSG_ACTION_DOCKER_KIND_MISMATCH,
action.Action.Exec.Kind))
+ assert.Equal(t, "mydockerhub/myimage",
action.Action.Exec.Image, TEST_MSG_ACTION_DOCKER_IMAGE_MISMATCH)
+ }
+ }
+}
+
// Test 14: validate manifest_parser.ComposeActions() method
func TestComposeActionsForLimits(t *testing.T) {
diff --git a/parsers/yamlparser.go b/parsers/yamlparser.go
index 2267e065..aa67d9cc 100644
--- a/parsers/yamlparser.go
+++ b/parsers/yamlparser.go
@@ -90,24 +90,26 @@ type YAMLParser struct {
// Action is mapped to wsk.Action.*
// Used in both manifest and deployment files
type Action struct {
- Version string `yaml:"version"`
+ Name string
// TODO(): deprecate location in favor of function
- Location string `yaml:"location"`
- Function string `yaml:"function"`
- Code string `yaml:"code"`
- Runtime string `yaml:"runtime,omitempty"`
- Namespace string `yaml:"namespace"`
- Credential string `yaml:"credential"`
- Inputs map[string]Parameter `yaml:"inputs"`
- Outputs map[string]Parameter `yaml:"outputs"`
- Name string
+ Location string `yaml:"location"`
+ Version string `yaml:"version"`
+ Function string `yaml:"function"`
+ Code string `yaml:"code"`
+ Runtime string `yaml:"runtime,omitempty"`
+ Namespace string `yaml:"namespace"`
+ Credential string `yaml:"credential"`
+ ExposedUrl string `yaml:"exposedUrl"`
+ Webexport string `yaml:"web-export"`
+ Main string `yaml:"main"`
+ Docker string `yaml:"docker,omitempty"`
+ Native bool `yaml:"native,omitempty"`
+ Limits *Limits `yaml:"limits"`
+ Inputs map[string]Parameter `yaml:"inputs"`
+ Outputs map[string]Parameter `yaml:"outputs"`
Annotations map[string]interface{} `yaml:"annotations,omitempty"`
// TODO() this is propoagated from package to every action within that
package
//Parameters map[string]interface{} `yaml:parameters`
- ExposedUrl string `yaml:"exposedUrl"`
- Webexport string `yaml:"web-export"`
- Main string `yaml:"main"`
- Limits *Limits `yaml:"limits"`
}
type Limits struct {
diff --git a/tests/dat/manifest_data_compose_actions_for_docker.yaml
b/tests/dat/manifest_data_compose_actions_for_docker.yaml
new file mode 100644
index 00000000..7fb98193
--- /dev/null
+++ b/tests/dat/manifest_data_compose_actions_for_docker.yaml
@@ -0,0 +1,39 @@
+#
+# 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.
+#
+
+packages:
+ IntegrationTestDocker:
+ actions:
+ OpenWhiskSkeleton:
+ docker: openwhisk/skeleton
+ OpenWhiskSkeletonWithNative:
+ native: true
+ CustomDockerAction1:
+ function: ../src/integration/docker/actions/exec.zip
+ docker: openwhisk/skeleton
+ CustomDockerAction2:
+ function: ../src/integration/docker/actions/exec.zip
+ native: true
+ CustomDockerAction3:
+ function: ../src/integration/docker/actions/bash
+ native: true
+ CustomDockerAction4:
+ function: ../src/integration/docker/actions/go
+ native: true
+ CustomDockerAction5:
+ function: ../src/integration/docker/actions/go
+ docker: mydockerhub/myimage
+
diff --git a/tests/src/integration/docker/actions/bash/exec
b/tests/src/integration/docker/actions/bash/exec
new file mode 100755
index 00000000..85837170
--- /dev/null
+++ b/tests/src/integration/docker/actions/bash/exec
@@ -0,0 +1,2 @@
+#!/bin/bash
+echo "{ \"hello\": \"ran without a docker pull!\" }"
diff --git a/tests/src/integration/docker/actions/exec.zip
b/tests/src/integration/docker/actions/exec.zip
new file mode 100644
index 00000000..e81c16a9
Binary files /dev/null and b/tests/src/integration/docker/actions/exec.zip
differ
diff --git a/tests/src/integration/docker/actions/go/exec
b/tests/src/integration/docker/actions/go/exec
new file mode 100755
index 00000000..eb278583
Binary files /dev/null and b/tests/src/integration/docker/actions/go/exec differ
diff --git a/tests/src/integration/docker/actions/go/sample.go
b/tests/src/integration/docker/actions/go/sample.go
new file mode 100644
index 00000000..a7fb638d
--- /dev/null
+++ b/tests/src/integration/docker/actions/go/sample.go
@@ -0,0 +1,44 @@
+/*
+ * 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 main
+
+import "encoding/json"
+import "fmt"
+import "os"
+
+func main() {
+ //program receives one argument: the JSON object as a string
+ arg := os.Args[1]
+
+ // unmarshal the string to a JSON object
+ var obj map[string]interface{}
+ json.Unmarshal([]byte(arg), &obj)
+
+ // can optionally log to stdout (or stderr)
+ fmt.Println("hello Go action")
+
+ name, ok := obj["name"].(string)
+ if !ok {
+ name = "Stranger"
+ }
+
+ // last line of stdout is the result JSON object as a string
+ msg := map[string]string{"msg": ("Hello, " + name + "!")}
+ res, _ := json.Marshal(msg)
+ fmt.Println(string(res))
+}
diff --git a/tests/src/integration/docker/docker_test.go
b/tests/src/integration/docker/docker_test.go
new file mode 100644
index 00000000..6f50f396
--- /dev/null
+++ b/tests/src/integration/docker/docker_test.go
@@ -0,0 +1,39 @@
+// +build integration
+
+/*
+ * 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 tests
+
+import (
+
"github.com/apache/incubator-openwhisk-wskdeploy/tests/src/integration/common"
+ "github.com/stretchr/testify/assert"
+ "os"
+ "testing"
+)
+
+func TestDocker(t *testing.T) {
+ wskdeploy := common.NewWskdeploy()
+ _, err := wskdeploy.DeployManifestPathOnly(manifestPath)
+ assert.Equal(t, nil, err, "Failed to deploy based on the manifest
file.")
+ _, err = wskdeploy.UndeployManifestPathOnly(manifestPath)
+ assert.Equal(t, nil, err, "Failed to undeploy based on the manifest
file.")
+}
+
+var (
+ manifestPath = os.Getenv("GOPATH") +
"/src/github.com/apache/incubator-openwhisk-wskdeploy/tests/src/integration/docker/manifest.yaml"
+)
diff --git a/tests/src/integration/docker/manifest.yaml
b/tests/src/integration/docker/manifest.yaml
new file mode 100644
index 00000000..e876e89b
--- /dev/null
+++ b/tests/src/integration/docker/manifest.yaml
@@ -0,0 +1,46 @@
+#
+# 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.
+#
+
+packages:
+ IntegrationTestDocker:
+ actions:
+ OpenWhiskSkeleton:
+ docker: openwhisk/skeleton
+ OpenWhiskSkeletonWithNative:
+ native: true
+ CustomDockerAction1:
+ function: actions/exec.zip
+ docker: openwhisk/skeleton
+ CustomDockerAction2:
+ function: actions/exec.zip
+ native: true
+ CustomDockerAction3:
+ function: actions/bash
+ native: true
+ CustomDockerAction4:
+ function: actions/go
+ native: true
+ sequences:
+ docker-series:
+ actions: OpenWhiskSkeleton, OpenWhiskSkeletonWithNative,
CustomDockerAction1, CustomDockerAction2, CustomDockerAction3,
CustomDockerAction4
+ triggers:
+ # trigger to activate helloworld sequence
+ triggerDocker:
+ rules:
+ # rule associating trigger with sequence of helloworld actions
+ ruleMappingDocker:
+ trigger: triggerDocker
+ action: docker-series
diff --git a/utils/misc.go b/utils/misc.go
index e10a37e3..1e4196a3 100644
--- a/utils/misc.go
+++ b/utils/misc.go
@@ -20,7 +20,6 @@ package utils
import (
"archive/zip"
"bufio"
- "encoding/base64"
"errors"
"fmt"
"io"
@@ -181,74 +180,6 @@ func (zw *ZipWritter) Zip() error {
return nil
}
-// below codes is from wsk cli with tiny adjusts.
-func GetExec(artifact string, kind string, isDocker bool, mainEntry string)
(*whisk.Exec, error) {
- var err error
- var code string
- var content []byte
- 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_FILE_EXTENSION {
- content, err = new(ContentReader).ReadLocal(artifact)
- if err != nil {
- return nil, err
- }
- code = string(content)
- exec.Code = &code
- }
-
- if len(kind) > 0 {
- exec.Kind = kind
- } else if isDocker {
- exec.Kind = "blackbox"
- if ext != ZIP_FILE_EXTENSION {
- exec.Image = artifact
- } else {
- exec.Image = "openwhisk/dockerskeleton"
- }
- } else {
- 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)
- }
- }
-
- // Error if entry point is not specified for Java
- if len(mainEntry) != 0 {
- exec.Main = mainEntry
- } else {
- if exec.Kind == "java" {
- return nil, javaEntryError()
- }
- }
-
- // Base64 encode the zip file content
- if ext == ZIP_FILE_EXTENSION {
- code = base64.StdEncoding.EncodeToString([]byte(code))
- exec.Code = &code
- }
-
- return exec, nil
-}
-
func zipKindError() error {
errMsg := wski18n.T("creating an action from a .zip artifact requires
specifying the action kind explicitly")
diff --git a/utils/runtimes.go b/utils/runtimes.go
index bf3cc770..15399efb 100644
--- a/utils/runtimes.go
+++ b/utils/runtimes.go
@@ -40,6 +40,8 @@ const (
HTTP_CONTENT_TYPE_KEY = "Content-Type"
HTTP_CONTENT_TYPE_VALUE = "application/json; charset=UTF-8"
RUNTIME_NOT_SPECIFIED = "NOT SPECIFIED"
+ BLACKBOX = "blackbox"
+ HTTP_FILE_EXTENSION = "http"
)
// Structs used to denote the OpenWhisk Runtime information
----------------------------------------------------------------
This is an automated message from the Apache Git Service.
To respond to the message, please log on GitHub and use the
URL above to go to the specific comment.
For queries about this service, please contact Infrastructure at:
[email protected]
With regards,
Apache Git Services