# `client-go` Basic Usage (without using Shared Informer) https://pkg.go.dev/k8s.io/client-go@v0.20.0/kubernetes Listing and getting ConfigMaps ``` clientset := kubernetes.New(cfg) resp, err := clientset.CoreV1().ConfigMaps(ns).List(...) cm, err := clientset.CoreV1().ConfigMaps(ns).Get(...) ``` Each call to `List` or `Get` makes a new connection to the API server and reads from etcd. These calls are subject to [API Priority and Fairness](https://kubernetes.io/docs/concepts/cluster-administration/flow-control/), rate limits, exponential backoff, etc. Watching ConfigMap changes ```go= w, err := clientset.CoreV1().ConfigMaps(ns).Watch(...) for e := range w.ResultChan() { cm, ok := e.Object.(*corev1.ConfigMap) switch e.Type { case watch.Added: // TODO: handle new ConfigMap case watch.Modified: // TODO: handle updated ConfigMap } } ``` And to stop: ```go= w.Stop() ``` Each call to `Watch` opens a long-running connection to the API server, which is closed when you `Stop`. # Optimizing Reads https://pkg.go.dev/k8s.io/client-go@v0.20.0/informers#NewSharedInformerFactoryWithOptions Instead of using the client directly, you can pass it to a `SharedInformerFactory`, which you can use to create "Informers", which you can use to watch for updates: ```go= sif := NewSharedInformerFactoryWithOptions(...) cminf := sif.Core().V1().ConfigMaps() stopCh := make(chan struct{}) inf := cminf.Informer() inf.AddEventHandler(cache.ResourceEventHandlerFuncs{ OnAdd: func(obj interface{}) { cm, ok := obj.(*corev1.ConfigMap) }, OnUpdate: func(oldCM, newCM interface{}) { // TODO: do something with the update. }, OnDelete: func(obj interface{}) { ... }, }) inf.Run(stopCh) ``` And to stop the watcher: ```go= close(stopCh) ``` Or, to list or get objects: ```go= // List all ConfigMaps in a namespace, or get one by name. lis := cminf.Lister() resp, err := lis.ConfigMaps(ns).List(...) cm, err := lis.ConfigMaps(ns).Get(...) ... ``` These calls are served from the shared informer's cache, which it keeps up-to-date by initiating a _single_ `Watch` request. ## Benefits of `SharedInformerFactory` - update events give you both `old` and `new`, don't require another API call to get previous version - `old` can also be ignored, it didn't really cost anything to read it - it does a single `Watch` on the type and populates a shared indexed cache - clients of the Informer and Lister pull from the cache `NewSharedInformerFactoryWithOptions` can also take a `TweakListOptions` to further limit the scope of the watch, if you only care about objects with a specific label, for example. ## Disadvantages - sometimes the cache can be out-of-date, especially immediately after an update - in Tekton's experience, sometimes a second or two; not usually minutes - you can build in retries to account for that - fallback to direct client-go Get/List/Watch if you absolutely need consistent reads Make sure to use a single `SharedInformerFactory` which you pass around and reuse, which lets all informers/listers created from it share a cache and index. Each `SharedInformerFactory` makes its own Watch connections and keeps its own cache.