Skip to content

Commit

Permalink
[RFC-0008] Custom Event Metadata from Annotations
Browse files Browse the repository at this point in the history
Signed-off-by: Matheus Pimenta <[email protected]>
  • Loading branch information
matheuscscp committed Dec 24, 2024
1 parent a8278c8 commit 862534d
Show file tree
Hide file tree
Showing 3 changed files with 94 additions and 36 deletions.
3 changes: 3 additions & 0 deletions apis/event/v1beta1/event.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,9 @@ import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)

// Group is the API Group for the Event API.
const Group = "event.toolkit.fluxcd.io"

// These constants define valid event severity values.
const (
// EventSeverityTrace represents a trace event, usually
Expand Down
14 changes: 13 additions & 1 deletion runtime/events/recorder.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,11 @@ import (
"encoding/json"
"errors"
"fmt"
"maps"
"net/http"
"net/url"
"os"
"strings"
"time"

"github.com/go-logr/logr"
Expand Down Expand Up @@ -145,7 +147,7 @@ func (r *Recorder) Eventf(object runtime.Object, eventtype, reason, messageFmt s
// It also logs the event if debug logs are enabled in the logger.
func (r *Recorder) AnnotatedEventf(
object runtime.Object,
annotations map[string]string,
inputAnnotations map[string]string,
eventtype, reason string,
messageFmt string, args ...interface{}) {

Expand All @@ -154,6 +156,16 @@ func (r *Recorder) AnnotatedEventf(
r.Log.Error(err, "failed to get object reference")
}

// Add object annotations to the annotations.
annotations := maps.Clone(inputAnnotations)
if annotatedObject, ok := object.(interface{ GetAnnotations() map[string]string }); ok {
for k, v := range annotatedObject.GetAnnotations() {
if strings.HasPrefix(k, eventv1.Group+"/") {
annotations[k] = v
}
}
}

// Add object info in the logger.
log := r.Log.WithValues("name", ref.Name, "namespace", ref.Namespace, "reconciler kind", ref.Kind)

Expand Down
113 changes: 78 additions & 35 deletions runtime/events/recorder_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,48 +25,91 @@ import (

"github.com/stretchr/testify/require"
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
ctrl "sigs.k8s.io/controller-runtime"

eventv1 "github.com/fluxcd/pkg/apis/event/v1beta1"
)

func TestEventRecorder_AnnotatedEventf(t *testing.T) {
requestCount := 0
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
requestCount++
b, err := io.ReadAll(r.Body)
require.NoError(t, err)

var payload eventv1.Event
err = json.Unmarshal(b, &payload)
require.NoError(t, err)

require.Equal(t, "ConfigMap", payload.InvolvedObject.Kind)
require.Equal(t, "webapp", payload.InvolvedObject.Name)
require.Equal(t, "gitops-system", payload.InvolvedObject.Namespace)
require.Equal(t, "true", payload.Metadata["test"])
require.Equal(t, "sync", payload.Reason)

}))
defer ts.Close()

eventRecorder, err := NewRecorder(env, ctrl.Log, ts.URL, "test-controller")
require.NoError(t, err)

obj := &corev1.ConfigMap{}
obj.Namespace = "gitops-system"
obj.Name = "webapp"

meta := map[string]string{
"test": "true",
for _, tt := range []struct {
name string
object runtime.Object
expectedMetadata map[string]string
}{
{
name: "event with ConfigMap",
object: &corev1.ConfigMap{
ObjectMeta: metav1.ObjectMeta{
Name: "webapp",
Namespace: "gitops-system",
Annotations: map[string]string{
"event.toolkit.fluxcd.io/deploymentID": "e076e315-5a48-41c3-81c8-8d8bdee7d74d",
"event.toolkit.fluxcd.io/image": "ghcr.io/stefanprodan/podinfo:6.5.0",
},
},
},
expectedMetadata: map[string]string{
"test": "true",
"event.toolkit.fluxcd.io/deploymentID": "e076e315-5a48-41c3-81c8-8d8bdee7d74d",
"event.toolkit.fluxcd.io/image": "ghcr.io/stefanprodan/podinfo:6.5.0",
},
},
{
name: "event with ObjectReference for ConfigMap (does not panic with runtime.Object without annotations)",
object: &corev1.ObjectReference{
Name: "webapp",
Namespace: "gitops-system",
Kind: "ConfigMap",
},
expectedMetadata: map[string]string{
"test": "true",
},
},
} {
t.Run(tt.name, func(t *testing.T) {
requestCount := 0
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
requestCount++
b, err := io.ReadAll(r.Body)
require.NoError(t, err)

var payload eventv1.Event
err = json.Unmarshal(b, &payload)
require.NoError(t, err)

require.Equal(t, "ConfigMap", payload.InvolvedObject.Kind)
require.Equal(t, "webapp", payload.InvolvedObject.Name)
require.Equal(t, "gitops-system", payload.InvolvedObject.Namespace)
require.Equal(t, "sync", payload.Reason)
require.Equal(t, "sync object", payload.Message)

for k, v := range tt.expectedMetadata {
require.Equal(t, v, payload.Metadata[k])
}
}))
defer ts.Close()

eventRecorder, err := NewRecorder(env, ctrl.Log, ts.URL, "test-controller")
require.NoError(t, err)

obj := tt.object

meta := map[string]string{
"test": "true",
}

const msg = "sync object"

eventRecorder.AnnotatedEventf(obj, meta, corev1.EventTypeNormal, "sync", "%s", msg)
require.Equal(t, 2, requestCount)

// When a trace event is sent, it's dropped, no new request.
eventRecorder.AnnotatedEventf(obj, meta, eventv1.EventTypeTrace, "sync", "%s", msg)
require.Equal(t, 2, requestCount)
})
}

eventRecorder.AnnotatedEventf(obj, meta, corev1.EventTypeNormal, "sync", "sync %s", obj.Name)
require.Equal(t, 2, requestCount)

// When a trace event is sent, it's dropped, no new request.
eventRecorder.AnnotatedEventf(obj, meta, eventv1.EventTypeTrace, "sync", "sync %s", obj.Name)
require.Equal(t, 2, requestCount)
}

func TestEventRecorder_AnnotatedEventf_Retry(t *testing.T) {
Expand Down

0 comments on commit 862534d

Please sign in to comment.