Cluster Resource Watcher
A Kubernetes client tool that demonstrates three core client-go patterns: listing pods across all namespaces, creating a ConfigMap resource, and watching for pod changes in real-time using a shared informer. It auto-detects whether it's running in-cluster or locally (via ~/.kube/config) and handles graceful shutdown on Ctrl+C.
package main
import (
"context"
"fmt"
"log"
"os"
"os/signal"
"path/filepath"
"time"
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/client-go/informers"
"k8s.io/client-go/kubernetes"
"k8s.io/client-go/rest"
"k8s.io/client-go/tools/cache"
"k8s.io/client-go/tools/clientcmd"
"k8s.io/client-go/util/homedir"
)
func main() {
clientset, err := buildClient()
if err != nil {
log.Fatalf("building client: %v", err)
}
// 1. List existing pods
fmt.Println("=== Current Pods ===")
pods, err := clientset.CoreV1().Pods("").List(
context.Background(), metav1.ListOptions{},
)
if err != nil {
log.Fatalf("listing pods: %v", err)
}
for _, pod := range pods.Items {
fmt.Printf(" %s/%s [%s]\n", pod.Namespace, pod.Name, pod.Status.Phase)
}
// 2. Create a ConfigMap
fmt.Println("\n=== Creating ConfigMap ===")
cm := &corev1.ConfigMap{
ObjectMeta: metav1.ObjectMeta{
Name: "devops-tool-config",
Namespace: "default",
},
Data: map[string]string{
"version": "1.0.0",
"log_level": "debug",
},
}
created, err := clientset.CoreV1().ConfigMaps("default").Create(
context.Background(), cm, metav1.CreateOptions{},
)
if err != nil {
log.Printf("create configmap: %v (may already exist)", err)
} else {
fmt.Printf(" Created: %s (uid: %s)\n", created.Name, created.UID)
}
// 3. Watch pods with informer
fmt.Println("\n=== Watching Pods (Ctrl+C to stop) ===")
ctx, cancel := signal.NotifyContext(context.Background(), os.Interrupt)
defer cancel()
factory := informers.NewSharedInformerFactory(clientset, 60*time.Second)
podInformer := factory.Core().V1().Pods().Informer()
podInformer.AddEventHandler(cache.ResourceEventHandlerFuncs{
AddFunc: func(obj interface{}) {
pod := obj.(*corev1.Pod)
fmt.Printf(" [+] %s/%s\n", pod.Namespace, pod.Name)
},
DeleteFunc: func(obj interface{}) {
pod := obj.(*corev1.Pod)
fmt.Printf(" [-] %s/%s\n", pod.Namespace, pod.Name)
},
})
factory.Start(ctx.Done())
factory.WaitForCacheSync(ctx.Done())
<-ctx.Done()
fmt.Println("\nShutting down.")
}
func buildClient() (*kubernetes.Clientset, error) {
config, err := rest.InClusterConfig()
if err != nil {
kubeconfig := filepath.Join(homedir.HomeDir(), ".kube", "config")
config, err = clientcmd.BuildConfigFromFlags("", kubeconfig)
if err != nil {
return nil, err
}
}
return kubernetes.NewForConfig(config)
}