Shifna Zarnaz

Simplified Version of ketall Plugin

Photo by Campaign Creators on Unsplash

Introduction:

Kubernetes is a powerful container orchestration platform that allows you to manage and scale your applications efficiently. As a Kubernetes administrator or operator, it’s crucial to have visibility into the resources running in your cluster. In this blog post, we’ll explore how to use Go and the Kubernetes client-go library to discover and list resources in a Kubernetes cluster. By the end of this post, you’ll have a clear understanding of how to programmatically access and retrieve information about the resources in your cluster.Kubectl plugin to show really all kubernetes resources.

Prerequisites:

Before we begin, make sure you have the following prerequisites:

  • Go programming language installed (version 1.13 or higher)
  • Access to a Kubernetes cluster
  • Basic knowledge of Go programming and Kubernetes concepts

Code Overview:

Let’s dive into the code and understand how it works.

  • 1. Import Statements: We start by importing the necessary packages, including the Kubernetes client-go packages, which will provide us with the required functionalities to interact with the cluster.
  •                     
                          import (
                            "context"
                            "encoding/json"
                            "fmt"
                            "io/ioutil"
                            "log"
                            "path/filepath"
                            "time"
                           
                            metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
                            "k8s.io/client-go/discovery"
                            "k8s.io/client-go/dynamic"
                            "k8s.io/client-go/tools/clientcmd"
                            "k8s.io/client-go/util/homedir"
                           )
                        
                      
  • 2. Resource Struct: Next, we define a struct called Resource that will hold the information about each Kubernetes resource. This struct includes fields such as Resource (resource name), Namespace, Age (creation time duration), and Kind (resource kind).
  •                     
                          type Resource struct {
                            Resource  string `json:"resource"`
                            Namespace string `json:"namespace"`
                            Age       string `json:"age"`
                            Kind      string `json:"kind"`
                           }
                        
                      
  • 3. Main Function: The main function is the entry point of our program. Here, we load the kubeconfig file to access the cluster configuration.
  •                     
                          func main() {
                            // Load the kubeconfig file to get access to the cluster
                            var kubeconfig string
                            if home := homedir.HomeDir(); home != "" {
                             kubeconfig = filepath.Join(home, ".kube", "config")
                            }
                           
                            // Build the configuration from the kubeconfig file
                            config, err := clientcmd.BuildConfigFromFlags("", kubeconfig)
                            if err != nil {
                             panic(err)
                            }
                        
                      
  • 4. Discovery and Dynamic Client: We create a discovery client using the provided configuration. This client allows us to discover all the available resources in the cluster. Additionally, we create a dynamic client that will enable us to list the resources.
  •                     
                          // Create a new discovery client to discover all resources in the cluster
     dc := discovery.NewDiscoveryClientForConfigOrDie(config)
    
     // Create a new dynamic client to list resources in the cluster
     dynamicClient, err := dynamic.NewForConfig(config)
     if err != nil {
      panic(err)
     }
                        
                      
  • 5. Discovering Resources: To get a list of all available API groups and versions in the cluster, we use the discovery client. We then iterate over each group and list all the resources within that group. For each resource, we extract information such as the name, namespace, age, and kind. We store this information in the Resource struct and add it to a slice of resources.
  •                     
                          // Get a list of all available API groups and versions in the cluster
     resourceLists, err := dc.ServerPreferredResources()
     if err != nil {
      panic(err)
     }
    
     gvrs, err := discovery.GroupVersionResources(resourceLists)
     if err != nil {
      panic(err)
     }
    
     var resources []Resource
    
     // Iterate over all available API groups and versions and list all resources in each group
     for gvr := range gvrs {
      // List all resources in the group
      list, err := dynamicClient.Resource(gvr).Namespace("").List(context.Background(), metav1.ListOptions{})
      if err != nil {
       // fmt.Printf("Error listing %s: %v\n", gvr.String(), err)
       continue
      }
    
      for _, item := range list.Items {
       age := time.Since(item.GetCreationTimestamp().Time).Round(time.Second).String()
       resources = append(resources, Resource{
        Resource:  item.GetName(),
        Namespace: item.GetNamespace(),
        Age:       age,
        Kind:      item.GetKind(),
       })
      }
     }
                        
                      
  • 6. JSON Serialization: Once we have collected all the resources, we serialize the slice of Resource structs into JSON format using the json.Marshal function. We then write the JSON data to a file named "output.json" using the ioutil.WriteFile function.
  •                     
                          data, err := json.Marshal(resources)
     er := ioutil.WriteFile("output.json", data, 0644)
     if er != nil {
      log.Fatal(err)
     }
     if err != nil {
      panic(err)
     }
    
     fmt.Println(string(data))
                        
                      
  • 7. Full Code for reference:
  •                     
                          package main
    
    import (
     "context"
     "encoding/json"
     "fmt"
     "io/ioutil"
     "log"
     "path/filepath"
     "time"
    
     metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
     "k8s.io/client-go/discovery"
     "k8s.io/client-go/dynamic"
     "k8s.io/client-go/tools/clientcmd"
     "k8s.io/client-go/util/homedir"
    )
    
    type Resource struct {
     Resource  string `json:"resource"`
     Namespace string `json:"namespace"`
     Age       string `json:"age"`
     Kind      string `json:"kind"`
    }
    
    func main() {
     // Load the kubeconfig file to get access to the cluster
     var kubeconfig string
     if home := homedir.HomeDir(); home != "" {
      kubeconfig = filepath.Join(home, ".kube", "config")
     }
    
     // Build the configuration from the kubeconfig file
     config, err := clientcmd.BuildConfigFromFlags("", kubeconfig)
     if err != nil {
      panic(err)
     }
    
    
     // Create a new discovery client to discover all resources in the cluster
     dc := discovery.NewDiscoveryClientForConfigOrDie(config)
    
     // Create a new dynamic client to list resources in the cluster
     dynamicClient, err := dynamic.NewForConfig(config)
     if err != nil {
      panic(err)
     }
    
     // Get a list of all available API groups and versions in the cluster
     resourceLists, err := dc.ServerPreferredResources()
     if err != nil {
      panic(err)
     }
    
     gvrs, err := discovery.GroupVersionResources(resourceLists)
     if err != nil {
      panic(err)
     }
    
     var resources []Resource
    
     // Iterate over all available API groups and versions and list all resources in each group
     for gvr := range gvrs {
      // List all resources in the group
      list, err := dynamicClient.Resource(gvr).Namespace("").List(context.Background(), metav1.ListOptions{})
      if err != nil {
       // fmt.Printf("Error listing %s: %v\n", gvr.String(), err)
       continue
      }
    
      for _, item := range list.Items {
       age := time.Since(item.GetCreationTimestamp().Time).Round(time.Second).String()
       resources = append(resources, Resource{
        Resource:  item.GetName(),
        Namespace: item.GetNamespace(),
        Age:       age,
        Kind:      item.GetKind(),
       })
      }
     }
    
     // Print the list of resources in JSON format
     data, err := json.Marshal(resources)
     er := ioutil.WriteFile("filter.json", data, 0644)
     if er != nil {
      log.Fatal(err)
     }
     if err != nil {
      panic(err)
     }
    
     fmt.Println(string(data))
    }
                        
                      

Conclusion:

In this blog post, we learned how to use Go and the Kubernetes client-go library to discover and list resources in a Kubernetes cluster. The code provided allows us to programmatically access the cluster, retrieve information about the resources, and store them in a JSON file. By understanding and customizing this code, you can build powerful tools for managing and monitoring your Kubernetes resources.

Remember to handle errors and add appropriate error checking when working with production code. Additionally, consider implementing error handling and logging mechanisms to enhance the reliability and observability of your code.

Feel free to experiment with the code, explore additional functionalities provided by the Kubernetes client-go library, and adapt it to your specific use cases. Happy coding and exploring the world of Kubernetes resource management with Go!