# `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.