Basic Operations: Listing Resources

We've set up our connection (rest.Config) and created our gateway (kubernetes.Clientset). Now it's time for the payoff: actually interacting with the cluster! The most fundamental operation is often retrieving information, so let's start by listing some common resources like Pods and Nodes.

Listing Pods

To list resources, we use the List method available on the resource interface we obtained from the clientset (e.g., podsClient.List(...)). This method typically takes two arguments:

  1. context.Context: Used for managing deadlines, cancellation signals, and request-scoped values. We'll usually start with context.TODO() or context.Background() for simple examples.

  2. metav1.ListOptions: Allows you to specify options for filtering and pagination, such as filtering by labels (LabelSelector) or limiting the number of results (Limit). For now, we'll use empty options (metav1.ListOptions{}) to get all resources (within the selected namespace).

The List method returns a pointer to a list struct (e.g., *v1.PodList for Pods) and an error. The list struct contains an Items field, which is a slice of the actual resource objects (e.g., []v1.Pod).

Let's modify our previous code to list all Pods in the default namespace:

package main

import (
	"context" // Import context package
	"flag"
	"fmt" // Import fmt for printing
	"log"   // Use log for better error output
	"path/filepath"

	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" // Import metav1
	"k8s.io/client-go/kubernetes"
	"k8s.io/client-go/tools/clientcmd"
	"k8s.io/client-go/util/homedir"
	// v1 "k8s.io/api/core/v1" // We'll use the Pod type later
)

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()

	// Load configuration
	config, err := clientcmd.BuildConfigFromFlags("", *kubeconfig)
	if err != nil {
		log.Fatalf("Error building kubeconfig: %s", err.Error())
	}

	// Create the clientset
	clientset, err := kubernetes.NewForConfig(config)
	if err != nil {
		log.Fatalf("Error creating clientset: %s", err.Error())
	}

	// --- List Pods ---
	fmt.Println("Listing Pods in 'default' namespace:")

	// Get the Pods client for the 'default' namespace
	podsClient := clientset.CoreV1().Pods("default")

	// List Pods using empty ListOptions
	podList, err := podsClient.List(context.TODO(), metav1.ListOptions{})
	if err != nil {
		log.Fatalf("Error listing pods: %s", err.Error())
	}

	// Iterate through the returned Pods and print their names and statuses
	if len(podList.Items) == 0 {
		fmt.Println("  No pods found in the default namespace.")
	} else {
		for i, pod := range podList.Items {
			fmt.Printf("  Pod %d: Name=%s, Status=%s, IP=%s\n",
				i+1,
				pod.Name,
				pod.Status.Phase, // e.g., Pending, Running, Succeeded, Failed, Unknown
				pod.Status.PodIP,  // The IP address assigned to the Pod
			)
		}
	}
	// --- End List Pods ---

	// We can add Node listing logic next...
}

When you run this code (assuming your kubeconfig points to a valid cluster), it will connect to the cluster, get a list of Pods in the default namespace, and print their name, status phase, and IP address.

Listing Nodes (Cluster-Scoped)

Listing cluster-scoped resources like Nodes follows the same pattern, but you access the resource client directly from the group/version client (e.g., clientset.CoreV1().Nodes()) without specifying a namespace.

Let's add code to list Nodes:

package main

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

	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	v1 "k8s.io/api/core/v1" // Import core v1 types for Node Address
	"k8s.io/client-go/kubernetes"
	"k8s.io/client-go/tools/clientcmd"
	"k8s.io/client-go/util/homedir"
)

func main() {
	// ... (kubeconfig and clientset setup code from previous example) ...
	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()

	config, err := clientcmd.BuildConfigFromFlags("", *kubeconfig)
	if err != nil {
		log.Fatalf("Error building kubeconfig: %s", err.Error())
	}

	clientset, err := kubernetes.NewForConfig(config)
	if err != nil {
		log.Fatalf("Error creating clientset: %s", err.Error())
	}


	// --- List Pods (Keep previous code or remove for focus) ---
	fmt.Println("Listing Pods in 'default' namespace:")
	podsClient := clientset.CoreV1().Pods("default")
	podList, err := podsClient.List(context.TODO(), metav1.ListOptions{})
	if err != nil {
		log.Printf("Warning: Error listing pods: %s (Continuing with Nodes)\n", err.Error())
	} else {
		if len(podList.Items) == 0 {
			fmt.Println("  No pods found in the default namespace.")
		} else {
			for i, pod := range podList.Items {
				fmt.Printf("  Pod %d: Name=%s, Status=%s, IP=%s\n",
					i+1,
					pod.Name,
					pod.Status.Phase,
					pod.Status.PodIP,
				)
			}
		}
	}
	fmt.Println("---") // Separator


	// --- List Nodes ---
	fmt.Println("Listing Nodes:")

	// Get the Nodes client (cluster-scoped)
	nodesClient := clientset.CoreV1().Nodes()

	// List Nodes
	nodeList, err := nodesClient.List(context.TODO(), metav1.ListOptions{})
	if err != nil {
		log.Fatalf("Error listing nodes: %s", err.Error())
	}

	// Iterate through nodes and print info
	if len(nodeList.Items) == 0 {
		fmt.Println("  No nodes found in the cluster.")
	} else {
		for i, node := range nodeList.Items {
			// Extract InternalIP for simplicity (might need more robust logic)
			internalIP := "N/A"
			for _, addr := range node.Status.Addresses {
				if addr.Type == v1.NodeInternalIP {
					internalIP = addr.Address
					break
				}
			}
			fmt.Printf("  Node %d: Name=%s, InternalIP=%s, KubeletVersion=%s\n",
				i+1,
				node.Name,
				internalIP,
				node.Status.NodeInfo.KubeletVersion,
			)
		}
	}
	// --- End List Nodes ---
}

This extended example now also lists all the Nodes in your cluster, showing their name, internal IP address, and the version of the Kubelet running on them. Notice how we access node.Status.Addresses to find the specific IP type we're interested in. Kubernetes objects often have rich status fields containing valuable runtime information.

Listing Namespaces would follow the exact same pattern as Nodes, as Namespaces are also cluster-scoped: clientset.CoreV1().Namespaces().List(...).

These basic List operations form the foundation for many Kubernetes interactions. You can fetch resources, inspect their status, and use that information to make decisions in your Go programs. Next, we'll touch upon how to handle potential errors when making these API calls.

Last updated

Was this helpful?