Skip to content

Commit fcca1f4

Browse files
committed
✨ Allow to use builder.OnlyMetadata option with Watches
This change allows builders to use builder.OnlyMetadata when creating extra watches with .Watches(...). The code inspects the passed source.Source, and if it's of type *source.Kind, it calls the internal project method that allows to use metav1.PartialObjectMetadata in mapping functions. Signed-off-by: Vince Prignano <[email protected]>
1 parent 9975e29 commit fcca1f4

File tree

3 files changed

+73
-9
lines changed

3 files changed

+73
-9
lines changed

pkg/builder/controller.go

Lines changed: 16 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -116,9 +116,10 @@ func (blder *Builder) Owns(object client.Object, opts ...OwnsOption) *Builder {
116116

117117
// WatchesInput represents the information set by Watches method.
118118
type WatchesInput struct {
119-
src source.Source
120-
eventhandler handler.EventHandler
121-
predicates []predicate.Predicate
119+
src source.Source
120+
eventhandler handler.EventHandler
121+
predicates []predicate.Predicate
122+
objectProjection objectProjection
122123
}
123124

124125
// Watches exposes the lower-level ControllerManagedBy Watches functions through the builder. Consider using
@@ -255,10 +256,20 @@ func (blder *Builder) doWatch() error {
255256
for _, w := range blder.watchesInput {
256257
allPredicates := append([]predicate.Predicate(nil), blder.globalPredicates...)
257258
allPredicates = append(allPredicates, w.predicates...)
258-
if err := blder.ctrl.Watch(w.src, w.eventhandler, allPredicates...); err != nil {
259-
return err
259+
watchSource := w.src
260+
261+
// If the source of this watch is of type *source.Kind, project it.
262+
if srckind, ok := watchSource.(*source.Kind); ok {
263+
typeForSrc, err := blder.project(srckind.Type, w.objectProjection)
264+
if err != nil {
265+
return err
266+
}
267+
watchSource = &source.Kind{Type: typeForSrc}
260268
}
261269

270+
if err := blder.ctrl.Watch(watchSource, w.eventhandler, allPredicates...); err != nil {
271+
return err
272+
}
262273
}
263274
return nil
264275
}

pkg/builder/controller_test.go

Lines changed: 49 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -371,14 +371,61 @@ var _ = Describe("application", func() {
371371
Expect(err).NotTo(HaveOccurred())
372372
})
373373

374-
It("should support watching For & Owns as metadata", func() {
374+
It("should support watching For, Owns, and Watch as metadata", func() {
375+
statefulSetMaps := make(chan *metav1.PartialObjectMetadata)
376+
375377
bldr := ControllerManagedBy(mgr).
376378
For(&appsv1.Deployment{}, OnlyMetadata).
377-
Owns(&appsv1.ReplicaSet{}, OnlyMetadata)
379+
Owns(&appsv1.ReplicaSet{}, OnlyMetadata).
380+
Watches(&source.Kind{Type: &appsv1.StatefulSet{}},
381+
handler.EnqueueRequestsFromMapFunc(func(o client.Object) []reconcile.Request {
382+
ometa := o.(*metav1.PartialObjectMetadata)
383+
statefulSetMaps <- ometa
384+
return nil
385+
}),
386+
OnlyMetadata)
378387

379388
ctx, cancel := context.WithCancel(context.Background())
380389
defer cancel()
381390
doReconcileTest(ctx, "8", bldr, mgr, true)
391+
392+
By("Creating a new stateful set")
393+
set := &appsv1.StatefulSet{
394+
ObjectMeta: metav1.ObjectMeta{
395+
Namespace: "default",
396+
Name: "test1",
397+
Labels: map[string]string{
398+
"foo": "bar",
399+
},
400+
},
401+
Spec: appsv1.StatefulSetSpec{
402+
Selector: &metav1.LabelSelector{
403+
MatchLabels: map[string]string{"foo": "bar"},
404+
},
405+
Template: corev1.PodTemplateSpec{
406+
ObjectMeta: metav1.ObjectMeta{Labels: map[string]string{"foo": "bar"}},
407+
Spec: corev1.PodSpec{
408+
Containers: []corev1.Container{
409+
{
410+
Name: "nginx",
411+
Image: "nginx",
412+
},
413+
},
414+
},
415+
},
416+
},
417+
}
418+
err := mgr.GetClient().Create(context.TODO(), set)
419+
Expect(err).NotTo(HaveOccurred())
420+
421+
By("Checking that the mapping function has been called")
422+
Eventually(func() bool {
423+
metaSet := <-statefulSetMaps
424+
Expect(metaSet.Name).To(Equal(set.Name))
425+
Expect(metaSet.Namespace).To(Equal(set.Namespace))
426+
Expect(metaSet.Labels).To(Equal(set.Labels))
427+
return true
428+
}).Should(BeTrue())
382429
})
383430
})
384431
})

pkg/builder/options.go

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,11 @@ func (p projectAs) ApplyToOwns(opts *OwnsInput) {
9494
opts.objectProjection = objectProjection(p)
9595
}
9696

97+
// ApplyToOwns applies this configuration to the given OwnsInput options.
98+
func (p projectAs) ApplyToWatches(opts *WatchesInput) {
99+
opts.objectProjection = objectProjection(p)
100+
}
101+
97102
var (
98103
// OnlyMetadata tells the controller to *only* cache metadata, and to watch
99104
// the the API server in metadata-only form. This is useful when watching
@@ -104,8 +109,9 @@ var (
104109
// unstructured cache.
105110
OnlyMetadata = projectAs(projectAsMetadata)
106111

107-
_ ForOption = OnlyMetadata
108-
_ OwnsOption = OnlyMetadata
112+
_ ForOption = OnlyMetadata
113+
_ OwnsOption = OnlyMetadata
114+
_ WatchesOption = OnlyMetadata
109115
)
110116

111117
// }}}

0 commit comments

Comments
 (0)