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

ACM-7397: Reduce the reconciles caused from policy set updates #133

Merged
merged 1 commit into from
Oct 19, 2023

Conversation

JeffeyL
Copy link
Contributor

@JeffeyL JeffeyL commented Oct 3, 2023

Currently, a reconcile request is made for every single policy in the policy set on update. A custom event handler was added to only reconcile the differing policies between the new and old policy sets. policySetPredicateFuncs was also removed, and the logic was added to the policySetMapper function.

Ref: https://issues.redhat.com/browse/ACM-7397

@openshift-ci openshift-ci bot requested review from gparvin and mprahl October 3, 2023 23:25
@JeffeyL JeffeyL marked this pull request as draft October 4, 2023 05:33
@JeffeyL JeffeyL marked this pull request as ready for review October 4, 2023 05:33
@JeffeyL JeffeyL marked this pull request as draft October 4, 2023 05:43

// EnqueueRequestsFromMapFunc same as original EnqueueRequestsFromMapFunc
// execept this doesn't queue old object for update
type enqueueRequestsFromMapFuncWithDiff struct {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is policyset specific. Could you please rename it to indicate that?

}

diffPolicySet := policiesv1beta1.PolicySet{Spec: policiesv1beta1.PolicySetSpec{
Policies: diffPolicies,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If I'm reading this correctly, diffPolicies only contains policies that were previously in the PolicySet where as it should contain policies that are no longer in the old PolicySet and the new policies in the new PolicySet.

@@ -21,6 +21,11 @@ func policySetMapper(_ client.Client) handler.MapFunc {

var result []reconcile.Request

// There was no difference between the new and old sets
if len(object.(*policiesv1beta1.PolicySet).Spec.Policies) == 0 {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This isn't needed since Go can range over an empty slice.

}
}

diffPolicySet := policiesv1beta1.PolicySet{Spec: policiesv1beta1.PolicySetSpec{
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this required? It seems to me that the event handler could just directly enqueue the policies rather create a fake PolicySet for the policySetMapper.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wasn't able to figure out how to directly enqueue the policies, as they are of type NonEmptyString and I couldn't figure out how to convert them to policyv1.Policy type.

@JustinKuli
Copy link
Member

I think this still needs some changes from Matt's comments, but also just let me know if you need any help rebasing this on top of the recent changes. I re-organized all the mappers and things, which will probably get in your way :/

@JeffeyL
Copy link
Contributor Author

JeffeyL commented Oct 17, 2023

Reopen after syncing changes

@mprahl
Copy link
Member

mprahl commented Oct 17, 2023

I think it'd be cleaner if you did something like this (untested):

diff --git a/controllers/common/policyset_handler.go b/controllers/common/policyset_handler.go
index bf4f676..e6ccbf4 100644
--- a/controllers/common/policyset_handler.go
+++ b/controllers/common/policyset_handler.go
@@ -7,41 +7,55 @@ import (
        "context"
 
        // "k8s.io/apimachinery/pkg/api/equality"
+
+       "k8s.io/apimachinery/pkg/types"
        "k8s.io/client-go/util/workqueue"
        "sigs.k8s.io/controller-runtime/pkg/client"
        "sigs.k8s.io/controller-runtime/pkg/event"
-       "sigs.k8s.io/controller-runtime/pkg/handler"
+       "sigs.k8s.io/controller-runtime/pkg/reconcile"
 
        policiesv1beta1 "open-cluster-management.io/governance-policy-propagator/api/v1beta1"
 )
 
-var _ handler.EventHandler = &enqueueRequestsFromPolicySetMapFunc{}
+// EnqueueRequestsFromPolicySet adds reconcile requests for every policy in the policy set, except on updates, it'll
+// only return the diff.
+type EnqueueRequestsFromPolicySet struct{}
+
+// mapPolicySetToPolicies maps a PolicySet to all the Policies in its policies list.
+func mapPolicySetToRequests(object client.Object) []reconcile.Request {
+       log := log.WithValues("policySetName", object.GetName(), "namespace", object.GetNamespace())
+       log.V(2).Info("Reconcile Request for PolicySet")
 
-// EnqueueRequestsFromMapFunc same as original EnqueueRequestsFromMapFunc
-// execept this only queues the diff between the new and old objects
-func EnqueueRequestsFromPolicySetMapFunc(fn handler.MapFunc) handler.EventHandler {
-       return &enqueueRequestsFromPolicySetMapFunc{
-               toRequests: fn,
+       var result []reconcile.Request
+
+       //nolint:forcetypeassert
+       policySet := object.(*policiesv1beta1.PolicySet)
+
+       for _, plc := range policySet.Spec.Policies {
+               log.V(2).Info("Found reconciliation request from a policyset", "policyName", string(plc))
+
+               request := reconcile.Request{NamespacedName: types.NamespacedName{
+                       Name:      string(plc),
+                       Namespace: object.GetNamespace(),
+               }}
+               result = append(result, request)
        }
-}
 
-// EnqueueRequestsFromMapFunc same as original EnqueueRequestsFromMapFunc
-// execept this doesn't queue old object for update
-type enqueueRequestsFromPolicySetMapFunc struct {
-       // Mapper transforms the argument into a slice of keys to be reconciled
-       toRequests handler.MapFunc
+       return result
 }
 
 // Create implements EventHandler
-func (e *enqueueRequestsFromPolicySetMapFunc) Create(ctx context.Context, evt event.CreateEvent,
+func (e *EnqueueRequestsFromPolicySet) Create(ctx context.Context, evt event.CreateEvent,
        q workqueue.RateLimitingInterface,
 ) {
-       e.mapAndEnqueue(ctx, q, evt.Object)
+       for _, policy := range mapPolicySetToRequests(evt.Object) {
+               q.Add(policy)
+       }
 }
 
 // Update implements EventHandler
 // Enqueues the diff between the new and old policy sets in the UpdateEvent
-func (e *enqueueRequestsFromPolicySetMapFunc) Update(ctx context.Context, evt event.UpdateEvent,
+func (e *EnqueueRequestsFromPolicySet) Update(ctx context.Context, evt event.UpdateEvent,
        q workqueue.RateLimitingInterface,
 ) {
        //nolint:forcetypeassert
@@ -73,33 +87,31 @@ func (e *enqueueRequestsFromPolicySetMapFunc) Update(ctx context.Context, evt ev
                }
        }
 
-       diffPolicySet := policiesv1beta1.PolicySet{Spec: policiesv1beta1.PolicySetSpec{
-               Policies: diffPolicies,
-       }}
+       for _, plc := range diffPolicies {
+               log.V(2).Info("Found reconciliation request from a policyset", "policyName", string(plc))
 
-       e.mapAndEnqueue(ctx, q, &diffPolicySet)
+               request := reconcile.Request{NamespacedName: types.NamespacedName{
+                       Name:      string(plc),
+                       Namespace: newPolicySet.GetNamespace(),
+               }}
+               q.Add(request)
+       }
 }
 
 // Delete implements EventHandler
-func (e *enqueueRequestsFromPolicySetMapFunc) Delete(ctx context.Context, evt event.DeleteEvent,
+func (e *EnqueueRequestsFromPolicySet) Delete(ctx context.Context, evt event.DeleteEvent,
        q workqueue.RateLimitingInterface,
 ) {
-       e.mapAndEnqueue(ctx, q, evt.Object)
+       for _, policy := range mapPolicySetToRequests(evt.Object) {
+               q.Add(policy)
+       }
 }
 
 // Generic implements EventHandler
-func (e *enqueueRequestsFromPolicySetMapFunc) Generic(ctx context.Context, evt event.GenericEvent,
+func (e *EnqueueRequestsFromPolicySet) Generic(ctx context.Context, evt event.GenericEvent,
        q workqueue.RateLimitingInterface,
 ) {
-       e.mapAndEnqueue(ctx, q, evt.Object)
-}
-
-func (e *enqueueRequestsFromPolicySetMapFunc) mapAndEnqueue(ctx context.Context, q workqueue.RateLimitingInterface,
-       object client.Object,
-) {
-       for _, req := range e.toRequests(ctx, object) {
-               q.Add(req)
+       for _, policy := range mapPolicySetToRequests(evt.Object) {
+               q.Add(policy)
        }
 }
-
-// var NeverEnqueue = predicate.NewPredicateFuncs(func(o client.Object) bool { return false })
diff --git a/controllers/propagator/rootpolicy_setup.go b/controllers/propagator/rootpolicy_setup.go
index 5081738..aadd7fe 100644
--- a/controllers/propagator/rootpolicy_setup.go
+++ b/controllers/propagator/rootpolicy_setup.go
@@ -7,7 +7,6 @@ import (
        "context"
 
        "k8s.io/apimachinery/pkg/api/equality"
-       "k8s.io/apimachinery/pkg/types"
        clusterv1beta1 "open-cluster-management.io/api/cluster/v1beta1"
        appsv1 "open-cluster-management.io/multicloud-operators-subscription/pkg/apis/apps/placementrule/v1"
        ctrl "sigs.k8s.io/controller-runtime"
@@ -44,8 +43,7 @@ func (r *RootPolicyReconciler) SetupWithManager(mgr ctrl.Manager, maxConcurrentR
                        builder.WithPredicates(rootPolicyNonStatusUpdates())).
                Watches(
                        &policiesv1beta1.PolicySet{},
-                       common.EnqueueRequestsFromPolicySetMapFunc(mapPolicySetToPolicies),
-                       builder.WithPredicates(policySetPolicyListChanged())).
+                       &common.EnqueueRequestsFromPolicySet{}).
                Watches(
                        &policiesv1.PlacementBinding{},
                        handler.EnqueueRequestsFromMapFunc(mapBindingToPolicies(mgr.GetClient())),
@@ -90,50 +88,6 @@ func rootPolicyNonStatusUpdates() predicate.Funcs {
        }
 }
 
-// mapPolicySetToPolicies maps a PolicySet to all the Policies in its policies list.
-func mapPolicySetToPolicies(_ context.Context, object client.Object) []reconcile.Request {
-       log := log.WithValues("policySetName", object.GetName(), "namespace", object.GetNamespace())
-       log.V(2).Info("Reconcile Request for PolicySet")
-
-       var result []reconcile.Request
-
-       //nolint:forcetypeassert
-       policySet := object.(*policiesv1beta1.PolicySet)
-
-       for _, plc := range policySet.Spec.Policies {
-               log.V(2).Info("Found reconciliation request from a policyset", "policyName", string(plc))
-
-               request := reconcile.Request{NamespacedName: types.NamespacedName{
-                       Name:      string(plc),
-                       Namespace: object.GetNamespace(),
-               }}
-               result = append(result, request)
-       }
-
-       return result
-}
-
-// policySetPolicyListChanged triggers reconciliation if the list of policies in the PolicySet has
-// changed, or if the PolicySet was just created or deleted.
-func policySetPolicyListChanged() predicate.Funcs {
-       return predicate.Funcs{
-               UpdateFunc: func(e event.UpdateEvent) bool {
-                       //nolint:forcetypeassert
-                       policySetObjNew := e.ObjectNew.(*policiesv1beta1.PolicySet)
-                       //nolint:forcetypeassert
-                       policySetObjOld := e.ObjectOld.(*policiesv1beta1.PolicySet)
-
-                       return !equality.Semantic.DeepEqual(policySetObjNew.Spec.Policies, policySetObjOld.Spec.Policies)
-               },
-               CreateFunc: func(e event.CreateEvent) bool {
-                       return true
-               },
-               DeleteFunc: func(e event.DeleteEvent) bool {
-                       return true
-               },
-       }
-}
-
 // mapBindingToPolicies maps a PlacementBinding to the Policies that are either directly in its
 // subjects list, or are in a PolicySet which is a subject of this PlacementBinding.
 func mapBindingToPolicies(c client.Client) handler.MapFunc {

controllers/common/policyset_handler.go Outdated Show resolved Hide resolved
controllers/common/policyset_handler.go Outdated Show resolved Hide resolved
controllers/common/policyset_handler.go Outdated Show resolved Hide resolved
@JeffeyL JeffeyL force-pushed the ACM-7397 branch 3 times, most recently from 338aaf3 to de9ea56 Compare October 18, 2023 15:50
@JeffeyL JeffeyL marked this pull request as ready for review October 18, 2023 16:19
@openshift-ci openshift-ci bot requested a review from dhaiducek October 18, 2023 16:19
@JeffeyL JeffeyL requested a review from mprahl October 18, 2023 16:19
Copy link
Member

@mprahl mprahl left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@JeffeyL, this looks great! Just a few comments to update/remove.

Currently, a reconcile request is made for every single policy in the policy set on update. A custome event handler was added to only reconcile the differing policies between the new and old policy sets.

ref: https://issues.redhat.com/browse/ACM-7397
Signed-off-by: Jeffrey Luo <[email protected]>
@openshift-ci openshift-ci bot added the lgtm label Oct 19, 2023
@openshift-ci
Copy link

openshift-ci bot commented Oct 19, 2023

[APPROVALNOTIFIER] This PR is APPROVED

This pull-request has been approved by: JeffeyL, mprahl

The full list of commands accepted by this bot can be found here.

The pull request process is described here

Needs approval from an approver in each of these files:

Approvers can indicate their approval by writing /approve in a comment
Approvers can cancel approval by writing /approve cancel in a comment

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants