mirror of
https://github.com/imjasonh/client-go2
synced 2026-07-18 06:37:34 +00:00
Add support for label and field selectors in informers
- Add InformOptions struct with ListOptions and ResyncPeriod fields - Update all client methods to accept options parameters - Add comprehensive examples in main.go demonstrating: - Label selector filtering (test=example) - Field selector filtering (status.phase=Running) - Custom resync periods - Add tests for label and field selectors with mock query parameter support - Fix nil pointer handling in all client methods - Update mock transport to handle query parameters correctly 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
parent
d2b0d467e5
commit
e5071fc0cf
6 changed files with 445 additions and 64 deletions
206
main.go
206
main.go
|
|
@ -3,6 +3,7 @@ package main
|
|||
import (
|
||||
"context"
|
||||
"log"
|
||||
"time"
|
||||
|
||||
"github.com/imjasonh/client-go2/generic"
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
|
|
@ -24,7 +25,7 @@ func main() {
|
|||
if err != nil {
|
||||
log.Fatal("creating pod client:", err)
|
||||
}
|
||||
pods, err := podClient.List(ctx, "kube-system")
|
||||
pods, err := podClient.List(ctx, "kube-system", nil)
|
||||
if err != nil {
|
||||
log.Fatal("listing pods:", err)
|
||||
}
|
||||
|
|
@ -39,55 +40,206 @@ func main() {
|
|||
log.Fatal("creating configmap client:", err)
|
||||
}
|
||||
|
||||
// Start an informer to log all adds/updates/deletes for ConfigMaps.
|
||||
// Example 1: Start an informer for ALL ConfigMaps (no selector)
|
||||
log.Println("Starting informer for ALL ConfigMaps...")
|
||||
cmc.Inform(ctx, generic.InformerHandler[*corev1.ConfigMap]{
|
||||
OnAdd: func(key string, obj *corev1.ConfigMap) {
|
||||
log.Printf("ConfigMap added: %s/%s", obj.Namespace, obj.Name)
|
||||
log.Printf("[ALL] ConfigMap added: %s/%s", obj.Namespace, obj.Name)
|
||||
},
|
||||
OnUpdate: func(key string, oldObj, newObj *corev1.ConfigMap) {
|
||||
log.Printf("ConfigMap updated: %s/%s (old: %s, new: %s)", oldObj.Namespace, oldObj.Name, oldObj.Data, newObj.Data)
|
||||
log.Printf("[ALL] ConfigMap updated: %s/%s", oldObj.Namespace, oldObj.Name)
|
||||
},
|
||||
OnDelete: func(key string, obj *corev1.ConfigMap) {
|
||||
log.Printf("ConfigMap deleted: %s/%s", obj.Namespace, obj.Name)
|
||||
log.Printf("[ALL] ConfigMap deleted: %s/%s", obj.Namespace, obj.Name)
|
||||
},
|
||||
OnError: func(obj any, err error) {
|
||||
log.Printf("Error in ConfigMap informer: %v (object: %v, type: %T)", err, obj, obj)
|
||||
log.Printf("[ALL] Error in ConfigMap informer: %v", err)
|
||||
},
|
||||
}, nil)
|
||||
|
||||
// Example 2: Start an informer with label selector
|
||||
log.Println("Starting informer for ConfigMaps with label test=example...")
|
||||
cmc.Inform(ctx, generic.InformerHandler[*corev1.ConfigMap]{
|
||||
OnAdd: func(key string, obj *corev1.ConfigMap) {
|
||||
log.Printf("[LABELED] ConfigMap added: %s/%s (labels: %v)", obj.Namespace, obj.Name, obj.Labels)
|
||||
},
|
||||
OnUpdate: func(key string, oldObj, newObj *corev1.ConfigMap) {
|
||||
log.Printf("[LABELED] ConfigMap updated: %s/%s", oldObj.Namespace, oldObj.Name)
|
||||
},
|
||||
OnDelete: func(key string, obj *corev1.ConfigMap) {
|
||||
log.Printf("[LABELED] ConfigMap deleted: %s/%s", obj.Namespace, obj.Name)
|
||||
},
|
||||
OnError: func(obj any, err error) {
|
||||
log.Printf("[LABELED] Error: %v", err)
|
||||
},
|
||||
}, &generic.InformOptions{
|
||||
ListOptions: metav1.ListOptions{
|
||||
LabelSelector: "test=example",
|
||||
},
|
||||
})
|
||||
|
||||
// Create a ConfigMap
|
||||
cm, err := cmc.Create(ctx, "kube-system", &corev1.ConfigMap{
|
||||
// Example 3: Start a Pod informer with field selector for running pods only
|
||||
log.Println("Starting informer for Running Pods only...")
|
||||
podClient.Inform(ctx, generic.InformerHandler[*corev1.Pod]{
|
||||
OnAdd: func(key string, obj *corev1.Pod) {
|
||||
log.Printf("[RUNNING] Pod added: %s/%s (phase: %s)", obj.Namespace, obj.Name, obj.Status.Phase)
|
||||
},
|
||||
OnUpdate: func(key string, oldObj, newObj *corev1.Pod) {
|
||||
if oldObj.Status.Phase != newObj.Status.Phase {
|
||||
log.Printf("[RUNNING] Pod phase changed: %s/%s (%s -> %s)",
|
||||
oldObj.Namespace, oldObj.Name, oldObj.Status.Phase, newObj.Status.Phase)
|
||||
}
|
||||
},
|
||||
OnDelete: func(key string, obj *corev1.Pod) {
|
||||
log.Printf("[RUNNING] Pod deleted: %s/%s", obj.Namespace, obj.Name)
|
||||
},
|
||||
OnError: func(obj any, err error) {
|
||||
log.Printf("[RUNNING] Error: %v", err)
|
||||
},
|
||||
}, &generic.InformOptions{
|
||||
ListOptions: metav1.ListOptions{
|
||||
FieldSelector: "status.phase=Running",
|
||||
},
|
||||
})
|
||||
|
||||
// Example 4: Informer with custom resync period
|
||||
resync := 30 * time.Second
|
||||
log.Printf("Starting informer with custom resync period of %v...\n", resync)
|
||||
cmc.Inform(ctx, generic.InformerHandler[*corev1.ConfigMap]{
|
||||
OnAdd: func(key string, obj *corev1.ConfigMap) {
|
||||
log.Printf("[RESYNC] ConfigMap added: %s/%s", obj.Namespace, obj.Name)
|
||||
},
|
||||
OnUpdate: func(key string, oldObj, newObj *corev1.ConfigMap) {
|
||||
log.Printf("[RESYNC] ConfigMap updated: %s/%s", oldObj.Namespace, oldObj.Name)
|
||||
},
|
||||
OnDelete: func(key string, obj *corev1.ConfigMap) {
|
||||
log.Printf("[RESYNC] ConfigMap deleted: %s/%s", obj.Namespace, obj.Name)
|
||||
},
|
||||
OnError: func(obj any, err error) {
|
||||
log.Printf("[RESYNC] Error: %v", err)
|
||||
},
|
||||
}, &generic.InformOptions{
|
||||
ListOptions: metav1.ListOptions{
|
||||
LabelSelector: "special=resync-test",
|
||||
},
|
||||
ResyncPeriod: &resync,
|
||||
})
|
||||
|
||||
// Wait a moment for informers to sync
|
||||
time.Sleep(2 * time.Second)
|
||||
|
||||
// Create ConfigMaps with different labels to demonstrate selectors
|
||||
log.Println("\nCREATING TEST RESOURCES...")
|
||||
|
||||
// Create a ConfigMap without labels (will only show in ALL informer)
|
||||
cm1, err := cmc.Create(ctx, "default", &corev1.ConfigMap{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
GenerateName: "foo-",
|
||||
GenerateName: "no-labels-",
|
||||
},
|
||||
Data: map[string]string{
|
||||
"hello": "world",
|
||||
},
|
||||
}, nil)
|
||||
if err != nil {
|
||||
log.Printf("Error creating cm1: %v", err)
|
||||
} else {
|
||||
log.Printf("Created ConfigMap without labels: %s", cm1.Name)
|
||||
}
|
||||
|
||||
// Create a ConfigMap with test=example label (will show in LABELED informer)
|
||||
cm2, err := cmc.Create(ctx, "default", &corev1.ConfigMap{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
GenerateName: "labeled-",
|
||||
Labels: map[string]string{
|
||||
"test": "example",
|
||||
},
|
||||
},
|
||||
Data: map[string]string{
|
||||
"hello": "labeled",
|
||||
},
|
||||
}, nil)
|
||||
if err != nil {
|
||||
log.Printf("Error creating cm2: %v", err)
|
||||
} else {
|
||||
log.Printf("Created ConfigMap with test=example label: %s", cm2.Name)
|
||||
}
|
||||
|
||||
// Create a ConfigMap with special=resync-test label (will show in RESYNC informer)
|
||||
cm3, err := cmc.Create(ctx, "default", &corev1.ConfigMap{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
GenerateName: "resync-test-",
|
||||
Labels: map[string]string{
|
||||
"special": "resync-test",
|
||||
},
|
||||
},
|
||||
Data: map[string]string{
|
||||
"hello": "resync",
|
||||
},
|
||||
}, nil)
|
||||
if err != nil {
|
||||
log.Printf("Error creating cm3: %v", err)
|
||||
} else {
|
||||
log.Printf("Created ConfigMap with special=resync-test label: %s", cm3.Name)
|
||||
}
|
||||
|
||||
// Wait for create events
|
||||
time.Sleep(2 * time.Second)
|
||||
|
||||
// Update the labeled ConfigMap
|
||||
if cm2 != nil {
|
||||
log.Printf("\nUPDATING CONFIGMAP %s", cm2.Name)
|
||||
cm2.Data["hello"] = "updated"
|
||||
_, err = cmc.Update(ctx, "default", cm2, nil)
|
||||
if err != nil {
|
||||
log.Printf("Error updating cm2: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
// Wait for update events
|
||||
time.Sleep(2 * time.Second)
|
||||
|
||||
// Clean up
|
||||
log.Println("\nCLEANING UP...")
|
||||
if cm1 != nil {
|
||||
if err := cmc.Delete(ctx, "default", cm1.Name, nil); err != nil {
|
||||
log.Printf("Error deleting cm1: %v", err)
|
||||
}
|
||||
}
|
||||
if cm2 != nil {
|
||||
if err := cmc.Delete(ctx, "default", cm2.Name, nil); err != nil {
|
||||
log.Printf("Error deleting cm2: %v", err)
|
||||
}
|
||||
}
|
||||
if cm3 != nil {
|
||||
if err := cmc.Delete(ctx, "default", cm3.Name, nil); err != nil {
|
||||
log.Printf("Error deleting cm3: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
// Wait for delete events
|
||||
time.Sleep(2 * time.Second)
|
||||
|
||||
// List ConfigMaps with label selector
|
||||
log.Println("\nLISTING CONFIGMAPS WITH LABEL test=example")
|
||||
labeledCMs, err := cmc.List(ctx, "", &metav1.ListOptions{
|
||||
LabelSelector: "test=example",
|
||||
})
|
||||
if err != nil {
|
||||
log.Fatal("creating configmap:", err)
|
||||
log.Printf("Error listing labeled configmaps: %v", err)
|
||||
} else {
|
||||
log.Printf("Found %d ConfigMaps with test=example label:", len(labeledCMs))
|
||||
for _, cm := range labeledCMs {
|
||||
log.Printf("- %s/%s", cm.Namespace, cm.Name)
|
||||
}
|
||||
}
|
||||
|
||||
// Update the ConfigMap
|
||||
cm.Data["hello"] = "universe" // Update the ConfigMap
|
||||
log.Println("UPDATING CONFIGMAP", cm.Name)
|
||||
_, err = cmc.Update(ctx, "kube-system", cm)
|
||||
if err != nil {
|
||||
log.Fatal("updating configmap:", err)
|
||||
}
|
||||
|
||||
// Delete the ConfigMap
|
||||
log.Println("DELETING CONFIGMAP", cm.Name)
|
||||
if err := cmc.Delete(ctx, "kube-system", cm.Name); err != nil {
|
||||
log.Fatal("deleting configmap:", err)
|
||||
}
|
||||
|
||||
// List ConfigMaps
|
||||
cms, err := cmc.List(ctx, "kube-system")
|
||||
// List all ConfigMaps in kube-system
|
||||
log.Println("\nLISTING ALL CONFIGMAPS IN kube-system")
|
||||
cms, err := cmc.List(ctx, "kube-system", nil)
|
||||
if err != nil {
|
||||
log.Fatal("listing configmaps:", err)
|
||||
}
|
||||
log.Println("LISTING CONFIGMAPS")
|
||||
log.Printf("Found %d ConfigMaps in kube-system:", len(cms))
|
||||
for _, cm := range cms {
|
||||
log.Println("-", cm.Name)
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue