DEV Community

Shannon
Shannon

Posted on

4

Writing events to pods using client-go

In the past, I've written controllers utilizing Kubebuilder, where the instantiation of an event recorder is basically done for you with a mgr.GetRecorder() method in the controller manager.

When writing a small binary with client-go, however, I found it surprisingly difficult. It took digging through kubernetes source code to find a couple examples, and I thought I'd document it here.


Scaffolding

Before running the code, let's spin up a pod in the default namespace kubectl run nginx --image=nginx --namespace default

We will be attaching sample events to this pod.

Code

Full code here since some of the packages are specifically named

package main

import (
    "context"
    "path/filepath"
    "time"

    log "github.com/sirupsen/logrus"
    corev1 "k8s.io/api/core/v1"
    v1 "k8s.io/api/core/v1"
    metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    "k8s.io/apimachinery/pkg/runtime"
    "k8s.io/client-go/kubernetes"
    typedv1core "k8s.io/client-go/kubernetes/typed/core/v1"
    "k8s.io/client-go/rest"
    "k8s.io/client-go/tools/clientcmd"
    "k8s.io/client-go/tools/record"
    "k8s.io/client-go/util/homedir"
)

func main() {
    kubeconfig := filepath.Join(homedir.HomeDir(), ".kube", "config")
    config, err := clientcmd.BuildConfigFromFlags("", kubeconfig)
    if err != nil {
        log.Info("connecting with config in-cluster")
        config, err = rest.InClusterConfig()
        if err != nil {
            log.Fatal(err)
        }
    }
    if err != nil {
        log.Fatal(err)
    }

    clientset, err := kubernetes.NewForConfig(config)
    if err != nil {
        log.Fatal(err)
    }

    pod, err := clientset.CoreV1().Pods("default").Get(context.TODO(), "nginx", metav1.GetOptions{})
    if err != nil {
        log.Fatal(err)
    }

    scheme := runtime.NewScheme()
    _ = corev1.AddToScheme(scheme)

    // all the good events stuff is here
    eventBroadcaster := record.NewBroadcaster()
    eventBroadcaster.StartStructuredLogging(4)
    eventBroadcaster.StartRecordingToSink(&typedv1core.EventSinkImpl{Interface: clientset.CoreV1().Events("")})
    eventRecorder := eventBroadcaster.NewRecorder(scheme, v1.EventSource{})
    eventRecorder.Event(pod, corev1.EventTypeNormal, "is this thing on", "is this thing on")
    eventBroadcaster.Shutdown()
    time.Sleep(2 * time.Second)
}

Enter fullscreen mode Exit fullscreen mode

Things worth noting:

  • You must add the corev1 scheme for the pod, as it is a corev1 object type. If you are using any other object type, make sure to add the appropriate scheme.
  • I added a time.Sleep() because of the asynchronous nature of this. Without some wait time, the process will exit too quickly and no events will populate.
  • eventRecorder.Eventf() is a lot more useful than my above example
  • If anyone understands the internal workings of k8s events better, please send some articles my way ;)

Seeing the events with kubectl

We can see it by querying events:

(⎈|kind-kind:default)learning/write-events-client-go » k get events -n default | grep "is this thing on"
71s         Normal   is this thing on   pod/nginx   is this thing on
Enter fullscreen mode Exit fullscreen mode

But more interestingly, we can also see them attached to the pod object itself:

(⎈|kind-kind:default)learning/write-events-client-go » k describe pod nginx | grep -A 10 Events
Events:
  Type    Reason            Age   From               Message
  ----    ------            ----  ----               -------
  Normal  Scheduled         37s   default-scheduler  Successfully assigned default/nginx to kind-control-plane
  Normal  Pulling           37s   kubelet            Pulling image "nginx"
  Normal  Pulled            36s   kubelet            Successfully pulled image "nginx" in 1.112899376s
  Normal  Created           36s   kubelet            Created container nginx
  Normal  Started           36s   kubelet            Started container nginx
  Normal  is this thing on  10s                      is this thing on
Enter fullscreen mode Exit fullscreen mode

Heroku

Built for developers, by developers.

Whether you're building a simple prototype or a business-critical product, Heroku's fully-managed platform gives you the simplest path to delivering apps quickly — using the tools and languages you already love!

Learn More

Top comments (1)

Collapse
 
asakalan profile image
Codehang

Great article, helped me solve my problem!

Quickstart image

Django MongoDB Backend Quickstart! A Step-by-Step Tutorial

Get up and running with the new Django MongoDB Backend Python library! This tutorial covers creating a Django application, connecting it to MongoDB Atlas, performing CRUD operations, and configuring the Django admin for MongoDB.

Watch full video →

👋 Kindness is contagious

Engage with a wealth of insights in this thoughtful article, cherished by the supportive DEV Community. Coders of every background are encouraged to bring their perspectives and bolster our collective wisdom.

A sincere “thank you” often brightens someone’s day—share yours in the comments below!

On DEV, the act of sharing knowledge eases our journey and forges stronger community ties. Found value in this? A quick thank-you to the author can make a world of difference.

Okay