github.com/argoproj/argo-cd/v2@v2.10.9/applicationset/controllers/clustereventhandler.go (about) 1 package controllers 2 3 import ( 4 "context" 5 "fmt" 6 7 log "github.com/sirupsen/logrus" 8 9 "k8s.io/apimachinery/pkg/types" 10 "k8s.io/client-go/util/workqueue" 11 ctrl "sigs.k8s.io/controller-runtime" 12 "sigs.k8s.io/controller-runtime/pkg/client" 13 "sigs.k8s.io/controller-runtime/pkg/event" 14 15 "github.com/argoproj/argo-cd/v2/applicationset/generators" 16 argoprojiov1alpha1 "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1" 17 ) 18 19 // clusterSecretEventHandler is used when watching Secrets to check if they are ArgoCD Cluster Secrets, and if so 20 // requeue any related ApplicationSets. 21 type clusterSecretEventHandler struct { 22 //handler.EnqueueRequestForOwner 23 Log log.FieldLogger 24 Client client.Client 25 } 26 27 func (h *clusterSecretEventHandler) Create(e event.CreateEvent, q workqueue.RateLimitingInterface) { 28 h.queueRelatedAppGenerators(q, e.Object) 29 } 30 31 func (h *clusterSecretEventHandler) Update(e event.UpdateEvent, q workqueue.RateLimitingInterface) { 32 h.queueRelatedAppGenerators(q, e.ObjectNew) 33 } 34 35 func (h *clusterSecretEventHandler) Delete(e event.DeleteEvent, q workqueue.RateLimitingInterface) { 36 h.queueRelatedAppGenerators(q, e.Object) 37 } 38 39 func (h *clusterSecretEventHandler) Generic(e event.GenericEvent, q workqueue.RateLimitingInterface) { 40 h.queueRelatedAppGenerators(q, e.Object) 41 } 42 43 // addRateLimitingInterface defines the Add method of workqueue.RateLimitingInterface, allow us to easily mock 44 // it for testing purposes. 45 type addRateLimitingInterface interface { 46 Add(item interface{}) 47 } 48 49 func (h *clusterSecretEventHandler) queueRelatedAppGenerators(q addRateLimitingInterface, object client.Object) { 50 // Check for label, lookup all ApplicationSets that might match the cluster, queue them all 51 if object.GetLabels()[generators.ArgoCDSecretTypeLabel] != generators.ArgoCDSecretTypeCluster { 52 return 53 } 54 55 h.Log.WithFields(log.Fields{ 56 "namespace": object.GetNamespace(), 57 "name": object.GetName(), 58 }).Info("processing event for cluster secret") 59 60 appSetList := &argoprojiov1alpha1.ApplicationSetList{} 61 err := h.Client.List(context.Background(), appSetList) 62 if err != nil { 63 h.Log.WithError(err).Error("unable to list ApplicationSets") 64 return 65 } 66 67 h.Log.WithField("count", len(appSetList.Items)).Info("listed ApplicationSets") 68 for _, appSet := range appSetList.Items { 69 70 foundClusterGenerator := false 71 for _, generator := range appSet.Spec.Generators { 72 if generator.Clusters != nil { 73 foundClusterGenerator = true 74 break 75 } 76 77 if generator.Matrix != nil { 78 ok, err := nestedGeneratorsHaveClusterGenerator(generator.Matrix.Generators) 79 if err != nil { 80 h.Log. 81 WithFields(log.Fields{ 82 "namespace": appSet.GetNamespace(), 83 "name": appSet.GetName(), 84 }). 85 WithError(err). 86 Error("Unable to check if ApplicationSet matrix generators have cluster generator") 87 } 88 if ok { 89 foundClusterGenerator = true 90 break 91 } 92 } 93 94 if generator.Merge != nil { 95 ok, err := nestedGeneratorsHaveClusterGenerator(generator.Merge.Generators) 96 if err != nil { 97 h.Log. 98 WithFields(log.Fields{ 99 "namespace": appSet.GetNamespace(), 100 "name": appSet.GetName(), 101 }). 102 WithError(err). 103 Error("Unable to check if ApplicationSet merge generators have cluster generator") 104 } 105 if ok { 106 foundClusterGenerator = true 107 break 108 } 109 } 110 } 111 if foundClusterGenerator { 112 113 // TODO: only queue the AppGenerator if the labels match this cluster 114 req := ctrl.Request{NamespacedName: types.NamespacedName{Namespace: appSet.Namespace, Name: appSet.Name}} 115 q.Add(req) 116 } 117 } 118 } 119 120 // nestedGeneratorsHaveClusterGenerator iterate over provided nested generators to check if they have a cluster generator. 121 func nestedGeneratorsHaveClusterGenerator(generators []argoprojiov1alpha1.ApplicationSetNestedGenerator) (bool, error) { 122 for _, generator := range generators { 123 if ok, err := nestedGeneratorHasClusterGenerator(generator); ok || err != nil { 124 return ok, err 125 } 126 } 127 return false, nil 128 } 129 130 // nestedGeneratorHasClusterGenerator checks if the provided generator has a cluster generator. 131 func nestedGeneratorHasClusterGenerator(nested argoprojiov1alpha1.ApplicationSetNestedGenerator) (bool, error) { 132 if nested.Clusters != nil { 133 return true, nil 134 } 135 136 if nested.Matrix != nil { 137 nestedMatrix, err := argoprojiov1alpha1.ToNestedMatrixGenerator(nested.Matrix) 138 if err != nil { 139 return false, fmt.Errorf("unable to get nested matrix generator: %w", err) 140 } 141 if nestedMatrix != nil { 142 hasClusterGenerator, err := nestedGeneratorsHaveClusterGenerator(nestedMatrix.ToMatrixGenerator().Generators) 143 if err != nil { 144 return false, fmt.Errorf("error evaluating nested matrix generator: %w", err) 145 } 146 return hasClusterGenerator, nil 147 } 148 } 149 150 if nested.Merge != nil { 151 nestedMerge, err := argoprojiov1alpha1.ToNestedMergeGenerator(nested.Merge) 152 if err != nil { 153 return false, fmt.Errorf("unable to get nested merge generator: %w", err) 154 } 155 if nestedMerge != nil { 156 hasClusterGenerator, err := nestedGeneratorsHaveClusterGenerator(nestedMerge.ToMergeGenerator().Generators) 157 if err != nil { 158 return false, fmt.Errorf("error evaluating nested merge generator: %w", err) 159 } 160 return hasClusterGenerator, nil 161 } 162 } 163 164 return false, nil 165 }