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  }