...

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  	"k8s.io/klog/v2"
    33  	"sigs.k8s.io/controller-runtime/pkg/client"
    34  )
    35  
    36  type ChaosRecorder interface {
    37  	Event(object runtime.Object, ev ChaosEvent)
    38  }
    39  
    40  type chaosRecorder struct {
    41  	log    logr.Logger
    42  	source v1.EventSource
    43  	client client.Client
    44  	scheme *runtime.Scheme
    45  }
    46  
    47  func (r *chaosRecorder) Event(object runtime.Object, ev ChaosEvent) {
    48  	eventtype := ev.Type()
    49  	reason := ev.Reason()
    50  	message := ev.Message()
    51  
    52  	annotations, err := generateAnnotations(ev)
    53  	if err != nil {
    54  		r.log.Error(err, "failed to generate annotations for event", "event", ev)
    55  	}
    56  
    57  	ref, err := ref.GetReference(r.scheme, object)
    58  	if err != nil {
    59  		r.log.Error(err, "fail to construct reference", "object", object)
    60  		return
    61  	}
    62  
    63  	if !util.ValidateEventType(eventtype) {
    64  		klog.Errorf("Unsupported event type: '%v'", eventtype)
    65  		return
    66  	}
    67  
    68  	event := r.makeEvent(ref, annotations, eventtype, reason, message)
    69  	event.Source = r.source
    70  	go func() {
    71  		err := r.client.Create(context.TODO(), event)
    72  		if err != nil {
    73  			r.log.Error(err, "fail to submit event", "event", event)
    74  		}
    75  	}()
    76  }
    77  
    78  func (r *chaosRecorder) makeEvent(ref *v1.ObjectReference, annotations map[string]string, eventtype, reason, message string) *v1.Event {
    79  	t := metav1.Time{Time: time.Now()}
    80  	namespace := ref.Namespace
    81  	if namespace == "" {
    82  		namespace = metav1.NamespaceDefault
    83  	}
    84  	return &v1.Event{
    85  		ObjectMeta: metav1.ObjectMeta{
    86  			Name:        fmt.Sprintf("%v.%x", ref.Name, t.UnixNano()),
    87  			Namespace:   namespace,
    88  			Annotations: annotations,
    89  		},
    90  		InvolvedObject: *ref,
    91  		Reason:         reason,
    92  		Message:        message,
    93  		FirstTimestamp: t,
    94  		LastTimestamp:  t,
    95  		Count:          1,
    96  		Type:           eventtype,
    97  	}
    98  }
    99  
   100  type ChaosEvent interface {
   101  	Type() string
   102  	Reason() string
   103  	Message() string
   104  }
   105  
   106  var allEvents = make(map[string]ChaosEvent)
   107  
   108  func register(ev ...ChaosEvent) {
   109  	for _, ev := range ev {
   110  		val := reflect.ValueOf(ev)
   111  		val = reflect.Indirect(val)
   112  
   113  		allEvents[strcase.ToKebab(val.Type().Name())] = ev
   114  	}
   115  }
   116  
   117  type RecorderBuilder struct {
   118  	c      client.Client
   119  	logger logr.Logger
   120  	scheme *runtime.Scheme
   121  }
   122  
   123  func (b *RecorderBuilder) Build(name string) ChaosRecorder {
   124  	return &chaosRecorder{
   125  		log: b.logger.WithName("event-recorder-" + name),
   126  		source: v1.EventSource{
   127  			Component: name,
   128  		},
   129  		client: b.c,
   130  		scheme: b.scheme,
   131  	}
   132  }
   133  
   134  func NewRecorderBuilder(c client.Client, logger logr.Logger, scheme *runtime.Scheme) *RecorderBuilder {
   135  	return &RecorderBuilder{
   136  		c,
   137  		logger,
   138  		scheme,
   139  	}
   140  }
   141  
   142  type debugRecorder struct {
   143  	Events map[types.NamespacedName][]ChaosEvent
   144  }
   145  
   146  func (d *debugRecorder) Event(object runtime.Object, ev ChaosEvent) {
   147  	obj := object.(metav1.Object)
   148  	id := types.NamespacedName{
   149  		Namespace: obj.GetNamespace(),
   150  		Name:      obj.GetName(),
   151  	}
   152  
   153  	if d.Events[id] == nil {
   154  		d.Events[id] = []ChaosEvent{}
   155  	}
   156  
   157  	d.Events[id] = append(d.Events[id], ev)
   158  }
   159  
   160  func NewDebugRecorder() *debugRecorder {
   161  	return &debugRecorder{
   162  		Events: make(map[types.NamespacedName][]ChaosEvent),
   163  	}
   164  }
   165