...

Source file src/github.com/chaos-mesh/chaos-mesh/controllers/utils/recorder/recorder.go

Documentation: github.com/chaos-mesh/chaos-mesh/controllers/utils/recorder

     1  // Copyright 2021 Chaos Mesh Authors.
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  // http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    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