1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 package recorder
17
18 import (
19 "context"
20 "fmt"
21 "reflect"
22 "time"
23
24 "github.com/go-logr/logr"
25 "github.com/iancoleman/strcase"
26 v1 "k8s.io/api/core/v1"
27 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
28 "k8s.io/apimachinery/pkg/runtime"
29 "k8s.io/apimachinery/pkg/types"
30 "k8s.io/client-go/tools/record/util"
31 ref "k8s.io/client-go/tools/reference"
32 "sigs.k8s.io/controller-runtime/pkg/client"
33
34 "github.com/chaos-mesh/chaos-mesh/pkg/metrics"
35 )
36
37 type ChaosRecorder interface {
38 Event(object runtime.Object, ev ChaosEvent)
39 }
40
41 type chaosRecorder struct {
42 log logr.Logger
43 source v1.EventSource
44 client client.Client
45 scheme *runtime.Scheme
46 metricsCollector *metrics.ChaosControllerManagerMetricsCollector
47 }
48
49 func (r *chaosRecorder) Event(object runtime.Object, ev ChaosEvent) {
50 eventtype := ev.Type()
51 reason := ev.Reason()
52 message := ev.Message()
53
54 annotations, err := generateAnnotations(ev)
55 if err != nil {
56 r.log.Error(err, "failed to generate annotations for event", "event", ev)
57 }
58
59 ref, err := ref.GetReference(r.scheme, object)
60 if err != nil {
61 r.log.Error(err, "fail to construct reference", "object", object)
62 return
63 }
64
65 if !util.ValidateEventType(eventtype) {
66 r.log.Error(fmt.Errorf("unsupported event type:'%v'", eventtype), "eventtype", eventtype)
67 return
68 }
69
70 event := r.makeEvent(ref, annotations, eventtype, reason, message)
71 event.Source = r.source
72 go func() {
73 err := r.client.Create(context.TODO(), event)
74 if err != nil {
75 r.log.Error(err, "fail to submit event", "event", event)
76 } else {
77 r.metricsCollector.EmittedEvents.WithLabelValues(event.Type, event.Reason, event.Namespace).Inc()
78 }
79 }()
80 }
81
82 func (r *chaosRecorder) makeEvent(ref *v1.ObjectReference, annotations map[string]string, eventtype, reason, message string) *v1.Event {
83 t := metav1.Time{Time: time.Now()}
84 namespace := ref.Namespace
85 if namespace == "" {
86 namespace = metav1.NamespaceDefault
87 }
88 return &v1.Event{
89 ObjectMeta: metav1.ObjectMeta{
90 Name: fmt.Sprintf("%v.%x", ref.Name, t.UnixNano()),
91 Namespace: namespace,
92 Annotations: annotations,
93 },
94 InvolvedObject: *ref,
95 Reason: reason,
96 Message: message,
97 FirstTimestamp: t,
98 LastTimestamp: t,
99 Count: 1,
100 Type: eventtype,
101 }
102 }
103
104 type ChaosEvent interface {
105 Type() string
106 Reason() string
107 Message() string
108 }
109
110 var allEvents = make(map[string]ChaosEvent)
111
112 func register(ev ...ChaosEvent) {
113 for _, ev := range ev {
114 val := reflect.ValueOf(ev)
115 val = reflect.Indirect(val)
116
117 allEvents[strcase.ToKebab(val.Type().Name())] = ev
118 }
119 }
120
121 type RecorderBuilder struct {
122 c client.Client
123 logger logr.Logger
124 scheme *runtime.Scheme
125 metricsCollector *metrics.ChaosControllerManagerMetricsCollector
126 }
127
128 func (b *RecorderBuilder) Build(name string) ChaosRecorder {
129 return &chaosRecorder{
130 log: b.logger.WithName("event-recorder-" + name),
131 source: v1.EventSource{
132 Component: name,
133 },
134 client: b.c,
135 scheme: b.scheme,
136 metricsCollector: b.metricsCollector,
137 }
138 }
139
140 func NewRecorderBuilder(c client.Client, logger logr.Logger, scheme *runtime.Scheme, metricsCollector *metrics.ChaosControllerManagerMetricsCollector) *RecorderBuilder {
141 return &RecorderBuilder{
142 c,
143 logger,
144 scheme,
145 metricsCollector,
146 }
147 }
148
149 type debugRecorder struct {
150 Events map[types.NamespacedName][]ChaosEvent
151 }
152
153 func (d *debugRecorder) Event(object runtime.Object, ev ChaosEvent) {
154 obj := object.(metav1.Object)
155 id := types.NamespacedName{
156 Namespace: obj.GetNamespace(),
157 Name: obj.GetName(),
158 }
159
160 if d.Events[id] == nil {
161 d.Events[id] = []ChaosEvent{}
162 }
163
164 d.Events[id] = append(d.Events[id], ev)
165 }
166
167 func NewDebugRecorder() *debugRecorder {
168 return &debugRecorder{
169 Events: make(map[types.NamespacedName][]ChaosEvent),
170 }
171 }
172