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 2018/04/10 20:28:19 UTC

[incubator-openwhisk-wskdeploy] branch master updated: Refactoring ManifestReader (#863)

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 de4e3d8  Refactoring ManifestReader (#863)
de4e3d8 is described below

commit de4e3d80046ef691d91c4bf24b462c39916f5195
Author: Priti Desai <pd...@us.ibm.com>
AuthorDate: Tue Apr 10 13:28:16 2018 -0700

    Refactoring ManifestReader (#863)
    
    * cleaning up manifestreader
    
    * adding error messages to wski18n
    
    * adding integration test on actions
    
    * Adding trigger tests
    
    * adding error message for trigger
    
    * adding rule test
    
    * fixing integration test
    
    * fixing integration test
    
    * integration test fix
    
    * dropping doc on errors
---
 cmd/root.go                                        |  16 +-
 deployers/manifestreader.go                        | 290 ++++++++-----------
 deployers/manifestreader_test.go                   | 319 ++++++++++++++++++++-
 deployers/servicedeployer.go                       |   2 -
 parsers/manifest_parser.go                         |   6 +-
 tests/dat/manifest_validate_action_all.yaml        |  43 +++
 tests/dat/manifest_validate_dependencies.yaml      |  42 +++
 .../dat/manifest_validate_dependencies_bogus.yaml  |  42 +++
 ...st_validate_package_inputs_and_annotations.yaml |  37 +++
 tests/dat/manifest_validate_rules_bogus.yaml       |  41 +++
 tests/dat/manifest_validate_sequences_bogus.yaml   |  38 +++
 tests/dat/manifest_validate_triggers_bogus.yaml    |  33 +++
 utils/dependencies.go                              |  10 +
 utils/gitreader.go                                 |   1 -
 wski18n/i18n_ids.go                                |  47 +--
 wski18n/i18n_resources.go                          |   4 +-
 wski18n/resources/en_US.all.json                   |  24 ++
 17 files changed, 777 insertions(+), 218 deletions(-)

diff --git a/cmd/root.go b/cmd/root.go
index 77b2e03..4d665dd 100644
--- a/cmd/root.go
+++ b/cmd/root.go
@@ -150,7 +150,7 @@ func loadDefaultDeploymentFileFromProjectPath(command string, projectPath string
 	} else if _, err := os.Stat(path.Join(projectPath, utils.DeploymentFileNameYml)); err == nil {
 		utils.Flags.DeploymentPath = path.Join(projectPath, utils.DeploymentFileNameYml)
 	}
-	displayCommandUsingFilenameMessage(command, wski18n.DEPLOYMENT_FILE, utils.Flags.ManifestPath)
+	displayCommandUsingFilenameMessage(command, wski18n.DEPLOYMENT_FILE, utils.Flags.DeploymentPath)
 	return nil
 }
 
@@ -167,14 +167,19 @@ func Deploy() error {
 	projectPath, _ := filepath.Abs(project_Path)
 
 	// If manifest filename is not provided, attempt to load default manifests from project path
+	// default manifests are manifest.yaml and manifest.yml
+	// return failure if none of the default manifest files were found
 	if utils.Flags.ManifestPath == "" {
 		if err := loadDefaultManifestFileFromProjectPath(wski18n.CMD_DEPLOY, projectPath); err != nil {
 			return err
 		}
 	}
 
+	// If deployment filename is not provided, attempt to load default deployment files from project path
+	// default deployments are deployment.yaml and deployment.yml
+	// continue processing manifest file, even if none of the default
+	// deployment files were found as deployment files are optional
 	if utils.Flags.DeploymentPath == "" {
-
 		if err := loadDefaultDeploymentFileFromProjectPath(wski18n.CMD_DEPLOY, projectPath); err != nil {
 			return err
 		}
@@ -182,7 +187,9 @@ func Deploy() error {
 
 	if utils.MayExists(utils.Flags.ManifestPath) {
 
+		// Create an instance of ServiceDeployer
 		var deployer = deployers.NewServiceDeployer()
+		// Set Project Path, Manifest Path, and Deployment Path of ServiceDeployer
 		deployer.ProjectPath = projectPath
 		deployer.ManifestPath = utils.Flags.ManifestPath
 		deployer.DeploymentPath = utils.Flags.DeploymentPath
@@ -191,6 +198,7 @@ func Deploy() error {
 		// master record of any dependency that has been downloaded
 		deployer.DependencyMaster = make(map[string]utils.DependencyRecord)
 
+		// Read credentials from Configuration file, manifest file or deployment file
 		clientConfig, error := deployers.NewWhiskConfig(
 			utils.Flags.CfgFile,
 			utils.Flags.DeploymentPath,
@@ -210,14 +218,14 @@ func Deploy() error {
 		// The auth, apihost and namespace have been chosen, so that we can check the supported runtimes here.
 		setSupportedRuntimes(clientConfig.Host)
 
+		// Construct Deployment Plan
 		err := deployer.ConstructDeploymentPlan()
-
 		if err != nil {
 			return err
 		}
 
+		// Deploy all OW entities
 		err = deployer.Deploy()
-
 		if err != nil {
 			return err
 		} else {
diff --git a/deployers/manifestreader.go b/deployers/manifestreader.go
index 1f9171d..c19fd68 100644
--- a/deployers/manifestreader.go
+++ b/deployers/manifestreader.go
@@ -18,13 +18,14 @@
 package deployers
 
 import (
-	"errors"
 	"strings"
 
+	"fmt"
 	"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/wskderrors"
+	"github.com/apache/incubator-openwhisk-wskdeploy/wski18n"
 )
 
 var clientConfig *whisk.Config
@@ -58,32 +59,31 @@ func (reader *ManifestReader) InitPackages(manifestParser *parsers.YAMLParser, m
 		return err
 	}
 	reader.SetPackages(packages)
-
 	return nil
 }
 
 // Wrapper parser to handle yaml dir
-func (deployer *ManifestReader) HandleYaml(sdeployer *ServiceDeployer, manifestParser *parsers.YAMLParser, manifest *parsers.YAML, managedAnnotations whisk.KeyValue) error {
+func (reader *ManifestReader) HandleYaml(sdeployer *ServiceDeployer, manifestParser *parsers.YAMLParser, manifest *parsers.YAML, managedAnnotations whisk.KeyValue) error {
 
 	var err error
 	var manifestName = manifest.Filepath
 
-	deps, err := manifestParser.ComposeDependenciesFromAllPackages(manifest, deployer.serviceDeployer.ProjectPath, deployer.serviceDeployer.ManifestPath, managedAnnotations)
+	deps, err := manifestParser.ComposeDependenciesFromAllPackages(manifest, reader.serviceDeployer.ProjectPath, reader.serviceDeployer.ManifestPath, managedAnnotations)
 	if err != nil {
 		return wskderrors.NewYAMLFileFormatError(manifestName, err)
 	}
 
-	actions, err := manifestParser.ComposeActionsFromAllPackages(manifest, deployer.serviceDeployer.ManifestPath, managedAnnotations)
+	actions, err := manifestParser.ComposeActionsFromAllPackages(manifest, reader.serviceDeployer.ManifestPath, managedAnnotations)
 	if err != nil {
 		return wskderrors.NewYAMLFileFormatError(manifestName, err)
 	}
 
-	sequences, err := manifestParser.ComposeSequencesFromAllPackages(deployer.serviceDeployer.ClientConfig.Namespace, manifest, deployer.serviceDeployer.ManifestPath, managedAnnotations)
+	sequences, err := manifestParser.ComposeSequencesFromAllPackages(reader.serviceDeployer.ClientConfig.Namespace, manifest, reader.serviceDeployer.ManifestPath, managedAnnotations)
 	if err != nil {
 		return wskderrors.NewYAMLFileFormatError(manifestName, err)
 	}
 
-	triggers, err := manifestParser.ComposeTriggersFromAllPackages(manifest, deployer.serviceDeployer.ManifestPath, managedAnnotations)
+	triggers, err := manifestParser.ComposeTriggersFromAllPackages(manifest, reader.serviceDeployer.ManifestPath, managedAnnotations)
 	if err != nil {
 		return wskderrors.NewYAMLFileFormatError(manifestName, err)
 	}
@@ -93,37 +93,37 @@ func (deployer *ManifestReader) HandleYaml(sdeployer *ServiceDeployer, manifestP
 		return wskderrors.NewYAMLFileFormatError(manifestName, err)
 	}
 
-	apis, err := manifestParser.ComposeApiRecordsFromAllPackages(deployer.serviceDeployer.ClientConfig, manifest)
+	apis, err := manifestParser.ComposeApiRecordsFromAllPackages(reader.serviceDeployer.ClientConfig, manifest)
 	if err != nil {
 		return wskderrors.NewYAMLFileFormatError(manifestName, err)
 	}
 
-	err = deployer.SetDependencies(deps)
+	err = reader.SetDependencies(deps)
 	if err != nil {
 		return wskderrors.NewYAMLFileFormatError(manifestName, err)
 	}
 
-	err = deployer.SetActions(actions)
+	err = reader.SetActions(actions)
 	if err != nil {
 		return wskderrors.NewYAMLFileFormatError(manifestName, err)
 	}
 
-	err = deployer.SetSequences(sequences)
+	err = reader.SetSequences(sequences)
 	if err != nil {
 		return wskderrors.NewYAMLFileFormatError(manifestName, err)
 	}
 
-	err = deployer.SetTriggers(triggers)
+	err = reader.SetTriggers(triggers)
 	if err != nil {
 		return wskderrors.NewYAMLFileFormatError(manifestName, err)
 	}
 
-	err = deployer.SetRules(rules)
+	err = reader.SetRules(rules)
 	if err != nil {
 		return wskderrors.NewYAMLFileFormatError(manifestName, err)
 	}
 
-	err = deployer.SetApis(apis)
+	err = reader.SetApis(apis)
 	if err != nil {
 		return wskderrors.NewYAMLFileFormatError(manifestName, err)
 	}
@@ -131,36 +131,6 @@ func (deployer *ManifestReader) HandleYaml(sdeployer *ServiceDeployer, manifestP
 	return nil
 }
 
-func (reader *ManifestReader) SetDependencies(deps map[string]utils.DependencyRecord) error {
-	for name, dep := range deps {
-		n := strings.Split(name, ":")
-		depName := n[1]
-		if depName == "" {
-			return nil
-		}
-		if !dep.IsBinding && !reader.IsUndeploy {
-			if _, exists := reader.serviceDeployer.DependencyMaster[depName]; !exists {
-				// dependency
-				gitReader := utils.NewGitReader(depName, dep)
-				err := gitReader.CloneDependency()
-				if err != nil {
-					return wskderrors.NewYAMLFileFormatError(depName, err)
-				}
-			} else {
-				// TODO: we should do a check to make sure this dependency is compatible with an already installed one.
-				// If not, we should throw dependency mismatch error.
-			}
-		}
-
-		// store in two places (one local to package to preserve relationship, one in master record to check for conflics
-		reader.serviceDeployer.Deployment.Packages[dep.Packagename].Dependencies[depName] = dep
-		reader.serviceDeployer.DependencyMaster[depName] = dep
-
-	}
-
-	return nil
-}
-
 func (reader *ManifestReader) SetPackages(packages map[string]*whisk.Package) error {
 
 	dep := reader.serviceDeployer
@@ -169,13 +139,6 @@ func (reader *ManifestReader) SetPackages(packages map[string]*whisk.Package) er
 	defer dep.mt.Unlock()
 
 	for _, pkg := range packages {
-		_, exist := dep.Deployment.Packages[pkg.Name]
-		if exist {
-			// TODO(): i18n of error message (or create a new named error)
-			// TODO(): Is there a better way to handle an existing dependency of same name?
-			err := errors.New("Package [" + pkg.Name + "] exists already.")
-			return wskderrors.NewYAMLParserErr(reader.serviceDeployer.ManifestPath, err)
-		}
 		newPack := NewDeploymentPackage()
 		newPack.Package = pkg
 		dep.Deployment.Packages[pkg.Name] = newPack
@@ -183,126 +146,78 @@ func (reader *ManifestReader) SetPackages(packages map[string]*whisk.Package) er
 	return nil
 }
 
-func (reader *ManifestReader) SetActions(actions []utils.ActionRecord) error {
+func (reader *ManifestReader) SetDependencies(deps map[string]utils.DependencyRecord) error {
 
 	dep := reader.serviceDeployer
 
 	dep.mt.Lock()
 	defer dep.mt.Unlock()
 
-	for _, manifestAction := range actions {
-		existAction, exists := reader.serviceDeployer.Deployment.Packages[manifestAction.Packagename].Actions[manifestAction.Action.Name]
-
-		if exists == true {
-			if existAction.Filepath == manifestAction.Filepath || manifestAction.Filepath == "" {
-				// we're adding a filesystem detected action so just updated code and filepath if needed
-				if manifestAction.Action.Exec.Kind != "" {
-					existAction.Action.Exec.Kind = manifestAction.Action.Exec.Kind
-				}
-
-				if manifestAction.Action.Exec.Code != nil {
-					code := *manifestAction.Action.Exec.Code
-					if code != "" {
-						existAction.Action.Exec.Code = manifestAction.Action.Exec.Code
-					}
-				}
-
-				existAction.Action.Annotations = manifestAction.Action.Annotations
-				existAction.Action.Limits = manifestAction.Action.Limits
-				existAction.Action.Parameters = manifestAction.Action.Parameters
-				existAction.Action.Version = manifestAction.Action.Version
-
-				if manifestAction.Filepath != "" {
-					existAction.Filepath = manifestAction.Filepath
-				}
-
-				err := reader.checkAction(existAction)
-				if err != nil {
-					return wskderrors.NewFileReadError(manifestAction.Filepath, err)
+	for name, dependency := range deps {
+		// name is <packagename>:<dependencylabel>
+		depName := strings.Split(name, ":")[1]
+		if len(depName) == 0 {
+			return nil
+		}
+		if !dependency.IsBinding && !reader.IsUndeploy {
+			if _, exists := dep.DependencyMaster[depName]; exists {
+				if !utils.CompareDependencyRecords(dep.DependencyMaster[depName], dependency) {
+					location := strings.Join([]string{dep.DependencyMaster[depName].Location, dependency.Location}, ",")
+					errmsg := wski18n.T(wski18n.ID_ERR_DEPENDENCIES_WITH_SAME_LABEL_X_dependency_X_location_X,
+						map[string]interface{}{wski18n.KEY_DEPENDENCY: depName,
+							wski18n.KEY_LOCATION: location})
+					return wskderrors.NewYAMLParserErr(dep.ManifestPath, errmsg)
 				}
-
-			} else {
-				// Action exists, but references two different sources
-				// TODO(): i18n of error message (or create a new named error)
-				err := errors.New("Conflict detected for action named [" +
-					existAction.Action.Name + "].\nFound two locations for source file: [" +
-					existAction.Filepath + "] and [" + manifestAction.Filepath + "]")
-				return wskderrors.NewYAMLParserErr(reader.serviceDeployer.ManifestPath, err)
 			}
-		} else {
-			// not a new action so update the action in the package
-			err := reader.checkAction(manifestAction)
+			gitReader := utils.NewGitReader(depName, dependency)
+			err := gitReader.CloneDependency()
 			if err != nil {
-				return wskderrors.NewFileReadError(manifestAction.Filepath, err)
+				return err
 			}
-			reader.serviceDeployer.Deployment.Packages[manifestAction.Packagename].Actions[manifestAction.Action.Name] = manifestAction
 		}
+		// store in two places (one local to package to preserve relationship, one in master record to check for conflics
+		dep.Deployment.Packages[dependency.Packagename].Dependencies[depName] = dependency
+		dep.DependencyMaster[depName] = dependency
 	}
 
 	return nil
 }
 
-// TODO create named errors
-// Check action record before deploying it
-// action record is created by reading and composing action elements from manifest file
-// Action.kind is mandatory which is set to
-// (1) action runtime for an action and (2) set to "sequence" for a sequence
-// Also, action executable code should be specified for any action
-func (reader *ManifestReader) checkAction(action utils.ActionRecord) error {
-	if action.Action.Exec.Kind == "" {
-		// TODO(): i18n of error message (or create a new named error)
-		err := errors.New("Action [" + action.Action.Name + "] has no kind set.")
-		return wskderrors.NewYAMLParserErr(reader.serviceDeployer.ManifestPath, err)
-	}
+func (reader *ManifestReader) SetActions(actions []utils.ActionRecord) error {
 
-	if action.Action.Exec.Code != nil {
-		code := *action.Action.Exec.Code
-		if code == "" && action.Action.Exec.Kind != parsers.YAML_KEY_SEQUENCE {
-			// TODO(): i18n of error message (or create a new named error)
-			err := errors.New("Action [" + action.Action.Name + "] has no source code.")
-			return wskderrors.NewYAMLParserErr(reader.serviceDeployer.ManifestPath, err)
+	dep := reader.serviceDeployer
+
+	dep.mt.Lock()
+	defer dep.mt.Unlock()
+
+	for _, manifestAction := range actions {
+		err := reader.checkAction(manifestAction)
+		if err != nil {
+			return err
 		}
+		dep.Deployment.Packages[manifestAction.Packagename].Actions[manifestAction.Action.Name] = manifestAction
 	}
-
 	return nil
 }
 
-func (reader *ManifestReader) SetSequences(actions []utils.ActionRecord) error {
+func (reader *ManifestReader) SetSequences(sequences []utils.ActionRecord) error {
 	dep := reader.serviceDeployer
 
 	dep.mt.Lock()
 	defer dep.mt.Unlock()
 
-	for _, seqAction := range actions {
-		// check if the sequence action is exist in actions
-		// If the sequence action exists in actions, return error
-		_, exists := reader.serviceDeployer.Deployment.Packages[seqAction.Packagename].Actions[seqAction.Action.Name]
-		if exists == true {
-			// TODO(798): i18n of error message (or create a new named error)
-			err := errors.New("Sequence action's name [" +
-				seqAction.Action.Name + "] is already used by an action.")
+	for _, sequence := range sequences {
+		// If the sequence name matches with any of the actions defined, return error
+		if _, exists := dep.Deployment.Packages[sequence.Packagename].Actions[sequence.Action.Name]; exists {
+			err := wski18n.T(wski18n.ID_ERR_SEQUENCE_HAVING_SAME_NAME_AS_ACTION_X_action_X,
+				map[string]interface{}{wski18n.KEY_SEQUENCE: sequence.Action.Name})
 			return wskderrors.NewYAMLParserErr(reader.serviceDeployer.ManifestPath, err)
 		}
-		existAction, exists := reader.serviceDeployer.Deployment.Packages[seqAction.Packagename].Sequences[seqAction.Action.Name]
-
-		if exists == true {
-			existAction.Action.Annotations = seqAction.Action.Annotations
-			existAction.Action.Exec.Kind = "sequence"
-			existAction.Action.Exec.Components = seqAction.Action.Exec.Components
-			existAction.Action.Publish = seqAction.Action.Publish
-			existAction.Action.Namespace = seqAction.Action.Namespace
-			existAction.Action.Limits = seqAction.Action.Limits
-			existAction.Action.Parameters = seqAction.Action.Parameters
-			existAction.Action.Version = seqAction.Action.Version
-		} else {
-			// not a new action so update the action in the package
-			err := reader.checkAction(seqAction)
-			if err != nil {
-				// TODO() Need a better error type here
-				return wskderrors.NewFileReadError(seqAction.Filepath, err)
-			}
-			reader.serviceDeployer.Deployment.Packages[seqAction.Packagename].Sequences[seqAction.Action.Name] = seqAction
+		err := reader.checkAction(sequence)
+		if err != nil {
+			return err
 		}
+		dep.Deployment.Packages[sequence.Packagename].Sequences[sequence.Action.Name] = sequence
 	}
 
 	return nil
@@ -317,19 +232,29 @@ func (reader *ManifestReader) SetTriggers(triggers []*whisk.Trigger) error {
 	defer dep.mt.Unlock()
 
 	for _, trigger := range triggers {
-		existTrigger, exist := dep.Deployment.Triggers[trigger.Name]
-		if exist {
-			existTrigger.Name = trigger.Name
-			existTrigger.ActivationId = trigger.ActivationId
-			existTrigger.Namespace = trigger.Namespace
-			existTrigger.Annotations = trigger.Annotations
-			existTrigger.Version = trigger.Version
-			existTrigger.Parameters = trigger.Parameters
-			existTrigger.Publish = trigger.Publish
-		} else {
-			dep.Deployment.Triggers[trigger.Name] = trigger
+		if _, exists := dep.Deployment.Triggers[trigger.Name]; exists {
+			var feed string
+			var existingFeed string
+			for _, a := range dep.Deployment.Triggers[trigger.Name].Annotations {
+				if a.Key == parsers.YAML_KEY_FEED {
+					existingFeed = a.Value.(string)
+				}
+			}
+			for _, a := range trigger.Annotations {
+				if a.Key == parsers.YAML_KEY_FEED {
+					feed = a.Value.(string)
+				}
+			}
+			if feed != existingFeed {
+				feed = fmt.Sprintf("%q", feed)
+				existingFeed = fmt.Sprintf("%q", existingFeed)
+				err := wski18n.T(wski18n.ID_ERR_CONFLICTING_TRIGGERS_ACROSS_PACKAGES_X_trigger_X_feed_X,
+					map[string]interface{}{wski18n.KEY_TRIGGER: trigger.Name,
+						wski18n.KEY_TRIGGER_FEED: strings.Join([]string{feed, existingFeed}, ", ")})
+				return wskderrors.NewYAMLParserErr(reader.serviceDeployer.ManifestPath, err)
+			}
 		}
-
+		dep.Deployment.Triggers[trigger.Name] = trigger
 	}
 	return nil
 }
@@ -341,19 +266,24 @@ func (reader *ManifestReader) SetRules(rules []*whisk.Rule) error {
 	defer dep.mt.Unlock()
 
 	for _, rule := range rules {
-		existRule, exist := dep.Deployment.Rules[rule.Name]
-		if exist {
-			existRule.Name = rule.Name
-			existRule.Publish = rule.Publish
-			existRule.Version = rule.Version
-			existRule.Namespace = rule.Namespace
-			existRule.Action = rule.Action
-			existRule.Trigger = rule.Trigger
-			existRule.Status = rule.Status
-		} else {
-			dep.Deployment.Rules[rule.Name] = rule
+		if _, exists := dep.Deployment.Rules[rule.Name]; exists {
+			action := rule.Action.(string)
+			existingAction := dep.Deployment.Rules[rule.Name].Action.(string)
+			trigger := rule.Trigger.(string)
+			existingTrigger := dep.Deployment.Rules[rule.Name].Trigger.(string)
+			if action != existingAction || trigger != existingTrigger {
+				action = fmt.Sprintf("%q", action)
+				existingAction = fmt.Sprintf("%q", existingAction)
+				trigger = fmt.Sprintf("%q", trigger)
+				existingTrigger = fmt.Sprintf("%q", existingTrigger)
+				err := wski18n.T(wski18n.ID_ERR_CONFLICTING_RULES_ACROSS_PACKAGES_X_rule_X_action_X_trigger_X,
+					map[string]interface{}{wski18n.KEY_RULE: rule.Name,
+						wski18n.KEY_TRIGGER: strings.Join([]string{trigger, existingTrigger}, ", "),
+						wski18n.KEY_ACTION:  strings.Join([]string{action, existingAction}, ", ")})
+				return wskderrors.NewYAMLParserErr(reader.serviceDeployer.ManifestPath, err)
+			}
 		}
-
+		dep.Deployment.Rules[rule.Name] = rule
 	}
 	return nil
 }
@@ -365,13 +295,31 @@ func (reader *ManifestReader) SetApis(ar []*whisk.ApiCreateRequest) error {
 	defer dep.mt.Unlock()
 
 	for _, api := range ar {
-		existApi, exist := dep.Deployment.Apis[api.ApiDoc.Action.Name]
-		if exist {
-			existApi.ApiDoc.ApiName = api.ApiDoc.ApiName
-		} else {
-			dep.Deployment.Apis[api.ApiDoc.Action.Name] = api
-		}
+		dep.Deployment.Apis[api.ApiDoc.Action.Name] = api
+	}
+	return nil
+}
 
+// Check action record before deploying it
+// action record is created by reading and composing action elements from manifest file
+// Action.kind is mandatory which is set to
+// (1) action runtime for an action and (2) set to "sequence" for a sequence
+// Also, action executable code should be specified for any action
+func (reader *ManifestReader) checkAction(action utils.ActionRecord) error {
+	if action.Action.Exec.Kind == "" {
+		err := wski18n.T(wski18n.ID_ERR_ACTION_WITHOUT_KIND_X_action_X,
+			map[string]interface{}{wski18n.KEY_ACTION: action.Action.Name})
+		return wskderrors.NewYAMLParserErr(reader.serviceDeployer.ManifestPath, err)
+	}
+
+	if action.Action.Exec.Code != nil {
+		code := *action.Action.Exec.Code
+		if code == "" && action.Action.Exec.Kind != parsers.YAML_KEY_SEQUENCE {
+			err := wski18n.T(wski18n.ID_ERR_ACTION_WITHOUT_SOURCE_X_action_X,
+				map[string]interface{}{wski18n.KEY_ACTION: action.Action.Name})
+			return wskderrors.NewYAMLParserErr(reader.serviceDeployer.ManifestPath, err)
+		}
 	}
+
 	return nil
 }
diff --git a/deployers/manifestreader_test.go b/deployers/manifestreader_test.go
index 7d5acbc..bf2e439 100644
--- a/deployers/manifestreader_test.go
+++ b/deployers/manifestreader_test.go
@@ -20,38 +20,327 @@
 package deployers
 
 import (
+	"fmt"
+	"testing"
+
 	"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/wskprint"
 	"github.com/stretchr/testify/assert"
-	"testing"
 )
 
 var mr *ManifestReader
 var ps *parsers.YAMLParser
 var ms *parsers.YAML
 
-func init() {
+const (
+	// local error messages
+	TEST_ERROR_BUILD_SERVICE_DEPLOYER        = "Manifest [%s]: Failed to build service deployer."
+	TEST_ERROR_MANIFEST_PARSE_FAILURE        = "Manifest [%s]: Failed to parse."
+	TEST_ERROR_MANIFEST_SET_PACKAGES         = "Manifest [%s]: Failed to set packages."
+	TEST_ERROR_FAILED_TO_REPORT_ERROR        = "Manifest [%s]: Failed to report parser error."
+	TEST_ERROR_MANIFEST_SET_ANNOTATION       = "[%s]: Failed to set Annotation value."
+	TEST_ERROR_MANIFEST_SET_INPUT_PARAMETER  = "[%s]: Failed to set input Parameter value."
+	TEST_ERROR_MANIFEST_SET_PUBLISH          = "Package [%s]: Failed to set publish."
+	TEST_ERROR_MANIFEST_SET_DEPENDENCIES     = "Package [%s]: Failed to set dependencies."
+	TEST_ERROR_MANIFEST_SET_ACTION_CODE      = "Action [%s]: Failed to set action code."
+	TEST_ERROR_MANIFEST_SET_ACTION_KIND      = "Action [%s]: Failed to set action kind."
+	TEST_ERROR_MANIFEST_SET_ACTION_WEB       = "Action [%s]: Failed to set web action."
+	TEST_ERROR_MANIFEST_SET_ACTION_CONDUCTOR = "Action [%s]: Failed to set conductor action."
+	TEST_ERROR_MANIFEST_SET_ACTION_IMAGE     = "Action [%s]: Failed to set action image."
+)
 
+func init() {
 	// Setup "trace" flag for unit tests based upon "go test" -v flag
 	utils.Flags.Trace = wskprint.DetectGoTestVerbose()
-
-	sd = NewServiceDeployer()
-	sd.ManifestPath = manifest_file
-	mr = NewManifestReader(sd)
-	ps = parsers.NewYAMLParser()
-	ms, _ = ps.ParseManifest(manifest_file)
 }
 
-// Test could parse Manifest file successfully
-func TestManifestReader_ParseManifest(t *testing.T) {
-	_, _, err := mr.ParseManifest()
-	assert.Equal(t, err, nil, "New ManifestReader failed")
+func buildServiceDeployer(manifestFile string) (*ServiceDeployer, error) {
+	deploymentFile := ""
+	var deployer = NewServiceDeployer()
+	deployer.ManifestPath = manifestFile
+	deployer.DeploymentPath = deploymentFile
+	deployer.Preview = utils.Flags.Preview
+
+	deployer.DependencyMaster = make(map[string]utils.DependencyRecord)
+
+	config := whisk.Config{
+		Namespace:        "test",
+		AuthToken:        "user:pass",
+		Host:             "host",
+		ApigwAccessToken: "token",
+	}
+	deployer.ClientConfig = &config
+
+	op, error := utils.ParseOpenWhisk(deployer.ClientConfig.Host)
+	if error == nil {
+		utils.SupportedRunTimes = utils.ConvertToMap(op)
+		utils.DefaultRunTimes = utils.DefaultRuntimes(op)
+		utils.FileExtensionRuntimeKindMap = utils.FileExtensionRuntimes(op)
+		utils.FileRuntimeExtensionsMap = utils.FileRuntimeExtensions(op)
+	}
+
+	return deployer, nil
 }
 
-// Test could Init root package successfully.
 func TestManifestReader_InitPackages(t *testing.T) {
-	err := mr.InitPackages(ps, ms, whisk.KeyValue{})
-	assert.Equal(t, err, nil, "Init Root Package failed")
+	manifestFile := "../tests/dat/manifest_validate_package_inputs_and_annotations.yaml"
+	deployer, err := buildServiceDeployer(manifestFile)
+	assert.Nil(t, err, fmt.Sprintf(TEST_ERROR_BUILD_SERVICE_DEPLOYER, manifestFile))
+
+	var manifestReader = NewManifestReader(deployer)
+	manifestReader.IsUndeploy = false
+	manifest, manifestParser, err := manifestReader.ParseManifest()
+	assert.Nil(t, err, fmt.Sprintf(TEST_ERROR_MANIFEST_PARSE_FAILURE, manifestFile))
+
+	err = manifestReader.InitPackages(manifestParser, manifest, whisk.KeyValue{})
+	assert.Nil(t, err, fmt.Sprintf(TEST_ERROR_MANIFEST_SET_PACKAGES, manifestFile))
+	assert.Equal(t, 3, len(deployer.Deployment.Packages), fmt.Sprintf(TEST_ERROR_MANIFEST_SET_PACKAGES, manifestFile, ""))
+
+	expectedParametersAndAnnotations := 0
+
+	for packageName, pack := range deployer.Deployment.Packages {
+		switch packageName {
+		case "helloworld1":
+			expectedParametersAndAnnotations = 0
+			assert.False(t, *pack.Package.Publish, fmt.Sprintf(TEST_ERROR_MANIFEST_SET_PUBLISH, packageName))
+		case "helloworld2":
+			expectedParametersAndAnnotations = 1
+			for _, param := range pack.Package.Parameters {
+				switch param.Key {
+				case "helloworld_input1":
+					assert.Equal(t, "value1", param.Value,
+						fmt.Sprintf(TEST_ERROR_MANIFEST_SET_INPUT_PARAMETER, packageName))
+				}
+			}
+			for _, annotation := range pack.Package.Annotations {
+				switch annotation.Key {
+				case "helloworld_annotation1":
+					assert.Equal(t, "value1", annotation.Value,
+						fmt.Sprintf(TEST_ERROR_MANIFEST_SET_ANNOTATION, packageName))
+				}
+			}
+			assert.False(t, *pack.Package.Publish, fmt.Sprintf(TEST_ERROR_MANIFEST_SET_PUBLISH, packageName))
+		case "helloworld3":
+			expectedParametersAndAnnotations = 2
+			for _, param := range pack.Package.Parameters {
+				switch param.Key {
+				case "helloworld_input1":
+					assert.Equal(t, "value1", param.Value,
+						fmt.Sprintf(TEST_ERROR_MANIFEST_SET_INPUT_PARAMETER, packageName))
+				case "helloworld_input2":
+					assert.Equal(t, "value2", param.Value,
+						fmt.Sprintf(TEST_ERROR_MANIFEST_SET_INPUT_PARAMETER, packageName))
+				}
+			}
+			for _, annotation := range pack.Package.Annotations {
+				switch annotation.Key {
+				case "helloworld_annotation1":
+					assert.Equal(t, "value1", annotation.Value,
+						fmt.Sprintf(TEST_ERROR_MANIFEST_SET_ANNOTATION, packageName))
+				case "helloworld_annotation2":
+					assert.Equal(t, "value2", annotation.Value,
+						fmt.Sprintf(TEST_ERROR_MANIFEST_SET_ANNOTATION, packageName))
+				}
+			}
+			assert.True(t, *pack.Package.Publish, fmt.Sprintf(TEST_ERROR_MANIFEST_SET_PUBLISH, packageName))
+		}
+		assert.Equal(t, expectedParametersAndAnnotations, len(pack.Package.Parameters),
+			fmt.Sprintf(TEST_ERROR_MANIFEST_SET_INPUT_PARAMETER, packageName))
+		assert.Equal(t, expectedParametersAndAnnotations, len(pack.Package.Annotations),
+			fmt.Sprintf(TEST_ERROR_MANIFEST_SET_ANNOTATION, packageName))
+	}
+}
+
+func TestManifestReader_SetDependencies(t *testing.T) {
+	manifestFile := "../tests/dat/manifest_validate_dependencies.yaml"
+	deployer, err := buildServiceDeployer(manifestFile)
+	assert.Nil(t, err, fmt.Sprintf(TEST_ERROR_BUILD_SERVICE_DEPLOYER, manifestFile))
+
+	var manifestReader = NewManifestReader(deployer)
+	manifestReader.IsUndeploy = false
+	manifest, manifestParser, err := manifestReader.ParseManifest()
+	assert.Nil(t, err, fmt.Sprintf(TEST_ERROR_MANIFEST_PARSE_FAILURE, manifestFile))
+
+	err = manifestReader.InitPackages(manifestParser, manifest, whisk.KeyValue{})
+	assert.Nil(t, err, fmt.Sprintf(TEST_ERROR_MANIFEST_SET_PACKAGES, manifestFile))
+
+	err = manifestReader.HandleYaml(deployer, manifestParser, manifest, whisk.KeyValue{})
+	assert.Nil(t, err, fmt.Sprintf(TEST_ERROR_MANIFEST_PARSE_FAILURE, manifestFile))
+
+	expectedLocationHelloWorlds := "https://github.com/apache/incubator-openwhisk-test/packages/helloworlds"
+	expectedLocationHelloWhisk := "https://github.com/apache/incubator-openwhisk-test/packages/hellowhisk"
+	expectedLocationUtils := "/whisk.system/utils"
+
+	for pkgName, pkg := range deployer.Deployment.Packages {
+		switch pkgName {
+		case "helloworld1":
+			for depName, dep := range pkg.Dependencies {
+				switch depName {
+				case "dependency1":
+				case "helloworlds":
+					assert.Equal(t, expectedLocationHelloWorlds, dep.Location,
+						fmt.Sprintf(TEST_ERROR_MANIFEST_SET_DEPENDENCIES, pkgName))
+				case "dependency2":
+					assert.Equal(t, expectedLocationHelloWhisk, dep.Location,
+						fmt.Sprintf(TEST_ERROR_MANIFEST_SET_DEPENDENCIES, pkgName))
+				case "dependency3":
+					assert.Equal(t, expectedLocationUtils, dep.Location,
+						fmt.Sprintf(TEST_ERROR_MANIFEST_SET_DEPENDENCIES, pkgName))
+				}
+			}
+
+		case "helloworld2":
+			for depName, dep := range pkg.Dependencies {
+				switch depName {
+				case "helloworlds":
+				case "dependency1":
+				case "dependency4":
+					assert.Equal(t, expectedLocationHelloWorlds, dep.Location,
+						fmt.Sprintf(TEST_ERROR_MANIFEST_SET_DEPENDENCIES, pkgName))
+				case "dependency5":
+					assert.Equal(t, expectedLocationHelloWhisk, dep.Location,
+						fmt.Sprintf(TEST_ERROR_MANIFEST_SET_DEPENDENCIES, pkgName))
+				case "dependency6":
+					assert.Equal(t, expectedLocationUtils, dep.Location,
+						fmt.Sprintf(TEST_ERROR_MANIFEST_SET_DEPENDENCIES, pkgName))
+				}
+			}
+		}
+	}
+}
+
+func TestManifestReader_SetDependencies_Bogus(t *testing.T) {
+	manifestFile := "../tests/dat/manifest_validate_dependencies_bogus.yaml"
+	deployer, err := buildServiceDeployer(manifestFile)
+	assert.Nil(t, err, fmt.Sprintf(TEST_ERROR_BUILD_SERVICE_DEPLOYER, manifestFile))
+
+	var manifestReader = NewManifestReader(deployer)
+	manifestReader.IsUndeploy = false
+	manifest, manifestParser, err := manifestReader.ParseManifest()
+	assert.Nil(t, err, fmt.Sprintf(TEST_ERROR_MANIFEST_PARSE_FAILURE, manifestFile))
+
+	err = manifestReader.InitPackages(manifestParser, manifest, whisk.KeyValue{})
+	assert.Nil(t, err, fmt.Sprintf(TEST_ERROR_MANIFEST_SET_PACKAGES, manifestFile))
+
+	err = manifestReader.HandleYaml(deployer, manifestParser, manifest, whisk.KeyValue{})
+	assert.NotNil(t, err, fmt.Sprintf(TEST_ERROR_FAILED_TO_REPORT_ERROR, manifestFile))
+}
+
+func TestManifestReader_SetActions(t *testing.T) {
+	manifestFile := "../tests/dat/manifest_validate_action_all.yaml"
+	deployer, err := buildServiceDeployer(manifestFile)
+	assert.Nil(t, err, fmt.Sprintf(TEST_ERROR_BUILD_SERVICE_DEPLOYER, manifestFile))
+
+	var manifestReader = NewManifestReader(deployer)
+	manifestReader.IsUndeploy = false
+	manifest, manifestParser, err := manifestReader.ParseManifest()
+	assert.Nil(t, err, fmt.Sprintf(TEST_ERROR_MANIFEST_PARSE_FAILURE, manifestFile))
+
+	err = manifestReader.InitPackages(manifestParser, manifest, whisk.KeyValue{})
+	assert.Nil(t, err, fmt.Sprintf(TEST_ERROR_MANIFEST_SET_PACKAGES, manifestFile))
+
+	err = manifestReader.HandleYaml(deployer, manifestParser, manifest, whisk.KeyValue{})
+	assert.Nil(t, err, fmt.Sprintf(TEST_ERROR_MANIFEST_PARSE_FAILURE, manifestFile))
+
+	expectedRuntime := "nodejs:6"
+	expectedImage := "openwhisk/skeleton"
+
+	for actionName, action := range deployer.Deployment.Packages["helloworld"].Actions {
+		switch actionName {
+		case "helloworld1":
+		case "helloworld2":
+			assert.NotEmpty(t, action.Action.Exec.Code,
+				fmt.Sprintf(TEST_ERROR_MANIFEST_SET_ACTION_CODE, actionName))
+			assert.Equal(t, expectedRuntime, action.Action.Exec.Kind,
+				fmt.Sprintf(TEST_ERROR_MANIFEST_SET_ACTION_KIND, actionName))
+		case "helloworld3":
+			for _, param := range action.Action.Parameters {
+				switch param.Key {
+				case "parameter1":
+					assert.Equal(t, "value1", param.Value,
+						fmt.Sprintf(TEST_ERROR_MANIFEST_SET_INPUT_PARAMETER, actionName))
+				case "parameter2":
+					assert.Equal(t, "value2", param.Value,
+						fmt.Sprintf(TEST_ERROR_MANIFEST_SET_INPUT_PARAMETER, actionName))
+				}
+			}
+			for _, annotation := range action.Action.Annotations {
+				switch annotation.Key {
+				case "annotation1":
+					assert.Equal(t, "value1", annotation.Value,
+						fmt.Sprintf(TEST_ERROR_MANIFEST_SET_ANNOTATION, actionName))
+				case "annotation2":
+					assert.Equal(t, "value2", annotation.Value,
+						fmt.Sprintf(TEST_ERROR_MANIFEST_SET_ANNOTATION, actionName))
+				}
+			}
+		case "helloworld4":
+			assert.True(t, action.Action.WebAction(),
+				fmt.Sprintf(TEST_ERROR_MANIFEST_SET_ACTION_WEB, actionName))
+		case "helloworld5":
+			for _, annotation := range action.Action.Annotations {
+				switch annotation.Key {
+				case "conductor":
+					assert.True(t, annotation.Value.(bool),
+						fmt.Sprintf(TEST_ERROR_MANIFEST_SET_ACTION_CONDUCTOR, actionName))
+				}
+			}
+		case "helloworld6":
+			assert.Equal(t, expectedImage, action.Action.Exec.Image,
+				fmt.Sprintf(TEST_ERROR_MANIFEST_SET_ACTION_IMAGE, actionName))
+		}
+	}
+}
+
+func TestManifestReader_SetSequences_Bogus(t *testing.T) {
+	manifestFile := "../tests/dat/manifest_validate_sequences_bogus.yaml"
+	deployer, err := buildServiceDeployer(manifestFile)
+	assert.Nil(t, err, fmt.Sprintf(TEST_ERROR_BUILD_SERVICE_DEPLOYER, manifestFile))
+
+	var manifestReader = NewManifestReader(deployer)
+	manifestReader.IsUndeploy = false
+	manifest, manifestParser, err := manifestReader.ParseManifest()
+	assert.Nil(t, err, fmt.Sprintf(TEST_ERROR_MANIFEST_PARSE_FAILURE, manifestFile))
+
+	err = manifestReader.InitPackages(manifestParser, manifest, whisk.KeyValue{})
+	assert.Nil(t, err, fmt.Sprintf(TEST_ERROR_MANIFEST_SET_PACKAGES, manifestFile))
+
+	err = manifestReader.HandleYaml(deployer, manifestParser, manifest, whisk.KeyValue{})
+	assert.NotNil(t, err, fmt.Sprintf(TEST_ERROR_FAILED_TO_REPORT_ERROR, manifestFile))
+}
+
+func TestManifestReader_SetTriggers_Bogus(t *testing.T) {
+	manifestFile := "../tests/dat/manifest_validate_triggers_bogus.yaml"
+	deployer, err := buildServiceDeployer(manifestFile)
+	assert.Nil(t, err, fmt.Sprintf(TEST_ERROR_BUILD_SERVICE_DEPLOYER, manifestFile))
+
+	var manifestReader = NewManifestReader(deployer)
+	manifestReader.IsUndeploy = false
+	manifest, manifestParser, err := manifestReader.ParseManifest()
+	assert.Nil(t, err, fmt.Sprintf(TEST_ERROR_MANIFEST_PARSE_FAILURE, manifestFile))
+
+	err = manifestReader.InitPackages(manifestParser, manifest, whisk.KeyValue{})
+	assert.Nil(t, err, fmt.Sprintf(TEST_ERROR_MANIFEST_SET_PACKAGES, manifestFile))
+
+	err = manifestReader.HandleYaml(deployer, manifestParser, manifest, whisk.KeyValue{})
+	assert.NotNil(t, err, fmt.Sprintf(TEST_ERROR_FAILED_TO_REPORT_ERROR, manifestFile))
+}
+
+func TestManifestReader_SetRules_Bogus(t *testing.T) {
+	manifestFile := "../tests/dat/manifest_validate_rules_bogus.yaml"
+	deployer, err := buildServiceDeployer(manifestFile)
+	assert.Nil(t, err, fmt.Sprintf(TEST_ERROR_BUILD_SERVICE_DEPLOYER, manifestFile))
+
+	var manifestReader = NewManifestReader(deployer)
+	manifestReader.IsUndeploy = false
+	manifest, manifestParser, err := manifestReader.ParseManifest()
+	assert.Nil(t, err, fmt.Sprintf(TEST_ERROR_MANIFEST_PARSE_FAILURE, manifestFile))
+
+	err = manifestReader.InitPackages(manifestParser, manifest, whisk.KeyValue{})
+	assert.Nil(t, err, fmt.Sprintf(TEST_ERROR_MANIFEST_SET_PACKAGES, manifestFile))
+
+	err = manifestReader.HandleYaml(deployer, manifestParser, manifest, whisk.KeyValue{})
+	assert.NotNil(t, err, fmt.Sprintf(TEST_ERROR_FAILED_TO_REPORT_ERROR, manifestFile))
 }
diff --git a/deployers/servicedeployer.go b/deployers/servicedeployer.go
index e35fe96..d3a9894 100644
--- a/deployers/servicedeployer.go
+++ b/deployers/servicedeployer.go
@@ -1570,8 +1570,6 @@ func (deployer *ServiceDeployer) getDependentDeployer(depName string, depRecord
 	depServiceDeployer.Client = deployer.Client
 	depServiceDeployer.ClientConfig = deployer.ClientConfig
 
-	depServiceDeployer.DependencyMaster = deployer.DependencyMaster
-
 	// share the master dependency list
 	depServiceDeployer.DependencyMaster = deployer.DependencyMaster
 
diff --git a/parsers/manifest_parser.go b/parsers/manifest_parser.go
index c63e4dd..3403820 100644
--- a/parsers/manifest_parser.go
+++ b/parsers/manifest_parser.go
@@ -20,14 +20,12 @@ package parsers
 import (
 	"encoding/base64"
 	"errors"
+	"gopkg.in/yaml.v2"
 	"io/ioutil"
 	"os"
 	"path"
-	"strings"
-
-	"gopkg.in/yaml.v2"
-
 	"path/filepath"
+	"strings"
 
 	"github.com/apache/incubator-openwhisk-client-go/whisk"
 	"github.com/apache/incubator-openwhisk-wskdeploy/utils"
diff --git a/tests/dat/manifest_validate_action_all.yaml b/tests/dat/manifest_validate_action_all.yaml
new file mode 100644
index 0000000..8faf05f
--- /dev/null
+++ b/tests/dat/manifest_validate_action_all.yaml
@@ -0,0 +1,43 @@
+#
+# 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:
+    helloworld:
+        actions:
+            helloworld1:
+                function: actions/hello.js
+            helloworld2:
+                code: const main = ({ msg }) => { console.log(msg); return {msg}; }
+                runtime: nodejs:6
+            helloworld3:
+                function: actions/hello.js
+                annotations:
+                    annotation1: value1
+                    annotation2: value2
+                inputs:
+                    parameter1: value1
+                    parameter2: value2
+            helloworld4:
+                function: actions/hello.js
+                web: true
+            helloworld5:
+                function: actions/hello.js
+                conductor: true
+            helloworld6:
+                function: actions/hello.js
+                docker: openwhisk/skeleton
+
+
diff --git a/tests/dat/manifest_validate_dependencies.yaml b/tests/dat/manifest_validate_dependencies.yaml
new file mode 100644
index 0000000..300cdad
--- /dev/null
+++ b/tests/dat/manifest_validate_dependencies.yaml
@@ -0,0 +1,42 @@
+#
+# 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:
+    helloworld1:
+        dependencies:
+            helloworlds:
+                location: github.com/apache/incubator-openwhisk-test/packages/helloworlds
+            dependency1:
+                location: github.com/apache/incubator-openwhisk-test/packages/helloworlds
+            dependency2:
+                location: github.com/apache/incubator-openwhisk-test/packages/hellowhisk
+            dependency3:
+                location: /whisk.system/utils
+    helloworld2:
+        dependencies:
+            helloworlds:
+                location: github.com/apache/incubator-openwhisk-test/packages/helloworlds
+            dependency1:
+                location: github.com/apache/incubator-openwhisk-test/packages/helloworlds
+            dependency4:
+                location: github.com/apache/incubator-openwhisk-test/packages/helloworlds
+            dependency5:
+                location: github.com/apache/incubator-openwhisk-test/packages/hellowhisk
+            dependency6:
+                location: /whisk.system/utils
+
+
+
diff --git a/tests/dat/manifest_validate_dependencies_bogus.yaml b/tests/dat/manifest_validate_dependencies_bogus.yaml
new file mode 100644
index 0000000..f75c3b1
--- /dev/null
+++ b/tests/dat/manifest_validate_dependencies_bogus.yaml
@@ -0,0 +1,42 @@
+#
+# 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:
+    helloworld1:
+        dependencies:
+            helloworlds:
+                location: github.com/apache/incubator-openwhisk-test/packages/helloworlds
+            dependency1:
+                location: github.com/apache/incubator-openwhisk-test/packages/helloworlds
+            dependency2:
+                location: github.com/apache/incubator-openwhisk-test/packages/hellowhisk
+            dependency3:
+                location: /whisk.system/utils
+    helloworld2:
+        dependencies:
+            helloworlds:
+                location: github.com/apache/incubator-openwhisk-test/packages/helloworlds
+            dependency1:
+                location: github.com/apache/incubator-openwhisk-test/packages/hellowhisk
+            dependency4:
+                location: github.com/apache/incubator-openwhisk-test/packages/helloworlds
+            dependency5:
+                location: github.com/apache/incubator-openwhisk-test/packages/hellowhisk
+            dependency6:
+                location: /whisk.system/utils
+
+
+
diff --git a/tests/dat/manifest_validate_package_inputs_and_annotations.yaml b/tests/dat/manifest_validate_package_inputs_and_annotations.yaml
new file mode 100644
index 0000000..ea5f253
--- /dev/null
+++ b/tests/dat/manifest_validate_package_inputs_and_annotations.yaml
@@ -0,0 +1,37 @@
+#
+# 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:
+    helloworld1:
+        license: Apache-2.0
+    helloworld2:
+        version: 2.0.0
+        license: MIT
+        inputs:
+            helloworld_input1: value1
+        annotations:
+            helloworld_annotation1: value1
+    helloworld3:
+        version: 3.0.0
+        license: GPL-3.0
+        inputs:
+            helloworld_input1: value1
+            helloworld_input2: value2
+        annotations:
+            helloworld_annotation1: value1
+            helloworld_annotation2: value2
+        public: true
+
diff --git a/tests/dat/manifest_validate_rules_bogus.yaml b/tests/dat/manifest_validate_rules_bogus.yaml
new file mode 100644
index 0000000..587929f
--- /dev/null
+++ b/tests/dat/manifest_validate_rules_bogus.yaml
@@ -0,0 +1,41 @@
+#
+# 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:
+    helloworld1:
+        actions:
+            hello:
+                function: actions/hello.js
+        triggers:
+            trigger1:
+        rules:
+            rule1:
+                trigger: trigger1
+                action: hello
+    helloworld2:
+        actions:
+            hello:
+                function: actions/hello.js
+        triggers:
+            trigger2:
+        rules:
+            rule1:
+                trigger: trigger2
+                action: hello
+            rule2:
+                trigger: trigger2
+                action: hello
+
diff --git a/tests/dat/manifest_validate_sequences_bogus.yaml b/tests/dat/manifest_validate_sequences_bogus.yaml
new file mode 100644
index 0000000..4d5425d
--- /dev/null
+++ b/tests/dat/manifest_validate_sequences_bogus.yaml
@@ -0,0 +1,38 @@
+#
+# 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:
+    helloworld:
+        actions:
+            hello:
+                function: actions/hello.js
+                runtime: nodejs:6
+            helloWithParams:
+                function: actions/hello.js
+                runtime: nodejs:6
+                inputs:
+                    name: Amy
+                    place: Paris
+            helloWithParams1:
+                function: actions/hello.js
+                runtime: nodejs:6
+                inputs:
+                    name: Amy
+                    place: Paris
+        sequences:
+            hello:
+                actions: helloWithParams, helloWithParams1
+
diff --git a/tests/dat/manifest_validate_triggers_bogus.yaml b/tests/dat/manifest_validate_triggers_bogus.yaml
new file mode 100644
index 0000000..2b3b2c2
--- /dev/null
+++ b/tests/dat/manifest_validate_triggers_bogus.yaml
@@ -0,0 +1,33 @@
+#
+# 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:
+    helloworld1:
+        actions:
+            hello:
+                function: actions/hello.js
+        triggers:
+            trigger1:
+            trigger2:
+    helloworld2:
+        actions:
+            hello:
+                function: actions/hello.js
+        triggers:
+            trigger1:
+            trigger2:
+                feed: /whisk.system/alarms/alarm
+
diff --git a/utils/dependencies.go b/utils/dependencies.go
index 9f1dd08..eba29b8 100644
--- a/utils/dependencies.go
+++ b/utils/dependencies.go
@@ -88,3 +88,13 @@ func LocationIsGithub(location string) bool {
 	paths := strings.SplitN(removeProtocol(location), "/", 2)
 	return strings.Contains(paths[0], GITHUB)
 }
+
+func CompareDependencyRecords(d1 DependencyRecord, d2 DependencyRecord) bool {
+	if (d1.Location == d2.Location) && (d1.Version == d2.Version) {
+		return true
+	}
+	if (d1.BaseRepo == d2.BaseRepo) && (d1.SubFolder == d2.SubFolder) && (d1.Version == d2.Version) {
+		return true
+	}
+	return false
+}
diff --git a/utils/gitreader.go b/utils/gitreader.go
index 9d9c84b..741da88 100644
--- a/utils/gitreader.go
+++ b/utils/gitreader.go
@@ -129,6 +129,5 @@ func (reader *GitReader) CloneDependency() error {
 	}
 
 	os.Rename(rootDir, depPath)
-
 	return nil
 }
diff --git a/wski18n/i18n_ids.go b/wski18n/i18n_ids.go
index eae4a0b..9807f0d 100644
--- a/wski18n/i18n_ids.go
+++ b/wski18n/i18n_ids.go
@@ -78,6 +78,9 @@ const (
 	KEY_DEPENDENCY      = "dependency"
 	KEY_LOCATION        = "location"
 	KEY_SEQUENCE        = "sequence"
+	KEY_TRIGGER         = "trigger"
+	KEY_TRIGGER_FEED    = "feed"
+	KEY_RULE            = "rule"
 )
 
 // DO NOT TRANSLATE
@@ -163,25 +166,31 @@ const (
 	ID_MSG_MANAGED_FOUND_DELETED_X_key_X_name_X_project_X = "msg_managed_found_deleted_entity"
 
 	// Errors
-	ID_ERR_DEPENDENCY_UNKNOWN_TYPE                                     = "msg_err_dependency_unknown_type"
-	ID_ERR_ENTITY_CREATE_X_key_X_err_X_code_X                          = "msg_err_entity_create"
-	ID_ERR_ENTITY_DELETE_X_key_X_err_X_code_X                          = "msg_err_entity_delete"
-	ID_ERR_FEED_INVOKE_X_err_X_code_X                                  = "msg_err_feed_invoke"
-	ID_ERR_KEY_MISSING_X_key_X                                         = "msg_err_key_missing_mandatory"
-	ID_ERR_MANIFEST_FILE_NOT_FOUND_X_path_X                            = "msg_err_manifest_not_found"
-	ID_ERR_NAME_MISMATCH_X_key_X_dname_X_dpath_X_mname_X_moath_X       = "msg_err_name_mismatch"
-	ID_ERR_RUNTIME_INVALID_X_runtime_X_action_X                        = "msg_err_runtime_invalid"
-	ID_ERR_RUNTIME_MISMATCH_X_runtime_X_ext_X_action_X                 = "msg_err_runtime_mismatch"
-	ID_ERR_RUNTIMES_GET_X_err_X                                        = "msg_err_runtimes_get"
-	ID_ERR_RUNTIME_ACTION_SOURCE_NOT_SUPPORTED_X_ext_X_action_X        = "msg_err_runtime_action_source_not_supported"
-	ID_ERR_URL_INVALID_X_urltype_X_url_X_filetype_X                    = "msg_err_url_invalid"
-	ID_ERR_URL_MALFORMED_X_urltype_X_url_X                             = "msg_err_url_malformed"
-	ID_ERR_API_MISSING_ACTION_OR_SEQUENCE_X_action_or_sequence_X_api_X = "msg_err_api_missing_action_or_sequence"
-	ID_ERR_ACTION_INVALID_X_action_X                                   = "msg_err_action_invalid"
-	ID_ERR_ACTION_MISSING_RUNTIME_WITH_CODE_X_action_X                 = "msg_err_action_missing_runtime_with_code"
-	ID_ERR_ACTION_FUNCTION_REMOTE_DIR_NOT_SUPPORTED_X_action_X_url_X   = "msg_err_action_function_remote_dir_not_supported"
-	ID_ERR_CANT_SAVE_DOCKER_RUNTIME                                    = "msg_err_cant_save_docker"
-	ID_ERR_FILE_ALREADY_EXISTS                                         = "msg_err_file_already_exists"
+	ID_ERR_DEPENDENCY_UNKNOWN_TYPE                                       = "msg_err_dependency_unknown_type"
+	ID_ERR_ENTITY_CREATE_X_key_X_err_X_code_X                            = "msg_err_entity_create"
+	ID_ERR_ENTITY_DELETE_X_key_X_err_X_code_X                            = "msg_err_entity_delete"
+	ID_ERR_FEED_INVOKE_X_err_X_code_X                                    = "msg_err_feed_invoke"
+	ID_ERR_KEY_MISSING_X_key_X                                           = "msg_err_key_missing_mandatory"
+	ID_ERR_MANIFEST_FILE_NOT_FOUND_X_path_X                              = "msg_err_manifest_not_found"
+	ID_ERR_NAME_MISMATCH_X_key_X_dname_X_dpath_X_mname_X_moath_X         = "msg_err_name_mismatch"
+	ID_ERR_RUNTIME_INVALID_X_runtime_X_action_X                          = "msg_err_runtime_invalid"
+	ID_ERR_RUNTIME_MISMATCH_X_runtime_X_ext_X_action_X                   = "msg_err_runtime_mismatch"
+	ID_ERR_RUNTIMES_GET_X_err_X                                          = "msg_err_runtimes_get"
+	ID_ERR_RUNTIME_ACTION_SOURCE_NOT_SUPPORTED_X_ext_X_action_X          = "msg_err_runtime_action_source_not_supported"
+	ID_ERR_URL_INVALID_X_urltype_X_url_X_filetype_X                      = "msg_err_url_invalid"
+	ID_ERR_URL_MALFORMED_X_urltype_X_url_X                               = "msg_err_url_malformed"
+	ID_ERR_API_MISSING_ACTION_OR_SEQUENCE_X_action_or_sequence_X_api_X   = "msg_err_api_missing_action_or_sequence"
+	ID_ERR_ACTION_INVALID_X_action_X                                     = "msg_err_action_invalid"
+	ID_ERR_ACTION_MISSING_RUNTIME_WITH_CODE_X_action_X                   = "msg_err_action_missing_runtime_with_code"
+	ID_ERR_ACTION_FUNCTION_REMOTE_DIR_NOT_SUPPORTED_X_action_X_url_X     = "msg_err_action_function_remote_dir_not_supported"
+	ID_ERR_CANT_SAVE_DOCKER_RUNTIME                                      = "msg_err_cant_save_docker"
+	ID_ERR_FILE_ALREADY_EXISTS                                           = "msg_err_file_already_exists"
+	ID_ERR_DEPENDENCIES_WITH_SAME_LABEL_X_dependency_X_location_X        = "msg_err_different_dependencies_with_same_label"
+	ID_ERR_ACTION_WITHOUT_KIND_X_action_X                                = "msg_err_action_without_kind"
+	ID_ERR_ACTION_WITHOUT_SOURCE_X_action_X                              = "msg_err_action_without_source"
+	ID_ERR_SEQUENCE_HAVING_SAME_NAME_AS_ACTION_X_action_X                = "msg_err_sequence_having_same_name_as_action"
+	ID_ERR_CONFLICTING_TRIGGERS_ACROSS_PACKAGES_X_trigger_X_feed_X       = "msg_err_conflicting_triggers_across_packages"
+	ID_ERR_CONFLICTING_RULES_ACROSS_PACKAGES_X_rule_X_action_X_trigger_X = "msg_err_conflicting_rules_across_packages"
 
 	// Server-side Errors (wskdeploy as an Action)
 	ID_ERR_JSON_MISSING_KEY_CMD = "msg_err_json_missing_cmd_key"
diff --git a/wski18n/i18n_resources.go b/wski18n/i18n_resources.go
index e77a8f5..030d962 100755
--- a/wski18n/i18n_resources.go
+++ b/wski18n/i18n_resources.go
@@ -97,7 +97,7 @@ func wski18nResourcesDe_deAllJson() (*asset, error) {
 	return a, nil
 }
 
-var _wski18nResourcesEn_usAllJson = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\xc4\x5b\x6f\x8f\xdb\x36\xd2\x7f\x9f\x4f\x31\x08\x1e\x20\x2d\xe0\x28\x69\x1f\x3c\xc0\x83\x00\x79\x91\x6b\xd2\x76\xaf\x4d\x36\xd8\xcd\x5e\x50\xe4\x16\x0a\x2d\x8d\x6d\xd6\x12\xa9\x23\x29\x3b\xae\xe1\xef\x7e\x98\x21\xf5\xc7\xde\xa5\xa4\x75\xda\xbb\xbe\xa9\xbb\x1c\xce\xfc\x66\x38\x9c\x7f\x54\x3f\x3d\x02\xd8\x3f\x02\x00\x78\x2c\xf3\xc7\x2f\xe0\x71\x69\x97\x69\x65\x70\x21\xbf\xa4\x68\x8c\x36\x8f\x67\x7e\xd5\x19 [...]
+var _wski18nResourcesEn_usAllJson = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\xc4\x5b\x5f\x8f\xdb\x36\x12\x7f\xcf\xa7\x18\x04\x07\xa4\x05\x1c\x6d\xda\xc3\x01\x87\x00\x79\xc8\x35\x69\x9b\x6b\x93\x0d\x76\x93\x0b\x8a\xdc\x42\xa1\xa5\xb1\xcd\x5a\x22\x75\x24\x65\xc7\x5d\xec\x77\x3f\xcc\x90\x94\x64\xaf\xf5\xc7\x4e\x7a\x97\x97\x68\x97\xe4\xcc\x6f\x86\xc3\xf9\x47\xee\xc7\x07\x00\xb7\x0f\x00\x00\x1e\xca\xfc\xe1\x53\x78\x58\xda\x65\x5a\x19\x5c\xc8\xcf\x29\x1a\xa3\xcd\xc3\x99\x1f\x75\x46\x28 [...]
 
 func wski18nResourcesEn_usAllJsonBytes() ([]byte, error) {
 	return bindataRead(
@@ -112,7 +112,7 @@ func wski18nResourcesEn_usAllJson() (*asset, error) {
 		return nil, err
 	}
 
-	info := bindataFileInfo{name: "wski18n/resources/en_US.all.json", size: 14286, mode: os.FileMode(420), modTime: time.Unix(1523039220, 0)}
+	info := bindataFileInfo{name: "wski18n/resources/en_US.all.json", size: 15831, mode: os.FileMode(420), modTime: time.Unix(1523315322, 0)}
 	a := &asset{bytes: bytes, info: info}
 	return a, nil
 }
diff --git a/wski18n/resources/en_US.all.json b/wski18n/resources/en_US.all.json
index f765820..5b32270 100644
--- a/wski18n/resources/en_US.all.json
+++ b/wski18n/resources/en_US.all.json
@@ -316,6 +316,30 @@
     "translation": "File already exists."
   },
   {
+    "id": "msg_err_different_dependencies_with_same_label",
+    "translation": "One single dependency [{{.dependency}}] has two different locations [{{.location}}]. This kind of specification is not supported. Please update the dependency label [{{.dependency}}] in the manifest file to point to same location or create two separate labels for two different locations."
+  },
+  {
+    "id": "msg_err_action_without_kind",
+    "translation": "Action [{{.action}}] has no kind set."
+  },
+  {
+    "id": "msg_err_action_without_code",
+    "translation": "Action [{{.action}}] has no source code."
+  },
+  {
+    "id": "msg_err_sequence_having_same_name_as_action",
+    "translation": "Sequence [{{.sequence}}] is already defined as an action under the same package. Actions and sequences can not have same name in a single package."
+  },
+  {
+    "id": "msg_err_conflicting_triggers_across_packages",
+    "translation": "One single trigger [{{.trigger}}] is defined with multiple feeds [{{.feed}}]. Triggers are created directly under the namespace, its not possible to have triggers with same name but different feeds. Please update trigger names in manifest file."
+  },
+  {
+    "id": "msg_err_conflicting_rules_across_packages",
+    "translation": "One single rule [{{.rule}}] is defined with different actions [{{.action}}] and/or triggers [{{.trigger}}]. Rules are created directly under the namespace, its not possible to have rules with same name but associated with different actions/triggers. Please update rule names in manifest file."
+  },
+  {
     "id": "WARNINGS",
     "translation": "================= WARNINGS ==================="
   },

-- 
To stop receiving notification emails like this one, please contact
mrutkowski@apache.org.