Reacting to Network Events
In the previous section, we set up the SharedInformerFactory
and registered placeholder event handler functions (onAddPod
, onUpdatePod
, onDeletePod
, etc.). Now, we need to implement the actual logic within these functions to react when Pods or NetworkPolicies are created, updated, or deleted in the cluster.
These handlers are the core of our monitoring application. They receive the relevant Kubernetes object involved in the event and allow us to perform actions based on the change.
Key Tasks in Event Handlers:
Type Assertion: The objects passed to the handlers (
obj
,oldObj
,newObj
) are of typeinterface{}
. The first step is always to cast (perform a type assertion) the object to the expected Kubernetes resource type pointer (e.g.,*v1.Pod
,*networkingv1.NetworkPolicy
). Always check theok
value returned by the type assertion to ensure the cast was successful.Handling Deletion Tombstones: The
DeleteFunc
might sometimes receive a special object calledcache.DeletedFinalStateUnknown
. This happens if the informer's watch connection dropped and it missed the actualDELETE
event, but later reconciled its cache and realized the object is gone. This "tombstone" object contains the last known state of the deleted object. You need code to handle this possibility and extract the actual deleted object from the tombstone.Extracting Information: Once you have the correctly typed object, you can access its fields (
metadata.name
,metadata.namespace
,status.podIP
,spec
, etc.) to get the information you need.Processing Logic: Perform your desired action based on the event. This could be logging, updating metrics, triggering alerts, modifying other Kubernetes resources (carefully!), or adding work items to a queue for more complex processing (common in controllers).
Implementing the Handlers:
Let's update our previous main.go
file by filling in the logic for our placeholder handler functions. We'll focus on logging the events and accessing basic information.
Key Changes and Concepts:
Import API Types: We now import
k8s.io/api/core/v1
andk8s.io/api/networking/v1
to work with the specific struct types.Type Assertions: Each handler now performs
pod, ok := obj.(*corev1.Pod)
or the equivalent forNetworkPolicy
. It logs an error if the assertion fails.Tombstone Handling:
onDeletePod
andonDeleteNetPol
include the standard pattern for checking ifobj
is acache.DeletedFinalStateUnknown
and extracting the actual object (tombstone.Obj
) if it is.Accessing Data: We access fields like
pod.Name
,pod.Namespace
,pod.Status.PodIP
,pod.Status.Phase
,netpol.Name
,netpol.Namespace
, etc., for logging.Update Noise Reduction: The
onUpdatePod
handler includes a basic check to see if relevant fields (like Phase or IP) or theResourceVersion
have changed before logging. This helps reduce noise from periodic resyncs or minor status updates that might trigger the UpdateFunc without a significant change. You might implement more sophisticated checks based on your specific needs (e.g., deep comparison of specs).Listers: We obtained the
podLister
andnetworkPolicyLister
and stored them globally (this is a simple approach; dependency injection is better for larger apps). While not heavily used in these basic logging handlers, a comment inonAddPod
shows how you could usepodLister.Get(pod.Spec.NodeName)
(or similar) to efficiently fetch related objects from the cache after sync.
Important Considerations for Handler Logic:
Keep Handlers Fast: Event handlers should execute quickly and avoid blocking operations (like complex computations or long network calls). The informer framework uses a limited number of goroutines to process events; blocking handlers can delay the processing of subsequent events.
Offload Work: For complex tasks triggered by an event, the recommended pattern (especially in controllers) is for the event handler to simply add a work item (e.g., the object's key
namespace/name
) to a rate-limited work queue. Separate worker goroutines pull items from the queue and perform the actual processing, using the Lister to get the current state of the object(s).Use Listers for Reads: When a handler needs information about the current state of the triggering object or related objects, always prefer using the Lister (
podLister.Get()
,podLister.List()
) over making directclientset.Get()
calls. Listers read from the synchronized local cache and avoid hitting the API server.
Last updated
Was this helpful?