@@ -19,6 +19,7 @@ package builder
19
19
import (
20
20
"errors"
21
21
"fmt"
22
+ "reflect"
22
23
"strings"
23
24
24
25
"github.com/go-logr/logr"
@@ -37,7 +38,6 @@ import (
37
38
)
38
39
39
40
// Supporting mocking out functions for testing.
40
- var newController = controller .New
41
41
var getGvk = apiutil .GVKForObject
42
42
43
43
// project represents other forms that we can use to
@@ -52,21 +52,32 @@ const (
52
52
)
53
53
54
54
// Builder builds a Controller.
55
- type Builder struct {
55
+ type Builder = TypedBuilder [reconcile.Request ]
56
+
57
+ // TypedBuilder builds a Controller. The request is the request type
58
+ // that is passed to the workqueue and then to the Reconciler.
59
+ // The workqueue de-duplicates identical requests.
60
+ type TypedBuilder [request comparable ] struct {
56
61
forInput ForInput
57
62
ownsInput []OwnsInput
58
- rawSources []source.Source
59
- watchesInput []WatchesInput
63
+ rawSources []source.TypedSource [ request ]
64
+ watchesInput []WatchesInput [ request ]
60
65
mgr manager.Manager
61
66
globalPredicates []predicate.Predicate
62
- ctrl controller.Controller
63
- ctrlOptions controller.Options
67
+ ctrl controller.TypedController [ request ]
68
+ ctrlOptions controller.TypedOptions [ request ]
64
69
name string
70
+ newController func (name string , mgr manager.Manager , options controller.TypedOptions [request ]) (controller.TypedController [request ], error )
65
71
}
66
72
67
73
// ControllerManagedBy returns a new controller builder that will be started by the provided Manager.
68
74
func ControllerManagedBy (m manager.Manager ) * Builder {
69
- return & Builder {mgr : m }
75
+ return TypedControllerManagedBy [reconcile.Request ](m )
76
+ }
77
+
78
+ // TypedControllerManagedBy returns a new typed controller builder that will be started by the provided Manager.
79
+ func TypedControllerManagedBy [request comparable ](m manager.Manager ) * TypedBuilder [request ] {
80
+ return & TypedBuilder [request ]{mgr : m }
70
81
}
71
82
72
83
// ForInput represents the information set by the For method.
@@ -81,7 +92,7 @@ type ForInput struct {
81
92
// update events by *reconciling the object*.
82
93
// This is the equivalent of calling
83
94
// Watches(&source.Kind{Type: apiType}, &handler.EnqueueRequestForObject{}).
84
- func (blder * Builder ) For (object client.Object , opts ... ForOption ) * Builder {
95
+ func (blder * TypedBuilder [ request ] ) For (object client.Object , opts ... ForOption ) * TypedBuilder [ request ] {
85
96
if blder .forInput .object != nil {
86
97
blder .forInput .err = fmt .Errorf ("For(...) should only be called once, could not assign multiple objects for reconciliation" )
87
98
return blder
@@ -111,7 +122,7 @@ type OwnsInput struct {
111
122
//
112
123
// By default, this is the equivalent of calling
113
124
// Watches(object, handler.EnqueueRequestForOwner([...], ownerType, OnlyControllerOwner())).
114
- func (blder * Builder ) Owns (object client.Object , opts ... OwnsOption ) * Builder {
125
+ func (blder * TypedBuilder [ request ] ) Owns (object client.Object , opts ... OwnsOption ) * TypedBuilder [ request ] {
115
126
input := OwnsInput {object : object }
116
127
for _ , opt := range opts {
117
128
opt .ApplyToOwns (& input )
@@ -122,9 +133,9 @@ func (blder *Builder) Owns(object client.Object, opts ...OwnsOption) *Builder {
122
133
}
123
134
124
135
// WatchesInput represents the information set by Watches method.
125
- type WatchesInput struct {
136
+ type WatchesInput [ request comparable ] struct {
126
137
obj client.Object
127
- handler handler.EventHandler
138
+ handler handler.TypedEventHandler [client. Object , request ]
128
139
predicates []predicate.Predicate
129
140
objectProjection objectProjection
130
141
}
@@ -134,8 +145,12 @@ type WatchesInput struct {
134
145
//
135
146
// This is the equivalent of calling
136
147
// WatchesRawSource(source.Kind(cache, object, eventHandler, predicates...)).
137
- func (blder * Builder ) Watches (object client.Object , eventHandler handler.EventHandler , opts ... WatchesOption ) * Builder {
138
- input := WatchesInput {
148
+ func (blder * TypedBuilder [request ]) Watches (
149
+ object client.Object ,
150
+ eventHandler handler.TypedEventHandler [client.Object , request ],
151
+ opts ... WatchesOption [request ],
152
+ ) * TypedBuilder [request ] {
153
+ input := WatchesInput [request ]{
139
154
obj : object ,
140
155
handler : eventHandler ,
141
156
}
@@ -175,8 +190,12 @@ func (blder *Builder) Watches(object client.Object, eventHandler handler.EventHa
175
190
// In the first case, controller-runtime will create another cache for the
176
191
// concrete type on top of the metadata cache; this increases memory
177
192
// consumption and leads to race conditions as caches are not in sync.
178
- func (blder * Builder ) WatchesMetadata (object client.Object , eventHandler handler.EventHandler , opts ... WatchesOption ) * Builder {
179
- opts = append (opts , OnlyMetadata )
193
+ func (blder * TypedBuilder [request ]) WatchesMetadata (
194
+ object client.Object ,
195
+ eventHandler handler.TypedEventHandler [client.Object , request ],
196
+ opts ... WatchesOption [request ],
197
+ ) * TypedBuilder [request ] {
198
+ opts = append (opts , projectAs [request ](projectAsMetadata ))
180
199
return blder .Watches (object , eventHandler , opts ... )
181
200
}
182
201
@@ -187,7 +206,7 @@ func (blder *Builder) WatchesMetadata(object client.Object, eventHandler handler
187
206
// This method is only exposed for more advanced use cases, most users should use one of the higher level functions.
188
207
//
189
208
// WatchesRawSource does not respect predicates configured through WithEventFilter.
190
- func (blder * Builder ) WatchesRawSource (src source.Source ) * Builder {
209
+ func (blder * TypedBuilder [ request ] ) WatchesRawSource (src source.TypedSource [ request ] ) * TypedBuilder [ request ] {
191
210
blder .rawSources = append (blder .rawSources , src )
192
211
193
212
return blder
@@ -197,19 +216,19 @@ func (blder *Builder) WatchesRawSource(src source.Source) *Builder {
197
216
// trigger reconciliations. For example, filtering on whether the resource version has changed.
198
217
// Given predicate is added for all watched objects.
199
218
// Defaults to the empty list.
200
- func (blder * Builder ) WithEventFilter (p predicate.Predicate ) * Builder {
219
+ func (blder * TypedBuilder [ request ] ) WithEventFilter (p predicate.Predicate ) * TypedBuilder [ request ] {
201
220
blder .globalPredicates = append (blder .globalPredicates , p )
202
221
return blder
203
222
}
204
223
205
224
// WithOptions overrides the controller options used in doController. Defaults to empty.
206
- func (blder * Builder ) WithOptions (options controller.Options ) * Builder {
225
+ func (blder * TypedBuilder [ request ] ) WithOptions (options controller.TypedOptions [ request ] ) * TypedBuilder [ request ] {
207
226
blder .ctrlOptions = options
208
227
return blder
209
228
}
210
229
211
230
// WithLogConstructor overrides the controller options's LogConstructor.
212
- func (blder * Builder ) WithLogConstructor (logConstructor func (* reconcile. Request ) logr.Logger ) * Builder {
231
+ func (blder * TypedBuilder [ request ] ) WithLogConstructor (logConstructor func (* request ) logr.Logger ) * TypedBuilder [ request ] {
213
232
blder .ctrlOptions .LogConstructor = logConstructor
214
233
return blder
215
234
}
@@ -219,19 +238,19 @@ func (blder *Builder) WithLogConstructor(logConstructor func(*reconcile.Request)
219
238
// (underscores and alphanumeric characters only).
220
239
//
221
240
// By default, controllers are named using the lowercase version of their kind.
222
- func (blder * Builder ) Named (name string ) * Builder {
241
+ func (blder * TypedBuilder [ request ] ) Named (name string ) * TypedBuilder [ request ] {
223
242
blder .name = name
224
243
return blder
225
244
}
226
245
227
246
// Complete builds the Application Controller.
228
- func (blder * Builder ) Complete (r reconcile.Reconciler ) error {
247
+ func (blder * TypedBuilder [ request ] ) Complete (r reconcile.TypedReconciler [ request ] ) error {
229
248
_ , err := blder .Build (r )
230
249
return err
231
250
}
232
251
233
252
// Build builds the Application Controller and returns the Controller it created.
234
- func (blder * Builder ) Build (r reconcile.Reconciler ) (controller.Controller , error ) {
253
+ func (blder * TypedBuilder [ request ] ) Build (r reconcile.TypedReconciler [ request ] ) (controller.TypedController [ request ] , error ) {
235
254
if r == nil {
236
255
return nil , fmt .Errorf ("must provide a non-nil Reconciler" )
237
256
}
@@ -255,7 +274,7 @@ func (blder *Builder) Build(r reconcile.Reconciler) (controller.Controller, erro
255
274
return blder .ctrl , nil
256
275
}
257
276
258
- func (blder * Builder ) project (obj client.Object , proj objectProjection ) (client.Object , error ) {
277
+ func (blder * TypedBuilder [ request ] ) project (obj client.Object , proj objectProjection ) (client.Object , error ) {
259
278
switch proj {
260
279
case projectAsNormal :
261
280
return obj , nil
@@ -272,17 +291,23 @@ func (blder *Builder) project(obj client.Object, proj objectProjection) (client.
272
291
}
273
292
}
274
293
275
- func (blder * Builder ) doWatch () error {
294
+ func (blder * TypedBuilder [ request ] ) doWatch () error {
276
295
// Reconcile type
277
296
if blder .forInput .object != nil {
278
297
obj , err := blder .project (blder .forInput .object , blder .forInput .objectProjection )
279
298
if err != nil {
280
299
return err
281
300
}
282
- hdler := & handler.EnqueueRequestForObject {}
301
+
302
+ if reflect .TypeFor [request ]() != reflect .TypeOf (reconcile.Request {}) {
303
+ return fmt .Errorf ("For() can only be used with reconcile.Request, got %T" , reflect .TypeFor [request ]())
304
+ }
305
+
306
+ var hdler handler.TypedEventHandler [client.Object , request ]
307
+ reflect .ValueOf (& hdler ).Elem ().Set (reflect .ValueOf (& handler.EnqueueRequestForObject {}))
283
308
allPredicates := append ([]predicate.Predicate (nil ), blder .globalPredicates ... )
284
309
allPredicates = append (allPredicates , blder .forInput .predicates ... )
285
- src := source .Kind (blder .mgr .GetCache (), obj , hdler , allPredicates ... )
310
+ src := source .TypedKind (blder .mgr .GetCache (), obj , hdler , allPredicates ... )
286
311
if err := blder .ctrl .Watch (src ); err != nil {
287
312
return err
288
313
}
@@ -301,14 +326,20 @@ func (blder *Builder) doWatch() error {
301
326
if ! own .matchEveryOwner {
302
327
opts = append (opts , handler .OnlyControllerOwner ())
303
328
}
304
- hdler := handler .EnqueueRequestForOwner (
329
+
330
+ if reflect .TypeFor [request ]() != reflect .TypeOf (reconcile.Request {}) {
331
+ return errors .New ("Owns() is not supported for TypedBuilder" )
332
+ }
333
+
334
+ var hdler handler.TypedEventHandler [client.Object , request ]
335
+ reflect .ValueOf (& hdler ).Elem ().Set (reflect .ValueOf (handler .EnqueueRequestForOwner (
305
336
blder .mgr .GetScheme (), blder .mgr .GetRESTMapper (),
306
337
blder .forInput .object ,
307
338
opts ... ,
308
- )
339
+ )))
309
340
allPredicates := append ([]predicate.Predicate (nil ), blder .globalPredicates ... )
310
341
allPredicates = append (allPredicates , own .predicates ... )
311
- src := source .Kind (blder .mgr .GetCache (), obj , hdler , allPredicates ... )
342
+ src := source .TypedKind (blder .mgr .GetCache (), obj , hdler , allPredicates ... )
312
343
if err := blder .ctrl .Watch (src ); err != nil {
313
344
return err
314
345
}
@@ -325,7 +356,7 @@ func (blder *Builder) doWatch() error {
325
356
}
326
357
allPredicates := append ([]predicate.Predicate (nil ), blder .globalPredicates ... )
327
358
allPredicates = append (allPredicates , w .predicates ... )
328
- if err := blder .ctrl .Watch (source .Kind (blder .mgr .GetCache (), projected , w .handler , allPredicates ... )); err != nil {
359
+ if err := blder .ctrl .Watch (source .TypedKind [client. Object , request ] (blder .mgr .GetCache (), projected , w .handler , allPredicates ... )); err != nil {
329
360
return err
330
361
}
331
362
}
@@ -337,7 +368,7 @@ func (blder *Builder) doWatch() error {
337
368
return nil
338
369
}
339
370
340
- func (blder * Builder ) getControllerName (gvk schema.GroupVersionKind , hasGVK bool ) (string , error ) {
371
+ func (blder * TypedBuilder [ request ] ) getControllerName (gvk schema.GroupVersionKind , hasGVK bool ) (string , error ) {
341
372
if blder .name != "" {
342
373
return blder .name , nil
343
374
}
@@ -347,7 +378,7 @@ func (blder *Builder) getControllerName(gvk schema.GroupVersionKind, hasGVK bool
347
378
return strings .ToLower (gvk .Kind ), nil
348
379
}
349
380
350
- func (blder * Builder ) doController (r reconcile.Reconciler ) error {
381
+ func (blder * TypedBuilder [ request ] ) doController (r reconcile.TypedReconciler [ request ] ) error {
351
382
globalOpts := blder .mgr .GetControllerOptions ()
352
383
353
384
ctrlOptions := blder .ctrlOptions
@@ -401,9 +432,10 @@ func (blder *Builder) doController(r reconcile.Reconciler) error {
401
432
)
402
433
}
403
434
404
- ctrlOptions .LogConstructor = func (req * reconcile. Request ) logr.Logger {
435
+ ctrlOptions .LogConstructor = func (in * request ) logr.Logger {
405
436
log := log
406
- if req != nil {
437
+
438
+ if req , ok := any (in ).(* reconcile.Request ); ok && req != nil {
407
439
if hasGVK {
408
440
log = log .WithValues (gvk .Kind , klog .KRef (req .Namespace , req .Name ))
409
441
}
@@ -415,7 +447,11 @@ func (blder *Builder) doController(r reconcile.Reconciler) error {
415
447
}
416
448
}
417
449
450
+ if blder .newController == nil {
451
+ blder .newController = controller .NewTyped [request ]
452
+ }
453
+
418
454
// Build the controller and return.
419
- blder .ctrl , err = newController (controllerName , blder .mgr , ctrlOptions )
455
+ blder .ctrl , err = blder . newController (controllerName , blder .mgr , ctrlOptions )
420
456
return err
421
457
}
0 commit comments