|
1 | 1 | # Creating Events
|
2 | 2 |
|
3 |
| -It is often useful to publish *Event* objects from the controller Reconcile function. Events |
4 |
| -allow users to see what is going on with a particular object, and allow automated processes |
5 |
| -to see and respond to them. |
| 3 | +It is often useful to publish *Event* objects from the controller Reconcile function. |
| 4 | + |
| 5 | +Events allow users to see what is going on with a particular object, and allow automated processes to see and respond to them. |
6 | 6 |
|
7 | 7 | {% panel style="success", title="Getting Events" %}
|
8 |
| -Recent Events for an object may be viewed by running `kubectl describe` |
| 8 | +Recent Events for an object can be viewed by running `kubectl describe` |
9 | 9 | {% endpanel %}
|
10 | 10 |
|
11 | 11 | {% method %}
|
12 | 12 |
|
13 | 13 | Events are published from a Controller using an [EventRecorder](https://github.com/kubernetes/client-go/blob/master/tools/record/event.go#L56),
|
14 | 14 | which can be created for a Controller by calling `GetRecorder(name string)` on a Manager.
|
15 | 15 |
|
| 16 | +`Name` should be identifiable and descriptive as it will appear in the `From` column of `kubectl describe` command. |
| 17 | + |
| 18 | +{% sample lang="go" %} |
| 19 | +```go |
| 20 | +// Annotation for generating RBAC role for writing Events |
| 21 | +// +kubebuilder:rbac:groups="",resources=events,verbs=create;patch |
| 22 | +``` |
| 23 | + |
| 24 | +```go |
| 25 | +// ReconcileContainerSet reconciles a ContainerSet object |
| 26 | +type ReconcileContainerSet struct { |
| 27 | + client.Client |
| 28 | + scheme *runtime.Scheme |
| 29 | + recorder record.EventRecorder |
| 30 | +} |
| 31 | +``` |
| 32 | +```go |
| 33 | +// newReconciler returns a new reconcile.Reconciler |
| 34 | +func newReconciler(mgr manager.Manager) reconcile.Reconciler { |
| 35 | + return &ReconcileContainerSet{ |
| 36 | + Client: mgr.GetClient(), |
| 37 | + scheme: mgr.GetScheme(), |
| 38 | + recorder: mgr.GetRecorder("containerset-controller"), |
| 39 | + } |
| 40 | +} |
| 41 | +``` |
| 42 | +{% endmethod %} |
| 43 | + |
| 44 | +{% method %} |
| 45 | + |
| 46 | +## Writing Events |
| 47 | + |
| 48 | +Anatomy of an Event: |
| 49 | + |
16 | 50 | ```go
|
17 | 51 | Event(object runtime.Object, eventtype, reason, message string)
|
18 | 52 | ```
|
19 | 53 |
|
20 | 54 | - `object` is the object this event is about.
|
21 | 55 | - `eventtype` is the type of this event, and is either *Normal* or *Warning*.
|
22 |
| -- `reason` is the reason this event is generated. It should be short and unique with |
23 |
| - `UpperCamelCase` format. The value could appear in *switch* statements by automation. |
| 56 | +- `reason` is the reason this event is generated. It should be short and unique with `UpperCamelCase` format. The value could appear in *switch* statements by automation. |
24 | 57 | - `message` is intended to be consumed by humans.
|
25 | 58 |
|
| 59 | + |
| 60 | +Building on the example introduced in [Controller Example](../basics/simple_controller.md), we can add Events to our reconcile logic using `recorder` as our `EventRecorder` |
| 61 | + |
| 62 | +{% sample lang="go" %} |
| 63 | +```go |
| 64 | +var _ reconcile.Reconciler = &ContainerSetController{} |
| 65 | + |
| 66 | +func (r *ReconcileContainerSet) Reconcile(request reconcile.Request) (reconcile.Result, error) { |
| 67 | + instance := &workloadsv1beta1.ContainerSet{} |
| 68 | + err := r.Get(context.TODO(), request.NamespacedName, instance) |
| 69 | + if err != nil { |
| 70 | + if errors.IsNotFound(err) { |
| 71 | + // Object not found, return. Created objects are automatically garbage collected. |
| 72 | + // For additional cleanup logic use finalizers. |
| 73 | + return reconcile.Result{}, nil |
| 74 | + } |
| 75 | + // Error reading the object - requeue the request. |
| 76 | + return reconcile.Result{}, err |
| 77 | + } |
| 78 | + |
| 79 | + // TODO(user): Change this to be the object type created by your controller |
| 80 | + // Define the desired Deployment object |
| 81 | + deploy := &appsv1.Deployment{ |
| 82 | + ObjectMeta: metav1.ObjectMeta{ |
| 83 | + Name: instance.Name + "-deployment", |
| 84 | + Namespace: instance.Namespace, |
| 85 | + }, |
| 86 | + Spec: appsv1.DeploymentSpec{ |
| 87 | + Selector: &metav1.LabelSelector{ |
| 88 | + MatchLabels: map[string]string{"deployment": instance.Name + "-deployment"}, |
| 89 | + }, |
| 90 | + Replicas: &instance.Spec.Replicas, |
| 91 | + Template: corev1.PodTemplateSpec{ |
| 92 | + ObjectMeta: metav1.ObjectMeta{Labels: map[string]string{"deployment": instance.Name + "-deployment"}}, |
| 93 | + Spec: corev1.PodSpec{ |
| 94 | + Containers: []corev1.Container{ |
| 95 | + { |
| 96 | + Name: instance.Name, |
| 97 | + Image: instance.Spec.Image, |
| 98 | + }, |
| 99 | + }, |
| 100 | + }, |
| 101 | + }, |
| 102 | + }, |
| 103 | + } |
| 104 | + |
| 105 | + if err := controllerutil.SetControllerReference(instance, deploy, r.scheme); err != nil { |
| 106 | + return reconcile.Result{}, err |
| 107 | + } |
| 108 | + |
| 109 | + // TODO(user): Change this for the object type created by your controller |
| 110 | + // Check if the Deployment already exists |
| 111 | + found := &appsv1.Deployment{} |
| 112 | + err = r.Get(context.TODO(), types.NamespacedName{Name: deploy.Name, Namespace: deploy.Namespace}, found) |
| 113 | + if err != nil && errors.IsNotFound(err) { |
| 114 | + log.Printf("Creating Deployment %s/%s\n", deploy.Namespace, deploy.Name) |
| 115 | + err = r.Create(context.TODO(), deploy) |
| 116 | + if err != nil { |
| 117 | + return reconcile.Result{}, err |
| 118 | + } |
| 119 | + // Write an event to the ContainerSet instance with the namespace and name of the |
| 120 | + // created deployment |
| 121 | + r.recorder.Event(instance, "Normal", "Created", fmt.Sprintf("Created deployment %s/%s", deploy.Namespace, deploy.Name)) |
| 122 | + } else if err != nil { |
| 123 | + return reconcile.Result{}, err |
| 124 | + } |
| 125 | + |
| 126 | + // TODO(user): Change this for the object type created by your controller |
| 127 | + // Update the found object and write the result back if there are any changes |
| 128 | + if !reflect.DeepEqual(deploy.Spec, found.Spec) { |
| 129 | + found.Spec = deploy.Spec |
| 130 | + log.Printf("Updating Deployment %s/%s\n", deploy.Namespace, deploy.Name) |
| 131 | + err = r.Update(context.TODO(), found) |
| 132 | + if err != nil { |
| 133 | + return reconcile.Result{}, err |
| 134 | + } |
| 135 | + // Write an event to the ContainerSet instance with the namespace and name of the |
| 136 | + // updated deployment |
| 137 | + r.recorder.Event(instance, "Normal", "Updated", fmt.Sprintf("Updated deployment %s/%s", deploy.Namespace, deploy.Name)) |
| 138 | + } |
| 139 | + return reconcile.Result{}, nil |
| 140 | +} |
| 141 | +``` |
| 142 | + |
26 | 143 | {% endmethod %}
|
0 commit comments