1
0
Fork 0
mirror of https://github.com/imjasonh/client-go2 synced 2026-07-07 00:33:49 +00:00
No description
Find a file
Jason Hall 245a7bff43
Merge pull request #14 from imjasonh/dependabot/github_actions/actions/checkout-6
chore(deps): bump actions/checkout from 5 to 6
2025-12-08 23:53:34 -05:00
.github Merge pull request #14 from imjasonh/dependabot/github_actions/actions/checkout-6 2025-12-08 23:53:34 -05:00
controller Remove obsolete +build directives from e2e test files 2025-12-09 04:46:20 +00:00
examples Fix controller example to implement correct interface method (#6) 2025-07-28 19:57:12 +00:00
generic Remove obsolete +build directives from e2e test files 2025-12-09 04:46:20 +00:00
.gitignore Update to use stable Go and add comprehensive CI/CD 2025-07-23 11:41:20 -04:00
CLAUDE.md Add expansion methods and missing client-go operations (#3) 2025-07-28 14:35:31 +00:00
go.mod chore(deps): bump the k8s-dependencies group with 3 updates 2025-12-08 10:46:10 +00:00
go.sum chore(deps): bump the k8s-dependencies group with 3 updates 2025-12-08 10:46:10 +00:00
LICENSE Create LICENSE 2022-09-21 15:00:38 -04:00
main.go Add lister functionality and improve controller framework 2025-07-28 15:13:22 -04:00
Makefile Add lister functionality and improve controller framework 2025-07-28 15:13:22 -04:00
README.md Add CRD support to generic client 2025-12-07 02:00:56 -05:00

Experimenting with k8s.io/client-go and Go generics

Build

This is an experimental type-parameter-aware client that wraps k8s.io/client-go/rest, in surprisingly little code.

Features

  • Type-safe generic client - Work with strongly-typed Kubernetes objects instead of unstructured.Unstructured
  • Zero code generation - Uses Go generics instead of code generation
  • Full CRUD operations - List, Get, Create, Update, Delete, Patch, Watch, DeleteCollection, and UpdateStatus support
  • Informer support - Watch for changes with type-safe event handlers
  • Automatic GVR inference - No need to manually specify GroupVersionResource for standard Kubernetes types
  • Expansion methods - Resource-specific operations like Pod.GetLogs() and Service.ProxyGet()
  • Support for CRDs
  • Label/Field selectors - Filter resources using Kubernetes selectors
  • SubResource access - Generic method to access any subresource
  • Generic Controller Framework - Build Kubernetes controllers with automatic update detection and conflict resolution

Usage

See the example for comprehensive usage examples.

Quick Examples

Basic CRUD Operations

// Create a client with automatic GVR inference
client, err := generic.NewClient[*corev1.Pod](config)

// List pods with label selector
pods, err := client.List(ctx, "default", &metav1.ListOptions{
    LabelSelector: "app=nginx",
})

// Get a specific pod
pod, err := client.Get(ctx, "default", "my-pod", nil)

Expansion Methods for Pods (client-go compatible)

// Start with a generic client for pods
client, err := generic.NewClient[*corev1.Pod](config)

// Get a namespace-scoped PodClient that implements typedcorev1.PodExpansion
podClient := client.PodClient("default")  // Will panic if T is not *corev1.Pod

// Get pod logs (matches client-go API)
req := podClient.GetLogs("my-pod", &corev1.PodLogOptions{
    Container: "nginx",
    TailLines: &tailLines,
})
logs, err := req.DoRaw(ctx)

// Bind pod to node
err = podClient.Bind(ctx, binding, metav1.CreateOptions{})

// Evict pod
err = podClient.Evict(ctx, eviction)

// For cluster-scoped operations, use the generic client directly
pods, err := client.List(ctx, "default", nil)

Expansion Methods for Services (client-go compatible)

// Start with a generic client for services
client, err := generic.NewClient[*corev1.Service](config)

// Get a namespace-scoped ServiceClient that implements typedcorev1.ServiceExpansion
serviceClient := client.ServiceClient("default")  // Will panic if T is not *corev1.Service

// Use proxy to access service endpoints
req := serviceClient.ProxyGet("http", "my-service", "80", "api/health", nil)
resp, err := req.DoRaw(ctx)

Generic SubResource Access

// Access any subresource using the generic method
req := client.SubResource("default", "my-pod", "status")
status, err := req.DoRaw(ctx)

Watch Resources

// Watch for pod changes with label selector
watcher, err := client.Watch(ctx, "default", &metav1.ListOptions{
    LabelSelector: "app=nginx",
})

defer watcher.Stop()

for event := range watcher.ResultChan() {
    pod := event.Object.(*corev1.Pod)
    fmt.Printf("Event: %s Pod: %s\n", event.Type, pod.Name)
}

Delete Collection

// Delete all pods with specific label
err = client.DeleteCollection(ctx, "default", nil, &metav1.ListOptions{
    LabelSelector: "app=test",
})

// Delete all pods in namespace
err = client.DeleteCollection(ctx, "default", nil, nil)

Update Status

// Update only the status subresource
pod.Status.Phase = corev1.PodRunning
pod.Status.Conditions = append(pod.Status.Conditions, corev1.PodCondition{
    Type:   corev1.PodReady,
    Status: corev1.ConditionTrue,
})

updated, err := client.UpdateStatus(ctx, "default", pod, nil)

Controller Framework

The controller package provides a simple framework for building Kubernetes controllers:

// Create a reconciler
reconciler := controller.ReconcilerFunc[*corev1.ConfigMap](
    func(ctx context.Context, cm *corev1.ConfigMap) error {
        // Modify the ConfigMap - changes are automatically detected and persisted
        cm.Annotations["processed"] = "true"
        cm.Labels["controller"] = "my-controller"
        return nil
    })

// Create and run controller
client, _ := generic.NewClient[*corev1.ConfigMap](config)
ctrl := controller.New(client, reconciler, &controller.Options[*corev1.ConfigMap]{
    Namespace:   "default",
    Concurrency: 5,
})
ctrl.Run(ctx)

See the controller documentation and example controller for more details.

Testing

Unit tests against a mock REST client:

make test

End-to-end tests against a real K8s cluster:

make e2e

⚠️ THIS IS AN EXPERIMENT

This is just for demo purposes.

The name client-go2 is a placeholder, and a joke.