Recently I’ve written about how to access Kubernetes resources from Go, which works with structured objects. There may be times when you want to work with unstructured components though. Recently I needed to do this because I didn’t know at compile time the types of objects I would be working with.
Kubernetes provides a dynamic client from client-go to give you this functionality. You can import the dynamic
package from k8s.io/client-go/dynamic
.
Creating the dynamic client can be done like the following:
1
dynamicClient, err := dynamic.NewForConfig(kubeConfig)
We need to create a GroupVersionResource
from k8s.io/apimachinery/pkg/runtime/schema
:
1
2
3
4
5
gvr := schema.GroupVersionResource{
Group: "",
Version: "v1",
Resource: "pods",
}
In my sample application I want to list out pods, so the group is the core group which is an empty string. The version is v1
and the resource is set to pods
. Now we can list them out:
1
2
3
4
5
6
7
8
9
10
11
12
pods, err := dynamicClient.Resource(gvr).Namespace("kube-system").List(context.Background(), v1.ListOptions{})
if err != nil {
fmt.Printf("error getting pods: %v\n", err)
os.Exit(1)
}
for _, pod := range pods.Items {
fmt.Printf(
"Name: %s\n",
pod.Object["metadata"].(map[string]interface{})["name"],
)
}
The unstructured items have a type of map[string]interface{}
, so as you crawl through the resources and their fields you have to use type assertion.
The full code for this example is below:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
package main
import (
"context"
"fmt"
"os"
"path/filepath"
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/client-go/dynamic"
"k8s.io/client-go/tools/clientcmd"
)
func main() {
fmt.Println("Get pod names with the dynamic client")
userHomeDir, err := os.UserHomeDir()
if err != nil {
fmt.Printf("error getting user home dir: %v\n", err)
os.Exit(1)
}
kubeConfigPath := filepath.Join(userHomeDir, ".kube", "config")
fmt.Printf("Using kubeconfig: %s\n", kubeConfigPath)
kubeConfig, err := clientcmd.BuildConfigFromFlags("", kubeConfigPath)
if err != nil {
fmt.Printf("error getting Kubernetes config: %v\n", err)
os.Exit(1)
}
dynamicClient, err := dynamic.NewForConfig(kubeConfig)
if err != nil {
fmt.Printf("error creating dynamic client: %v\n", err)
os.Exit(1)
}
gvr := schema.GroupVersionResource{
Group: "",
Version: "v1",
Resource: "pods",
}
pods, err := dynamicClient.Resource(gvr).Namespace("kube-system").List(context.Background(), v1.ListOptions{})
if err != nil {
fmt.Printf("error getting pods: %v\n", err)
os.Exit(1)
}
for _, pod := range pods.Items {
fmt.Printf(
"Name: %s\n",
pod.Object["metadata"].(map[string]interface{})["name"],
)
}
}
And the output of running this against a cluster:
1
2
3
4
5
6
7
8
9
10
11
$ go run .
Get pod names with the dynamic client
Using kubeconfig: /home/trstringer/.kube/config
Name: coredns-558bd4d5db-fwrmj
Name: coredns-558bd4d5db-l2k52
Name: etcd-kind-control-plane
Name: kindnet-46gtb
Name: kube-apiserver-kind-control-plane
Name: kube-controller-manager-kind-control-plane
Name: kube-proxy-5t7c9
Name: kube-scheduler-kind-control-plane
Hopefully this blog post has shown how to use the dynamic client to work with unstructured Kubernetes resources!