github.com/argoproj/argo-cd/v3@v3.2.1/controller/hook.go (about) 1 package controller 2 3 import ( 4 "context" 5 6 "github.com/argoproj/gitops-engine/pkg/health" 7 "github.com/argoproj/gitops-engine/pkg/sync/common" 8 "github.com/argoproj/gitops-engine/pkg/sync/hook" 9 "github.com/argoproj/gitops-engine/pkg/utils/kube" 10 log "github.com/sirupsen/logrus" 11 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 12 "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" 13 "k8s.io/client-go/rest" 14 15 "github.com/argoproj/argo-cd/v3/util/lua" 16 17 "github.com/argoproj/argo-cd/v3/pkg/apis/application/v1alpha1" 18 ) 19 20 var ( 21 postDeleteHook = "PostDelete" 22 postDeleteHooks = map[string]string{ 23 "argocd.argoproj.io/hook": postDeleteHook, 24 "helm.sh/hook": "post-delete", 25 } 26 ) 27 28 func isHook(obj *unstructured.Unstructured) bool { 29 return hook.IsHook(obj) || isPostDeleteHook(obj) 30 } 31 32 func isPostDeleteHook(obj *unstructured.Unstructured) bool { 33 if obj == nil || obj.GetAnnotations() == nil { 34 return false 35 } 36 for k, v := range postDeleteHooks { 37 if val, ok := obj.GetAnnotations()[k]; ok && val == v { 38 return true 39 } 40 } 41 return false 42 } 43 44 func (ctrl *ApplicationController) executePostDeleteHooks(app *v1alpha1.Application, proj *v1alpha1.AppProject, liveObjs map[kube.ResourceKey]*unstructured.Unstructured, config *rest.Config, logCtx *log.Entry) (bool, error) { 45 appLabelKey, err := ctrl.settingsMgr.GetAppInstanceLabelKey() 46 if err != nil { 47 return false, err 48 } 49 var revisions []string 50 for _, src := range app.Spec.GetSources() { 51 revisions = append(revisions, src.TargetRevision) 52 } 53 54 targets, _, _, err := ctrl.appStateManager.GetRepoObjs(context.Background(), app, app.Spec.GetSources(), appLabelKey, revisions, false, false, false, proj, true) 55 if err != nil { 56 return false, err 57 } 58 runningHooks := map[kube.ResourceKey]*unstructured.Unstructured{} 59 for key, obj := range liveObjs { 60 if isPostDeleteHook(obj) { 61 runningHooks[key] = obj 62 } 63 } 64 65 expectedHook := map[kube.ResourceKey]*unstructured.Unstructured{} 66 for _, obj := range targets { 67 if obj.GetNamespace() == "" { 68 obj.SetNamespace(app.Spec.Destination.Namespace) 69 } 70 if !isPostDeleteHook(obj) { 71 continue 72 } 73 if runningHook := runningHooks[kube.GetResourceKey(obj)]; runningHook == nil { 74 expectedHook[kube.GetResourceKey(obj)] = obj 75 } 76 } 77 createdCnt := 0 78 for _, obj := range expectedHook { 79 _, err = ctrl.kubectl.CreateResource(context.Background(), config, obj.GroupVersionKind(), obj.GetName(), obj.GetNamespace(), obj, metav1.CreateOptions{}) 80 if err != nil { 81 return false, err 82 } 83 createdCnt++ 84 } 85 if createdCnt > 0 { 86 logCtx.Infof("Created %d post-delete hooks", createdCnt) 87 return false, nil 88 } 89 resourceOverrides, err := ctrl.settingsMgr.GetResourceOverrides() 90 if err != nil { 91 return false, err 92 } 93 healthOverrides := lua.ResourceHealthOverrides(resourceOverrides) 94 95 progressingHooksCnt := 0 96 for _, obj := range runningHooks { 97 hookHealth, err := health.GetResourceHealth(obj, healthOverrides) 98 if err != nil { 99 return false, err 100 } 101 if hookHealth == nil { 102 logCtx.WithFields(log.Fields{ 103 "group": obj.GroupVersionKind().Group, 104 "version": obj.GroupVersionKind().Version, 105 "kind": obj.GetKind(), 106 "name": obj.GetName(), 107 "namespace": obj.GetNamespace(), 108 }).Info("No health check defined for resource, considering it healthy") 109 hookHealth = &health.HealthStatus{ 110 Status: health.HealthStatusHealthy, 111 } 112 } 113 if hookHealth.Status == health.HealthStatusProgressing { 114 progressingHooksCnt++ 115 } 116 } 117 if progressingHooksCnt > 0 { 118 logCtx.Infof("Waiting for %d post-delete hooks to complete", progressingHooksCnt) 119 return false, nil 120 } 121 122 return true, nil 123 } 124 125 func (ctrl *ApplicationController) cleanupPostDeleteHooks(liveObjs map[kube.ResourceKey]*unstructured.Unstructured, config *rest.Config, logCtx *log.Entry) (bool, error) { 126 resourceOverrides, err := ctrl.settingsMgr.GetResourceOverrides() 127 if err != nil { 128 return false, err 129 } 130 healthOverrides := lua.ResourceHealthOverrides(resourceOverrides) 131 132 pendingDeletionCount := 0 133 aggregatedHealth := health.HealthStatusHealthy 134 var hooks []*unstructured.Unstructured 135 for _, obj := range liveObjs { 136 if !isPostDeleteHook(obj) { 137 continue 138 } 139 hookHealth, err := health.GetResourceHealth(obj, healthOverrides) 140 if err != nil { 141 return false, err 142 } 143 if hookHealth == nil { 144 hookHealth = &health.HealthStatus{ 145 Status: health.HealthStatusHealthy, 146 } 147 } 148 if health.IsWorse(aggregatedHealth, hookHealth.Status) { 149 aggregatedHealth = hookHealth.Status 150 } 151 hooks = append(hooks, obj) 152 } 153 154 for _, obj := range hooks { 155 for _, policy := range hook.DeletePolicies(obj) { 156 if (policy != common.HookDeletePolicyHookFailed || aggregatedHealth != health.HealthStatusDegraded) && (policy != common.HookDeletePolicyHookSucceeded || aggregatedHealth != health.HealthStatusHealthy) { 157 continue 158 } 159 pendingDeletionCount++ 160 if obj.GetDeletionTimestamp() != nil { 161 continue 162 } 163 logCtx.Infof("Deleting post-delete hook %s/%s", obj.GetNamespace(), obj.GetName()) 164 err = ctrl.kubectl.DeleteResource(context.Background(), config, obj.GroupVersionKind(), obj.GetName(), obj.GetNamespace(), metav1.DeleteOptions{}) 165 if err != nil { 166 return false, err 167 } 168 } 169 } 170 if pendingDeletionCount > 0 { 171 logCtx.Infof("Waiting for %d post-delete hooks to be deleted", pendingDeletionCount) 172 return false, nil 173 } 174 return true, nil 175 }