Official Go client library for Kubernetes API - typed clients, controllers, and cluster interaction tools
The dynamic client provides untyped access to any Kubernetes resource, including Custom Resources (CRDs). It works with unstructured.Unstructured objects instead of typed structs.
k8s.io/client-go/dynamick8s.io/apimachinery/pkg/apis/meta/v1/unstructuredk8s.io/client-go/dynamic/dynamicinformerk8s.io/client-go/dynamic/dynamiclisterimport (
"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"
)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)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) NamespaceableResourceInterfaceThe 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")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)
}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{})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")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)
}// 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{})err := dynamicClient.Resource(gvr).Namespace("default").Delete(
context.TODO(),
"my-crontab",
metav1.DeleteOptions{})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"})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)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")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)
}
}Install with Tessl CLI
npx tessl i tessl/golang-k8s-io--client-go