CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/golang-k8s-io--client-go

Official Go client library for Kubernetes API - typed clients, controllers, and cluster interaction tools

Overview
Eval results
Files

quick-start.mddocs/guides/

Quick Start Guide

This guide will help you get started with k8s.io/client-go and perform common operations with Kubernetes clusters.

Prerequisites

  • Go 1.21 or later
  • Access to a Kubernetes cluster
  • kubectl configured (optional, for out-of-cluster usage)

Installation

Add k8s.io/client-go to your project:

go get k8s.io/client-go@v0.35.0

Step 1: Create a Client

Option A: In-Cluster Configuration

Use this when your application runs inside a Kubernetes pod:

package main

import (
    "fmt"
    "k8s.io/client-go/kubernetes"
    "k8s.io/client-go/rest"
)

func main() {
    // Creates configuration using the pod's service account
    config, err := rest.InClusterConfig()
    if err != nil {
        panic(err.Error())
    }

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

    fmt.Println("Connected to cluster!")
}

Option B: Out-of-Cluster Configuration

Use this for local development or CLI tools:

package main

import (
    "fmt"
    "path/filepath"
    "k8s.io/client-go/kubernetes"
    "k8s.io/client-go/tools/clientcmd"
    "k8s.io/client-go/util/homedir"
)

func main() {
    // Build kubeconfig path
    var kubeconfig string
    if home := homedir.HomeDir(); home != "" {
        kubeconfig = filepath.Join(home, ".kube", "config")
    }

    // Load 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())
    }

    fmt.Println("Connected to cluster!")
}

Option C: Smart Configuration (Try In-Cluster, Fall Back to Kubeconfig)

package main

import (
    "fmt"
    "path/filepath"
    "k8s.io/client-go/kubernetes"
    "k8s.io/client-go/rest"
    "k8s.io/client-go/tools/clientcmd"
    "k8s.io/client-go/util/homedir"
)

func getClient() (*kubernetes.Clientset, error) {
    // Try in-cluster config first
    config, err := rest.InClusterConfig()
    if err != nil {
        // Fall back to kubeconfig
        var kubeconfig string
        if home := homedir.HomeDir(); home != "" {
            kubeconfig = filepath.Join(home, ".kube", "config")
        }
        config, err = clientcmd.BuildConfigFromFlags("", kubeconfig)
        if err != nil {
            return nil, err
        }
    }

    return kubernetes.NewForConfig(config)
}

func main() {
    clientset, err := getClient()
    if err != nil {
        panic(err.Error())
    }

    fmt.Println("Connected to cluster!")
}

Step 2: Basic Resource Operations

Listing Resources

List all pods in a namespace:

import (
    "context"
    "fmt"
    metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)

func listPods(clientset *kubernetes.Clientset, namespace string) {
    pods, err := clientset.CoreV1().Pods(namespace).List(context.TODO(), metav1.ListOptions{})
    if err != nil {
        panic(err.Error())
    }

    fmt.Printf("There are %d pods in namespace %s\n", len(pods.Items), namespace)
    for _, pod := range pods.Items {
        fmt.Printf("- %s (Status: %s)\n", pod.Name, pod.Status.Phase)
    }
}

List deployments:

func listDeployments(clientset *kubernetes.Clientset, namespace string) {
    deployments, err := clientset.AppsV1().Deployments(namespace).List(context.TODO(), metav1.ListOptions{})
    if err != nil {
        panic(err.Error())
    }

    fmt.Printf("There are %d deployments in namespace %s\n", len(deployments.Items), namespace)
    for _, deployment := range deployments.Items {
        fmt.Printf("- %s (Replicas: %d/%d)\n", 
            deployment.Name, 
            deployment.Status.ReadyReplicas, 
            *deployment.Spec.Replicas)
    }
}

Getting a Specific Resource

Get a single pod by name:

func getPod(clientset *kubernetes.Clientset, namespace, name string) {
    pod, err := clientset.CoreV1().Pods(namespace).Get(context.TODO(), name, metav1.GetOptions{})
    if err != nil {
        panic(err.Error())
    }

    fmt.Printf("Pod: %s\n", pod.Name)
    fmt.Printf("Namespace: %s\n", pod.Namespace)
    fmt.Printf("Status: %s\n", pod.Status.Phase)
    fmt.Printf("Node: %s\n", pod.Spec.NodeName)
    fmt.Printf("IP: %s\n", pod.Status.PodIP)
}

Creating Resources

Create a namespace:

import (
    corev1 "k8s.io/api/core/v1"
    metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)

func createNamespace(clientset *kubernetes.Clientset, name string) {
    namespace := &corev1.Namespace{
        ObjectMeta: metav1.ObjectMeta{
            Name: name,
        },
    }

    result, err := clientset.CoreV1().Namespaces().Create(context.TODO(), namespace, metav1.CreateOptions{})
    if err != nil {
        panic(err.Error())
    }

    fmt.Printf("Created namespace %s\n", result.Name)
}

Create a deployment:

import (
    appsv1 "k8s.io/api/apps/v1"
    corev1 "k8s.io/api/core/v1"
    metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)

func createDeployment(clientset *kubernetes.Clientset, namespace string) {
    replicas := int32(2)
    deployment := &appsv1.Deployment{
        ObjectMeta: metav1.ObjectMeta{
            Name: "demo-deployment",
        },
        Spec: appsv1.DeploymentSpec{
            Replicas: &replicas,
            Selector: &metav1.LabelSelector{
                MatchLabels: map[string]string{
                    "app": "demo",
                },
            },
            Template: corev1.PodTemplateSpec{
                ObjectMeta: metav1.ObjectMeta{
                    Labels: map[string]string{
                        "app": "demo",
                    },
                },
                Spec: corev1.PodSpec{
                    Containers: []corev1.Container{
                        {
                            Name:  "nginx",
                            Image: "nginx:1.21",
                            Ports: []corev1.ContainerPort{
                                {
                                    ContainerPort: 80,
                                },
                            },
                        },
                    },
                },
            },
        },
    }

    result, err := clientset.AppsV1().Deployments(namespace).Create(context.TODO(), deployment, metav1.CreateOptions{})
    if err != nil {
        panic(err.Error())
    }

    fmt.Printf("Created deployment %s\n", result.Name)
}

Updating Resources

Update a deployment's replica count:

func updateDeploymentReplicas(clientset *kubernetes.Clientset, namespace, name string, replicas int32) {
    // Get the deployment
    deployment, err := clientset.AppsV1().Deployments(namespace).Get(context.TODO(), name, metav1.GetOptions{})
    if err != nil {
        panic(err.Error())
    }

    // Update replicas
    deployment.Spec.Replicas = &replicas

    // Apply update
    result, err := clientset.AppsV1().Deployments(namespace).Update(context.TODO(), deployment, metav1.UpdateOptions{})
    if err != nil {
        panic(err.Error())
    }

    fmt.Printf("Updated deployment %s to %d replicas\n", result.Name, *result.Spec.Replicas)
}

Deleting Resources

Delete a pod:

func deletePod(clientset *kubernetes.Clientset, namespace, name string) {
    err := clientset.CoreV1().Pods(namespace).Delete(context.TODO(), name, metav1.DeleteOptions{})
    if err != nil {
        panic(err.Error())
    }

    fmt.Printf("Deleted pod %s\n", name)
}

Delete a deployment:

func deleteDeployment(clientset *kubernetes.Clientset, namespace, name string) {
    deletePolicy := metav1.DeletePropagationForeground
    err := clientset.AppsV1().Deployments(namespace).Delete(context.TODO(), name, metav1.DeleteOptions{
        PropagationPolicy: &deletePolicy,
    })
    if err != nil {
        panic(err.Error())
    }

    fmt.Printf("Deleted deployment %s\n", name)
}

Step 3: Error Handling

Always handle errors properly using the apierrors package:

import (
    apierrors "k8s.io/apimachinery/pkg/api/errors"
)

func safePodGet(clientset *kubernetes.Clientset, namespace, name string) {
    pod, err := clientset.CoreV1().Pods(namespace).Get(context.TODO(), name, metav1.GetOptions{})
    if err != nil {
        if apierrors.IsNotFound(err) {
            fmt.Printf("Pod %s not found in namespace %s\n", name, namespace)
            return
        } else if apierrors.IsForbidden(err) {
            fmt.Println("Permission denied")
            return
        } else if apierrors.IsUnauthorized(err) {
            fmt.Println("Authentication failed")
            return
        }
        panic(err.Error())
    }

    fmt.Printf("Found pod: %s\n", pod.Name)
}

Step 4: Using Selectors

Label Selectors

Filter resources by labels:

func listPodsByLabel(clientset *kubernetes.Clientset, namespace string) {
    // List pods with label app=demo
    pods, err := clientset.CoreV1().Pods(namespace).List(context.TODO(), metav1.ListOptions{
        LabelSelector: "app=demo",
    })
    if err != nil {
        panic(err.Error())
    }

    fmt.Printf("Found %d pods with label app=demo\n", len(pods.Items))
    
    // Multiple label selectors
    pods, err = clientset.CoreV1().Pods(namespace).List(context.TODO(), metav1.ListOptions{
        LabelSelector: "app=demo,environment=production",
    })
}

Field Selectors

Filter by field values:

func listRunningPods(clientset *kubernetes.Clientset, namespace string) {
    // List only running pods
    pods, err := clientset.CoreV1().Pods(namespace).List(context.TODO(), metav1.ListOptions{
        FieldSelector: "status.phase=Running",
    })
    if err != nil {
        panic(err.Error())
    }

    fmt.Printf("Found %d running pods\n", len(pods.Items))
}

Step 5: Complete Example

Here's a complete working example that demonstrates multiple operations:

package main

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

    appsv1 "k8s.io/api/apps/v1"
    corev1 "k8s.io/api/core/v1"
    apierrors "k8s.io/apimachinery/pkg/api/errors"
    metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    "k8s.io/client-go/kubernetes"
    "k8s.io/client-go/tools/clientcmd"
    "k8s.io/client-go/util/homedir"
)

func main() {
    // Setup client
    var kubeconfig string
    if home := homedir.HomeDir(); home != "" {
        kubeconfig = filepath.Join(home, ".kube", "config")
    }

    config, err := clientcmd.BuildConfigFromFlags("", kubeconfig)
    if err != nil {
        panic(err.Error())
    }

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

    namespace := "demo-namespace"
    deploymentName := "demo-deployment"

    // Create namespace
    fmt.Println("Creating namespace...")
    ns := &corev1.Namespace{
        ObjectMeta: metav1.ObjectMeta{
            Name: namespace,
        },
    }
    _, err = clientset.CoreV1().Namespaces().Create(context.TODO(), ns, metav1.CreateOptions{})
    if err != nil && !apierrors.IsAlreadyExists(err) {
        panic(err.Error())
    }

    // Create deployment
    fmt.Println("Creating deployment...")
    replicas := int32(2)
    deployment := &appsv1.Deployment{
        ObjectMeta: metav1.ObjectMeta{
            Name: deploymentName,
        },
        Spec: appsv1.DeploymentSpec{
            Replicas: &replicas,
            Selector: &metav1.LabelSelector{
                MatchLabels: map[string]string{"app": "demo"},
            },
            Template: corev1.PodTemplateSpec{
                ObjectMeta: metav1.ObjectMeta{
                    Labels: map[string]string{"app": "demo"},
                },
                Spec: corev1.PodSpec{
                    Containers: []corev1.Container{
                        {
                            Name:  "nginx",
                            Image: "nginx:1.21",
                            Ports: []corev1.ContainerPort{
                                {ContainerPort: 80},
                            },
                        },
                    },
                },
            },
        },
    }

    _, err = clientset.AppsV1().Deployments(namespace).Create(context.TODO(), deployment, metav1.CreateOptions{})
    if err != nil && !apierrors.IsAlreadyExists(err) {
        panic(err.Error())
    }

    // List pods
    fmt.Println("Listing pods...")
    pods, err := clientset.CoreV1().Pods(namespace).List(context.TODO(), metav1.ListOptions{})
    if err != nil {
        panic(err.Error())
    }

    fmt.Printf("Found %d pods:\n", len(pods.Items))
    for _, pod := range pods.Items {
        fmt.Printf("  - %s (Status: %s)\n", pod.Name, pod.Status.Phase)
    }

    fmt.Println("Done!")
}

Next Steps

  • Build Controllers: Learn about Informers and Controllers
  • Work with Custom Resources: Explore the Dynamic Client
  • Advanced Patterns: Check out Usage Patterns
  • Real-World Examples: See Real-World Scenarios

Common Issues

"unable to load in-cluster configuration"

This error occurs when trying to use rest.InClusterConfig() outside a pod. Use kubeconfig-based configuration instead.

"the server could not find the requested resource"

Check that:

  1. The API group/version exists in your cluster
  2. You're using the correct resource name (usually plural)
  3. Custom Resource Definitions (CRDs) are installed if working with custom resources

"Unauthorized" or "Forbidden" errors

Ensure your service account or kubeconfig user has the necessary RBAC permissions.

Helpful Tips

  1. Context Management: Always use context.TODO() or a proper context with timeouts
  2. Rate Limiting: Configure appropriate QPS and Burst settings for production
  3. Testing: Use fake.NewSimpleClientset() for unit tests
  4. Logging: Add logging to track API calls and debug issues
  5. Resource Cleanup: Always clean up resources in production code

Install with Tessl CLI

npx tessl i tessl/golang-k8s-io--client-go

docs

index.md

tile.json