Skip to content

Commit 87ba582

Browse files
author
Per Goncalves da Silva
committed
enforce ResourceEvent types
Signed-off-by: Per Goncalves da Silva <[email protected]>
1 parent 8db7f9f commit 87ba582

File tree

4 files changed

+70
-29
lines changed

4 files changed

+70
-29
lines changed

pkg/lib/kubestate/kubestate.go

Lines changed: 27 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,9 @@ package kubestate
22

33
import (
44
"context"
5+
"fmt"
6+
"k8s.io/client-go/tools/cache"
7+
"sigs.k8s.io/controller-runtime/pkg/client"
58
)
69

710
type State interface {
@@ -148,6 +151,7 @@ const (
148151
type ResourceEvent interface {
149152
Type() ResourceEventType
150153
Resource() interface{}
154+
String() string
151155
}
152156

153157
type resourceEvent struct {
@@ -163,14 +167,33 @@ func (r resourceEvent) Resource() interface{} {
163167
return r.resource
164168
}
165169

166-
func NewUpdateEvent(resource interface{}) ResourceEvent {
167-
return resourceEvent{
168-
eventType: ResourceUpdated,
169-
resource: resource,
170+
func (r resourceEvent) String() string {
171+
key, err := cache.MetaNamespaceKeyFunc(r.resource)
172+
173+
// should not happen as resources must be either cache.ExplicitKey
174+
// or client.Object
175+
if err != nil {
176+
panic("could not get resource key: " + err.Error())
170177
}
178+
return key
179+
}
180+
181+
func NewUpdateEvent(resource interface{}) ResourceEvent {
182+
return NewResourceEvent(ResourceUpdated, resource)
171183
}
172184

173185
func NewResourceEvent(eventType ResourceEventType, resource interface{}) ResourceEvent {
186+
// assert resource type
187+
// only accept cache.ExplicitKey or client.Objects
188+
switch r := resource.(type) {
189+
case string:
190+
resource = cache.ExplicitKey(r)
191+
case cache.ExplicitKey:
192+
case client.Object:
193+
default:
194+
panic(fmt.Sprintf("NewResourceEvent called with invalid resource type: %T", resource))
195+
}
196+
174197
return resourceEvent{
175198
eventType: eventType,
176199
resource: resource,

pkg/lib/queueinformer/config.go

Lines changed: 0 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -81,19 +81,6 @@ func (c *queueInformerConfig) validateQueue() (err error) {
8181
}
8282

8383
func defaultKeyFunc(obj interface{}) (string, bool) {
84-
// Get keys nested in resource events up to depth 2
85-
keyable := false
86-
for d := 0; d < 2 && !keyable; d++ {
87-
switch v := obj.(type) {
88-
case string:
89-
return v, true
90-
case kubestate.ResourceEvent:
91-
obj = v.Resource()
92-
default:
93-
keyable = true
94-
}
95-
}
96-
9784
k, err := cache.DeletionHandlingMetaNamespaceKeyFunc(obj)
9885
if err != nil {
9986
return k, false

pkg/lib/queueinformer/main/main.go

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
package main
2+
3+
import (
4+
"fmt"
5+
"github.com/operator-framework/operator-lifecycle-manager/pkg/lib/kubestate"
6+
"k8s.io/client-go/tools/cache"
7+
)
8+
9+
func main() {
10+
k, ok := cache.MetaNamespaceKeyFunc(kubestate.NewUpdateEvent("bob"))
11+
fmt.Printf("key: %s (%t)\n", k, ok)
12+
}

pkg/lib/queueinformer/queueinformer_operator.go

Lines changed: 31 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -262,6 +262,30 @@ func (o *operator) worker(ctx context.Context, loop *QueueInformer) {
262262
}
263263

264264
func (o *operator) processNextWorkItem(ctx context.Context, loop *QueueInformer) bool {
265+
// **************************** WARNING ****************************
266+
// The QueueInformer listens to resource events raised by its
267+
// (client-go) informer. For Add/Update event, it extracts the key
268+
// for the resource and adds to its queue (that we Get() from below)
269+
// a ResourceEvent carrying the key.
270+
// **Except** if it is a deletion event. In that case,
271+
// ResourceEvent carries the resource object (or tombstone).
272+
// The sync'er expects a ResourceEvent carrying the resource.
273+
// So, in the case of an Add/Update event coming from the queue,
274+
// the resource is acquired from the index (through the key), and then
275+
// a ResourceEvent carrying the resource is handed to the syncer.
276+
// It should also be noted that throughout the code, items are added to
277+
// queueinformers out of band of informer notifications.
278+
// The fact that the queueinformers queue processes ResourceEvents, which
279+
// themselves encapsulate an interface{} "Resource" make it tricky for the
280+
// queue to dedup. Previous to the writing of this comment, the queue was
281+
// processing strings and ResourceEvents, which led to concurrent processing
282+
// of the same resource. To address this, we enforce (with panic) that the resource
283+
// in the ResourceEvent must either be a cache.ExplicitKey or a client.Object.
284+
// We then make sure that the ResourceEvent's String() returns the key for the
285+
// encapsulated resource. Thus, independent of the resource type, the queue always
286+
// processes it by key and dedups appropriately.
287+
// Furthermore, we also enforce here that Add/Update events always contain
288+
// cache.ExplicitKey as their Resource
265289
queue := loop.queue
266290
item, quit := queue.Get()
267291

@@ -271,28 +295,23 @@ func (o *operator) processNextWorkItem(ctx context.Context, loop *QueueInformer)
271295
defer queue.Done(item)
272296

273297
logger := o.logger.WithField("item", item)
274-
logger.WithField("queue-length", queue.Len()).Trace("popped queue")
298+
logger.WithField("queue-length", queue.Len()).Info("popped queue")
275299

276300
var event = item
277301
if item.Type() != kubestate.ResourceDeleted {
278-
// Get the key
279-
//key, keyable := loop.key(item)
280-
//if !keyable {
281-
// logger.WithField("item", item).Warn("could not form key")
282-
// queue.Forget(item)
283-
// return true
284-
//}
285-
key, ok := item.Resource().(string)
286-
if !ok {
287-
panic(fmt.Sprintf("unexpected item resource type: %T", item.Resource()))
302+
key, keyable := loop.key(item)
303+
if !keyable {
304+
logger.WithField("item", item).Warn("could not form key")
305+
queue.Forget(item)
306+
return true
288307
}
289308

290309
logger = logger.WithField("cache-key", key)
291310

292311
// Get the current cached version of the resource
293312
var exists bool
294313
var err error
295-
resource, exists, err := loop.indexer.GetByKey(key)
314+
resource, exists, err := loop.indexer.GetByKey(string(key))
296315
if err != nil {
297316
logger.WithError(err).Error("cache get failed")
298317
queue.Forget(item)

0 commit comments

Comments
 (0)