Skip to content

Commit 8738e91

Browse files
authored
Merge pull request #2091 from alvaroaleman/no-for
✨Builder: Do not require For
2 parents ca4b4de + ca7cda4 commit 8738e91

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)