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

dynamic-client.mddocs/reference/

Dynamic Client

Back to Index

The dynamic client provides untyped access to any Kubernetes resource, including Custom Resources (CRDs). It works with unstructured.Unstructured objects instead of typed structs.

Package Information

  • Package: k8s.io/client-go/dynamic
  • Related: k8s.io/apimachinery/pkg/apis/meta/v1/unstructured
  • Informers: k8s.io/client-go/dynamic/dynamicinformer
  • Listers: k8s.io/client-go/dynamic/dynamiclister

Core Imports

import (
    "k8s.io/client-go/dynamic"
    "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
    "k8s.io/apimachinery/pkg/runtime/schema"
    metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)

Creating a Dynamic Client

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

// From config
config, err := rest.InClusterConfig()
dynamicClient, err := dynamic.NewForConfig(config)

// With custom HTTP client
httpClient := &http.Client{}
dynamicClient, err := dynamic.NewForConfigAndClient(config, httpClient)

// Panic on error
dynamicClient := dynamic.NewForConfigOrDie(config)

DynamicClient Type

type DynamicClient struct {
    // Has unexported fields
}

func New(c rest.Interface) *DynamicClient
func NewForConfig(inConfig *rest.Config) (*DynamicClient, error)
func NewForConfigAndClient(inConfig *rest.Config, h *http.Client) (*DynamicClient, error)
func NewForConfigOrDie(c *rest.Config) *DynamicClient

func (c *DynamicClient) Resource(resource schema.GroupVersionResource) NamespaceableResourceInterface

Group Version Resource (GVR)

The dynamic client identifies resources using GroupVersionResource:

import "k8s.io/apimachinery/pkg/runtime/schema"

// Define GVR for custom resource
gvr := schema.GroupVersionResource{
    Group:    "stable.example.com",
    Version:  "v1",
    Resource: "crontabs",
}

// Built-in resources
podsGVR := schema.GroupVersionResource{
    Group:    "",  // Core API has empty group
    Version:  "v1",
    Resource: "pods",
}

deploymentsGVR := schema.GroupVersionResource{
    Group:    "apps",
    Version:  "v1",
    Resource: "deployments",
}

// Get resource interface
resource := dynamicClient.Resource(gvr)

// For namespaced resources
resource := dynamicClient.Resource(gvr).Namespace("default")

Resource Interfaces

type Interface interface {
    Resource(resource schema.GroupVersionResource) NamespaceableResourceInterface
}

type NamespaceableResourceInterface interface {
    Namespace(string) ResourceInterface
    ResourceInterface
}

type ResourceInterface interface {
    Create(ctx context.Context, obj *unstructured.Unstructured, options metav1.CreateOptions, subresources ...string) (*unstructured.Unstructured, error)
    Update(ctx context.Context, obj *unstructured.Unstructured, options metav1.UpdateOptions, subresources ...string) (*unstructured.Unstructured, error)
    UpdateStatus(ctx context.Context, obj *unstructured.Unstructured, options metav1.UpdateOptions) (*unstructured.Unstructured, error)
    Delete(ctx context.Context, name string, options metav1.DeleteOptions, subresources ...string) error
    DeleteCollection(ctx context.Context, options metav1.DeleteOptions, listOptions metav1.ListOptions) error
    Get(ctx context.Context, name string, options metav1.GetOptions, subresources ...string) (*unstructured.Unstructured, error)
    List(ctx context.Context, opts metav1.ListOptions) (*unstructured.UnstructuredList, error)
    Watch(ctx context.Context, opts metav1.ListOptions) (watch.Interface, error)
    Patch(ctx context.Context, name string, pt types.PatchType, data []byte, options metav1.PatchOptions, subresources ...string) (*unstructured.Unstructured, error)
    Apply(ctx context.Context, name string, obj *unstructured.Unstructured, options metav1.ApplyOptions, subresources ...string) (*unstructured.Unstructured, error)
    ApplyStatus(ctx context.Context, name string, obj *unstructured.Unstructured, options metav1.ApplyOptions) (*unstructured.Unstructured, error)
}

Working with Unstructured Data

Creating Objects

import "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"

// Create unstructured object
obj := &unstructured.Unstructured{
    Object: map[string]interface{}{
        "apiVersion": "stable.example.com/v1",
        "kind":       "CronTab",
        "metadata": map[string]interface{}{
            "name":      "my-crontab",
            "namespace": "default",
        },
        "spec": map[string]interface{}{
            "cronSpec": "* * * * */5",
            "image":    "my-cron-image",
            "replicas": 1,
        },
    },
}

gvr := schema.GroupVersionResource{
    Group:    "stable.example.com",
    Version:  "v1",
    Resource: "crontabs",
}

result, err := dynamicClient.Resource(gvr).Namespace("default").Create(
    context.TODO(),
    obj,
    metav1.CreateOptions{})

Getting Objects

obj, err := dynamicClient.Resource(gvr).Namespace("default").Get(
    context.TODO(),
    "my-crontab",
    metav1.GetOptions{})

if err != nil {
    // Handle error
}

// Access fields
name, found, err := unstructured.NestedString(obj.Object, "metadata", "name")
cronSpec, found, err := unstructured.NestedString(obj.Object, "spec", "cronSpec")
replicas, found, err := unstructured.NestedInt64(obj.Object, "spec", "replicas")

Listing Objects

list, err := dynamicClient.Resource(gvr).Namespace("default").List(
    context.TODO(),
    metav1.ListOptions{
        LabelSelector: "app=myapp",
    })

for _, item := range list.Items {
    name := item.GetName()
    namespace := item.GetNamespace()
    fmt.Printf("Found: %s/%s\n", namespace, name)
}

Updating Objects

// Get current version
obj, err := dynamicClient.Resource(gvr).Namespace("default").Get(
    context.TODO(), "my-crontab", metav1.GetOptions{})

// Modify using helper functions
err = unstructured.SetNestedField(obj.Object, "* * * * */10", "spec", "cronSpec")
err = unstructured.SetNestedField(obj.Object, int64(2), "spec", "replicas")

// Update
result, err := dynamicClient.Resource(gvr).Namespace("default").Update(
    context.TODO(),
    obj,
    metav1.UpdateOptions{})

Deleting Objects

err := dynamicClient.Resource(gvr).Namespace("default").Delete(
    context.TODO(),
    "my-crontab",
    metav1.DeleteOptions{})

Unstructured Helper Functions

import "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"

// Get nested values
str, found, err := unstructured.NestedString(obj.Object, "spec", "field")
num, found, err := unstructured.NestedInt64(obj.Object, "spec", "replicas")
bool, found, err := unstructured.NestedBool(obj.Object, "spec", "enabled")
slice, found, err := unstructured.NestedSlice(obj.Object, "spec", "items")
map, found, err := unstructured.NestedMap(obj.Object, "spec", "config")
stringSlice, found, err := unstructured.NestedStringSlice(obj.Object, "spec", "tags")

// Set nested values
err := unstructured.SetNestedField(obj.Object, "value", "spec", "field")
err := unstructured.SetNestedField(obj.Object, int64(3), "spec", "replicas")
err := unstructured.SetNestedSlice(obj.Object, slice, "spec", "items")
err := unstructured.SetNestedMap(obj.Object, map, "spec", "config")
err := unstructured.SetNestedStringSlice(obj.Object, []string{"a", "b"}, "spec", "tags")

// Remove nested fields
unstructured.RemoveNestedField(obj.Object, "spec", "field")

// Object accessors
obj.GetName()
obj.GetNamespace()
obj.GetLabels()
obj.GetAnnotations()
obj.GetKind()
obj.GetAPIVersion()
obj.GetResourceVersion()
obj.GetUID()

obj.SetName("name")
obj.SetNamespace("namespace")
obj.SetLabels(map[string]string{"key": "value"})
obj.SetAnnotations(map[string]string{"key": "value"})

Converting Between Typed and Unstructured

import (
    "k8s.io/apimachinery/pkg/runtime"
    corev1 "k8s.io/api/core/v1"
)

// Typed to unstructured
pod := &corev1.Pod{ /* ... */ }
unstructuredObj, err := runtime.DefaultUnstructuredConverter.ToUnstructured(pod)
u := &unstructured.Unstructured{Object: unstructuredObj}

// Unstructured to typed
u := &unstructured.Unstructured{ /* ... */ }
pod := &corev1.Pod{}
err := runtime.DefaultUnstructuredConverter.FromUnstructured(u.Object, pod)

Dynamic Informers

import (
    "k8s.io/client-go/dynamic/dynamicinformer"
    "k8s.io/client-go/tools/cache"
)

// Create dynamic informer factory
factory := dynamicinformer.NewDynamicSharedInformerFactory(dynamicClient, time.Minute*10)

// Get informer for specific GVR
informer := factory.ForResource(gvr)

// Add event handlers
informer.Informer().AddEventHandler(cache.ResourceEventHandlerFuncs{
    AddFunc: func(obj interface{}) {
        u := obj.(*unstructured.Unstructured)
        fmt.Printf("Added: %s\n", u.GetName())
    },
    UpdateFunc: func(oldObj, newObj interface{}) {
        u := newObj.(*unstructured.Unstructured)
        fmt.Printf("Updated: %s\n", u.GetName())
    },
    DeleteFunc: func(obj interface{}) {
        u := obj.(*unstructured.Unstructured)
        fmt.Printf("Deleted: %s\n", u.GetName())
    },
})

// Start informers
stopCh := make(chan struct{})
factory.Start(stopCh)
factory.WaitForCacheSync(stopCh)

// Use lister
lister := informer.Lister()
obj, err := lister.Get("my-crontab")

Complete Custom Resource Example

package main

import (
    "context"
    "fmt"

    "k8s.io/client-go/dynamic"
    "k8s.io/client-go/tools/clientcmd"
    "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
    "k8s.io/apimachinery/pkg/runtime/schema"
    metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)

func main() {
    // Create config
    config, err := clientcmd.BuildConfigFromFlags("", clientcmd.RecommendedHomeFile)
    if err != nil {
        panic(err)
    }

    // Create dynamic client
    dynamicClient, err := dynamic.NewForConfig(config)
    if err != nil {
        panic(err)
    }

    // Define custom resource GVR
    gvr := schema.GroupVersionResource{
        Group:    "mygroup.example.com",
        Version:  "v1",
        Resource: "myresources",
    }

    // Create custom resource
    obj := &unstructured.Unstructured{
        Object: map[string]interface{}{
            "apiVersion": "mygroup.example.com/v1",
            "kind":       "MyResource",
            "metadata": map[string]interface{}{
                "name": "example",
            },
            "spec": map[string]interface{}{
                "field1": "value1",
                "field2": 42,
            },
        },
    }

    result, err := dynamicClient.Resource(gvr).Namespace("default").Create(
        context.TODO(), obj, metav1.CreateOptions{})
    if err != nil {
        panic(err)
    }

    fmt.Printf("Created: %s\n", result.GetName())

    // Get custom resource
    obj, err = dynamicClient.Resource(gvr).Namespace("default").Get(
        context.TODO(), "example", metav1.GetOptions{})
    if err != nil {
        panic(err)
    }

    // Extract field
    field1, _, _ := unstructured.NestedString(obj.Object, "spec", "field1")
    fmt.Printf("field1: %s\n", field1)

    // List custom resources
    list, err := dynamicClient.Resource(gvr).Namespace("default").List(
        context.TODO(), metav1.ListOptions{})
    if err != nil {
        panic(err)
    }

    for _, item := range list.Items {
        fmt.Printf("Found: %s\n", item.GetName())
    }

    // Delete custom resource
    err = dynamicClient.Resource(gvr).Namespace("default").Delete(
        context.TODO(), "example", metav1.DeleteOptions{})
    if err != nil {
        panic(err)
    }
}

Use Cases

  1. Custom Resource Controllers: Build controllers for CRDs without code generation
  2. Generic Tools: Build kubectl-like tools that work with any resource
  3. Dynamic Discovery: Work with resources discovered at runtime
  4. Testing: Test controllers with arbitrary resource types
  5. Migration Tools: Copy resources between clusters without typed clients
  6. API Exploration: Explore unfamiliar APIs programmatically

Best Practices

  1. Validate GVRs: Ensure the GVR matches the actual API resource
  2. Use Discovery: Discover available resources before accessing them
  3. Type Safety: Consider using typed clients when possible for better IDE support
  4. Error Handling: Handle unstructured data errors carefully
  5. Schema Validation: Validate custom resource schemas before operations
  6. Watch Performance: Be mindful of watch overhead with many resource types
  7. Conversion: Use runtime converters for typed/unstructured conversion

Related Documentation

Back to Index

Install with Tessl CLI

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

docs

index.md

tile.json