Skip to content

Commit 3658a3c

Browse files
committed
Add as predicates as variadic args for Owns,For and Watches func
1 parent 524b614 commit 3658a3c

File tree

2 files changed

+105
-23
lines changed

2 files changed

+105
-23
lines changed

pkg/builder/controller.go

Lines changed: 42 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -37,15 +37,15 @@ var getGvk = apiutil.GVKForObject
3737

3838
// Builder builds a Controller.
3939
type Builder struct {
40-
apiType runtime.Object
41-
mgr manager.Manager
42-
predicates []predicate.Predicate
43-
managedObjects []runtime.Object
44-
watchRequest []watchRequest
45-
config *rest.Config
46-
ctrl controller.Controller
47-
ctrlOptions controller.Options
48-
name string
40+
apiType apiType
41+
mgr manager.Manager
42+
globalPredicates []predicate.Predicate
43+
managedObjects []managedObjects
44+
watchRequest []watchRequest
45+
config *rest.Config
46+
ctrl controller.Controller
47+
ctrlOptions controller.Options
48+
name string
4949
}
5050

5151
// ControllerManagedBy returns a new controller builder that will be started by the provided Manager
@@ -63,32 +63,47 @@ func (blder *Builder) ForType(apiType runtime.Object) *Builder {
6363
return blder.For(apiType)
6464
}
6565

66+
type apiType struct {
67+
item runtime.Object
68+
predicates []predicate.Predicate
69+
}
70+
6671
// For defines the type of Object being *reconciled*, and configures the ControllerManagedBy to respond to create / delete /
6772
// update events by *reconciling the object*.
6873
// This is the equivalent of calling
6974
// Watches(&source.Kind{Type: apiType}, &handler.EnqueueRequestForObject{})
70-
func (blder *Builder) For(apiType runtime.Object) *Builder {
71-
blder.apiType = apiType
75+
func (blder *Builder) For(reconciledAPIType runtime.Object, prct ...predicate.Predicate) *Builder {
76+
blder.apiType = apiType{
77+
item: reconciledAPIType,
78+
predicates: prct,
79+
}
7280
return blder
7381
}
7482

83+
type managedObjects struct {
84+
item runtime.Object
85+
predicates []predicate.Predicate
86+
}
87+
7588
// Owns defines types of Objects being *generated* by the ControllerManagedBy, and configures the ControllerManagedBy to respond to
7689
// create / delete / update events by *reconciling the owner object*. This is the equivalent of calling
7790
// Watches(&handler.EnqueueRequestForOwner{&source.Kind{Type: <ForType-apiType>}, &handler.EnqueueRequestForOwner{OwnerType: apiType, IsController: true})
78-
func (blder *Builder) Owns(apiType runtime.Object) *Builder {
79-
blder.managedObjects = append(blder.managedObjects, apiType)
91+
func (blder *Builder) Owns(apiType runtime.Object, prct ...predicate.Predicate) *Builder {
92+
blder.managedObjects = append(blder.managedObjects, managedObjects{item: apiType, predicates: prct})
8093
return blder
8194
}
8295

8396
type watchRequest struct {
8497
src source.Source
8598
eventhandler handler.EventHandler
99+
predicates []predicate.Predicate
86100
}
87101

88102
// Watches exposes the lower-level ControllerManagedBy Watches functions through the builder. Consider using
89103
// Owns or For instead of Watches directly.
90-
func (blder *Builder) Watches(src source.Source, eventhandler handler.EventHandler) *Builder {
91-
blder.watchRequest = append(blder.watchRequest, watchRequest{src: src, eventhandler: eventhandler})
104+
// Specified predicates are registered only for given source.
105+
func (blder *Builder) Watches(src source.Source, eventhandler handler.EventHandler, prct ...predicate.Predicate) *Builder {
106+
blder.watchRequest = append(blder.watchRequest, watchRequest{src: src, eventhandler: eventhandler, predicates: prct})
92107
return blder
93108
}
94109

@@ -102,9 +117,10 @@ func (blder *Builder) WithConfig(config *rest.Config) *Builder {
102117

103118
// WithEventFilter sets the event filters, to filter which create/update/delete/generic events eventually
104119
// trigger reconciliations. For example, filtering on whether the resource version has changed.
120+
// Given predicate is added for all watched objects.
105121
// Defaults to the empty list.
106122
func (blder *Builder) WithEventFilter(p predicate.Predicate) *Builder {
107-
blder.predicates = append(blder.predicates, p)
123+
blder.globalPredicates = append(blder.globalPredicates, p)
108124
return blder
109125
}
110126

@@ -157,28 +173,31 @@ func (blder *Builder) Build(r reconcile.Reconciler) (controller.Controller, erro
157173

158174
func (blder *Builder) doWatch() error {
159175
// Reconcile type
160-
src := &source.Kind{Type: blder.apiType}
176+
src := &source.Kind{Type: blder.apiType.item}
161177
hdler := &handler.EnqueueRequestForObject{}
162-
err := blder.ctrl.Watch(src, hdler, blder.predicates...)
178+
allPredicates := append(blder.globalPredicates, blder.apiType.predicates...)
179+
err := blder.ctrl.Watch(src, hdler, allPredicates...)
163180
if err != nil {
164181
return err
165182
}
166183

167184
// Watches the managed types
168185
for _, obj := range blder.managedObjects {
169-
src := &source.Kind{Type: obj}
186+
src := &source.Kind{Type: obj.item}
170187
hdler := &handler.EnqueueRequestForOwner{
171-
OwnerType: blder.apiType,
188+
OwnerType: blder.apiType.item,
172189
IsController: true,
173190
}
174-
if err := blder.ctrl.Watch(src, hdler, blder.predicates...); err != nil {
191+
allPredicates := append(blder.globalPredicates, obj.predicates...)
192+
if err := blder.ctrl.Watch(src, hdler, allPredicates...); err != nil {
175193
return err
176194
}
177195
}
178196

179197
// Do the watch requests
180198
for _, w := range blder.watchRequest {
181-
if err := blder.ctrl.Watch(w.src, w.eventhandler, blder.predicates...); err != nil {
199+
allPredicates := append(blder.globalPredicates, w.predicates...)
200+
if err := blder.ctrl.Watch(w.src, w.eventhandler, allPredicates...); err != nil {
182201
return err
183202
}
184203

@@ -196,7 +215,7 @@ func (blder *Builder) getControllerName() (string, error) {
196215
if blder.name != "" {
197216
return blder.name, nil
198217
}
199-
gvk, err := getGvk(blder.apiType, blder.mgr.GetScheme())
218+
gvk, err := getGvk(blder.apiType.item, blder.mgr.GetScheme())
200219
if err != nil {
201220
return "", err
202221
}

pkg/builder/controller_test.go

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,8 +31,10 @@ import (
3131
"k8s.io/apimachinery/pkg/runtime/schema"
3232
"k8s.io/apimachinery/pkg/types"
3333
"sigs.k8s.io/controller-runtime/pkg/controller"
34+
"sigs.k8s.io/controller-runtime/pkg/event"
3435
"sigs.k8s.io/controller-runtime/pkg/handler"
3536
"sigs.k8s.io/controller-runtime/pkg/manager"
37+
"sigs.k8s.io/controller-runtime/pkg/predicate"
3638
"sigs.k8s.io/controller-runtime/pkg/reconcile"
3739
"sigs.k8s.io/controller-runtime/pkg/scheme"
3840
"sigs.k8s.io/controller-runtime/pkg/source"
@@ -184,6 +186,67 @@ var _ = Describe("application", func() {
184186
close(done)
185187
}, 10)
186188
})
189+
190+
Describe("Set custom predicates", func() {
191+
It("should execute registered predicates only for assigned kind", func(done Done) {
192+
m, err := manager.New(cfg, manager.Options{})
193+
Expect(err).NotTo(HaveOccurred())
194+
195+
var (
196+
deployPrctExecuted = false
197+
replicaSetPrctExecuted = false
198+
allPrctExecuted = 0
199+
)
200+
201+
deployPrct := predicate.Funcs{
202+
CreateFunc: func(e event.CreateEvent) bool {
203+
defer GinkgoRecover()
204+
// check that it was called only for deployment
205+
Expect(e.Meta).To(BeAssignableToTypeOf(&appsv1.Deployment{}))
206+
deployPrctExecuted = true
207+
return true
208+
},
209+
}
210+
211+
replicaSetPrct := predicate.Funcs{
212+
CreateFunc: func(e event.CreateEvent) bool {
213+
defer GinkgoRecover()
214+
// check that it was called only for replicaset
215+
Expect(e.Meta).To(BeAssignableToTypeOf(&appsv1.ReplicaSet{}))
216+
replicaSetPrctExecuted = true
217+
return true
218+
},
219+
}
220+
221+
allPrct := predicate.Funcs{
222+
CreateFunc: func(e event.CreateEvent) bool {
223+
defer GinkgoRecover()
224+
//check that it was called for all registered kinds
225+
Expect(e.Meta).Should(Or(
226+
BeAssignableToTypeOf(&appsv1.Deployment{}),
227+
BeAssignableToTypeOf(&appsv1.ReplicaSet{}),
228+
))
229+
230+
allPrctExecuted++
231+
return true
232+
},
233+
}
234+
235+
bldr := ControllerManagedBy(m).
236+
For(&appsv1.Deployment{}, deployPrct).
237+
Owns(&appsv1.ReplicaSet{}, replicaSetPrct).
238+
WithEventFilter(allPrct)
239+
240+
doReconcileTest("5", stop, bldr, m, true)
241+
242+
Expect(deployPrctExecuted).To(BeTrue(), "Deploy predicated should be called at least once")
243+
Expect(replicaSetPrctExecuted).To(BeTrue(), "ReplicaSet predicated should be called at least once")
244+
Expect(allPrctExecuted).To(BeNumerically(">=", 2), "Global Predicated should be called at least twice")
245+
246+
close(done)
247+
})
248+
})
249+
187250
})
188251

189252
func doReconcileTest(nameSuffix string, stop chan struct{}, blder *Builder, mgr manager.Manager, complete bool) {

0 commit comments

Comments
 (0)