Custom Controller react to addfunc event handler before cache hassynced? Is this behaviour correct?

4/10/2019

Below is my snippet

factory := informers.NewFilteredSharedInformerFactory(clientset, 0, "", func(o *metaV1.ListOptions) {
    o.LabelSelector = "node-role.kubernetes.io/master="
})

nodeInformer := factory.Core().V1().Nodes().Informer()

i.lister = factory.Core().V1().Nodes().Lister()

nodeInformer.AddEventHandler(
    cache.ResourceEventHandlerFuncs{
        AddFunc: func(obj interface{}) {
            i.updateIPs()
        },
        UpdateFunc: func(oldobj, newObj interface{}) {
            i.updateIPs()
        },
        DeleteFunc: func(obj interface{}) {
            i.updateIPs()
        },
    })

factory.Start(ctx.Done())

if !cache.WaitForCacheSync(ctx.Done(), nodeInformer.HasSynced) {
    runtime.HandleError(fmt.Errorf("Timed out waiting for caches to sync"))
    return
}

Full code is at: https://github.com/tweakmy/fotofona/blob/master/nodeinformer.go

This is the result when I run my unit test:

   $ go test -v -run TestInformer
   === RUN   TestInformer
   Update ip host [10.0.0.1]
   cache is synced
   --- PASS: TestInformer (3.10s)

Is this the expected behaviour? How do I get it to list after the cache has synced and react to event handler after cache has synced.

-- user642318
client-go
go
kubernetes

1 Answer

4/10/2019

What you're seeing is the standard behavior. While you wait for syncing to take place notifications are received and the appropriate notification handlers are executed.

Your code is different wrt most code that uses the informer API because processing happens directly in the notification handlers. That smells like edge-based behavior, while Kubernetes encourages level-based behavior.

The typical pattern when writing a Kubernetes controller (this is what you should do) is having the notification handlers do nothing but enqueuing a reference (typically a namespaced name) to the notified object in a workqueue, and have a pool of workers (goroutines if you're using go) extract such references, use them to retrieve the whole object from the Informer's cache and then do the actual processing. So what is typically done is syncing the Informers' caches before starting the workers, so that workers won't start processing before the cache has synced. To be explicit, updateIPs() should not be executed in the notification handlers code, but in the workers' body.

At this page there's a self-contained example of the usual pattern. More precisely, see this code for an example of typical notification handlers and this code for an example of how syncing is done wrt starting the workers.

-- Matteo
Source: StackOverflow