Watch Resources¶
React to real-time Kubernetes resource changes.
Always Stop Watches
Use defer watcher.Stop() to clean up resources. Watches hold server connections that must be released.
Watch Interface¶
The Kubernetes watch interface provides a streaming channel of resource events. This is more efficient than polling for changes.
graph LR
Watch[Start Watch] --> Stream[Event Stream]
Stream --> Added[Added]
Stream --> Modified[Modified]
Stream --> Deleted[Deleted]
Added --> Handle[Handle Event]
Modified --> Handle
Deleted --> Handle
%% Ghostty Hardcore Theme
style Watch fill:#fd971e,color:#1b1d1e
style Stream fill:#65d9ef,color:#1b1d1e
style Added fill:#a7e22e,color:#1b1d1e
style Modified fill:#9e6ffe,color:#1b1d1e
style Deleted fill:#f92572,color:#1b1d1e
style Handle fill:#65d9ef,color:#1b1d1e
Basic Watch¶
package k8s
import (
"context"
appsv1 "k8s.io/api/apps/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/watch"
)
// WatchDeployments watches for deployment changes
func (c *Client) WatchDeployments(ctx context.Context) (watch.Interface, error) {
return c.Clientset.AppsV1().Deployments(c.Namespace).Watch(ctx, metav1.ListOptions{})
}
Event Handling¶
Always Stop Watches
Use defer watcher.Stop() to clean up resources. Watches hold server connections that must be released.
// Example usage
func watchExample(ctx context.Context, client *Client) error {
watcher, err := client.WatchDeployments(ctx)
if err != nil {
return err
}
defer watcher.Stop()
for event := range watcher.ResultChan() {
deployment := event.Object.(*appsv1.Deployment)
switch event.Type {
case watch.Added:
fmt.Printf("Deployment added: %s\n", deployment.Name)
case watch.Modified:
fmt.Printf("Deployment modified: %s\n", deployment.Name)
case watch.Deleted:
fmt.Printf("Deployment deleted: %s\n", deployment.Name)
}
}
return nil
}
Watch with Label Filter¶
// WatchDeploymentsWithLabel watches deployments matching a label selector
func (c *Client) WatchDeploymentsWithLabel(ctx context.Context, labelSelector string) (watch.Interface, error) {
return c.Clientset.AppsV1().Deployments(c.Namespace).Watch(ctx, metav1.ListOptions{
LabelSelector: labelSelector,
})
}
Wait for Condition¶
Use watches to wait for a specific condition:
// WaitForDeploymentReady waits until a deployment has all replicas ready
func (c *Client) WaitForDeploymentReady(ctx context.Context, name string, timeout time.Duration) error {
ctx, cancel := context.WithTimeout(ctx, timeout)
defer cancel()
watcher, err := c.Clientset.AppsV1().Deployments(c.Namespace).Watch(ctx, metav1.ListOptions{
FieldSelector: fmt.Sprintf("metadata.name=%s", name),
})
if err != nil {
return err
}
defer watcher.Stop()
for event := range watcher.ResultChan() {
if event.Type == watch.Error {
return fmt.Errorf("watch error")
}
deployment := event.Object.(*appsv1.Deployment)
if deployment.Status.ReadyReplicas == *deployment.Spec.Replicas {
return nil
}
}
return fmt.Errorf("timeout waiting for deployment %s", name)
}
Best Practices¶
| Practice | Description |
|---|---|
| Use context cancellation | Pass context for clean shutdown |
| Always defer Stop() | Release watch connections |
| Handle reconnection | Watches can disconnect; implement retry logic |
| Use field selectors | Filter by name when watching single resources |
| Set timeouts | Prevent indefinite waits |
Use watches for real-time updates instead of polling.