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

[incubator-openwhisk-wskdeploy] branch master updated: Adding Action Annotations (#631)

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 2d400be  Adding Action Annotations (#631)
2d400be is described below

commit 2d400be898f2400c38fa6107b1e8245be9cc8333
Author: Priti Desai <pd...@us.ibm.com>
AuthorDate: Wed Nov 1 14:17:19 2017 -0700

    Adding Action Annotations (#631)
    
    * fixing annotation on actions
    
    * adding better unit test
    
    * adding integration test
    
    * Adding a new line
    
    * adding similar precedence order for package and trigger annotations
    
    * Adding translatable error message and fixing few grammatical errors
---
 deployers/deploymentreader.go                      | 118 ++++++++-----
 deployers/deploymentreader_test.go                 | 183 ++++++++++++++-------
 deployers/servicedeployer.go                       |   8 +-
 parsers/manifest_parser.go                         |  38 ++++-
 parsers/manifest_parser_test.go                    |  34 ++++
 .../deployment_validate_action_annotations.yaml    |  31 ++++
 .../dat/manifest_validate_action_annotations.yaml  |  65 ++++++++
 .../validate-action-annotations/actions/hello.js   |  26 +++
 .../deployment-with-invalid-annotations.yaml       |  22 +++
 .../validate-action-annotations/deployment.yaml    |  31 ++++
 .../validate-action-annotations/manifest.yaml      |  65 ++++++++
 .../validate-action-annotations_test.go            |  48 ++++++
 .../deployment.yaml                                |  53 +++---
 .../manifest.yaml                                  |  25 +++
 .../validate-bind-inputs-anno_test.go              |  73 ++++++--
 utils/misc.go                                      |  71 --------
 utils/webaction.go                                 |  94 +++++++++++
 17 files changed, 753 insertions(+), 232 deletions(-)

diff --git a/deployers/deploymentreader.go b/deployers/deploymentreader.go
index d9e5759..21dd39e 100644
--- a/deployers/deploymentreader.go
+++ b/deployers/deploymentreader.go
@@ -21,6 +21,7 @@ import (
 	"github.com/apache/incubator-openwhisk-client-go/whisk"
 	"github.com/apache/incubator-openwhisk-wskdeploy/parsers"
 	"github.com/apache/incubator-openwhisk-wskdeploy/utils"
+	"github.com/apache/incubator-openwhisk-wskdeploy/wski18n"
 )
 
 type DeploymentReader struct {
@@ -51,15 +52,20 @@ func (reader *DeploymentReader) HandleYaml() error {
 // Update entities with deployment settings
 func (reader *DeploymentReader) BindAssets() error {
 
-	reader.bindPackageInputsAndAnnotations()
-	reader.bindActionInputsAndAnnotations()
-	reader.bindTriggerInputsAndAnnotations()
+	if err := reader.bindPackageInputsAndAnnotations(); err != nil {
+		return err
+	}
+	if err := reader.bindActionInputsAndAnnotations(); err != nil {
+		return err
+	}
+	if err := reader.bindTriggerInputsAndAnnotations(); err != nil {
+		return err
+	}
 
 	return nil
-
 }
 
-func (reader *DeploymentReader) bindPackageInputsAndAnnotations() {
+func (reader *DeploymentReader) bindPackageInputsAndAnnotations() error {
 
 	packMap := make(map[string]parsers.Package)
 
@@ -121,25 +127,33 @@ func (reader *DeploymentReader) bindPackageInputsAndAnnotations() {
 			serviceDeployPack.Package.Parameters = keyValArr
 		}
 
-		keyValArr = make(whisk.KeyValueArr, 0)
-
 		if len(pack.Annotations) > 0 {
+			// iterate over each annotation from deployment file
 			for name, input := range pack.Annotations {
-				var keyVal whisk.KeyValue
-
-				keyVal.Key = name
-				keyVal.Value = utils.GetEnvVar(input)
-
-				keyValArr = append(keyValArr, keyVal)
+				// check if annotation key in deployment file exists in manifest file
+				// setting a bool flag to false assuming key does not exist in manifest
+				keyExistsInManifest := false
+				// iterate over each annotation from manifest file
+				for i, a := range serviceDeployPack.Package.Annotations {
+					if name == a.Key {
+						// annotation key is found in manifest
+						keyExistsInManifest = true
+						// overwrite annotation in manifest file with deployment file
+						serviceDeployPack.Package.Annotations[i].Value = input
+						break
+					}
+				}
+				if !keyExistsInManifest {
+					err := wski18n.T("Annotation key \"" + name + "\" does not exist in manifest file but specified in deployment file.")
+					return utils.NewYAMLFormatError(err)
+				}
 			}
-
-			serviceDeployPack.Package.Annotations = keyValArr
 		}
-
 	}
+	return nil
 }
 
-func (reader *DeploymentReader) bindActionInputsAndAnnotations() {
+func (reader *DeploymentReader) bindActionInputsAndAnnotations() error {
 
 	packMap := make(map[string]parsers.Package)
 
@@ -202,28 +216,34 @@ func (reader *DeploymentReader) bindActionInputsAndAnnotations() {
 				}
 			}
 
-			keyValArr = make(whisk.KeyValueArr, 0)
-
-			if len(action.Annotations) > 0 {
+			if wskAction, exists := serviceDeployPack.Actions[actionName]; exists {
+				// iterate over each annotation from deployment file
 				for name, input := range action.Annotations {
-					var keyVal whisk.KeyValue
-
-					keyVal.Key = name
-					keyVal.Value = input
-
-					keyValArr = append(keyValArr, keyVal)
-				}
-
-				if wskAction, exists := serviceDeployPack.Actions[actionName]; exists {
-					wskAction.Action.Annotations = keyValArr
+					// check if annotation key in deployment file exists in manifest file
+					// setting a bool flag to false assuming key does not exist in manifest
+					keyExistsInManifest := false
+					// iterate over each annotation from manifest file
+					for i, a := range wskAction.Action.Annotations {
+						if name == a.Key {
+							// annotation key is found in manifest
+							keyExistsInManifest = true
+							// overwrite annotation in manifest file with deployment file
+							wskAction.Action.Annotations[i].Value = input
+							break
+						}
+					}
+					if !keyExistsInManifest {
+						err := wski18n.T("Annotation key \"" + name + "\" does not exist in manifest file but specified in deployment file.")
+						return utils.NewYAMLFormatError(err)
+					}
 				}
 			}
 		}
-
 	}
+	return nil
 }
 
-func (reader *DeploymentReader) bindTriggerInputsAndAnnotations() {
+func (reader *DeploymentReader) bindTriggerInputsAndAnnotations() error {
 
 	packMap := make(map[string]parsers.Package)
 
@@ -282,23 +302,31 @@ func (reader *DeploymentReader) bindTriggerInputsAndAnnotations() {
 				}
 			}
 
-			keyValArr = make(whisk.KeyValueArr, 0)
-
-			if len(trigger.Annotations) > 0 {
+			if wskTrigger, exists := serviceDeployment.Triggers[triggerName]; exists {
+				// iterate over each annotation from deployment file
 				for name, input := range trigger.Annotations {
-					var keyVal whisk.KeyValue
-
-					keyVal.Key = name
-					keyVal.Value = input
-
-					keyValArr = append(keyValArr, keyVal)
-				}
-
-				if wskTrigger, exists := serviceDeployment.Triggers[triggerName]; exists {
-					wskTrigger.Annotations = keyValArr
+					// check if annotation key in deployment file exists in manifest file
+					// setting a bool flag to false assuming key does not exist in manifest
+					keyExistsInManifest := false
+					// iterate over each annotation from manifest file
+					for i, a := range wskTrigger.Annotations {
+						if name == a.Key {
+							// annotation key is found in manifest
+							keyExistsInManifest = true
+							// overwrite annotation in manifest file with deployment file
+							wskTrigger.Annotations[i].Value = input
+							break
+						}
+					}
+					if !keyExistsInManifest {
+						err := wski18n.T("Annotation key \"" + name + "\" does not exist in manifest file but specified in deployment file.")
+						return utils.NewYAMLFormatError(err)
+					}
 				}
 			}
+
 		}
 
 	}
+	return nil
 }
diff --git a/deployers/deploymentreader_test.go b/deployers/deploymentreader_test.go
index 53e326b..a4a518e 100644
--- a/deployers/deploymentreader_test.go
+++ b/deployers/deploymentreader_test.go
@@ -20,9 +20,10 @@
 package deployers
 
 import (
+	"github.com/apache/incubator-openwhisk-client-go/whisk"
 	"github.com/stretchr/testify/assert"
 	"testing"
-	"github.com/apache/incubator-openwhisk-client-go/whisk"
+	"reflect"
 )
 
 var sd *ServiceDeployer
@@ -87,70 +88,124 @@ func TestDeploymentReader_bindTrigger(t *testing.T) {
 }
 
 func TestDeploymentReader_bindTrigger_packages(t *testing.T) {
-    //init variables
-    sDeployer := NewServiceDeployer()
-    sDeployer.DeploymentPath = "../tests/dat/deployment-deploymentreader-test-packages.yml"
-    sDeployer.Deployment.Triggers["locationUpdate"] = new(whisk.Trigger)
-
-    //parse deployment and bind triggers input and annotation
-    dReader := NewDeploymentReader(sDeployer)
-    dReader.HandleYaml()
-    dReader.bindTriggerInputsAndAnnotations()
-
-    trigger := sDeployer.Deployment.Triggers["locationUpdate"]
-    for _, param := range trigger.Parameters {
-        switch param.Key {
-        case "name":
-            assert.Equal(t, "Bernie", param.Value, "Failed to set inputs")
-        case "place":
-            assert.Equal(t, "DC", param.Value, "Failed to set inputs")
-        default:
-            assert.Fail(t, "Failed to get inputs key")
-
-        }
-    }
-    for _, annos := range trigger.Annotations {
-        switch annos.Key {
-        case "bbb":
-            assert.Equal(t, "this is an annotation", annos.Value, "Failed to set annotations")
-        default:
-            assert.Fail(t, "Failed to get annotation key")
-
-        }
-    }
+	//init variables
+	sDeployer := NewServiceDeployer()
+	sDeployer.DeploymentPath = "../tests/dat/deployment-deploymentreader-test-packages.yml"
+	sDeployer.Deployment.Triggers["locationUpdate"] = new(whisk.Trigger)
+
+	//parse deployment and bind triggers input and annotation
+	dReader := NewDeploymentReader(sDeployer)
+	dReader.HandleYaml()
+	dReader.bindTriggerInputsAndAnnotations()
+
+	trigger := sDeployer.Deployment.Triggers["locationUpdate"]
+	for _, param := range trigger.Parameters {
+		switch param.Key {
+		case "name":
+			assert.Equal(t, "Bernie", param.Value, "Failed to set inputs")
+		case "place":
+			assert.Equal(t, "DC", param.Value, "Failed to set inputs")
+		default:
+			assert.Fail(t, "Failed to get inputs key")
+
+		}
+	}
+	for _, annos := range trigger.Annotations {
+		switch annos.Key {
+		case "bbb":
+			assert.Equal(t, "this is an annotation", annos.Value, "Failed to set annotations")
+		default:
+			assert.Fail(t, "Failed to get annotation key")
+
+		}
+	}
 }
 
 func TestDeploymentReader_bindTrigger_package(t *testing.T) {
-    //init variables
-    sDeployer := NewServiceDeployer()
-    sDeployer.DeploymentPath = "../tests/dat/deployment-deploymentreader-test-package.yml"
-    sDeployer.Deployment.Triggers["locationUpdate"] = new(whisk.Trigger)
-
-    //parse deployment and bind triggers input and annotation
-    dReader := NewDeploymentReader(sDeployer)
-    dReader.HandleYaml()
-    dReader.bindTriggerInputsAndAnnotations()
-
-    assert.Equal(t, "triggerrule", dReader.DeploymentDescriptor.Package.Packagename)
-    trigger := sDeployer.Deployment.Triggers["locationUpdate"]
-    for _, param := range trigger.Parameters {
-        switch param.Key {
-        case "name":
-            assert.Equal(t, "Bernie", param.Value, "Failed to set inputs")
-        case "place":
-            assert.Equal(t, "DC", param.Value, "Failed to set inputs")
-        default:
-            assert.Fail(t, "Failed to get inputs key")
-
-        }
-    }
-    for _, annos := range trigger.Annotations {
-        switch annos.Key {
-        case "bbb":
-            assert.Equal(t, "this is an annotation", annos.Value, "Failed to set annotations")
-        default:
-            assert.Fail(t, "Failed to get annotation key")
-
-        }
-    }
+	//init variables
+	sDeployer := NewServiceDeployer()
+	sDeployer.DeploymentPath = "../tests/dat/deployment-deploymentreader-test-package.yml"
+	sDeployer.Deployment.Triggers["locationUpdate"] = new(whisk.Trigger)
+
+	//parse deployment and bind triggers input and annotation
+	dReader := NewDeploymentReader(sDeployer)
+	dReader.HandleYaml()
+	dReader.bindTriggerInputsAndAnnotations()
+
+	assert.Equal(t, "triggerrule", dReader.DeploymentDescriptor.Package.Packagename)
+	trigger := sDeployer.Deployment.Triggers["locationUpdate"]
+	for _, param := range trigger.Parameters {
+		switch param.Key {
+		case "name":
+			assert.Equal(t, "Bernie", param.Value, "Failed to set inputs")
+		case "place":
+			assert.Equal(t, "DC", param.Value, "Failed to set inputs")
+		default:
+			assert.Fail(t, "Failed to get inputs key")
+
+		}
+	}
+	for _, annos := range trigger.Annotations {
+		switch annos.Key {
+		case "bbb":
+			assert.Equal(t, "this is an annotation", annos.Value, "Failed to set annotations")
+		default:
+			assert.Fail(t, "Failed to get annotation key")
+
+		}
+	}
+}
+
+func TestDeploymentReader_BindAssets_ActionAnnotations(t *testing.T) {
+	sDeployer := NewServiceDeployer()
+	sDeployer.DeploymentPath = "../tests/dat/deployment_validate_action_annotations.yaml"
+	sDeployer.ManifestPath = "../tests/dat/manifest_validate_action_annotations.yaml"
+
+	//parse deployment and bind triggers input and annotation
+	dReader := NewDeploymentReader(sDeployer)
+	dReader.HandleYaml()
+	err := dReader.bindActionInputsAndAnnotations()
+
+	assert.Nil(t, err, "Failed to bind action annotations")
+
+	pkg_name := "packageActionAnnotations"
+	pkg := dReader.DeploymentDescriptor.Packages[pkg_name]
+	assert.NotNil(t, pkg, "Could not find package with name " + pkg_name)
+	action_name := "helloworld"
+	action := dReader.DeploymentDescriptor.GetProject().Packages[pkg_name].Actions[action_name]
+	assert.NotNil(t, action, "Could not find action with name " + action_name)
+	actual_annotations := action.Annotations
+	expected_annotations := map[string]interface{}{
+		"action_annotation_1": "this is annotation 1",
+		"action_annotation_2": "this is annotation 2",
+	}
+	assert.Equal(t, len(actual_annotations), len(expected_annotations), "Could not find expected number of annotations specified in manifest file")
+	eq := reflect.DeepEqual(actual_annotations, expected_annotations)
+	assert.True(t, eq, "Expected list of annotations does not match with actual list, expected annotations: %v actual annotations: %v", expected_annotations, actual_annotations)
+
+	pkg_name = "packageActionAnnotationsWithWebAction"
+	pkg = dReader.DeploymentDescriptor.Packages[pkg_name]
+	assert.NotNil(t, pkg, "Could not find package with name " + pkg_name)
+	action = dReader.DeploymentDescriptor.GetProject().Packages[pkg_name].Actions[action_name]
+	assert.NotNil(t, action, "Could not find action with name " + action_name)
+	actual_annotations = action.Annotations
+	expected_annotations["web-export"] = true
+	assert.Equal(t, len(actual_annotations), len(expected_annotations), "Could not find expected number of annotations specified in manifest file")
+	eq = reflect.DeepEqual(actual_annotations, expected_annotations)
+	assert.True(t, eq, "Expected list of annotations does not match with actual list, expected annotations: %v actual annotations: %v", expected_annotations, actual_annotations)
+
+	pkg_name = "packageActionAnnotationsFromDeployment"
+	pkg = dReader.DeploymentDescriptor.Packages[pkg_name]
+	assert.NotNil(t, pkg, "Could not find package with name " + pkg_name)
+	action = dReader.DeploymentDescriptor.GetProject().Packages[pkg_name].Actions[action_name]
+	assert.NotNil(t, action, "Could not find action with name " + action_name)
+	actual_annotations = action.Annotations
+	expected_annotations = map[string]interface{}{
+		"action_annotation_1": "this is annotation 1 from deployment",
+		"action_annotation_2": "this is annotation 2 from deployment",
+	}
+	assert.Equal(t, len(actual_annotations), len(expected_annotations), "Could not find expected number of annotations specified in manifest file")
+	eq = reflect.DeepEqual(actual_annotations, expected_annotations)
+	assert.True(t, eq, "Expected list of annotations does not match with actual list, expected annotations: %v actual annotations: %v", expected_annotations, actual_annotations)
 }
+
diff --git a/deployers/servicedeployer.go b/deployers/servicedeployer.go
index 15ce427..57b1c64 100644
--- a/deployers/servicedeployer.go
+++ b/deployers/servicedeployer.go
@@ -175,7 +175,9 @@ func (deployer *ServiceDeployer) ConstructDeploymentPlan() error {
 				return utils.NewYAMLFormatError(errorString)
 			}
 		}
-		deploymentReader.BindAssets()
+		if err := deploymentReader.BindAssets(); err != nil {
+			return err
+		}
 	}
 
 	return err
@@ -250,7 +252,9 @@ func (deployer *ServiceDeployer) ConstructUnDeploymentPlan() (*DeploymentProject
 			}
 		}
 
-		deploymentReader.BindAssets()
+		if err := deploymentReader.BindAssets(); err != nil {
+			return deployer.Deployment, err
+		}
 	}
 
 	verifiedPlan := deployer.Deployment
diff --git a/parsers/manifest_parser.go b/parsers/manifest_parser.go
index 771b4f7..7028831 100644
--- a/parsers/manifest_parser.go
+++ b/parsers/manifest_parser.go
@@ -277,6 +277,19 @@ func (dm *YAMLParser) ComposePackage(pkg Package, packageName string, filePath s
 	if len(keyValArr) > 0 {
 		pag.Parameters = keyValArr
 	}
+
+	// set Package Annotations
+	listOfAnnotations := make(whisk.KeyValueArr, 0)
+	for name, value := range pkg.Annotations {
+		var keyVal whisk.KeyValue
+		keyVal.Key = name
+		keyVal.Value = utils.GetEnvVar(value)
+		listOfAnnotations = append(listOfAnnotations, keyVal)
+	}
+	if len(listOfAnnotations) > 0 {
+		pag.Annotations = append(pag.Annotations, listOfAnnotations...)
+	}
+
 	return pag, nil
 }
 
@@ -523,24 +536,22 @@ func (dm *YAMLParser) ComposeActions(filePath string, actions map[string]Action,
 		/*
  		 *  Action.Annotations
  		 */
-		keyValArr = make(whisk.KeyValueArr, 0)
+		listOfAnnotations := make(whisk.KeyValueArr, 0)
 		for name, value := range action.Annotations {
 			var keyVal whisk.KeyValue
 			keyVal.Key = name
 			keyVal.Value = utils.GetEnvVar(value)
-			keyValArr = append(keyValArr, keyVal)
-			// TODO{} Fix Annottions; they are not added to Action if web-export key is not present
-			// Need to assure annotations are added/set even if web-export is not set on the action.
+			listOfAnnotations = append(listOfAnnotations, keyVal)
+		}
+		if len(listOfAnnotations) > 0 {
+			wskaction.Annotations = append(wskaction.Annotations, listOfAnnotations...)
 		}
 
 		/*
   		 *  Web Export
   		 */
-		// only set the webaction when the annotations are not empty.
 		if action.Webexport == "true" {
-			// TODO() why is this commented out?  we should now support annotations...
-			//wskaction.Annotations = keyValArr
-			wskaction.Annotations, errorParser = utils.WebAction("yes", keyValArr, action.Name, false)
+			wskaction.Annotations, errorParser = utils.WebAction("yes", listOfAnnotations, false)
 			if errorParser != nil {
 				return s1, errorParser
 			}
@@ -668,6 +679,17 @@ func (dm *YAMLParser) ComposeTriggers(filePath string, pkg Package) ([]*whisk.Tr
 			wsktrigger.Parameters = keyValArr
 		}
 
+		listOfAnnotations := make(whisk.KeyValueArr, 0)
+		for name, value := range trigger.Annotations {
+			var keyVal whisk.KeyValue
+			keyVal.Key = name
+			keyVal.Value = utils.GetEnvVar(value)
+			listOfAnnotations = append(listOfAnnotations, keyVal)
+		}
+		if len(listOfAnnotations) > 0 {
+			wsktrigger.Annotations = append(wsktrigger.Annotations, listOfAnnotations...)
+		}
+
 		t1 = append(t1, wsktrigger)
 	}
 	return t1, nil
diff --git a/parsers/manifest_parser_test.go b/parsers/manifest_parser_test.go
index 3037a6f..19ebc07 100644
--- a/parsers/manifest_parser_test.go
+++ b/parsers/manifest_parser_test.go
@@ -1595,3 +1595,37 @@ func TestPackageName_Env_Var(t *testing.T) {
         assert.Equal(t, "Apache-2.0", pkg.License, "Get the wrong license.")
     }
 }
+
+func TestComposeActionForAnnotations(t *testing.T) {
+    manifestFile := "../tests/dat/manifest_validate_action_annotations.yaml"
+    mm := NewYAMLParser()
+    manifest, _ := mm.ParseManifest(manifestFile)
+    pkg_name := "packageActionAnnotations"
+    pkg := manifest.Packages[pkg_name]
+    assert.NotNil(t, pkg, "Could not find package with name " + pkg_name)
+    action_name := "helloworld"
+    action := pkg.Actions[action_name]
+    assert.NotNil(t, action, "Could not find action with name " + action_name)
+    actual_annotations := action.Annotations
+    expected_annotations := map[string]interface{} {
+        "action_annotation_1": "this is annotation 1",
+        "action_annotation_2": "this is annotation 2",
+        "action_annotation_3": "this is annotation 3",
+        "action_annotation_4": "this is annotation 4",
+    }
+    assert.Equal(t, len(actual_annotations), len(expected_annotations), "Could not find expected number of annotations specified in manifest file")
+    eq := reflect.DeepEqual(actual_annotations, expected_annotations)
+    assert.True(t, eq, "Expected list of annotations does not match with actual list, expected annotations: %v actual annotations: %v", expected_annotations, actual_annotations)
+
+    pkg_name = "packageActionAnnotationsWithWebAction"
+    pkg = manifest.Packages[pkg_name]
+    assert.NotNil(t, pkg, "Could not find package with name " + pkg_name)
+    action = pkg.Actions[action_name]
+    assert.NotNil(t, action, "Could not find action with name " + action_name)
+    actual_annotations = action.Annotations
+    expected_annotations["web-export"] = true
+    assert.Equal(t, len(actual_annotations), len(expected_annotations), "Could not find expected number of annotations specified in manifest file")
+    eq = reflect.DeepEqual(actual_annotations, expected_annotations)
+    assert.True(t, eq, "Expected list of annotations does not match with actual list, expected annotations: %v actual annotations: %v", expected_annotations, actual_annotations)
+}
+
diff --git a/tests/dat/deployment_validate_action_annotations.yaml b/tests/dat/deployment_validate_action_annotations.yaml
new file mode 100644
index 0000000..a76e48f
--- /dev/null
+++ b/tests/dat/deployment_validate_action_annotations.yaml
@@ -0,0 +1,31 @@
+project:
+    name: TestActionAnnotations
+    packages:
+        packageActionAnnotations:
+            actions:
+                helloworld:
+                    inputs:
+                        name: Amy
+                        place: New York
+                    annotations:
+                        action_annotation_1: this is annotation 1
+                        action_annotation_2: this is annotation 2
+        packageActionAnnotationsFromDeployment:
+            actions:
+                helloworld:
+                    inputs:
+                        name: Amy
+                        place: New York
+                    annotations:
+                        action_annotation_1: this is annotation 1 from deployment
+                        action_annotation_2: this is annotation 2 from deployment
+        packageActionAnnotationsWithWebAction:
+            actions:
+                helloworld:
+                    inputs:
+                        name: Amy
+                        place: New York
+                    annotations:
+                        action_annotation_1: this is annotation 1
+                        action_annotation_2: this is annotation 2
+                        web-export: true
diff --git a/tests/dat/manifest_validate_action_annotations.yaml b/tests/dat/manifest_validate_action_annotations.yaml
new file mode 100644
index 0000000..030fb6a
--- /dev/null
+++ b/tests/dat/manifest_validate_action_annotations.yaml
@@ -0,0 +1,65 @@
+packages:
+    packageActionAnnotations:
+        actions:
+            helloworld:
+                function: actions/hello.js
+                runtime: nodejs:6
+                inputs:
+                    name:
+                        type: string
+                        description: name of a person
+                    place:
+                        type: string
+                        description: location of a person
+                outputs:
+                    payload:
+                        type: string
+                        description: a simple greeting message, Hello World!
+                annotations:
+                    action_annotation_1: this is annotation 1
+                    action_annotation_2: this is annotation 2
+                    action_annotation_3: this is annotation 3
+                    action_annotation_4: this is annotation 4
+    packageActionAnnotationsFromDeployment:
+        actions:
+            helloworld:
+                function: actions/hello.js
+                runtime: nodejs:6
+                inputs:
+                    name:
+                        type: string
+                        description: name of a person
+                    place:
+                        type: string
+                        description: location of a person
+                outputs:
+                    payload:
+                        type: string
+                        description: a simple greeting message, Hello World!
+                annotations:
+                    action_annotation_1: this is annotation 1
+                    action_annotation_2: this is annotation 2
+                    action_annotation_3: this is annotation 3
+                    action_annotation_4: this is annotation 4
+    packageActionAnnotationsWithWebAction:
+        actions:
+            helloworld:
+                function: actions/hello.js
+                runtime: nodejs:6
+                inputs:
+                    name:
+                        type: string
+                        description: name of a person
+                    place:
+                        type: string
+                        description: location of a person
+                outputs:
+                    payload:
+                        type: string
+                        description: a simple greeting message, Hello World!
+                annotations:
+                    action_annotation_1: this is annotation 1
+                    action_annotation_2: this is annotation 2
+                    action_annotation_3: this is annotation 3
+                    action_annotation_4: this is annotation 4
+                    web-export: true
diff --git a/tests/src/integration/validate-action-annotations/actions/hello.js b/tests/src/integration/validate-action-annotations/actions/hello.js
new file mode 100644
index 0000000..25fdafb
--- /dev/null
+++ b/tests/src/integration/validate-action-annotations/actions/hello.js
@@ -0,0 +1,26 @@
+/*
+ * 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.
+ */
+
+/*
+ * Return a simple greeting message for the whole world.
+ */
+function main(params) {
+    msg = "Hello, " + params.name + " from " + params.place;
+    console.log(msg)
+    return { payload:  msg };
+}
+
diff --git a/tests/src/integration/validate-action-annotations/deployment-with-invalid-annotations.yaml b/tests/src/integration/validate-action-annotations/deployment-with-invalid-annotations.yaml
new file mode 100644
index 0000000..b551e8f
--- /dev/null
+++ b/tests/src/integration/validate-action-annotations/deployment-with-invalid-annotations.yaml
@@ -0,0 +1,22 @@
+project:
+    name: TestActionAnnotations
+    packages:
+        packageActionAnnotations:
+            actions:
+                helloworld:
+                    inputs:
+                        name: Amy
+                        place: New York
+                    annotations:
+                        action_annotation_5: this is annotation 5
+                        action_annotation_6: this is annotation 6
+        packageActionAnnotationsWithWebAction:
+            actions:
+                helloworld:
+                    inputs:
+                        name: Amy
+                        place: New York
+                    annotations:
+                        action_annotation_5: this is annotation 5
+                        action_annotation_6: this is annotation 6
+                        web-export: true
diff --git a/tests/src/integration/validate-action-annotations/deployment.yaml b/tests/src/integration/validate-action-annotations/deployment.yaml
new file mode 100644
index 0000000..a76e48f
--- /dev/null
+++ b/tests/src/integration/validate-action-annotations/deployment.yaml
@@ -0,0 +1,31 @@
+project:
+    name: TestActionAnnotations
+    packages:
+        packageActionAnnotations:
+            actions:
+                helloworld:
+                    inputs:
+                        name: Amy
+                        place: New York
+                    annotations:
+                        action_annotation_1: this is annotation 1
+                        action_annotation_2: this is annotation 2
+        packageActionAnnotationsFromDeployment:
+            actions:
+                helloworld:
+                    inputs:
+                        name: Amy
+                        place: New York
+                    annotations:
+                        action_annotation_1: this is annotation 1 from deployment
+                        action_annotation_2: this is annotation 2 from deployment
+        packageActionAnnotationsWithWebAction:
+            actions:
+                helloworld:
+                    inputs:
+                        name: Amy
+                        place: New York
+                    annotations:
+                        action_annotation_1: this is annotation 1
+                        action_annotation_2: this is annotation 2
+                        web-export: true
diff --git a/tests/src/integration/validate-action-annotations/manifest.yaml b/tests/src/integration/validate-action-annotations/manifest.yaml
new file mode 100644
index 0000000..030fb6a
--- /dev/null
+++ b/tests/src/integration/validate-action-annotations/manifest.yaml
@@ -0,0 +1,65 @@
+packages:
+    packageActionAnnotations:
+        actions:
+            helloworld:
+                function: actions/hello.js
+                runtime: nodejs:6
+                inputs:
+                    name:
+                        type: string
+                        description: name of a person
+                    place:
+                        type: string
+                        description: location of a person
+                outputs:
+                    payload:
+                        type: string
+                        description: a simple greeting message, Hello World!
+                annotations:
+                    action_annotation_1: this is annotation 1
+                    action_annotation_2: this is annotation 2
+                    action_annotation_3: this is annotation 3
+                    action_annotation_4: this is annotation 4
+    packageActionAnnotationsFromDeployment:
+        actions:
+            helloworld:
+                function: actions/hello.js
+                runtime: nodejs:6
+                inputs:
+                    name:
+                        type: string
+                        description: name of a person
+                    place:
+                        type: string
+                        description: location of a person
+                outputs:
+                    payload:
+                        type: string
+                        description: a simple greeting message, Hello World!
+                annotations:
+                    action_annotation_1: this is annotation 1
+                    action_annotation_2: this is annotation 2
+                    action_annotation_3: this is annotation 3
+                    action_annotation_4: this is annotation 4
+    packageActionAnnotationsWithWebAction:
+        actions:
+            helloworld:
+                function: actions/hello.js
+                runtime: nodejs:6
+                inputs:
+                    name:
+                        type: string
+                        description: name of a person
+                    place:
+                        type: string
+                        description: location of a person
+                outputs:
+                    payload:
+                        type: string
+                        description: a simple greeting message, Hello World!
+                annotations:
+                    action_annotation_1: this is annotation 1
+                    action_annotation_2: this is annotation 2
+                    action_annotation_3: this is annotation 3
+                    action_annotation_4: this is annotation 4
+                    web-export: true
diff --git a/tests/src/integration/validate-action-annotations/validate-action-annotations_test.go b/tests/src/integration/validate-action-annotations/validate-action-annotations_test.go
new file mode 100644
index 0000000..f48aa29
--- /dev/null
+++ b/tests/src/integration/validate-action-annotations/validate-action-annotations_test.go
@@ -0,0 +1,48 @@
+// +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"
+)
+
+var path = "/src/github.com/apache/incubator-openwhisk-wskdeploy/tests/src/integration/validate-action-annotations/"
+
+func TestActionAnnotations(t *testing.T) {
+	manifestPath   := os.Getenv("GOPATH") + path + "manifest.yaml"
+	deploymentPath := os.Getenv("GOPATH") + path + "deployment.yaml"
+	wskdeploy := common.NewWskdeploy()
+	_, err := wskdeploy.Deploy(manifestPath, deploymentPath)
+	assert.Equal(t, nil, err, "Failed to deploy based on the manifest and deployment files.")
+	_, err = wskdeploy.Undeploy(manifestPath, deploymentPath)
+	assert.Equal(t, nil, err, "Failed to undeploy based on the manifest and deployment files.")
+}
+
+func TestInvalidActionAnnotations(t *testing.T) {
+	manifestPath   := os.Getenv("GOPATH") + path + "manifest.yaml"
+	deploymentPath := os.Getenv("GOPATH") + path + "deployment-with-invalid-annotations.yaml"
+	wskdeploy := common.NewWskdeploy()
+	_, err := wskdeploy.Deploy(manifestPath, deploymentPath)
+	assert.NotNil(t, err, "Failed to validate invalid annotations in deployment file")
+}
+
diff --git a/tests/src/integration/validate-binding-inputs-annotations/deployment.yaml b/tests/src/integration/validate-binding-inputs-annotations/deployment.yaml
index ec0526d..bf51b00 100644
--- a/tests/src/integration/validate-binding-inputs-annotations/deployment.yaml
+++ b/tests/src/integration/validate-binding-inputs-annotations/deployment.yaml
@@ -1,27 +1,28 @@
 project:
-  name: BindingTest
-  packages:
-    packagebinding:
-      inputs:
-        name: daisy
-        city: Beijing
-      annotations:
-        tag: hello
-        aaa: this is an annotation
-      actions:
-        helloworld:
-          inputs:
-            name: Amy
-            place: Paris
-          annotations:
-            tag: hello
-            aaa: this is an annotation
-      triggers:
-        dbtrigger:
-          inputs:
-            dbname: cats
-            docid: 1234567
-          annotations:
-            tag: hello
-            aaa: this is an annotation
-
+    name: BindingTest
+    packages:
+        packagebinding:
+            inputs:
+                name: daisy
+                city: Beijing
+            actions:
+                helloworld:
+                    inputs:
+                        name: Amy
+                        place: Paris
+            triggers:
+                dbtrigger:
+                    inputs:
+                        dbname: cats
+                        docid: 1234567
+        packageBindingFromDeployment:
+            annotations:
+                package_annotation_1: this is a package annotation 1 from deployment
+            actions:
+                helloworld:
+                    annotations:
+                        action_annotation_1: this is an action annotation 1 from deployment
+            triggers:
+                dbtrigger1:
+                    annotations:
+                        trigger_annotation_1: this is a trigger annotation 1 from deployment
diff --git a/tests/src/integration/validate-binding-inputs-annotations/manifest.yaml b/tests/src/integration/validate-binding-inputs-annotations/manifest.yaml
index 5ecc86f..f2a6b6e 100644
--- a/tests/src/integration/validate-binding-inputs-annotations/manifest.yaml
+++ b/tests/src/integration/validate-binding-inputs-annotations/manifest.yaml
@@ -1,5 +1,8 @@
 packages:
     packagebinding:
+        annotations:
+            package_annotation_1: this is a package annotation 1
+            package_annotation_2: this is a package annotation 2
         actions:
             helloworld:
                 function: actions/helloworld.js
@@ -15,6 +18,9 @@ packages:
                     payload:
                         type: string
                         description: a simple greeting message, Hello World!
+                annotations:
+                    action_annotation_1: this is an action annotation 1
+                    action_annotation_2: this is an action annotation 2
         triggers:
             dbtrigger:
                 inputs:
@@ -24,4 +30,23 @@ packages:
                     docid:
                         type: string
                         description: doc ID
+                annotations:
+                    trigger_annotation_1: this is a trigger annotation 1
+                    trigger_annotation_2: this is a trigger annotation 2
+    packageBindingFromDeployment:
+        annotations:
+            package_annotation_1: this is a package annotation 1
+            package_annotation_2: this is a package annotation 2
+        actions:
+            helloworld:
+                function: actions/helloworld.js
+                runtime: nodejs:6
+                annotations:
+                    action_annotation_1: this is an action annotation 1
+                    action_annotation_2: this is an action annotation 2
+        triggers:
+            dbtrigger1:
+                annotations:
+                    trigger_annotation_1: this is a trigger annotation 1
+                    trigger_annotation_2: this is a trigger annotation 2
 
diff --git a/tests/src/integration/validate-binding-inputs-annotations/validate-bind-inputs-anno_test.go b/tests/src/integration/validate-binding-inputs-annotations/validate-bind-inputs-anno_test.go
index 40184fd..8e0d159 100644
--- a/tests/src/integration/validate-binding-inputs-annotations/validate-bind-inputs-anno_test.go
+++ b/tests/src/integration/validate-binding-inputs-annotations/validate-bind-inputs-anno_test.go
@@ -26,7 +26,13 @@ import (
 	"testing"
 )
 
+var PATH = "/src/github.com/apache/incubator-openwhisk-wskdeploy/tests/src/integration/validate-binding-inputs-annotations/"
+
 func TestBindingInputsAnnotations(t *testing.T) {
+
+	manifestPath   := os.Getenv("GOPATH") + PATH + "manifest.yaml"
+	deploymentPath := os.Getenv("GOPATH") + PATH + "deployment.yaml"
+
 	wskdeploy := common.NewWskdeploy()
 	// verify the inputs & annotations are set
 	deploymentObjects, err := wskdeploy.GetDeploymentObjects(manifestPath, deploymentPath)
@@ -47,10 +53,10 @@ func TestBindingInputsAnnotations(t *testing.T) {
 	}
 	for _, annos := range wskpkg.Annotations {
 		switch annos.Key {
-		case "tag":
-			assert.Equal(t, "hello", annos.Value, "Failed to get package annotations")
-		case "aaa":
-			assert.Equal(t, "this is an annotation", annos.Value, "Failed to get package annotations")
+		case "package_annotation_1":
+			assert.Equal(t, "this is a package annotation 1", annos.Value, "Failed to get package annotations")
+		case "package_annotation_2":
+			assert.Equal(t, "this is a package annotation 2", annos.Value, "Failed to get package annotations")
 		default:
 			assert.Fail(t, "Failed to get package annotation key")
 		}
@@ -70,10 +76,10 @@ func TestBindingInputsAnnotations(t *testing.T) {
 	}
 	for _, annos := range wskaction.Annotations {
 		switch annos.Key {
-		case "tag":
-			assert.Equal(t, "hello", annos.Value, "Failed to get action annotations")
-		case "aaa":
-			assert.Equal(t, "this is an annotation", annos.Value, "Failed to get action annotations")
+		case "action_annotation_1":
+			assert.Equal(t, "this is an action annotation 1", annos.Value, "Failed to get action annotations")
+		case "action_annotation_2":
+			assert.Equal(t, "this is an action annotation 2", annos.Value, "Failed to get action annotations")
 		default:
 			assert.Fail(t, "Failed to get action annotation key")
 		}
@@ -93,15 +99,54 @@ func TestBindingInputsAnnotations(t *testing.T) {
 	}
 	for _, annos := range wsktrigger.Annotations {
 		switch annos.Key {
-		case "tag":
-			assert.Equal(t, "hello", annos.Value, "Failed to get trigger annotations")
-		case "aaa":
-			assert.Equal(t, "this is an annotation", annos.Value, "Failed to get trigger annotations")
+		case "trigger_annotation_1":
+			assert.Equal(t, "this is a trigger annotation 1", annos.Value, "Failed to get action annotations")
+		case "trigger_annotation_2":
+			assert.Equal(t, "this is a trigger annotation 2", annos.Value, "Failed to get action annotations")
 		default:
 			assert.Fail(t, "Failed to get annotation key")
 		}
 	}
 
+	// verify the annotations of a package
+	pkgobj = deploymentObjects.Packages["packageBindingFromDeployment"]
+	wskpkg = pkgobj.Package
+	for _, annos := range wskpkg.Annotations {
+		switch annos.Key {
+		case "package_annotation_1":
+			assert.Equal(t, "this is a package annotation 1 from deployment", annos.Value, "Failed to get package annotations")
+		case "package_annotation_2":
+			assert.Equal(t, "this is a package annotation 2", annos.Value, "Failed to get package annotations")
+		default:
+			assert.Fail(t, "Failed to get package annotation key")
+		}
+	}
+	// verify the annotations of action
+	wskaction = pkgobj.Actions["helloworld"].Action
+	for _, annos := range wskaction.Annotations {
+		switch annos.Key {
+		case "action_annotation_1":
+			assert.Equal(t, "this is an action annotation 1 from deployment", annos.Value, "Failed to get action annotations")
+		case "action_annotation_2":
+			assert.Equal(t, "this is an action annotation 2", annos.Value, "Failed to get action annotations")
+		default:
+			assert.Fail(t, "Failed to get action annotation key")
+		}
+	}
+	// verify the inputs & annotations of trigger
+	wsktrigger = deploymentObjects.Triggers["dbtrigger1"]
+	for _, annos := range wsktrigger.Annotations {
+		switch annos.Key {
+		case "trigger_annotation_1":
+			assert.Equal(t, "this is a trigger annotation 1 from deployment", annos.Value, "Failed to get action annotations")
+		case "trigger_annotation_2":
+			assert.Equal(t, "this is a trigger annotation 2", annos.Value, "Failed to get action annotations")
+		default:
+			assert.Fail(t, "Failed to get annotation key")
+		}
+	}
+
+
 	// testing deploy and undeploy
 	_, err = wskdeploy.Deploy(manifestPath, deploymentPath)
 	assert.NoError(t, err, "Failed to deploy based on the manifest and deployment files.")
@@ -109,7 +154,3 @@ func TestBindingInputsAnnotations(t *testing.T) {
 	assert.NoError(t, err, "Failed to undeploy based on the manifest and deployment files.")
 }
 
-var (
-	manifestPath   = os.Getenv("GOPATH") + "/src/github.com/apache/incubator-openwhisk-wskdeploy/tests/src/integration/validate-binding-inputs-annotations/manifest.yaml"
-	deploymentPath = os.Getenv("GOPATH") + "/src/github.com/apache/incubator-openwhisk-wskdeploy/tests/src/integration/validate-binding-inputs-annotations/deployment.yaml"
-)
diff --git a/utils/misc.go b/utils/misc.go
index b37b059..b9d862e 100644
--- a/utils/misc.go
+++ b/utils/misc.go
@@ -331,77 +331,6 @@ func javaEntryError() error {
 	return errors.New(errMsg)
 }
 
-//for web action support, code from wsk cli with tiny adjustments
-const WEB_EXPORT_ANNOT = "web-export"
-const RAW_HTTP_ANNOT = "raw-http"
-const FINAL_ANNOT = "final"
-
-func WebAction(webMode string, annotations whisk.KeyValueArr, entityName string, fetch bool) (whisk.KeyValueArr, error) {
-	switch strings.ToLower(webMode) {
-	case "yes":
-		fallthrough
-	case "true":
-		return webActionAnnotations(fetch, annotations, entityName, addWebAnnotations)
-	case "no":
-		fallthrough
-	case "false":
-		return webActionAnnotations(fetch, annotations, entityName, deleteWebAnnotations)
-	case "raw":
-		return webActionAnnotations(fetch, annotations, entityName, addRawAnnotations)
-	default:
-		return nil, errors.New(webMode)
-	}
-}
-
-type WebActionAnnotationMethod func(annotations whisk.KeyValueArr) whisk.KeyValueArr
-
-func webActionAnnotations(
-	fetchAnnotations bool,
-	annotations whisk.KeyValueArr,
-	entityName string,
-	webActionAnnotationMethod WebActionAnnotationMethod) (whisk.KeyValueArr, error) {
-	if annotations != nil || !fetchAnnotations {
-		annotations = webActionAnnotationMethod(annotations)
-	}
-
-	return annotations, nil
-}
-
-func addWebAnnotations(annotations whisk.KeyValueArr) whisk.KeyValueArr {
-	annotations = deleteWebAnnotationKeys(annotations)
-	annotations = addKeyValue(WEB_EXPORT_ANNOT, true, annotations)
-	annotations = addKeyValue(RAW_HTTP_ANNOT, false, annotations)
-	annotations = addKeyValue(FINAL_ANNOT, true, annotations)
-
-	return annotations
-}
-
-func deleteWebAnnotations(annotations whisk.KeyValueArr) whisk.KeyValueArr {
-	annotations = deleteWebAnnotationKeys(annotations)
-	annotations = addKeyValue(WEB_EXPORT_ANNOT, false, annotations)
-	annotations = addKeyValue(RAW_HTTP_ANNOT, false, annotations)
-	annotations = addKeyValue(FINAL_ANNOT, false, annotations)
-
-	return annotations
-}
-
-func addRawAnnotations(annotations whisk.KeyValueArr) whisk.KeyValueArr {
-	annotations = deleteWebAnnotationKeys(annotations)
-	annotations = addKeyValue(WEB_EXPORT_ANNOT, true, annotations)
-	annotations = addKeyValue(RAW_HTTP_ANNOT, true, annotations)
-	annotations = addKeyValue(FINAL_ANNOT, true, annotations)
-
-	return annotations
-}
-
-func deleteWebAnnotationKeys(annotations whisk.KeyValueArr) whisk.KeyValueArr {
-	annotations = deleteKey(WEB_EXPORT_ANNOT, annotations)
-	annotations = deleteKey(RAW_HTTP_ANNOT, annotations)
-	annotations = deleteKey(FINAL_ANNOT, annotations)
-
-	return annotations
-}
-
 func deleteKey(key string, keyValueArr whisk.KeyValueArr) whisk.KeyValueArr {
 	for i := 0; i < len(keyValueArr); i++ {
 		if keyValueArr[i].Key == key {
diff --git a/utils/webaction.go b/utils/webaction.go
new file mode 100644
index 0000000..f9f6e4e
--- /dev/null
+++ b/utils/webaction.go
@@ -0,0 +1,94 @@
+/*
+ * 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 (
+	"errors"
+	"github.com/apache/incubator-openwhisk-client-go/whisk"
+	"strings"
+)
+
+//for web action support, code from wsk cli with tiny adjustments
+const WEB_EXPORT_ANNOT = "web-export"
+const RAW_HTTP_ANNOT = "raw-http"
+const FINAL_ANNOT = "final"
+
+func WebAction(webMode string, annotations whisk.KeyValueArr, fetch bool) (whisk.KeyValueArr, error) {
+	switch strings.ToLower(webMode) {
+	case "yes":
+		fallthrough
+	case "true":
+		return webActionAnnotations(fetch, annotations, addWebAnnotations)
+	case "no":
+		fallthrough
+	case "false":
+		return webActionAnnotations(fetch, annotations, deleteWebAnnotations)
+	case "raw":
+		return webActionAnnotations(fetch, annotations, addRawAnnotations)
+	default:
+		return nil, errors.New(webMode)
+	}
+}
+
+type WebActionAnnotationMethod func(annotations whisk.KeyValueArr) whisk.KeyValueArr
+
+func webActionAnnotations(
+	fetchAnnotations bool,
+	annotations whisk.KeyValueArr,
+	webActionAnnotationMethod WebActionAnnotationMethod) (whisk.KeyValueArr, error) {
+	if annotations != nil || !fetchAnnotations {
+		annotations = webActionAnnotationMethod(annotations)
+	}
+
+	return annotations, nil
+}
+
+func addWebAnnotations(annotations whisk.KeyValueArr) whisk.KeyValueArr {
+	annotations = deleteWebAnnotationKeys(annotations)
+	annotations = addKeyValue(WEB_EXPORT_ANNOT, true, annotations)
+	annotations = addKeyValue(RAW_HTTP_ANNOT, false, annotations)
+	annotations = addKeyValue(FINAL_ANNOT, true, annotations)
+
+	return annotations
+}
+
+func deleteWebAnnotations(annotations whisk.KeyValueArr) whisk.KeyValueArr {
+	annotations = deleteWebAnnotationKeys(annotations)
+	annotations = addKeyValue(WEB_EXPORT_ANNOT, false, annotations)
+	annotations = addKeyValue(RAW_HTTP_ANNOT, false, annotations)
+	annotations = addKeyValue(FINAL_ANNOT, false, annotations)
+
+	return annotations
+}
+
+func addRawAnnotations(annotations whisk.KeyValueArr) whisk.KeyValueArr {
+	annotations = deleteWebAnnotationKeys(annotations)
+	annotations = addKeyValue(WEB_EXPORT_ANNOT, true, annotations)
+	annotations = addKeyValue(RAW_HTTP_ANNOT, true, annotations)
+	annotations = addKeyValue(FINAL_ANNOT, true, annotations)
+
+	return annotations
+}
+
+func deleteWebAnnotationKeys(annotations whisk.KeyValueArr) whisk.KeyValueArr {
+	annotations = deleteKey(WEB_EXPORT_ANNOT, annotations)
+	annotations = deleteKey(RAW_HTTP_ANNOT, annotations)
+	annotations = deleteKey(FINAL_ANNOT, annotations)
+
+	return annotations
+}

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