-
Notifications
You must be signed in to change notification settings - Fork 2
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
ROX-19221: Fix reconciling with label selector for multiple reconcilers #42
Changes from 3 commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -21,6 +21,7 @@ import ( | |
"errors" | ||
"fmt" | ||
errs "github.com/pkg/errors" | ||
"sigs.k8s.io/controller-runtime/pkg/event" | ||
"strings" | ||
"sync" | ||
"time" | ||
|
@@ -276,7 +277,7 @@ func WithOverrideValues(overrides map[string]string) Option { | |
} | ||
} | ||
|
||
// WithDependentWatchesEnabled is an Option that configures whether the | ||
// SkipDependentWatches is an Option that configures whether the | ||
// Reconciler will register watches for dependent objects in releases and | ||
// trigger reconciliations when they change. | ||
// | ||
|
@@ -597,7 +598,7 @@ func WithControllerSetupFunc(f ControllerSetupFunc) Option { | |
} | ||
|
||
// ControllerSetup allows restricted access to the Controller using the WithControllerSetupFunc option. | ||
// Currently the only supposed configuration is adding additional watchers do the controller. | ||
// Currently, the only supposed configuration is adding additional watchers do the controller. | ||
type ControllerSetup interface { | ||
// Watch takes events provided by a Source and uses the EventHandler to | ||
// enqueue reconcile.Requests in response to the events. | ||
|
@@ -650,6 +651,11 @@ func (r *Reconciler) Reconcile(ctx context.Context, req ctrl.Request) (res ctrl. | |
return ctrl.Result{}, err | ||
} | ||
|
||
if r.selectorPredicate != nil && !r.selectorPredicate.Generic(event.GenericEvent{Object: obj}) { | ||
log.V(1).Info("Label selector does not match, skipping reconcile") | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can you add the affected object and labels to the log message? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Name and namespace is part of the logger
|
||
return ctrl.Result{}, nil | ||
} | ||
|
||
// The finalizer must be present on the CR before we can do anything. Otherwise, if the reconciliation fails, | ||
// there might be resources created by the chart that will not be garbage-collected | ||
// (cluster-scoped resources or resources in other namespaces, which are not bound by an owner reference). | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -484,6 +484,7 @@ var _ = Describe("Reconciler", func() { | |
Expect(mgr.GetCache().WaitForCacheSync(ctx)).To(BeTrue()) | ||
|
||
obj = testutil.BuildTestCR(gvk) | ||
obj.SetLabels(map[string]string{"foo": "bar"}) | ||
objKey = types.NamespacedName{Namespace: obj.GetNamespace(), Name: obj.GetName()} | ||
req = reconcile.Request{NamespacedName: objKey} | ||
|
||
|
@@ -517,6 +518,8 @@ var _ = Describe("Reconciler", func() { | |
cancel() | ||
}) | ||
|
||
selector := metav1.LabelSelector{MatchLabels: map[string]string{"foo": "bar"}} | ||
|
||
// After migration to Ginkgo v2 this can be rewritten using e.g. DescribeTable. | ||
parameterizedReconcilerTests := func(opts reconcilerTestSuiteOpts) { | ||
BeforeEach(func() { | ||
|
@@ -535,6 +538,7 @@ var _ = Describe("Reconciler", func() { | |
WithUpgradeAnnotations(annotation.UpgradeDescription{}), | ||
WithUninstallAnnotations(annotation.UninstallDescription{}), | ||
WithPauseReconcileAnnotation("my.domain/pause-reconcile"), | ||
WithSelector(selector), | ||
WithOverrideValues(map[string]string{ | ||
"image.repository": "custom-nginx", | ||
}), | ||
|
@@ -550,6 +554,7 @@ var _ = Describe("Reconciler", func() { | |
WithUpgradeAnnotations(annotation.UpgradeDescription{}), | ||
WithUninstallAnnotations(annotation.UninstallDescription{}), | ||
WithPauseReconcileAnnotation("my.domain/pause-reconcile"), | ||
WithSelector(selector), | ||
WithOverrideValues(map[string]string{ | ||
"image.repository": "custom-nginx", | ||
}), | ||
|
@@ -1392,6 +1397,40 @@ var _ = Describe("Reconciler", func() { | |
}) | ||
}) | ||
}) | ||
When("label selector succeeds", func() { | ||
It("reconciles only matching label", func() { | ||
By("setting a broken action client getter for the reconciler", func() { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This describes the action, can you describe it's impact/reason why it is necessary to add a broken action client getter? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If possible, the solution you've proposed using a custom There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Even though introducing a new result type is a small change but it might effect many part and I would like to reduce scope to the bug fixing only |
||
r.actionClientGetter = helmclient.ActionClientGetterFunc(func(client.Object) (helmclient.ActionInterface, error) { | ||
fakeClient := helmfake.NewActionClient() | ||
return &fakeClient, nil | ||
}) | ||
}) | ||
|
||
By("setting not matching label to the CR", func() { | ||
Expect(mgr.GetClient().Get(ctx, objKey, obj)).To(Succeed()) | ||
obj.SetLabels(map[string]string{"foo": "baz"}) | ||
Expect(mgr.GetClient().Update(ctx, obj)).To(Succeed()) | ||
}) | ||
|
||
By("reconciling is skipped, action client was not called and no error returned", func() { | ||
res, err := r.Reconcile(ctx, req) | ||
Expect(res).To(Equal(reconcile.Result{})) | ||
Expect(err).To(BeNil()) | ||
}) | ||
|
||
By("setting matching label to the CR", func() { | ||
Expect(mgr.GetClient().Get(ctx, objKey, obj)).To(Succeed()) | ||
obj.SetLabels(map[string]string{"foo": "bar"}) | ||
Expect(mgr.GetClient().Update(ctx, obj)).To(Succeed()) | ||
}) | ||
|
||
By("reconciling is not skipped and error returned because of broken action client", func() { | ||
res, err := r.Reconcile(ctx, req) | ||
Expect(res).To(Equal(reconcile.Result{})) | ||
Expect(err).To(MatchError("get not implemented")) | ||
}) | ||
}) | ||
}) | ||
}) | ||
}) | ||
}) | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why don't put labels on secrets instead? I.e. do a k8s client customization as it is done for the ownerReference.
Adding correct label selector on secrets watch may also give a performance benefit.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Passing predicate to the secret watch was initial approach. But the downside is that it hides filtering mechanism and it's pretty easy to overlook it in the future (e.g. adding additional watch). Filtering selector in the reconcile is straightforward and robust approach. But you are right, it's not the best in terms of CPU because Reconcile call is triggered
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The CPU consumption is minimal here, a reconcile call in itself it is not computationally expensive - it starts a function with exits immediately.