@@ -71,6 +71,7 @@ func NewDeploymentPackage() *DeploymentPackage {
7171// 3. Collect information about the source code files in the working directory
7272// 4. Create a deployment plan to create OpenWhisk service
7373type ServiceDeployer struct {
74+ ProjectName string
7475 Deployment * DeploymentProject
7576 Client * whisk.Client
7677 mt sync.RWMutex
@@ -85,6 +86,7 @@ type ServiceDeployer struct {
8586 InteractiveChoice bool
8687 ClientConfig * whisk.Config
8788 DependencyMaster map [string ]utils.DependencyRecord
89+ ManagedAnnotation whisk.KeyValue
8890}
8991
9092// NewServiceDeployer is a Factory to create a new ServiceDeployer
@@ -121,8 +123,26 @@ func (deployer *ServiceDeployer) ConstructDeploymentPlan() error {
121123 }
122124
123125 deployer .RootPackageName = manifest .Package .Packagename
126+ deployer .ProjectName = manifest .GetProject ().Name
127+
128+ // Generate Managed Annotations if its marked as a Managed Deployment
129+ // Managed deployments are the ones when OpenWhisk entities are deployed with command line flag --managed.
130+ // Which results in a hidden annotation in every OpenWhisk entity in manifest file.
131+ if utils .Flags .Managed {
132+ // OpenWhisk entities are annotated with Project Name and therefore
133+ // Project Name in manifest/deployment file is mandatory for managed deployments
134+ if deployer .ProjectName == "" {
135+ return utils .NewYAMLFormatError ("Project name in manifest file is mandatory for managed deployments" )
136+ }
137+ // Every OpenWhisk entity in the manifest file will be annotated with:
138+ //managed: '{"__OW__PROJECT__NAME": <name>, "__OW__PROJECT_HASH": <hash>, "__OW__FILE": <path>}'
139+ deployer .ManagedAnnotation , err = utils .GenerateManagedAnnotation (deployer .ProjectName , manifest .Filepath )
140+ if err != nil {
141+ return utils .NewYAMLFormatError (err .Error ())
142+ }
143+ }
124144
125- manifestReader .InitRootPackage (manifestParser , manifest )
145+ manifestReader .InitRootPackage (manifestParser , manifest , deployer . ManagedAnnotation )
126146
127147 if deployer .IsDefault == true {
128148 fileReader := NewFileSystemReader (deployer )
@@ -134,7 +154,7 @@ func (deployer *ServiceDeployer) ConstructDeploymentPlan() error {
134154 }
135155
136156 // process manifest file
137- err = manifestReader .HandleYaml (deployer , manifestParser , manifest )
157+ err = manifestReader .HandleYaml (deployer , manifestParser , manifest , deployer . ManagedAnnotation )
138158 if err != nil {
139159 return err
140160 }
@@ -194,7 +214,7 @@ func (deployer *ServiceDeployer) ConstructUnDeploymentPlan() (*DeploymentProject
194214 }
195215
196216 deployer .RootPackageName = manifest .Package .Packagename
197- manifestReader .InitRootPackage (manifestParser , manifest )
217+ manifestReader .InitRootPackage (manifestParser , manifest , whisk. KeyValue {} )
198218
199219 // process file system
200220 if deployer .IsDefault == true {
@@ -212,7 +232,7 @@ func (deployer *ServiceDeployer) ConstructUnDeploymentPlan() (*DeploymentProject
212232 }
213233
214234 // process manifest file
215- err = manifestReader .HandleYaml (deployer , manifestParser , manifest )
235+ err = manifestReader .HandleYaml (deployer , manifestParser , manifest , whisk. KeyValue {} )
216236 if err != nil {
217237 return deployer .Deployment , err
218238 }
@@ -338,6 +358,18 @@ func (deployer *ServiceDeployer) deployAssets() error {
338358 return err
339359 }
340360
361+ // During managed deployments, after deploying list of entities in a project
362+ // refresh previously deployed project entities, delete the assets which is no longer part of the project
363+ // i.e. in a subsequent managed deployment of the same project minus few OpenWhisk entities
364+ // from the manifest file must result in undeployment of those deleted entities
365+ if utils .Flags .Managed {
366+ if err := deployer .RefreshManagedEntities (deployer .ManagedAnnotation ); err != nil {
367+ errString := wski18n .T ("Undeployment of deleted entities did not complete sucessfully during managed deployment. Run `wskdeploy undeploy` to remove partially deployed assets.\n " )
368+ whisk .Debug (whisk .DbgError , errString )
369+ return err
370+ }
371+ }
372+
341373 return nil
342374}
343375
@@ -426,6 +458,134 @@ func (deployer *ServiceDeployer) DeployDependencies() error {
426458 return nil
427459}
428460
461+ func (deployer * ServiceDeployer ) RefreshManagedEntities (maValue whisk.KeyValue ) error {
462+
463+ ma := maValue .Value .(map [string ]interface {})
464+ if err := deployer .RefreshManagedTriggers (ma ); err != nil {
465+ return err
466+ }
467+
468+ //if err := deployer.RefreshManagedRules(ma); err != nil {
469+ // return err
470+ //}
471+
472+ //if err := deployer.RefreshManagedPackages(ma); err != nil {
473+ // return err
474+ //}
475+
476+ return nil
477+
478+ }
479+ func (deployer * ServiceDeployer ) RefreshManagedActions (packageName string , ma map [string ]interface {}) error {
480+ options := whisk.ActionListOptions {}
481+ // get a list of actions in your namespace
482+ actions , _ , err := deployer .Client .Actions .List (packageName , & options )
483+ if err != nil {
484+ return err
485+ }
486+ // iterate over list of actions to find an action with managed annotations
487+ // check if "managed" annotation is attached to an action
488+ for _ , action := range actions {
489+ // an annotation with "managed" key indicates that an action was deployed as part of managed deployment
490+ // if such annotation exists, check if it belongs to the current managed deployment
491+ // this action has attached managed annotations
492+ if a := action .Annotations .GetValue (utils .MANAGED ); a != nil {
493+ // decode the JSON blob and retrieve __OW_PROJECT_NAME and __OW_PROJECT_HASH
494+ aa := a .(map [string ]interface {})
495+ // we have found an action which was earlier part of the current project
496+ // and this action was deployed as part of managed deployment and now
497+ // must be undeployed as its not part of the project anymore
498+ // The annotation with same project name but different project hash indicates
499+ // that this action is deleted from the project in manifest file
500+ if aa [utils .OW_PROJECT_NAME ] == ma [utils .OW_PROJECT_NAME ] && aa [utils .OW_PROJECT_HASH ] != ma [utils .OW_PROJECT_HASH ] {
501+ actionName := strings .Join ([]string {packageName , action .Name }, "/" )
502+ output := wski18n .T ("Found the action {{.action}} which is deleted" +
503+ " from the current project {{.project}} in manifest file which is being undeployed.\n " ,
504+ map [string ]interface {}{"action" : actionName , "project" : aa [utils .OW_PROJECT_NAME ]})
505+ whisk .Debug (whisk .DbgInfo , output )
506+ _ , err := deployer .Client .Actions .Delete (actionName )
507+ if err != nil {
508+ return err
509+ }
510+ }
511+ }
512+ }
513+ return nil
514+ }
515+
516+ func (deployer * ServiceDeployer ) RefreshManagedTriggers (ma map [string ]interface {}) error {
517+ options := whisk.TriggerListOptions {}
518+ // Get list of triggers in your namespace
519+ triggers , _ , err := deployer .Client .Triggers .List (& options )
520+ if err != nil {
521+ return err
522+ }
523+ // iterate over the list of triggers to determine whether any of them was part of managed project
524+ // and now deleted from manifest file we can determine that from the managed annotation
525+ // If a trigger has attached managed annotation with the project name equals to the current project name
526+ // but the project hash is different (project hash differs since the trigger is deleted from the manifest file)
527+ for _ , trigger := range triggers {
528+ // trigger has attached managed annotation
529+ if a := trigger .Annotations .GetValue (utils .MANAGED ); a != nil {
530+ // decode the JSON blob and retrieve __OW_PROJECT_NAME and __OW_PROJECT_HASH
531+ ta := a .(map [string ]interface {})
532+ if ta [utils .OW_PROJECT_NAME ] == ma [utils .OW_PROJECT_NAME ] && ta [utils .OW_PROJECT_HASH ] != ma [utils .OW_PROJECT_HASH ] {
533+ // we have found a trigger which was earlier part of the current project
534+ output := wski18n .T ("Found the trigger {{.trigger}} which is deleted" +
535+ " from the current project {{.project}} in manifest file which is being undeployed.\n " ,
536+ map [string ]interface {}{"trigger" : trigger .Name , "project" : ma [utils .OW_PROJECT_NAME ]})
537+ whisk .Debug (whisk .DbgInfo , output )
538+ _ , _ , err := deployer .Client .Triggers .Delete (trigger .Name )
539+ if err != nil {
540+ return err
541+ }
542+ }
543+ }
544+ }
545+ return nil
546+ }
547+
548+ func (deployer * ServiceDeployer ) RefreshManagedRules (ma map [string ]interface {}) error {
549+ return nil
550+ }
551+
552+ func (deployer * ServiceDeployer ) RefreshManagedPackages (ma map [string ]interface {}) error {
553+ options := whisk.PackageListOptions {}
554+ // Get the list of packages in your namespace
555+ packages , _ , err := deployer .Client .Packages .List (& options )
556+ if err != nil {
557+ return err
558+ }
559+ // iterate over each package to find managed annotations
560+ // check if "managed" annotation is attached to a package
561+ // when managed project name matches with the current project name and project
562+ // hash differs, indicates that the package was part of the current project but
563+ // now is deleted from the manifest file and should be undeployed.
564+ for _ , pkg := range packages {
565+ if a := pkg .Annotations .GetValue (utils .MANAGED ); a != nil {
566+ // decode the JSON blob and retrieve __OW_PROJECT_NAME and __OW_PROJECT_HASH
567+ pa := a .(map [string ]interface {})
568+ // perform the similar check on the list of actions from this package
569+ // since package can not be deleted if its not empty (has any action or sequence)
570+ if err := deployer .RefreshManagedActions (pkg .Name , ma ); err != nil {
571+ return err
572+ }
573+ // we have found a package which was earlier part of the current project
574+ if pa [utils .OW_PROJECT_NAME ] == ma [utils .OW_PROJECT_NAME ] && pa [utils .OW_PROJECT_HASH ] != ma [utils .OW_PROJECT_HASH ] {
575+ output := wski18n .T ("Found the package {{.package}} which is deleted" +
576+ " from the current project {{.project}} in manifest file which is being undeployed.\n " ,
577+ map [string ]interface {}{"package" : pkg .Name , "project" : pa [utils .OW_PROJECT_NAME ]})
578+ whisk .Debug (whisk .DbgInfo , output )
579+ _ , err := deployer .Client .Packages .Delete (pkg .Name )
580+ if err != nil {
581+ return err
582+ }
583+ }
584+ }
585+ }
586+ return nil
587+ }
588+
429589func (deployer * ServiceDeployer ) DeployPackages () error {
430590 for _ , pack := range deployer .Deployment .Packages {
431591 err := deployer .createPackage (pack .Package )
0 commit comments