logo

Kubernetes - client-go

https://github.com/kubernetes/client-go

Built-in clients like kubectl and kubelet are all based on client-go.

config + http.Client = RESTClient

vendor/k8s.io/client-go/rest/client.go
vendor/k8s.io/client-go/rest/request.go

ListerWatcher

The ListerWatcher interface in the client-go SDK, used in all Go-based Kubernetes controllers, plays a critical role on how these events are generated, propagated and reliably consumed throughout the various Kubernetes components and actors.

type ListerWatcher interface {
  Lister
  Watcher
}

list and watch

client.Get()
client.Watch()

Cache

client-go cache:

  • Reflector watches a specified resource and causes all changes to be reflected in the given store.
  • Store is the interface of the cache; ExpirationCache is one of the implementation.

Package cache is a client-side caching mechanism. It is useful for reducing the number of server calls you'd otherwise need to make. Reflector watches a server and updates a Store. Two stores are provided; one that simply caches objects (for example, to allow a scheduler to list currently available nodes), and one that additionally acts as a FIFO queue (for example, to allow a scheduler to process incoming pods).

Clients

  • http.Client: Go's built-in HTTP Client.
  • rest.RESTClient: client-go provides a rest.RESTClient that wraps a http.Client.
  • kubernetes.Clientset: a set of typed clients that provides pre-generated local API objects for every core resource type (Pods, Deployments, Services, etc.).
  • dynamic.DynamicClient: client-go/dynamic package provides a dynamic client which can perform RESTful operations on arbitrary API resources. The struct dynamic.Interface uses unstructured.Unstructured to represent all object values from the API server. The dynamic package defers all data bindings until runtime.
import
"k8s.io/client-go/tools/clientcmd"
"k8s.io/client-go/dynamic"

dynamicClient, err := dynamic.NewForConfig(config)

Configuration

  • In-Cluster configuration: used when running inside a pod and uses the service account token mounted to the pod /var/run/secrets/kubernetes.io/serviceaccount/token.
    • aud should match API server’s --api-audiences flag.
  • Out-of-Cluster configuration: used when running outside of the cluster and uses either a provided kubeconfig file or the current user’s default kubeconfig file.

The controller-runtime library provides a GetConfig() that first attempts Out-of-Cluster configuration, and if it fails, attempts In-Cluster configuration.

Code Examples

RESTClientGetter

=> To RESTConfig: rest.Config ("k8s.io/client-go/rest") => To DiscoveryClient: discovery.CachedDiscoveryInterface ("k8s.io/client-go/discovery") => To RESTMapper: meta.RESTMapper ("k8s.io/apimachinery/pkg/api/meta")

Retry

import "k8s.io/client-go/util/retry"
retry.RetryOnConflict(retryBackoff, func() error {
    //logic
})

Backoff

ctx := context.Background()
secret := &corev1.Secret{}
if err := retry.OnError(wait.Backoff{
  Duration: 10 * time.Second,
  Steps:    6,
}, func(err error) bool { return true }, func() error {
  if err := c.Get(ctx, client.ObjectKey{
    Namespace: "kube-system",
    Name:      "foo-secret",
  }, secret); err != nil {
    return err
  }
  return nil
}); err != nil {
  return fmt.Errorf("failed to get secret foo-secret: %w", err)
}

Get in cluster rest config

// vendor/k8s.io/client-go/rest/config.go

// creates the in-cluster config
config, err := rest.InClusterConfig()

// creates the clientset
clientset, err := kubernetes.NewForConfig(config)

kubeconfig => clientSet

import (
  metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
  "k8s.io/client-go/kubernetes"
)

bytesArray := []byte(*kubeconfig)

clientConfig ,err:= clientcmd.NewClientConfigFromBytes(bytesArray)
if err!=nil {
	logs.Error(err.Error())
	return nil
}

config, _ := clientConfig.ClientConfig()
clientset, err := kubernetes.NewForConfig(config)

nodes, err := clientset.CoreV1().Nodes().List(context.Background(), metav1.ListOptions{})

kubeconfig => dynamic client

Dynamic client has no knowledge about the resource you want to consume.

config, err := rest.InClusterConfig()
if err != nil {
    return nil, err
}

dynClient, err := dynamic.NewForConfig(config)
if err != nil {
    return nil, err
}

secrets => kubeconfig

capiconfig "sigs.k8s.io/cluster-api/util/kubeconfig"

nn := types.NamespacedName{
  Name: consts.FooName,
  Namespace: consts.FooNamespace,
}
kubeconfig, err := capiconfig.FromSecret(ctx, client, nn)
// kubeconfig []byte

ClientGetter => Client

clientConfig, err := clientGetter.ToRESTConfig()
if err != nil {
    return err
}
client, err := client.New(clientConfig, client.Options{})
if err != nil {
    return err
}

kubeconfig => restconfig

import (
  "k8s.io/client-go/tools/clientcmd"
)
restConfig, err := clientcmd.RESTConfigFromKubeConfig(kubeconfig)

RateLimiter

rateLimiter is passed from config to client to request.