Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(appset): dont requeue appsets on status change #21364

Merged
Merged
Show file tree
Hide file tree
Changes from 14 commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
a3d4031
e2e
rumstead Dec 9, 2024
684bcd2
fix(appset): don't requeue on status changes
rumstead Jan 3, 2025
7f7aa64
fix spelling
rumstead Jan 3, 2025
8a72f1b
Merge branch 'master' into fix/dont-requeue-appsets-on-status-change
rumstead Jan 6, 2025
d348ec4
Merge remote-tracking branch 'upstream/master' into fix/dont-requeue-…
rumstead Jan 13, 2025
27e75e6
Merge remote-tracking branch 'upstream/master' into fix/dont-requeue-…
rumstead Jan 13, 2025
3d301bf
merge in annotation changes
rumstead Jan 13, 2025
3d7204a
Merge branch 'fix/dont-requeue-appsets-on-status-change' of github.co…
rumstead Jan 13, 2025
524df16
merge in annotation changes
rumstead Jan 13, 2025
45eb9ce
add more tests
rumstead Jan 14, 2025
919332a
Merge branch 'master' into fix/dont-requeue-appsets-on-status-change
rumstead Jan 14, 2025
73a29dd
lint fix
rumstead Jan 14, 2025
bf46f26
Merge branch 'master' into fix/dont-requeue-appsets-on-status-change
rumstead Jan 14, 2025
b675a6e
Merge branch 'master' into fix/dont-requeue-appsets-on-status-change
rumstead Jan 15, 2025
abad006
Update applicationset/controllers/applicationset_controller.go
rumstead Jan 16, 2025
223dd0a
Merge branch 'master' into fix/dont-requeue-appsets-on-status-change
rumstead Jan 16, 2025
98020e6
Merge branch 'master' into fix/dont-requeue-appsets-on-status-change
rumstead Jan 16, 2025
82829e2
fix linting
rumstead Jan 16, 2025
2c43a6d
fix linting
rumstead Jan 16, 2025
59100bd
Merge branch 'master' into fix/dont-requeue-appsets-on-status-change
rumstead Jan 17, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
124 changes: 88 additions & 36 deletions applicationset/controllers/applicationset_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -531,33 +531,6 @@ func ignoreNotAllowedNamespaces(namespaces []string) predicate.Predicate {
}
}

// ignoreWhenAnnotationApplicationSetRefreshIsRemoved returns a predicate that ignores updates to ApplicationSet resources
// when the ApplicationSetRefresh annotation is removed
// First reconciliation is triggered when the annotation is added by [webhook.go#refreshApplicationSet]
// Using this predicate we avoid a second reconciliation triggered by the controller himself when the annotation is removed.
func ignoreWhenAnnotationApplicationSetRefreshIsRemoved() predicate.Predicate {
return predicate.Funcs{
UpdateFunc: func(e event.UpdateEvent) bool {
oldAppset, isAppSet := e.ObjectOld.(*argov1alpha1.ApplicationSet)
if !isAppSet {
return false
}
newAppset, isAppSet := e.ObjectNew.(*argov1alpha1.ApplicationSet)
if !isAppSet {
return false
}

_, oldHasRefreshAnnotation := oldAppset.Annotations[common.AnnotationApplicationSetRefresh]
_, newHasRefreshAnnotation := newAppset.Annotations[common.AnnotationApplicationSetRefresh]

if oldHasRefreshAnnotation && !newHasRefreshAnnotation {
return false
}
return true
},
}
}

func appControllerIndexer(rawObj client.Object) []string {
// grab the job object, extract the owner...
app := rawObj.(*argov1alpha1.Application)
Expand All @@ -579,20 +552,20 @@ func (r *ApplicationSetReconciler) SetupWithManager(mgr ctrl.Manager, enableProg
return fmt.Errorf("error setting up with manager: %w", err)
}

ownsHandler := getOwnsHandlerPredicates(enableProgressiveSyncs)
appOwnsHandler := getApplicationOwnsHandler(enableProgressiveSyncs)
appSetOwnsHandler := getApplicationSetOwnsHandler()

return ctrl.NewControllerManagedBy(mgr).WithOptions(controller.Options{
MaxConcurrentReconciles: maxConcurrentReconciliations,
}).For(&argov1alpha1.ApplicationSet{}, builder.WithPredicates(ignoreWhenAnnotationApplicationSetRefreshIsRemoved())).
Owns(&argov1alpha1.Application{}, builder.WithPredicates(ownsHandler)).
}).For(&argov1alpha1.ApplicationSet{}, builder.WithPredicates(appSetOwnsHandler)).
Owns(&argov1alpha1.Application{}, builder.WithPredicates(appOwnsHandler)).
WithEventFilter(ignoreNotAllowedNamespaces(r.ApplicationSetNamespaces)).
Watches(
&corev1.Secret{},
&clusterSecretEventHandler{
Client: mgr.GetClient(),
Log: log.WithField("type", "createSecretEventHandler"),
}).
// TODO: also watch Applications and respond on changes if we own them.
Complete(r)
}

Expand Down Expand Up @@ -1486,7 +1459,7 @@ func syncApplication(application argov1alpha1.Application, prune bool) argov1alp
return application
}

func getOwnsHandlerPredicates(enableProgressiveSyncs bool) predicate.Funcs {
func getApplicationOwnsHandler(enableProgressiveSyncs bool) predicate.Funcs {
return predicate.Funcs{
CreateFunc: func(e event.CreateEvent) bool {
// if we are the owner and there is a create event, we most likely created it and do not need to
Expand Down Expand Up @@ -1523,8 +1496,8 @@ func getOwnsHandlerPredicates(enableProgressiveSyncs bool) predicate.Funcs {
if !isApp {
return false
}
requeue := shouldRequeueApplicationSet(appOld, appNew, enableProgressiveSyncs)
logCtx.WithField("requeue", requeue).Debugf("requeue: %t caused by application %s", requeue, appNew.Name)
requeue := shouldRequeueForApplication(appOld, appNew, enableProgressiveSyncs)
logCtx.WithField("requeue", requeue).Debugf("requeue caused by application %s", appNew.Name)
return requeue
},
GenericFunc: func(e event.GenericEvent) bool {
Expand All @@ -1541,13 +1514,13 @@ func getOwnsHandlerPredicates(enableProgressiveSyncs bool) predicate.Funcs {
}
}

// shouldRequeueApplicationSet determines when we want to requeue an ApplicationSet for reconciling based on an owned
// shouldRequeueForApplication determines when we want to requeue an ApplicationSet for reconciling based on an owned
// application change
// The applicationset controller owns a subset of the Application CR.
// We do not need to re-reconcile if parts of the application change outside the applicationset's control.
// An example being, Application.ApplicationStatus.ReconciledAt which gets updated by the application controller.
// Additionally, Application.ObjectMeta.ResourceVersion and Application.ObjectMeta.Generation which are set by K8s.
func shouldRequeueApplicationSet(appOld *argov1alpha1.Application, appNew *argov1alpha1.Application, enableProgressiveSyncs bool) bool {
func shouldRequeueForApplication(appOld *argov1alpha1.Application, appNew *argov1alpha1.Application, enableProgressiveSyncs bool) bool {
if appOld == nil || appNew == nil {
return false
}
Expand Down Expand Up @@ -1580,4 +1553,83 @@ func shouldRequeueApplicationSet(appOld *argov1alpha1.Application, appNew *argov
return false
}

func getApplicationSetOwnsHandler() predicate.Funcs {
return predicate.Funcs{
CreateFunc: func(e event.CreateEvent) bool {
appSet, isApp := e.Object.(*argov1alpha1.ApplicationSet)
if !isApp {
return false
}
log.WithField("applicationset", appSet.QualifiedName()).Debugln("received create event")
// Always queue a new applicationset
return true
},
DeleteFunc: func(e event.DeleteEvent) bool {
appSet, isApp := e.Object.(*argov1alpha1.ApplicationSet)
if !isApp {
return false
}
log.WithField("applicationset", appSet.QualifiedName()).Debugln("received delete event")
// Always queue for the deletion of an applicationset
return true
},
UpdateFunc: func(e event.UpdateEvent) bool {
appSetOld, isAppSet := e.ObjectOld.(*argov1alpha1.ApplicationSet)
if !isAppSet {
return false
}
appSetNew, isAppSet := e.ObjectNew.(*argov1alpha1.ApplicationSet)
if !isAppSet {
return false
}
requeue := shouldRequeueForApplicationSet(appSetOld, appSetNew)
log.WithField("applicationset", appSetNew.QualifiedName()).
WithField("requeue", requeue).Debugln("received update event")
return requeue
},
GenericFunc: func(e event.GenericEvent) bool {
appSet, isApp := e.Object.(*argov1alpha1.ApplicationSet)
if !isApp {
return false
}
log.WithField("applicationset", appSet.QualifiedName()).Debugln("received generic event")
// Always queue for the generic of an applicationset
return true
},
}
}

// shouldRequeueForApplicationSet determines when we need to requeue an applicationset
func shouldRequeueForApplicationSet(appSetOld, appSetNew *argov1alpha1.ApplicationSet) bool {
if appSetOld == nil || appSetNew == nil {
return false
}
// only compare the applicationset spec, annotations, labels and finalizers, specifically avoiding
// the status field. status is owned by the applicationset controller,
// and we do not need to requeue when it does bookkeeping
// NB: the ApplicationDestination comes from the ApplicationSpec being embedded
// in the ApplicationSetTemplate from the generators
if !cmp.Equal(appSetOld.Spec, appSetNew.Spec, cmpopts.EquateEmpty(), cmpopts.EquateComparable(argov1alpha1.ApplicationDestination{})) ||
!cmp.Equal(appSetOld.ObjectMeta.GetLabels(), appSetNew.ObjectMeta.GetLabels(), cmpopts.EquateEmpty()) ||
!cmp.Equal(appSetOld.ObjectMeta.GetFinalizers(), appSetNew.ObjectMeta.GetFinalizers(), cmpopts.EquateEmpty()) {
return true
}

// we do not want to requeue if the new ApplicationSet has the refresh annotation removed
// only when the annotation is added.
// it is possible that a different annotation changed at the same time and we miss
// the change but that is a rare edge case
rumstead marked this conversation as resolved.
Show resolved Hide resolved
if !cmp.Equal(appSetOld.ObjectMeta.GetAnnotations(), appSetNew.ObjectMeta.GetAnnotations(), cmpopts.EquateEmpty()) {
_, oldHasRefreshAnnotation := appSetOld.Annotations[common.AnnotationApplicationSetRefresh]
_, newHasRefreshAnnotation := appSetNew.Annotations[common.AnnotationApplicationSetRefresh]

if oldHasRefreshAnnotation && !newHasRefreshAnnotation {
return false
}
return true
}

return false
}

var _ handler.EventHandler = &clusterSecretEventHandler{}
Loading
Loading