Skip to content

Commit 7bbbc9b

Browse files
committed
Support mapping an object to multiple keys
1 parent 999ae5a commit 7bbbc9b

File tree

4 files changed

+87
-4
lines changed

4 files changed

+87
-4
lines changed

pkg/controller/controller.go

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -124,6 +124,14 @@ func (gc *GenericController) WatchTransformationOf(obj metav1.Object, mapFn even
124124
eventhandlers.MapAndEnqueue{Map: mapFn, Predicates: p})
125125
}
126126

127+
// WatchTransformationsOf watches objects matching obj's type and enqueues the keys returned by mapFn.
128+
func (gc *GenericController) WatchTransformationsOf(obj metav1.Object, mapFn eventhandlers.ObjToKeys,
129+
p ...predicates.Predicate) error {
130+
gc.once.Do(gc.init)
131+
return gc.queue.addEventHandler(obj,
132+
eventhandlers.MapAndEnqueue{MultiMap: mapFn, Predicates: p})
133+
}
134+
127135
// WatchEvents watches objects matching obj's type and uses the functions from provider to handle events.
128136
func (gc *GenericController) WatchEvents(obj metav1.Object, provider types.HandleFnProvider) error {
129137
gc.once.Do(gc.init)

pkg/controller/controller_test.go

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@ import (
2020
. "github.com/onsi/ginkgo"
2121
. "github.com/onsi/gomega"
2222

23+
"time"
24+
2325
"github.com/kubernetes-sigs/kubebuilder/pkg/controller/eventhandlers"
2426
"github.com/kubernetes-sigs/kubebuilder/pkg/controller/test"
2527
"github.com/kubernetes-sigs/kubebuilder/pkg/controller/types"
@@ -29,7 +31,6 @@ import (
2931
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
3032
"k8s.io/client-go/tools/cache"
3133
"k8s.io/client-go/util/workqueue"
32-
"time"
3334
)
3435

3536
var _ = Describe("GenericController", func() {
@@ -233,6 +234,25 @@ var _ = Describe("GenericController", func() {
233234
Expect(instance.GetMetrics().QueueLength).Should(Equal(0))
234235
})
235236

237+
It("should use the map function to reconcile multiple different keys", func() {
238+
// Listen for Pod changes
239+
Expect(instance.WatchTransformationsOf(&corev1.Pod{}, func(obj interface{}) []string {
240+
p := obj.(*corev1.Pod)
241+
return []string{
242+
p.Namespace + "-namespace/" + p.Name + "-name-1",
243+
p.Namespace + "-namespace/" + p.Name + "-name-2"}
244+
})).Should(Succeed())
245+
246+
fakePodInformer.Add(&corev1.Pod{ObjectMeta: metav1.ObjectMeta{Name: "test-pod", Namespace: "default"}})
247+
248+
val := ChannelResult{}
249+
Eventually(result).Should(Receive(&val.result))
250+
Expect(val.result).Should(Equal("default-namespace/test-pod-name-1"))
251+
Eventually(result).Should(Receive(&val.result))
252+
Expect(val.result).Should(Equal("default-namespace/test-pod-name-2"))
253+
Expect(instance.GetMetrics().QueueLength).Should(Equal(0))
254+
})
255+
236256
It("should call the event handling add function", func() {
237257
// Listen for Pod changes
238258
Expect(instance.WatchEvents(&corev1.Pod{},

pkg/controller/eventhandlers/eventhandlers.go

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,8 @@ type MapAndEnqueue struct {
3939
Predicates []predicates.Predicate
4040
// Map maps an object to a key that can be enqueued
4141
Map func(interface{}) string
42+
43+
MultiMap func(interface{}) []string
4244
}
4345

4446
// Get returns ResourceEventHandlerFuncs that Map an object to a Key and enqueue the key if it is non-empty
@@ -74,9 +76,15 @@ func (mp MapAndEnqueue) Get(r workqueue.RateLimitingInterface) cache.ResourceEve
7476

7577
// addRateLimited maps the obj to a string. If the string is non-empty, it is enqueued.
7678
func (mp MapAndEnqueue) addRateLimited(r workqueue.RateLimitingInterface, obj interface{}) {
77-
k := mp.Map(obj)
78-
if len(k) > 0 {
79-
r.AddRateLimited(k)
79+
if mp.Map != nil {
80+
if k := mp.Map(obj); len(k) > 0 {
81+
r.AddRateLimited(k)
82+
}
83+
}
84+
if mp.MultiMap != nil {
85+
for _, k := range mp.MultiMap(obj) {
86+
r.AddRateLimited(k)
87+
}
8088
}
8189
}
8290

@@ -141,6 +149,8 @@ func (m MapToController) Map(obj interface{}) string {
141149
// ObjToKey returns a string namespace/name key for an object
142150
type ObjToKey func(interface{}) string
143151

152+
type ObjToKeys func(interface{}) []string
153+
144154
// MapToSelf returns the namespace/name key of obj
145155
func MapToSelf(obj interface{}) string {
146156
if key, err := cache.MetaNamespaceKeyFunc(obj); err != nil {

pkg/controller/example_watchandmap_test.go

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,3 +71,48 @@ func ExampleGenericController_WatchTransformationOf() {
7171
// One time for program
7272
controller.RunInformersAndControllers(run.CreateRunArguments())
7373
}
74+
75+
func ExampleGenericController_WatchTransformationsOf() {
76+
// One time setup for program
77+
flag.Parse()
78+
informerFactory := config.GetKubernetesInformersOrDie()
79+
if err := controller.AddInformerProvider(&corev1.Pod{}, informerFactory.Core().V1().Pods()); err != nil {
80+
log.Fatalf("Could not set informer %v", err)
81+
}
82+
if err := controller.AddInformerProvider(&appsv1.ReplicaSet{}, informerFactory.Apps().V1().ReplicaSets()); err != nil {
83+
log.Fatalf("Could not set informer %v", err)
84+
}
85+
86+
// Per-controller setup
87+
c := &controller.GenericController{
88+
Reconcile: func(key types.ReconcileKey) error {
89+
fmt.Printf("Reconciling Pod %s\n", key)
90+
return nil
91+
},
92+
}
93+
err := c.Watch(&appsv1.ReplicaSet{})
94+
if err != nil {
95+
log.Fatalf("%v", err)
96+
}
97+
err = c.WatchTransformationsOf(&corev1.Pod{},
98+
func(i interface{}) []string {
99+
p, ok := i.(*corev1.Pod)
100+
if !ok {
101+
return []string{}
102+
}
103+
104+
// Find multiple parents based off the name
105+
return []string{
106+
p.Namespace + "/" + strings.Split(p.Name, "-")[0] + "-parent-1",
107+
p.Namespace + "/" + strings.Split(p.Name, "-")[0] + "-parent-2",
108+
}
109+
},
110+
)
111+
if err != nil {
112+
log.Fatalf("%v", err)
113+
}
114+
controller.AddController(c)
115+
116+
// One time for program
117+
controller.RunInformersAndControllers(run.CreateRunArguments())
118+
}

0 commit comments

Comments
 (0)