Accelerating Kubernetes Operations with Kubernetes Go Client.
Published: 2023-12-25 10:44:15

**Introduction:**
_Explore the powerful combination of Kubernetes and Go programming in this blog post. Learn how the Kubernetes Go client empowers developers to seamlessly interact with Kubernetes clusters, providing a flexible and efficient way to automate operations._
![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/tz6ozyzozfklzs96fmye.png)

**Prerequisites:**

_- Kubernetes Cluster running using minikube, AKS,EKS or GKE_

_- Go installed in machine._ 

_- Go programming basic understanding_

_- [Go Client Repo](https://github.com/kubernetes/client-go)_


**API Resources, Kinds, and Objects**
_**Resource Type:** In Kubernetes, a "Resource Type" refers to a specific kind of object or entity that can be managed within the Kubernetes cluster. Kubernetes uses a declarative model where users define the desired state of their applications or infrastructure in the form of YAML or JSON manifests. These manifests describe the configuration of various resource types, and Kubernetes is responsible for ensuring that the actual state of the cluster matches the desired state._

_**API Group:** In Kubernetes, an API group is a way to organize and categorize related sets of APIs. The Kubernetes API is designed to be extensible, and API groups help manage the complexity of the API surface by grouping related resources together.The structure of a Kubernetes API endpoint typically follows the pattern:_

```
/<API_GROUP>/<API_VERSION>/...
```
_Here:
API_GROUP: Identifies the group to which a particular resource belongs.
API_VERSION: Specifies the version of the API._

_**Object:** In Kubernetes, an "object" is a basic building block or unit of the system. Objects represent the state of the cluster and can be created, modified, or deleted to manage applications and other aspects of the system. Objects are defined in Kubernetes manifests, typically written in YAML or JSON, and are submitted to the Kubernetes API server for processing._

_**Kind:** In Kubernetes, "Kind" is a field within the metadata of a Kubernetes object that specifies the type or kind of the object. It is a required field and defines the type of resource being created, modified, or interacted with in the cluster. The kind field indicates the object's role and how it should be handled by Kubernetes._
![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/up6iqsjv7mnjnutmxfe1.png)
**Module k8s.io/api**
_The "k8s.io/client-go" module is the official Go client library for Kubernetes. It provides a set of packages and utilities to interact with the Kubernetes API and perform operations such as creating, updating, and deleting resources, as well as watching for changes in the cluster. To use the Kubernetes Go client libraries in your Go application, you typically import specific packages from "k8s.io/client-go."_

**Module k8s.io/apimachinery**
The k8s.io/apimachinery module in Go is part of the Kubernetes Go client libraries and provides a set of packages for working with Kubernetes objects and their metadata. It includes functionality for handling object serialization, conversion, and various utility functions related to Kubernetes API objects.
Key packages within k8s.io/apimachinery include:

_**metav1:**
This package provides types and functions for working with metadata in Kubernetes objects, such as labels, annotations, and timestamps._

_**runtime:**
The runtime package defines interfaces and functions for working with generic Kubernetes runtime objects. It includes serialization, encoding, and decoding functionality._

_**util:**
The util package contains utility functions for working with Kubernetes objects, such as conversion functions and label/selector matching._

_**schema:**
The schema package defines types and functions related to the schema of Kubernetes objects. It includes functionalities like OpenAPI validation and schema generation._

_Here is an example of how k8s.io/apimachinery might be used in conjunction with k8s.io/client-go_:

```
package main

import (
	"fmt"

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

func main() {
	// ... (clientset setup code, similar to the previous example)

	// Example: Working with metav1.ObjectMeta
	labels := map[string]string{"app": "example-app", "env": "production"}
	annotations := map[string]string{"description": "An example pod"}
	objectMeta := metav1.ObjectMeta{
		Name:        "example-pod",
		Namespace:   "default",
		Labels:      labels,
		Annotations: annotations,
	}

	// Example: Using metav1.Time for timestamps
	creationTimestamp := metav1.Time{Time: /* your timestamp here */}
	fmt.Printf("Creation Timestamp: %v\n", creationTimestamp)

	// ... (more examples using k8s.io/apimachinery)
}

```
**Ways of using go client to connect to Kubernetes**
_- Authenticating inside the cluster_
_- Authenticating outside the cluster_
> In this blog I am using "Authenticating outside the cluster" way.

**Demo** 
- Install go client
```
# wget https://go.dev/dl/go1.21.5.linux-amd64.tar.gz
# tar -C /usr/local -xzf go1.21.5.linux-amd64.tar.gz
# export PATH=$PATH:/usr/local/go/bin
# go version
```
_- Create a client directory and create main.go to connect to k8s cluster_

```
 # mkdir client-go-example
 # cd client-go-example/
 # go mod init client-go-example
```
**- Lets use go client to connect to k8s cluster and list the nodes **

```
# vim main.go
package main
import (
        "context"
        "flag"
        "fmt"
        "path/filepath"
        "k8s.io/client-go/kubernetes"
        "k8s.io/client-go/tools/clientcmd"
        "k8s.io/client-go/util/homedir"
        metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)
func main() {
        var kubeconfig *string
        if home := homedir.HomeDir(); home != "" {
                kubeconfig = flag.String("kubeconfig", filepath.Join(home, ".kube", "config"), "(optional) absolute path to the kubeconfig file")
        } else {
                kubeconfig = flag.String("kubeconfig", "", "absolute path to the kubeconfig file")
        }
        flag.Parse()
        // use the current context in kubeconfig
        config, err := clientcmd.BuildConfigFromFlags("", *kubeconfig)
        if err != nil {
                panic(err.Error())
        }

        // create the clientset
        clientset, err := kubernetes.NewForConfig(config)
        if err != nil {
                panic(err.Error())
        }
        nodeList, err := clientset.CoreV1().Nodes().List(context.TODO(), metav1.ListOptions{})
        if err != nil {
                panic(err.Error())
        }
        fmt.Println("Nodes in the cluster:")
        for _, node := range nodeList.Items {
                fmt.Printf("  %s\n", node.GetName())
        }
}

# go build
# go mod tidy
# go run main.go
```

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/yvwbktyycw533v9mpbhv.png)

**- Lets Connect to k8s and collect deployed Namespace in Cluster**

```
# vim main.go
package main

import (
        "context"
        "flag"
        "fmt"
        "path/filepath"

        "k8s.io/client-go/kubernetes"
        "k8s.io/client-go/tools/clientcmd"
        "k8s.io/client-go/util/homedir"
        metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)

func ListNameSpaces(coreClient kubernetes.Interface) {
        nsList, err := coreClient.CoreV1().
                Namespaces().
                List(context.Background(), metav1.ListOptions{})
        if err != nil {
                panic(err.Error())
        }

        for _, n := range nsList.Items {
                fmt.Printf("  %s\n", n.Name)
        }
}
func main() {
        var kubeconfig *string
        if home := homedir.HomeDir(); home != "" {
                kubeconfig = flag.String("kubeconfig", filepath.Join(home, ".kube", "config"), "(optional) absolute path to the kubeconfig file")
        } else {
                kubeconfig = flag.String("kubeconfig", "", "absolute path to the kubeconfig file")
        }
        flag.Parse()

        // use the current context in kubeconfig
        config, err := clientcmd.BuildConfigFromFlags("", *kubeconfig)
        if err != nil {
                panic(err.Error())
        }

        // create the clientset
        clientset, err := kubernetes.NewForConfig(config)
        if err != nil {
                panic(err.Error())
        }
        ListNameSpaces(clientset)
}

# go run main.go
```
![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/8rat8tfkicmpneh6fb5s.png)

**- Lets Connect to k8s and collect deployed pods id Namespace in Cluster**

```
package main

import (
        "context"
        "flag"
        "fmt"
        "path/filepath"

        "k8s.io/client-go/kubernetes"
        "k8s.io/client-go/tools/clientcmd"
        "k8s.io/client-go/util/homedir"
        metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)

func ListNameSpaces(coreClient kubernetes.Interface) {
        nsList, err := coreClient.CoreV1().
                Namespaces().
                List(context.Background(), metav1.ListOptions{})
        if err != nil {
                panic(err.Error())
        }

        for _, n := range nsList.Items {
               ListPods(coreClient, n.Name)
        }
}

func ListPods(coreClient kubernetes.Interface, namespace string){
        fmt.Printf("Pods in namespace: %s\n", namespace)
        pods, err := coreClient.CoreV1().Pods(namespace).List(context.TODO(), metav1.ListOptions{})
        if err != nil {
           fmt.Printf("Error getting pods in namespace %s: %v\n", namespace, err)
           }
        for _, pod := range pods.Items {
             fmt.Printf("  %s\n", pod.Name)
        }
        fmt.Println()
        }
func main() {
        var kubeconfig *string
        if home := homedir.HomeDir(); home != "" {
                kubeconfig = flag.String("kubeconfig", filepath.Join(home, ".kube", "config"), "(optional) absolute path to the kubeconfig file")
        } else {
                kubeconfig = flag.String("kubeconfig", "", "absolute path to the kubeconfig file")
        }
        flag.Parse()

        // use the current context in kubeconfig
        config, err := clientcmd.BuildConfigFromFlags("", *kubeconfig)
        if err != nil {
                panic(err.Error())
        }

        // create the clientset
        clientset, err := kubernetes.NewForConfig(config)
        if err != nil {
                panic(err.Error())
        }
        ListNameSpaces(clientset)
}

# go run main.go
```
![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/vfm8hhph2xydmgw7cfkn.png)

> These are some example how you can communicate with the Kubernetes cluster using go client. In next blog will see how can we mutate the Kubernets apis.