Skip to content

Commit ca7cda4

Browse files
committed
✨Builder: Do not require For
Requiring For does not make sense for all controllers as it is possible that their primary event triggering is not an object in the current cluster but for example an object in a different cluster or a source.Channel.
1 parent 222fb66 commit ca7cda4

File tree

2 files changed

+76
-17
lines changed

2 files changed

+76
-17
lines changed

pkg/builder/controller.go

Lines changed: 35 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ limitations under the License.
1717
package builder
1818

1919
import (
20+
"errors"
2021
"fmt"
2122
"strings"
2223

@@ -182,10 +183,6 @@ func (blder *Builder) Build(r reconcile.Reconciler) (controller.Controller, erro
182183
if blder.forInput.err != nil {
183184
return nil, blder.forInput.err
184185
}
185-
// Checking the reconcile type exist or not
186-
if blder.forInput.object == nil {
187-
return nil, fmt.Errorf("must provide an object for reconciliation")
188-
}
189186

190187
// Set the ControllerManagedBy
191188
if err := blder.doController(r); err != nil {
@@ -231,6 +228,9 @@ func (blder *Builder) doWatch() error {
231228
}
232229

233230
// Watches the managed types
231+
if len(blder.ownsInput) > 0 && blder.forInput.object == nil {
232+
return errors.New("Owns() can only be used together with For()")
233+
}
234234
for _, own := range blder.ownsInput {
235235
typeForSrc, err := blder.project(own.object, own.objectProjection)
236236
if err != nil {
@@ -249,6 +249,9 @@ func (blder *Builder) doWatch() error {
249249
}
250250

251251
// Do the watch requests
252+
if len(blder.watchesInput) == 0 && blder.forInput.object == nil {
253+
return errors.New("there are no watches configured, controller will never get triggered. Use For(), Owns() or Watches() to set them up")
254+
}
252255
for _, w := range blder.watchesInput {
253256
allPredicates := append([]predicate.Predicate(nil), blder.globalPredicates...)
254257
allPredicates = append(allPredicates, w.predicates...)
@@ -269,11 +272,14 @@ func (blder *Builder) doWatch() error {
269272
return nil
270273
}
271274

272-
func (blder *Builder) getControllerName(gvk schema.GroupVersionKind) string {
275+
func (blder *Builder) getControllerName(gvk schema.GroupVersionKind, hasGVK bool) (string, error) {
273276
if blder.name != "" {
274-
return blder.name
277+
return blder.name, nil
278+
}
279+
if !hasGVK {
280+
return "", errors.New("one of For() or Named() must be called")
275281
}
276-
return strings.ToLower(gvk.Kind)
282+
return strings.ToLower(gvk.Kind), nil
277283
}
278284

279285
func (blder *Builder) doController(r reconcile.Reconciler) error {
@@ -286,13 +292,18 @@ func (blder *Builder) doController(r reconcile.Reconciler) error {
286292

287293
// Retrieve the GVK from the object we're reconciling
288294
// to prepopulate logger information, and to optionally generate a default name.
289-
gvk, err := getGvk(blder.forInput.object, blder.mgr.GetScheme())
290-
if err != nil {
291-
return err
295+
var gvk schema.GroupVersionKind
296+
hasGVK := blder.forInput.object != nil
297+
if hasGVK {
298+
var err error
299+
gvk, err = getGvk(blder.forInput.object, blder.mgr.GetScheme())
300+
if err != nil {
301+
return err
302+
}
292303
}
293304

294305
// Setup concurrency.
295-
if ctrlOptions.MaxConcurrentReconciles == 0 {
306+
if ctrlOptions.MaxConcurrentReconciles == 0 && hasGVK {
296307
groupKind := gvk.GroupKind().String()
297308

298309
if concurrency, ok := globalOpts.GroupKindConcurrency[groupKind]; ok && concurrency > 0 {
@@ -305,21 +316,30 @@ func (blder *Builder) doController(r reconcile.Reconciler) error {
305316
ctrlOptions.CacheSyncTimeout = *globalOpts.CacheSyncTimeout
306317
}
307318

308-
controllerName := blder.getControllerName(gvk)
319+
controllerName, err := blder.getControllerName(gvk, hasGVK)
320+
if err != nil {
321+
return err
322+
}
309323

310324
// Setup the logger.
311325
if ctrlOptions.LogConstructor == nil {
312326
log := blder.mgr.GetLogger().WithValues(
313327
"controller", controllerName,
314-
"controllerGroup", gvk.Group,
315-
"controllerKind", gvk.Kind,
316328
)
329+
if hasGVK {
330+
log = log.WithValues(
331+
"controllerGroup", gvk.Group,
332+
"controllerKind", gvk.Kind,
333+
)
334+
}
317335

318336
ctrlOptions.LogConstructor = func(req *reconcile.Request) logr.Logger {
319337
log := log
320338
if req != nil {
339+
if hasGVK {
340+
log = log.WithValues(gvk.Kind, klog.KRef(req.Namespace, req.Name))
341+
}
321342
log = log.WithValues(
322-
gvk.Kind, klog.KRef(req.Namespace, req.Name),
323343
"namespace", req.Namespace, "name", req.Name,
324344
)
325345
}

pkg/builder/controller_test.go

Lines changed: 41 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -112,16 +112,55 @@ var _ = Describe("application", func() {
112112
Expect(instance).To(BeNil())
113113
})
114114

115-
It("should return an error if For function is not called", func() {
115+
It("should return an error if For and Named function are not called", func() {
116116
By("creating a controller manager")
117117
m, err := manager.New(cfg, manager.Options{})
118118
Expect(err).NotTo(HaveOccurred())
119119

120120
instance, err := ControllerManagedBy(m).
121+
Watches(&source.Kind{Type: &appsv1.ReplicaSet{}}, &handler.EnqueueRequestForObject{}).
122+
Build(noop)
123+
Expect(err).To(MatchError(ContainSubstring("one of For() or Named() must be called")))
124+
Expect(instance).To(BeNil())
125+
})
126+
127+
It("should return an error when using Owns without For", func() {
128+
By("creating a controller manager")
129+
m, err := manager.New(cfg, manager.Options{})
130+
Expect(err).NotTo(HaveOccurred())
131+
132+
instance, err := ControllerManagedBy(m).
133+
Named("my_controller").
121134
Owns(&appsv1.ReplicaSet{}).
122135
Build(noop)
123-
Expect(err).To(MatchError(ContainSubstring("must provide an object for reconciliation")))
136+
Expect(err).To(MatchError(ContainSubstring("Owns() can only be used together with For()")))
124137
Expect(instance).To(BeNil())
138+
139+
})
140+
141+
It("should return an error when there are no watches", func() {
142+
By("creating a controller manager")
143+
m, err := manager.New(cfg, manager.Options{})
144+
Expect(err).NotTo(HaveOccurred())
145+
146+
instance, err := ControllerManagedBy(m).
147+
Named("my_controller").
148+
Build(noop)
149+
Expect(err).To(MatchError(ContainSubstring("there are no watches configured, controller will never get triggered. Use For(), Owns() or Watches() to set them up")))
150+
Expect(instance).To(BeNil())
151+
})
152+
153+
It("should allow creating a controllerw without calling For", func() {
154+
By("creating a controller manager")
155+
m, err := manager.New(cfg, manager.Options{})
156+
Expect(err).NotTo(HaveOccurred())
157+
158+
instance, err := ControllerManagedBy(m).
159+
Named("my_controller").
160+
Watches(&source.Kind{Type: &appsv1.ReplicaSet{}}, &handler.EnqueueRequestForObject{}).
161+
Build(noop)
162+
Expect(err).NotTo(HaveOccurred())
163+
Expect(instance).NotTo(BeNil())
125164
})
126165

127166
It("should return an error if there is no GVK for an object, and thus we can't default the controller name", func() {

0 commit comments

Comments
 (0)