Skip to content

Commit ec51989

Browse files
rumsteaddshmelev
andauthored
fix(applicationset): requeue applicationste when application status changes (#23413)
Signed-off-by: rumstead <[email protected]> Co-authored-by: Dmitry Shmelev <[email protected]>
1 parent da2ef7d commit ec51989

File tree

2 files changed

+414
-32
lines changed

2 files changed

+414
-32
lines changed

applicationset/controllers/applicationset_controller.go

Lines changed: 111 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -127,8 +127,8 @@ func (r *ApplicationSetReconciler) Reconcile(ctx context.Context, req ctrl.Reque
127127
}()
128128

129129
// Do not attempt to further reconcile the ApplicationSet if it is being deleted.
130-
if applicationSetInfo.ObjectMeta.DeletionTimestamp != nil {
131-
appsetName := applicationSetInfo.ObjectMeta.Name
130+
if applicationSetInfo.DeletionTimestamp != nil {
131+
appsetName := applicationSetInfo.Name
132132
logCtx.Debugf("DeletionTimestamp is set on %s", appsetName)
133133
deleteAllowed := utils.DefaultPolicy(applicationSetInfo.Spec.SyncPolicy, r.Policy, r.EnablePolicyOverride).AllowDelete()
134134
if !deleteAllowed {
@@ -314,7 +314,7 @@ func (r *ApplicationSetReconciler) Reconcile(ctx context.Context, req ctrl.Reque
314314

315315
if applicationSetInfo.RefreshRequired() {
316316
delete(applicationSetInfo.Annotations, common.AnnotationApplicationSetRefresh)
317-
err := r.Client.Update(ctx, &applicationSetInfo)
317+
err := r.Update(ctx, &applicationSetInfo)
318318
if err != nil {
319319
logCtx.Warnf("error occurred while updating ApplicationSet: %v", err)
320320
_ = r.setApplicationSetStatusCondition(ctx,
@@ -489,7 +489,7 @@ func (r *ApplicationSetReconciler) validateGeneratedApplications(ctx context.Con
489489
}
490490

491491
appProject := &argov1alpha1.AppProject{}
492-
err := r.Client.Get(ctx, types.NamespacedName{Name: app.Spec.Project, Namespace: r.ArgoCDNamespace}, appProject)
492+
err := r.Get(ctx, types.NamespacedName{Name: app.Spec.Project, Namespace: r.ArgoCDNamespace}, appProject)
493493
if err != nil {
494494
if apierr.IsNotFound(err) {
495495
errorsByIndex[i] = fmt.Errorf("application references project %s which does not exist", app.Spec.Project)
@@ -553,20 +553,20 @@ func (r *ApplicationSetReconciler) SetupWithManager(mgr ctrl.Manager, enableProg
553553
return fmt.Errorf("error setting up with manager: %w", err)
554554
}
555555

556-
ownsHandler := getOwnsHandlerPredicates(enableProgressiveSyncs)
556+
appOwnsHandler := getApplicationOwnsHandler(enableProgressiveSyncs)
557+
appSetOwnsHandler := getApplicationSetOwnsHandler(enableProgressiveSyncs)
557558

558559
return ctrl.NewControllerManagedBy(mgr).WithOptions(controller.Options{
559560
MaxConcurrentReconciles: maxConcurrentReconciliations,
560-
}).For(&argov1alpha1.ApplicationSet{}).
561-
Owns(&argov1alpha1.Application{}, builder.WithPredicates(ownsHandler)).
561+
}).For(&argov1alpha1.ApplicationSet{}, builder.WithPredicates(appSetOwnsHandler)).
562+
Owns(&argov1alpha1.Application{}, builder.WithPredicates(appOwnsHandler)).
562563
WithEventFilter(ignoreNotAllowedNamespaces(r.ApplicationSetNamespaces)).
563564
Watches(
564565
&corev1.Secret{},
565566
&clusterSecretEventHandler{
566567
Client: mgr.GetClient(),
567568
Log: log.WithField("type", "createSecretEventHandler"),
568569
}).
569-
// TODO: also watch Applications and respond on changes if we own them.
570570
Complete(r)
571571
}
572572

@@ -625,7 +625,7 @@ func (r *ApplicationSetReconciler) createOrUpdateInCluster(ctx context.Context,
625625
preservedAnnotations = append(preservedAnnotations, defaultPreservedAnnotations...)
626626

627627
for _, key := range preservedAnnotations {
628-
if state, exists := found.ObjectMeta.Annotations[key]; exists {
628+
if state, exists := found.Annotations[key]; exists {
629629
if generatedApp.Annotations == nil {
630630
generatedApp.Annotations = map[string]string{}
631631
}
@@ -634,7 +634,7 @@ func (r *ApplicationSetReconciler) createOrUpdateInCluster(ctx context.Context,
634634
}
635635

636636
for _, key := range preservedLabels {
637-
if state, exists := found.ObjectMeta.Labels[key]; exists {
637+
if state, exists := found.Labels[key]; exists {
638638
if generatedApp.Labels == nil {
639639
generatedApp.Labels = map[string]string{}
640640
}
@@ -644,7 +644,7 @@ func (r *ApplicationSetReconciler) createOrUpdateInCluster(ctx context.Context,
644644

645645
// Preserve post-delete finalizers:
646646
// https://github.com/argoproj/argo-cd/issues/17181
647-
for _, finalizer := range found.ObjectMeta.Finalizers {
647+
for _, finalizer := range found.Finalizers {
648648
if strings.HasPrefix(finalizer, argov1alpha1.PostDeleteFinalizerName) {
649649
if generatedApp.Finalizers == nil {
650650
generatedApp.Finalizers = []string{}
@@ -653,10 +653,10 @@ func (r *ApplicationSetReconciler) createOrUpdateInCluster(ctx context.Context,
653653
}
654654
}
655655

656-
found.ObjectMeta.Annotations = generatedApp.Annotations
656+
found.Annotations = generatedApp.Annotations
657657

658-
found.ObjectMeta.Finalizers = generatedApp.Finalizers
659-
found.ObjectMeta.Labels = generatedApp.Labels
658+
found.Finalizers = generatedApp.Finalizers
659+
found.Labels = generatedApp.Labels
660660

661661
return controllerutil.SetControllerReference(&applicationSet, found, r.Scheme)
662662
})
@@ -710,7 +710,7 @@ func (r *ApplicationSetReconciler) createInCluster(ctx context.Context, logCtx *
710710

711711
func (r *ApplicationSetReconciler) getCurrentApplications(ctx context.Context, applicationSet argov1alpha1.ApplicationSet) ([]argov1alpha1.Application, error) {
712712
var current argov1alpha1.ApplicationList
713-
err := r.Client.List(ctx, &current, client.MatchingFields{".metadata.controller": applicationSet.Name}, client.InNamespace(applicationSet.Namespace))
713+
err := r.List(ctx, &current, client.MatchingFields{".metadata.controller": applicationSet.Name}, client.InNamespace(applicationSet.Namespace))
714714
if err != nil {
715715
return nil, fmt.Errorf("error retrieving applications: %w", err)
716716
}
@@ -721,9 +721,6 @@ func (r *ApplicationSetReconciler) getCurrentApplications(ctx context.Context, a
721721
// deleteInCluster will delete Applications that are currently on the cluster, but not in appList.
722722
// The function must be called after all generators had been called and generated applications
723723
func (r *ApplicationSetReconciler) deleteInCluster(ctx context.Context, logCtx *log.Entry, applicationSet argov1alpha1.ApplicationSet, desiredApplications []argov1alpha1.Application) error {
724-
// settingsMgr := settings.NewSettingsManager(context.TODO(), r.KubeClientset, applicationSet.Namespace)
725-
// argoDB := db.NewDB(applicationSet.Namespace, settingsMgr, r.KubeClientset)
726-
// clusterList, err := argoDB.ListClusters(ctx)
727724
clusterList, err := utils.ListClusters(ctx, r.KubeClientset, r.ArgoCDNamespace)
728725
if err != nil {
729726
return fmt.Errorf("error listing clusters: %w", err)
@@ -758,7 +755,7 @@ func (r *ApplicationSetReconciler) deleteInCluster(ctx context.Context, logCtx *
758755
continue
759756
}
760757

761-
err = r.Client.Delete(ctx, &app)
758+
err = r.Delete(ctx, &app)
762759
if err != nil {
763760
logCtx.WithError(err).Error("failed to delete Application")
764761
if firstError != nil {
@@ -830,7 +827,7 @@ func (r *ApplicationSetReconciler) removeFinalizerOnInvalidDestination(ctx conte
830827
if log.IsLevelEnabled(log.DebugLevel) {
831828
utils.LogPatch(appLog, patch, updated)
832829
}
833-
if err := r.Client.Patch(ctx, updated, patch); err != nil {
830+
if err := r.Patch(ctx, updated, patch); err != nil {
834831
return fmt.Errorf("error updating finalizers: %w", err)
835832
}
836833
// Application must have updated list of finalizers
@@ -852,7 +849,7 @@ func (r *ApplicationSetReconciler) removeOwnerReferencesOnDeleteAppSet(ctx conte
852849

853850
for _, app := range applications {
854851
app.SetOwnerReferences([]metav1.OwnerReference{})
855-
err := r.Client.Update(ctx, &app)
852+
err := r.Update(ctx, &app)
856853
if err != nil {
857854
return fmt.Errorf("error updating application: %w", err)
858855
}
@@ -1464,7 +1461,7 @@ func syncApplication(application argov1alpha1.Application, prune bool) argov1alp
14641461
return application
14651462
}
14661463

1467-
func getOwnsHandlerPredicates(enableProgressiveSyncs bool) predicate.Funcs {
1464+
func getApplicationOwnsHandler(enableProgressiveSyncs bool) predicate.Funcs {
14681465
return predicate.Funcs{
14691466
CreateFunc: func(e event.CreateEvent) bool {
14701467
// if we are the owner and there is a create event, we most likely created it and do not need to
@@ -1501,8 +1498,8 @@ func getOwnsHandlerPredicates(enableProgressiveSyncs bool) predicate.Funcs {
15011498
if !isApp {
15021499
return false
15031500
}
1504-
requeue := shouldRequeueApplicationSet(appOld, appNew, enableProgressiveSyncs)
1505-
logCtx.WithField("requeue", requeue).Debugf("requeue: %t caused by application %s", requeue, appNew.Name)
1501+
requeue := shouldRequeueForApplication(appOld, appNew, enableProgressiveSyncs)
1502+
logCtx.WithField("requeue", requeue).Debugf("requeue caused by application %s", appNew.Name)
15061503
return requeue
15071504
},
15081505
GenericFunc: func(e event.GenericEvent) bool {
@@ -1519,13 +1516,13 @@ func getOwnsHandlerPredicates(enableProgressiveSyncs bool) predicate.Funcs {
15191516
}
15201517
}
15211518

1522-
// shouldRequeueApplicationSet determines when we want to requeue an ApplicationSet for reconciling based on an owned
1519+
// shouldRequeueForApplication determines when we want to requeue an ApplicationSet for reconciling based on an owned
15231520
// application change
15241521
// The applicationset controller owns a subset of the Application CR.
15251522
// We do not need to re-reconcile if parts of the application change outside the applicationset's control.
15261523
// An example being, Application.ApplicationStatus.ReconciledAt which gets updated by the application controller.
15271524
// Additionally, Application.ObjectMeta.ResourceVersion and Application.ObjectMeta.Generation which are set by K8s.
1528-
func shouldRequeueApplicationSet(appOld *argov1alpha1.Application, appNew *argov1alpha1.Application, enableProgressiveSyncs bool) bool {
1525+
func shouldRequeueForApplication(appOld *argov1alpha1.Application, appNew *argov1alpha1.Application, enableProgressiveSyncs bool) bool {
15291526
if appOld == nil || appNew == nil {
15301527
return false
15311528
}
@@ -1535,9 +1532,9 @@ func shouldRequeueApplicationSet(appOld *argov1alpha1.Application, appNew *argov
15351532
// https://pkg.go.dev/reflect#DeepEqual
15361533
// ApplicationDestination has an unexported field so we can just use the == for comparison
15371534
if !cmp.Equal(appOld.Spec, appNew.Spec, cmpopts.EquateEmpty(), cmpopts.EquateComparable(argov1alpha1.ApplicationDestination{})) ||
1538-
!cmp.Equal(appOld.ObjectMeta.GetAnnotations(), appNew.ObjectMeta.GetAnnotations(), cmpopts.EquateEmpty()) ||
1539-
!cmp.Equal(appOld.ObjectMeta.GetLabels(), appNew.ObjectMeta.GetLabels(), cmpopts.EquateEmpty()) ||
1540-
!cmp.Equal(appOld.ObjectMeta.GetFinalizers(), appNew.ObjectMeta.GetFinalizers(), cmpopts.EquateEmpty()) {
1535+
!cmp.Equal(appOld.GetAnnotations(), appNew.GetAnnotations(), cmpopts.EquateEmpty()) ||
1536+
!cmp.Equal(appOld.GetLabels(), appNew.GetLabels(), cmpopts.EquateEmpty()) ||
1537+
!cmp.Equal(appOld.GetFinalizers(), appNew.GetFinalizers(), cmpopts.EquateEmpty()) {
15411538
return true
15421539
}
15431540

@@ -1558,4 +1555,89 @@ func shouldRequeueApplicationSet(appOld *argov1alpha1.Application, appNew *argov
15581555
return false
15591556
}
15601557

1558+
func getApplicationSetOwnsHandler(enableProgressiveSyncs bool) predicate.Funcs {
1559+
return predicate.Funcs{
1560+
CreateFunc: func(e event.CreateEvent) bool {
1561+
appSet, isApp := e.Object.(*argov1alpha1.ApplicationSet)
1562+
if !isApp {
1563+
return false
1564+
}
1565+
log.WithField("applicationset", appSet.QualifiedName()).Debugln("received create event")
1566+
// Always queue a new applicationset
1567+
return true
1568+
},
1569+
DeleteFunc: func(e event.DeleteEvent) bool {
1570+
appSet, isApp := e.Object.(*argov1alpha1.ApplicationSet)
1571+
if !isApp {
1572+
return false
1573+
}
1574+
log.WithField("applicationset", appSet.QualifiedName()).Debugln("received delete event")
1575+
// Always queue for the deletion of an applicationset
1576+
return true
1577+
},
1578+
UpdateFunc: func(e event.UpdateEvent) bool {
1579+
appSetOld, isAppSet := e.ObjectOld.(*argov1alpha1.ApplicationSet)
1580+
if !isAppSet {
1581+
return false
1582+
}
1583+
appSetNew, isAppSet := e.ObjectNew.(*argov1alpha1.ApplicationSet)
1584+
if !isAppSet {
1585+
return false
1586+
}
1587+
requeue := shouldRequeueForApplicationSet(appSetOld, appSetNew, enableProgressiveSyncs)
1588+
log.WithField("applicationset", appSetNew.QualifiedName()).
1589+
WithField("requeue", requeue).Debugln("received update event")
1590+
return requeue
1591+
},
1592+
GenericFunc: func(e event.GenericEvent) bool {
1593+
appSet, isApp := e.Object.(*argov1alpha1.ApplicationSet)
1594+
if !isApp {
1595+
return false
1596+
}
1597+
log.WithField("applicationset", appSet.QualifiedName()).Debugln("received generic event")
1598+
// Always queue for the generic of an applicationset
1599+
return true
1600+
},
1601+
}
1602+
}
1603+
1604+
// shouldRequeueForApplicationSet determines when we need to requeue an applicationset
1605+
func shouldRequeueForApplicationSet(appSetOld, appSetNew *argov1alpha1.ApplicationSet, enableProgressiveSyncs bool) bool {
1606+
if appSetOld == nil || appSetNew == nil {
1607+
return false
1608+
}
1609+
1610+
// Requeue if any ApplicationStatus.Status changed for Progressive sync strategy
1611+
if enableProgressiveSyncs {
1612+
if !cmp.Equal(appSetOld.Status.ApplicationStatus, appSetNew.Status.ApplicationStatus, cmpopts.EquateEmpty()) {
1613+
return true
1614+
}
1615+
}
1616+
1617+
// only compare the applicationset spec, annotations, labels and finalizers, specifically avoiding
1618+
// the status field. status is owned by the applicationset controller,
1619+
// and we do not need to requeue when it does bookkeeping
1620+
// NB: the ApplicationDestination comes from the ApplicationSpec being embedded
1621+
// in the ApplicationSetTemplate from the generators
1622+
if !cmp.Equal(appSetOld.Spec, appSetNew.Spec, cmpopts.EquateEmpty(), cmpopts.EquateComparable(argov1alpha1.ApplicationDestination{})) ||
1623+
!cmp.Equal(appSetOld.GetLabels(), appSetNew.GetLabels(), cmpopts.EquateEmpty()) ||
1624+
!cmp.Equal(appSetOld.GetFinalizers(), appSetNew.GetFinalizers(), cmpopts.EquateEmpty()) {
1625+
return true
1626+
}
1627+
1628+
// Requeue only when the refresh annotation is newly added to the ApplicationSet.
1629+
// Changes to other annotations made simultaneously might be missed, but such cases are rare.
1630+
if !cmp.Equal(appSetOld.GetAnnotations(), appSetNew.GetAnnotations(), cmpopts.EquateEmpty()) {
1631+
_, oldHasRefreshAnnotation := appSetOld.Annotations[common.AnnotationApplicationSetRefresh]
1632+
_, newHasRefreshAnnotation := appSetNew.Annotations[common.AnnotationApplicationSetRefresh]
1633+
1634+
if oldHasRefreshAnnotation && !newHasRefreshAnnotation {
1635+
return false
1636+
}
1637+
return true
1638+
}
1639+
1640+
return false
1641+
}
1642+
15611643
var _ handler.EventHandler = &clusterSecretEventHandler{}

0 commit comments

Comments
 (0)