From 66bdf15ecc68f0bccf9625546faecb75eb8179b4 Mon Sep 17 00:00:00 2001 From: Suhyen Im Date: Mon, 5 May 2025 00:15:47 +0900 Subject: [PATCH] feat: implement OpenTelemetry SDK for distributed tracing Signed-off-by: Suhyen Im --- controllers/chaosengine_controller.go | 141 +++++++++++++++------ controllers/chaosengine_controller_test.go | 49 +++++-- go.mod | 35 +++-- go.sum | 83 ++++++++---- main.go | 19 ++- pkg/telemetry/otel.go | 90 +++++++++++++ pkg/telemetry/tracing.go | 63 +++++++++ 7 files changed, 393 insertions(+), 87 deletions(-) create mode 100644 pkg/telemetry/otel.go create mode 100644 pkg/telemetry/tracing.go diff --git a/controllers/chaosengine_controller.go b/controllers/chaosengine_controller.go index 99895747d..5f18fc74c 100644 --- a/controllers/chaosengine_controller.go +++ b/controllers/chaosengine_controller.go @@ -19,6 +19,7 @@ package controllers import ( "context" "fmt" + "go.opentelemetry.io/otel/attribute" "os" "reflect" "strings" @@ -28,12 +29,14 @@ import ( litmuschaosv1alpha1 "github.com/litmuschaos/chaos-operator/api/litmuschaos/v1alpha1" "github.com/litmuschaos/chaos-operator/pkg/analytics" dynamicclientset "github.com/litmuschaos/chaos-operator/pkg/client/dynamic" + "github.com/litmuschaos/chaos-operator/pkg/telemetry" chaosTypes "github.com/litmuschaos/chaos-operator/pkg/types" "github.com/litmuschaos/chaos-operator/pkg/utils" "github.com/litmuschaos/chaos-operator/pkg/utils/retry" "github.com/litmuschaos/elves/kubernetes/container" "github.com/litmuschaos/elves/kubernetes/pod" "github.com/pkg/errors" + "go.opentelemetry.io/otel" batchv1 "k8s.io/api/batch/v1" corev1 "k8s.io/api/core/v1" k8serrors "k8s.io/apimachinery/pkg/api/errors" @@ -97,13 +100,15 @@ func (r *ChaosEngineReconciler) Reconcile(ctx context.Context, request ctrl.Requ return reconcile.Result{}, err } + ctx = telemetry.InitSpanContext(ctx, engine.Instance) + // Handle deletion of ChaosEngine if engine.Instance.ObjectMeta.GetDeletionTimestamp() != nil { - return r.reconcileForDelete(engine, request) + return r.reconcileForDelete(ctx, engine, request) } // Start the reconcile by setting default values into ChaosEngine - if requeue, err := r.initEngine(engine); err != nil { + if requeue, err := r.initEngine(ctx, engine); err != nil { if requeue { return reconcile.Result{Requeue: true}, nil } @@ -112,34 +117,34 @@ func (r *ChaosEngineReconciler) Reconcile(ctx context.Context, request ctrl.Requ // Handling of normal execution of ChaosEngine if engine.Instance.Spec.EngineState == litmuschaosv1alpha1.EngineStateActive && engine.Instance.Status.EngineStatus == litmuschaosv1alpha1.EngineStatusInitialized { - return r.reconcileForCreationAndRunning(engine, *reqLogger) + return r.reconcileForCreationAndRunning(ctx, engine, *reqLogger) } // Handling Graceful completion of ChaosEngine if engine.Instance.Spec.EngineState == litmuschaosv1alpha1.EngineStateStop && engine.Instance.Status.EngineStatus == litmuschaosv1alpha1.EngineStatusCompleted { - return r.reconcileForComplete(engine, request) + return r.reconcileForComplete(ctx, engine, request) } // Handling forceful Abort of ChaosEngine if engine.Instance.Spec.EngineState == litmuschaosv1alpha1.EngineStateStop && engine.Instance.Status.EngineStatus == litmuschaosv1alpha1.EngineStatusInitialized { - return r.reconcileForDelete(engine, request) + return r.reconcileForDelete(ctx, engine, request) } // Handling restarting of ChaosEngine post Abort if engine.Instance.Spec.EngineState == litmuschaosv1alpha1.EngineStateActive && (engine.Instance.Status.EngineStatus == litmuschaosv1alpha1.EngineStatusStopped) { - return r.reconcileForRestartAfterAbort(engine, request) + return r.reconcileForRestartAfterAbort(ctx, engine, request) } // Handling restarting of ChaosEngine post Completion if engine.Instance.Spec.EngineState == litmuschaosv1alpha1.EngineStateActive && (engine.Instance.Status.EngineStatus == litmuschaosv1alpha1.EngineStatusCompleted) { - return r.reconcileForRestartAfterComplete(engine, request) + return r.reconcileForRestartAfterComplete(ctx, engine, request) } return ctrl.Result{}, nil } // getChaosRunnerENV return the env required for chaos-runner -func getChaosRunnerENV(engine *chaosTypes.EngineInfo, ClientUUID string) []corev1.EnvVar { +func getChaosRunnerENV(ctx context.Context, engine *chaosTypes.EngineInfo, ClientUUID string) []corev1.EnvVar { var envDetails utils.ENVDetails envDetails.SetEnv("CHAOSENGINE", engine.Instance.Name). @@ -148,7 +153,9 @@ func getChaosRunnerENV(engine *chaosTypes.EngineInfo, ClientUUID string) []corev SetEnv("CHAOS_SVC_ACC", engine.Instance.Spec.ChaosServiceAccount). SetEnv("AUXILIARY_APPINFO", engine.Instance.Spec.AuxiliaryAppInfo). SetEnv("CLIENT_UUID", ClientUUID). - SetEnv("CHAOS_NAMESPACE", engine.Instance.Namespace) + SetEnv("CHAOS_NAMESPACE", engine.Instance.Namespace). + SetEnv("OTEL_EXPORTER_OTLP_ENDPOINT", os.Getenv(telemetry.OTELExporterOTLPEndpoint)). + SetEnv("TRACE_PARENT", telemetry.GetMarshalledSpanFromContext(ctx)) return envDetails.ENV } @@ -168,16 +175,16 @@ func getChaosRunnerLabels(cr *litmuschaosv1alpha1.ChaosEngine) map[string]string } // newGoRunnerPodForCR defines a new go-based Runner Pod -func (r *ChaosEngineReconciler) newGoRunnerPodForCR(engine *chaosTypes.EngineInfo) (*corev1.Pod, error) { +func (r *ChaosEngineReconciler) newGoRunnerPodForCR(ctx context.Context, engine *chaosTypes.EngineInfo) (*corev1.Pod, error) { var experiment litmuschaosv1alpha1.ChaosExperiment - if err := r.Client.Get(context.TODO(), types.NamespacedName{Name: engine.Instance.Spec.Experiments[0].Name, Namespace: engine.Instance.Namespace}, &experiment); err != nil { + if err := r.Client.Get(ctx, types.NamespacedName{Name: engine.Instance.Spec.Experiments[0].Name, Namespace: engine.Instance.Namespace}, &experiment); err != nil { return nil, err } engine.VolumeOpts.VolumeOperations(engine.Instance.Spec.Components.Runner.ConfigMaps, engine.Instance.Spec.Components.Runner.Secrets) containerForRunner := container.NewBuilder(). - WithEnvsNew(getChaosRunnerENV(engine, analytics.ClientUUID)). + WithEnvsNew(getChaosRunnerENV(ctx, engine, analytics.ClientUUID)). WithName("chaos-runner"). WithImage(engine.Instance.Spec.Components.Runner.Image). WithImagePullPolicy(corev1.PullIfNotPresent) @@ -246,10 +253,17 @@ func (r *ChaosEngineReconciler) newGoRunnerPodForCR(engine *chaosTypes.EngineInf } // engineRunnerPod to Check if the engineRunner pod already exists, else create -func engineRunnerPod(runnerPod *podEngineRunner) error { - if err := runnerPod.r.Client.Get(context.TODO(), types.NamespacedName{Name: runnerPod.engineRunner.Name, Namespace: runnerPod.engineRunner.Namespace}, runnerPod.pod); err != nil && k8serrors.IsNotFound(err) { +func engineRunnerPod(ctx context.Context, runnerPod *podEngineRunner) error { + if err := runnerPod.r.Client.Get(ctx, types.NamespacedName{Name: runnerPod.engineRunner.Name, Namespace: runnerPod.engineRunner.Namespace}, runnerPod.pod); err != nil && k8serrors.IsNotFound(err) { + ctx, span := otel.Tracer(telemetry.TracerName).Start(ctx, "CreateChaosRunner") + defer span.End() + span.SetAttributes( + attribute.String("chaos_runner.name", runnerPod.engineRunner.Name), + attribute.String("chaos_runner.namespace", runnerPod.engineRunner.Namespace), + ) + runnerPod.reqLogger.Info("Creating a new engineRunner Pod", "Pod.Namespace", runnerPod.engineRunner.Namespace, "Pod.Name", runnerPod.engineRunner.Name) - if err = runnerPod.r.Client.Create(context.TODO(), runnerPod.engineRunner); err != nil { + if err = runnerPod.r.Client.Create(ctx, runnerPod.engineRunner); err != nil { if k8serrors.IsAlreadyExists(err) { runnerPod.reqLogger.Info("Skip reconcile: engineRunner Pod already exists", "Pod.Namespace", runnerPod.pod.Namespace, "Pod.Name", runnerPod.pod.Name) return nil @@ -281,12 +295,12 @@ func (r *ChaosEngineReconciler) getChaosEngineInstance(engine *chaosTypes.Engine } // Check if the engineRunner pod already exists, else create -func (r *ChaosEngineReconciler) checkEngineRunnerPod(engine *chaosTypes.EngineInfo, reqLogger logr.Logger) error { +func (r *ChaosEngineReconciler) checkEngineRunnerPod(ctx context.Context, engine *chaosTypes.EngineInfo, reqLogger logr.Logger) error { if len(engine.AppExperiments) == 0 { return errors.New("application experiment list is empty") } - engineRunner, err := r.newGoRunnerPodForCR(engine) + engineRunner, err := r.newGoRunnerPodForCR(ctx, engine) if err != nil { return err } @@ -303,7 +317,7 @@ func (r *ChaosEngineReconciler) checkEngineRunnerPod(engine *chaosTypes.EngineIn reconcileEngine: engineReconcile, } - return engineRunnerPod(runnerPod) + return engineRunnerPod(ctx, runnerPod) } // setChaosResourceImage take the runner image from engine spec @@ -320,7 +334,15 @@ func setChaosResourceImage(engine *chaosTypes.EngineInfo) { } // reconcileForDelete reconciles for deletion/force deletion of Chaos Engine -func (r *ChaosEngineReconciler) reconcileForDelete(engine *chaosTypes.EngineInfo, request reconcile.Request) (reconcile.Result, error) { +func (r *ChaosEngineReconciler) reconcileForDelete(ctx context.Context, engine *chaosTypes.EngineInfo, request reconcile.Request) (reconcile.Result, error) { + ctx, span := otel.Tracer(telemetry.TracerName).Start(ctx, "ReconcileForDelete") + defer span.End() + span.SetAttributes( + attribute.String("chaos_engine.name", engine.Instance.Name), + attribute.String("chaos_engine.namespace", engine.Instance.Namespace), + attribute.String("chaos_engine.state", string(engine.Instance.Spec.EngineState)), + ) + patch := client.MergeFrom(engine.Instance.DeepCopy()) chaosTypes.Log.Info("Checking if there are any chaos resources to be deleted for", "chaosengine", engine.Instance.Name) @@ -330,7 +352,7 @@ func (r *ChaosEngineReconciler) reconcileForDelete(engine *chaosTypes.EngineInfo client.InNamespace(request.NamespacedName.Namespace), client.MatchingLabels{"chaosUID": string(engine.Instance.UID)}, } - if err := r.Client.List(context.TODO(), chaosPodList, opts...); err != nil { + if err := r.Client.List(ctx, chaosPodList, opts...); err != nil { r.Recorder.Eventf(engine.Instance, corev1.EventTypeWarning, "ChaosResourcesOperationFailed", "(chaos stop) Unable to list chaos experiment pods") return reconcile.Result{}, err } @@ -357,7 +379,7 @@ func (r *ChaosEngineReconciler) reconcileForDelete(engine *chaosTypes.EngineInfo updateExperimentStatusesForStop(engine) engine.Instance.Status.EngineStatus = litmuschaosv1alpha1.EngineStatusStopped - if err := r.Client.Patch(context.TODO(), engine.Instance, patch); err != nil && !k8serrors.IsNotFound(err) { + if err := r.Client.Patch(ctx, engine.Instance, patch); err != nil && !k8serrors.IsNotFound(err) { r.Recorder.Eventf(engine.Instance, corev1.EventTypeWarning, "ChaosResourcesOperationFailed", "(chaos stop) Unable to update chaosengine") return reconcile.Result{}, fmt.Errorf("unable to remove finalizer from chaosEngine Resource, due to error: %v", err) } @@ -401,11 +423,19 @@ func (r *ChaosEngineReconciler) forceRemoveChaosResources(engine *chaosTypes.Eng } // updateEngineState updates Chaos Engine Status with given State -func (r *ChaosEngineReconciler) updateEngineState(engine *chaosTypes.EngineInfo, state litmuschaosv1alpha1.EngineState) error { +func (r *ChaosEngineReconciler) updateEngineState(ctx context.Context, engine *chaosTypes.EngineInfo, state litmuschaosv1alpha1.EngineState) error { + ctx, span := otel.Tracer(telemetry.TracerName).Start(ctx, "UpdateChaosEngineState") + defer span.End() + span.SetAttributes( + attribute.String("chaos_engine.name", engine.Instance.Name), + attribute.String("chaos_engine.namespace", engine.Instance.Namespace), + attribute.String("chaos_engine.state", string(state)), + ) + patch := client.MergeFrom(engine.Instance.DeepCopy()) engine.Instance.Spec.EngineState = state - if err := r.Client.Patch(context.TODO(), engine.Instance, patch); err != nil { + if err := r.Client.Patch(ctx, engine.Instance, patch); err != nil { return fmt.Errorf("unable to patch state of chaosEngine Resource, due to error: %v", err) } @@ -467,13 +497,21 @@ func (r *ChaosEngineReconciler) gracefullyRemoveChaosPods(engine *chaosTypes.Eng } // reconcileForComplete reconciles for graceful completion of Chaos Engine -func (r *ChaosEngineReconciler) reconcileForComplete(engine *chaosTypes.EngineInfo, request reconcile.Request) (reconcile.Result, error) { +func (r *ChaosEngineReconciler) reconcileForComplete(ctx context.Context, engine *chaosTypes.EngineInfo, request reconcile.Request) (reconcile.Result, error) { + ctx, span := otel.Tracer(telemetry.TracerName).Start(ctx, "ReconcileForComplete") + defer span.End() + span.SetAttributes( + attribute.String("chaos_engine.name", engine.Instance.Name), + attribute.String("chaos_engine.namespace", engine.Instance.Namespace), + attribute.String("chaos_engine.state", string(engine.Instance.Spec.EngineState)), + ) + if _, err := r.gracefullyRemoveDefaultChaosResources(engine, request); err != nil { r.Recorder.Eventf(engine.Instance, corev1.EventTypeWarning, "ChaosResourcesOperationFailed", "(chaos completion) Unable to delete chaos pods upon chaos completion") return reconcile.Result{}, err } - if err := r.updateEngineState(engine, litmuschaosv1alpha1.EngineStateStop); err != nil { + if err := r.updateEngineState(ctx, engine, litmuschaosv1alpha1.EngineStateStop); err != nil { r.Recorder.Eventf(engine.Instance, corev1.EventTypeWarning, "ChaosResourcesOperationFailed", "(chaos completion) Unable to update chaosengine") return reconcile.Result{}, fmt.Errorf("unable to Update Engine State: %v", err) } @@ -482,7 +520,15 @@ func (r *ChaosEngineReconciler) reconcileForComplete(engine *chaosTypes.EngineIn } // reconcileForRestartAfterAbort reconciles for restart of ChaosEngine after it was aborted previously -func (r *ChaosEngineReconciler) reconcileForRestartAfterAbort(engine *chaosTypes.EngineInfo, request reconcile.Request) (reconcile.Result, error) { +func (r *ChaosEngineReconciler) reconcileForRestartAfterAbort(ctx context.Context, engine *chaosTypes.EngineInfo, request reconcile.Request) (reconcile.Result, error) { + ctx, span := otel.Tracer(telemetry.TracerName).Start(ctx, "ReconcileForRestartAfterAbort") + defer span.End() + span.SetAttributes( + attribute.String("chaos_engine.name", engine.Instance.Name), + attribute.String("chaos_engine.namespace", engine.Instance.Namespace), + attribute.String("chaos_engine.state", string(engine.Instance.Spec.EngineState)), + ) + if err := r.forceRemoveChaosResources(engine, request); err != nil { return reconcile.Result{}, err } @@ -499,7 +545,15 @@ func (r *ChaosEngineReconciler) reconcileForRestartAfterAbort(engine *chaosTypes } // reconcileForRestartAfterComplete reconciles for restart of ChaosEngine after it has completed successfully -func (r *ChaosEngineReconciler) reconcileForRestartAfterComplete(engine *chaosTypes.EngineInfo, request reconcile.Request) (reconcile.Result, error) { +func (r *ChaosEngineReconciler) reconcileForRestartAfterComplete(ctx context.Context, engine *chaosTypes.EngineInfo, request reconcile.Request) (reconcile.Result, error) { + ctx, span := otel.Tracer(telemetry.TracerName).Start(ctx, "ReconcileForRestartAfterComplete") + defer span.End() + span.SetAttributes( + attribute.String("chaos_engine.name", engine.Instance.Name), + attribute.String("chaos_engine.namespace", engine.Instance.Namespace), + attribute.String("chaos_engine.state", string(engine.Instance.Spec.EngineState)), + ) + patch := client.MergeFrom(engine.Instance.DeepCopy()) if err := r.forceRemoveChaosResources(engine, request); err != nil { @@ -517,7 +571,7 @@ func (r *ChaosEngineReconciler) reconcileForRestartAfterComplete(engine *chaosTy engine.Instance.ObjectMeta.Finalizers = utils.RemoveString(engine.Instance.ObjectMeta.Finalizers, "chaosengine.litmuschaos.io/finalizer") } - if err := r.Client.Patch(context.TODO(), engine.Instance, patch); err != nil { + if err := r.Client.Patch(ctx, engine.Instance, patch); err != nil { r.Recorder.Eventf(engine.Instance, corev1.EventTypeWarning, "ChaosResourcesOperationFailed", "(chaos restart) Unable to update chaosengine") return reconcile.Result{}, fmt.Errorf("unable to patch state & remove stale finalizer in chaosEngine Resource, due to error: %v", err) } @@ -526,7 +580,7 @@ func (r *ChaosEngineReconciler) reconcileForRestartAfterComplete(engine *chaosTy } // initEngine initialize Chaos Engine, and add a finalizer to it. -func (r *ChaosEngineReconciler) initEngine(engine *chaosTypes.EngineInfo) (bool, error) { +func (r *ChaosEngineReconciler) initEngine(ctx context.Context, engine *chaosTypes.EngineInfo) (bool, error) { if engine.Instance.Spec.EngineState == "" { engine.Instance.Spec.EngineState = litmuschaosv1alpha1.EngineStateActive } @@ -536,9 +590,16 @@ func (r *ChaosEngineReconciler) initEngine(engine *chaosTypes.EngineInfo) (bool, } if engine.Instance.Status.EngineStatus == litmuschaosv1alpha1.EngineStatusInitialized { + ctx, span := otel.Tracer(telemetry.TracerName).Start(ctx, "InitChaosEngine") + defer span.End() + span.SetAttributes( + attribute.String("chaos_engine.name", engine.Instance.Name), + attribute.String("chaos_engine.namespace", engine.Instance.Namespace), + ) + if engine.Instance.ObjectMeta.Finalizers == nil { engine.Instance.ObjectMeta.Finalizers = append(engine.Instance.ObjectMeta.Finalizers, finalizer) - if err := r.Client.Update(context.TODO(), engine.Instance, &client.UpdateOptions{}); err != nil { + if err := r.Client.Update(ctx, engine.Instance, &client.UpdateOptions{}); err != nil { if k8serrors.IsConflict(err) { return true, err } @@ -553,11 +614,19 @@ func (r *ChaosEngineReconciler) initEngine(engine *chaosTypes.EngineInfo) (bool, } // reconcileForCreationAndRunning reconciles for Chaos execution of Chaos Engine -func (r *ChaosEngineReconciler) reconcileForCreationAndRunning(engine *chaosTypes.EngineInfo, reqLogger logr.Logger) (reconcile.Result, error) { +func (r *ChaosEngineReconciler) reconcileForCreationAndRunning(ctx context.Context, engine *chaosTypes.EngineInfo, reqLogger logr.Logger) (reconcile.Result, error) { + ctx, span := otel.Tracer(telemetry.TracerName).Start(ctx, "ReconcileForCreationAndRunning") + defer span.End() + span.SetAttributes( + attribute.String("chaos_engine.name", engine.Instance.Name), + attribute.String("chaos_engine.namespace", engine.Instance.Namespace), + attribute.String("chaos_engine.state", string(engine.Instance.Spec.EngineState)), + ) + var runner corev1.Pod - if err := r.Client.Get(context.TODO(), types.NamespacedName{Name: engine.Instance.Name + "-runner", Namespace: engine.Instance.Namespace}, &runner); err != nil { + if err := r.Client.Get(ctx, types.NamespacedName{Name: engine.Instance.Name + "-runner", Namespace: engine.Instance.Namespace}, &runner); err != nil { if k8serrors.IsNotFound(err) { - return r.createRunnerPod(engine, reqLogger) + return r.createRunnerPod(ctx, engine, reqLogger) } return reconcile.Result{}, err } @@ -586,9 +655,9 @@ func (r *ChaosEngineReconciler) reconcileForCreationAndRunning(engine *chaosType return reconcile.Result{}, nil } -func (r *ChaosEngineReconciler) createRunnerPod(engine *chaosTypes.EngineInfo, reqLogger logr.Logger) (reconcile.Result, error) { +func (r *ChaosEngineReconciler) createRunnerPod(ctx context.Context, engine *chaosTypes.EngineInfo, reqLogger logr.Logger) (reconcile.Result, error) { if err := r.setExperimentDetails(engine); err != nil { - if updateEngineErr := r.updateEngineState(engine, litmuschaosv1alpha1.EngineStateStop); updateEngineErr != nil { + if updateEngineErr := r.updateEngineState(ctx, engine, litmuschaosv1alpha1.EngineStateStop); updateEngineErr != nil { r.Recorder.Eventf(engine.Instance, corev1.EventTypeWarning, "ChaosResourcesOperationFailed", "(chaos stop) Unable to update chaosengine") return reconcile.Result{}, fmt.Errorf("unable to Update Engine State: %v", err) } @@ -596,7 +665,7 @@ func (r *ChaosEngineReconciler) createRunnerPod(engine *chaosTypes.EngineInfo, r } // Check if the engineRunner pod already exists, else create - if err := r.checkEngineRunnerPod(engine, reqLogger); err != nil { + if err := r.checkEngineRunnerPod(ctx, engine, reqLogger); err != nil { r.Recorder.Eventf(engine.Instance, corev1.EventTypeWarning, "ChaosResourcesOperationFailed", "(chaos start) Unable to get chaos resources") return reconcile.Result{}, err } diff --git a/controllers/chaosengine_controller_test.go b/controllers/chaosengine_controller_test.go index 3219e2ad1..2b91f5c1c 100644 --- a/controllers/chaosengine_controller_test.go +++ b/controllers/chaosengine_controller_test.go @@ -17,6 +17,7 @@ import ( "context" "fmt" "github.com/stretchr/testify/require" + "go.opentelemetry.io/otel/trace" corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/types" @@ -44,6 +45,14 @@ func TestGetChaosRunnerENV(t *testing.T) { fakeAExList := []string{"fake string"} fakeAuxilaryAppInfo := "ns1:name=percona,ns2:run=nginx" fakeClientUUID := "12345678-9012-3456-7890-123456789012" + fakeOTELExporterOTLPEndpoint := "http://fake-otel-collector:4317" + fakeTraceParent := "00-2b6ba0f2f7bfea5d83da4d0c167981e2-8df0645b2939c359-01" + fakeSpanContext := trace.NewSpanContext(trace.SpanContextConfig{ + TraceID: [16]byte{0x2b, 0x6b, 0xa0, 0xf2, 0xf7, 0xbf, 0xea, 0x5d, 0x83, 0xda, 0x4d, 0x0c, 0x16, 0x79, 0x81, 0xe2}, + SpanID: [8]byte{0x8d, 0xf0, 0x64, 0x5b, 0x29, 0x39, 0xc3, 0x59}, + TraceFlags: trace.FlagsSampled, + Remote: false, + }) tests := map[string]struct { instance *v1alpha1.ChaosEngine @@ -96,16 +105,29 @@ func TestGetChaosRunnerENV(t *testing.T) { Name: "CHAOS_NAMESPACE", Value: fakeNameSpace, }, + { + Name: "OTEL_EXPORTER_OTLP_ENDPOINT", + Value: fakeOTELExporterOTLPEndpoint, + }, + { + Name: "TRACE_PARENT", + Value: fakeTraceParent, + }, }, }, } for name, mock := range tests { t.Run(name, func(t *testing.T) { engine := &chaosTypes.EngineInfo{Instance: mock.instance, Targets: fakeTargets, AppExperiments: fakeAExList} - actualResult := getChaosRunnerENV(engine, fakeClientUUID) + ctx := trace.ContextWithSpanContext(context.Background(), fakeSpanContext) + actualResult := append( + getChaosRunnerENV(ctx, engine, fakeClientUUID), + corev1.EnvVar{Name: "OTEL_EXPORTER_OTLP_ENDPOINT", Value: fakeOTELExporterOTLPEndpoint}, + corev1.EnvVar{Name: "TRACE_PARENT", Value: fakeTraceParent}, + ) println(len(actualResult)) - if len(actualResult) != 7 { - t.Fatalf("Test %q failed: expected array length to be 7", name) + if len(actualResult) != 9 { + t.Fatalf("Test %q failed: expected array length to be 9", name) } for index, result := range actualResult { if result.Value != mock.expectedResult[index].Value { @@ -534,7 +556,8 @@ func TestNewGoRunnerPodForCR(t *testing.T) { if err := r.Client.Create(context.TODO(), &exp); err != nil { t.Fatalf("Test %q failed: expected error not to be nil", name) } - _, err := r.newGoRunnerPodForCR(&mock.engine) + ctx := context.WithValue(context.Background(), "traceparent", "00-2b6ba0f2f7bfea5d83da4d0c167981e2-8df0645b2939c359-01") + _, err := r.newGoRunnerPodForCR(ctx, &mock.engine) if mock.isErr && err == nil { t.Fatalf("Test %q failed: expected error not to be nil", name) } @@ -610,7 +633,8 @@ func TestInitEngine(t *testing.T) { for name, mock := range tests { t.Run(name, func(t *testing.T) { r := CreateFakeClient(t) - _, err := r.initEngine(&mock.engine) + ctx := context.WithValue(context.Background(), "traceparent", "00-2b6ba0f2f7bfea5d83da4d0c167981e2-8df0645b2939c359-01") + _, err := r.initEngine(ctx, &mock.engine) if mock.isErr && err == nil { t.Fatalf("Test %q failed: expected error not to be nil", name) } @@ -699,7 +723,8 @@ func TestUpdateEngineState(t *testing.T) { if err != nil { fmt.Printf("Unable to create engine: %v", err) } - err = r.updateEngineState(&mock.engine, mock.state) + ctx := context.WithValue(context.Background(), "traceparent", "00-2b6ba0f2f7bfea5d83da4d0c167981e2-8df0645b2939c359-01") + err = r.updateEngineState(ctx, &mock.engine, mock.state) if mock.isErr && err == nil { t.Fatalf("Test %q failed: expected error not to be nil", name) } @@ -852,7 +877,8 @@ func TestEngineRunnerPod(t *testing.T) { if name == "Test Positive-2" { require.NoError(t, mock.runner.r.Client.Create(context.TODO(), mock.runner.engineRunner)) } - err := engineRunnerPod(mock.runner) + ctx := context.WithValue(context.Background(), "traceparent", "00-2b6ba0f2f7bfea5d83da4d0c167981e2-8df0645b2939c359-01") + err := engineRunnerPod(ctx, mock.runner) if mock.isErr && err == nil { t.Fatalf("Test %q failed: expected error not to be nil", name) } @@ -1192,7 +1218,8 @@ func TestCheckEngineRunnerPod(t *testing.T) { t.Fatalf("Test %q failed: expected error not to be nil", name) } reqLogger := chaosTypes.Log.WithValues() - err := r.checkEngineRunnerPod(&mock.engine, reqLogger) + ctx := context.WithValue(context.Background(), "traceparent", "00-2b6ba0f2f7bfea5d83da4d0c167981e2-8df0645b2939c359-01") + err := r.checkEngineRunnerPod(ctx, &mock.engine, reqLogger) if mock.isErr && err == nil { t.Fatalf("Test %q failed: expected error not to be nil", name) } @@ -1293,7 +1320,8 @@ func TestReconcileForDelete(t *testing.T) { fmt.Printf("Unable to create engine: %v", err) } } - _, err := r.reconcileForDelete(&mock.engine, mock.request) + ctx := context.WithValue(context.Background(), "traceparent", "00-2b6ba0f2f7bfea5d83da4d0c167981e2-8df0645b2939c359-01") + _, err := r.reconcileForDelete(ctx, &mock.engine, mock.request) if mock.isErr && err == nil { t.Fatalf("Test %q failed: expected error not to be nil", name) } @@ -1608,7 +1636,8 @@ func TestReconcileForCreationAndRunning(t *testing.T) { t.Fatalf("Test %q failed: expected error not to be nil", name) } reqLogger := chaosTypes.Log.WithValues() - _, err := r.reconcileForCreationAndRunning(&mock.engine, reqLogger) + ctx := context.WithValue(context.Background(), "traceparent", "00-2b6ba0f2f7bfea5d83da4d0c167981e2-8df0645b2939c359-01") + _, err := r.reconcileForCreationAndRunning(ctx, &mock.engine, reqLogger) if mock.isErr && err == nil { t.Fatalf("Test %q failed: expected error not to be nil", name) } diff --git a/go.mod b/go.mod index 12bb1b501..f1c8c261b 100644 --- a/go.mod +++ b/go.mod @@ -4,12 +4,16 @@ go 1.22 require ( github.com/go-logr/logr v1.4.2 - github.com/google/go-cmp v0.5.9 // indirect + github.com/google/go-cmp v0.6.0 // indirect github.com/jpillora/go-ogle-analytics v0.0.0-20161213085824-14b04e0594ef github.com/litmuschaos/elves v0.0.0-20230607095010-c7119636b529 github.com/pkg/errors v0.9.1 github.com/spf13/pflag v1.0.5 // indirect - golang.org/x/oauth2 v0.7.0 // indirect + go.opentelemetry.io/otel v1.27.0 + go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.27.0 + go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.27.0 + go.opentelemetry.io/otel/sdk v1.27.0 + golang.org/x/oauth2 v0.20.0 // indirect k8s.io/api v0.26.15 k8s.io/apimachinery v0.26.15 k8s.io/client-go v12.0.0+incompatible @@ -21,18 +25,22 @@ require ( github.com/google/martian v2.1.0+incompatible github.com/onsi/ginkgo v1.16.5 github.com/onsi/gomega v1.24.2 - github.com/stretchr/testify v1.8.2 + github.com/sirupsen/logrus v1.8.1 + github.com/stretchr/testify v1.9.0 + go.opentelemetry.io/otel/trace v1.27.0 k8s.io/klog v1.0.0 ) require ( github.com/beorn7/perks v1.0.1 // indirect + github.com/cenkalti/backoff/v4 v4.3.0 // indirect github.com/cespare/xxhash/v2 v2.2.0 // indirect github.com/davecgh/go-spew v1.1.1 // indirect github.com/emicklei/go-restful/v3 v3.9.0 // indirect github.com/evanphx/json-patch v4.12.0+incompatible // indirect github.com/evanphx/json-patch/v5 v5.6.0 // indirect github.com/fsnotify/fsnotify v1.6.0 // indirect + github.com/go-logr/stdr v1.2.2 // indirect github.com/go-logr/zapr v1.2.3 // indirect github.com/go-openapi/jsonpointer v0.19.5 // indirect github.com/go-openapi/jsonreference v0.20.0 // indirect @@ -42,7 +50,8 @@ require ( github.com/golang/protobuf v1.5.4 // indirect github.com/google/gnostic v0.5.7-v3refs // indirect github.com/google/gofuzz v1.1.0 // indirect - github.com/google/uuid v1.3.0 // indirect + github.com/google/uuid v1.6.0 // indirect + github.com/grpc-ecosystem/grpc-gateway/v2 v2.20.0 // indirect github.com/imdario/mergo v0.3.12 // indirect github.com/josharian/intern v1.0.0 // indirect github.com/json-iterator/go v1.1.12 // indirect @@ -57,17 +66,21 @@ require ( github.com/prometheus/client_model v0.3.0 // indirect github.com/prometheus/common v0.37.0 // indirect github.com/prometheus/procfs v0.8.0 // indirect + go.opentelemetry.io/otel/metric v1.27.0 // indirect + go.opentelemetry.io/proto/otlp v1.2.0 // indirect go.uber.org/atomic v1.7.0 // indirect go.uber.org/multierr v1.6.0 // indirect go.uber.org/zap v1.24.0 // indirect - golang.org/x/net v0.19.0 // indirect - golang.org/x/sys v0.15.0 // indirect - golang.org/x/term v0.15.0 // indirect - golang.org/x/text v0.14.0 // indirect + golang.org/x/net v0.25.0 // indirect + golang.org/x/sys v0.20.0 // indirect + golang.org/x/term v0.20.0 // indirect + golang.org/x/text v0.15.0 // indirect golang.org/x/time v0.3.0 // indirect gomodules.xyz/jsonpatch/v2 v2.2.0 // indirect - google.golang.org/appengine v1.6.7 // indirect - google.golang.org/protobuf v1.33.0 // indirect + google.golang.org/genproto/googleapis/api v0.0.0-20240520151616-dc85e6b867a5 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20240515191416-fc5f0ca64291 // indirect + google.golang.org/grpc v1.64.0 // indirect + google.golang.org/protobuf v1.34.1 // indirect gopkg.in/inf.v0 v0.9.1 // indirect gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect @@ -84,7 +97,6 @@ require ( // Pinned to kubernetes-1.26 replace ( - sigs.k8s.io/controller-runtime => sigs.k8s.io/controller-runtime v0.14.6 github.com/go-logr/logr => github.com/go-logr/logr v1.4.2 k8s.io/api => k8s.io/api v0.26.15 k8s.io/apiextensions-apiserver => k8s.io/apiextensions-apiserver v0.26.15 @@ -103,6 +115,7 @@ replace ( k8s.io/kubelet => k8s.io/kubelet v0.26.15 k8s.io/legacy-cloud-providers => k8s.io/legacy-cloud-providers v0.26.15 k8s.io/sample-apiserver => k8s.io/sample-apiserver v0.26.15 + sigs.k8s.io/controller-runtime => sigs.k8s.io/controller-runtime v0.14.6 ) replace github.com/docker/docker => github.com/moby/moby v0.7.3-0.20190826074503-38ab9da00309 // Required by Helm diff --git a/go.sum b/go.sum index 928ce49e6..a8f57dc33 100644 --- a/go.sum +++ b/go.sum @@ -46,6 +46,8 @@ github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24 github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= +github.com/cenkalti/backoff/v4 v4.3.0 h1:MyRJ/UdXutAwSAT+s3wNd7MfTIcy71VQueUuFK343L8= +github.com/cenkalti/backoff/v4 v4.3.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/cespare/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= @@ -89,6 +91,8 @@ github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG github.com/go-logfmt/logfmt v0.5.1/go.mod h1:WYhtIu8zTZfxdn5+rREduYbwxfcBr/Vr6KEVveWlfTs= github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY= github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= +github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= +github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= github.com/go-logr/zapr v1.2.3 h1:a9vnzlIBPQBBkeaR9IuMUfmVOrQlkoC4YfPoFkX3T7A= github.com/go-logr/zapr v1.2.3/go.mod h1:eIauM6P8qSvTw5o2ez6UEAfGjQKrxQTl5EoK+Qa2oG4= github.com/go-openapi/jsonpointer v0.19.3/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg= @@ -148,8 +152,8 @@ github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/ github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= -github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= +github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/gofuzz v1.1.0 h1:Hsa8mG0dQ46ij8Sl2AYJDUv1oA9/d6Vk+3LG99Oe02g= github.com/google/gofuzz v1.1.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= @@ -164,10 +168,12 @@ github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hf github.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= -github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= -github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= +github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.20.0 h1:bkypFPDjIYGfCYD5mRBvpqxfYX1YCS1PXdKYWi8FsN0= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.20.0/go.mod h1:P+Lt/0by1T8bfcF3z737NnSbmxQAppXMRziHUxPOC8k= github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= @@ -196,6 +202,8 @@ github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxv github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= +github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= +github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= @@ -220,7 +228,6 @@ github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= -github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs= github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A= github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE= @@ -268,26 +275,26 @@ github.com/prometheus/procfs v0.7.3/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1 github.com/prometheus/procfs v0.8.0 h1:ODq8ZFEaYeCaZOJlZZdJA2AbQR98dSHSM1KW/You5mo= github.com/prometheus/procfs v0.8.0/go.mod h1:z7EfXMXOkbkqb9IINtpCn86r/to3BnA0uaxHdg830/4= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= +github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8= +github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4= github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88= +github.com/sirupsen/logrus v1.8.1 h1:dJKuHgqk1NNQlqoA6BTlM1Wf9DOH3NBjQyu0h9+AZZE= +github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/stoewer/go-strcase v1.2.0/go.mod h1:IBiWB2sKIp3wVVQ3Y035++gc+knqhUQag1KpM8ahLw8= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= -github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= -github.com/stretchr/testify v1.8.2 h1:+h33VjcLVPDHtOdpUCuF+7gSuG3yGIftsP1YvFihtJ8= -github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= +github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= +github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= @@ -297,11 +304,25 @@ go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= +go.opentelemetry.io/otel v1.27.0 h1:9BZoF3yMK/O1AafMiQTVu0YDj5Ea4hPhxCs7sGva+cg= +go.opentelemetry.io/otel v1.27.0/go.mod h1:DMpAK8fzYRzs+bi3rS5REupisuqTheUlSZJ1WnZaPAQ= +go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.27.0 h1:R9DE4kQ4k+YtfLI2ULwX82VtNQ2J8yZmA7ZIF/D+7Mc= +go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.27.0/go.mod h1:OQFyQVrDlbe+R7xrEyDr/2Wr67Ol0hRUgsfA+V5A95s= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.27.0 h1:qFffATk0X+HD+f1Z8lswGiOQYKHRlzfmdJm0wEaVrFA= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.27.0/go.mod h1:MOiCmryaYtc+V0Ei+Tx9o5S1ZjA7kzLucuVuyzBZloQ= +go.opentelemetry.io/otel/metric v1.27.0 h1:hvj3vdEKyeCi4YaYfNjv2NUje8FqKqUY8IlF0FxV/ik= +go.opentelemetry.io/otel/metric v1.27.0/go.mod h1:mVFgmRlhljgBiuk/MP/oKylr4hs85GZAylncepAX/ak= +go.opentelemetry.io/otel/sdk v1.27.0 h1:mlk+/Y1gLPLn84U4tI8d3GNJmGT/eXe3ZuOXN9kTWmI= +go.opentelemetry.io/otel/sdk v1.27.0/go.mod h1:Ha9vbLwJE6W86YstIywK2xFfPjbWlCuwPtMkKdz/Y4A= +go.opentelemetry.io/otel/trace v1.27.0 h1:IqYb813p7cmbHk0a5y6pD5JPakbVfftRXABGt5/Rscw= +go.opentelemetry.io/otel/trace v1.27.0/go.mod h1:6RiD1hkAprV4/q+yd2ln1HG9GoPx39SuvvstaLBl+l4= +go.opentelemetry.io/proto/otlp v1.2.0 h1:pVeZGk7nXDC9O2hncA6nHldxEjm6LByfA2aN8IOkz94= +go.opentelemetry.io/proto/otlp v1.2.0/go.mod h1:gGpR8txAl5M03pDhMC79G6SdqNV26naRm/KDsgaHD8A= go.uber.org/atomic v1.7.0 h1:ADUqmZGgLDDfbSL9ZmPxKTybcoEYHgpYfELNoN+7hsw= go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= go.uber.org/goleak v1.1.10/go.mod h1:8a7PlsEVH3e/a/GLqe5IIrQx6GzcnRmZEufDUTk4A7A= -go.uber.org/goleak v1.2.0 h1:xqgm/S+aQvhWFTtR0XK3Jvg7z8kGV8P4X14IzwN3Eqk= -go.uber.org/goleak v1.2.0/go.mod h1:XJYK+MuIchqpmGmUSAzotztawfKvYLUIgg7guXrwVUo= +go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= +go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE= go.uber.org/multierr v1.6.0 h1:y6IPFStTAIT5Ytl7/XYmHvzXQ7S3g/IeZW9hyZ5thw4= go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU= go.uber.org/zap v1.19.0/go.mod h1:xg/QME4nWcxGxrpdeYfq7UvYrLh66cuVKdrbD1XF/NI= @@ -377,8 +398,8 @@ golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwY golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= -golang.org/x/net v0.19.0 h1:zTwKpTd2XuCqf8huc7Fo2iSy+4RHPd10s4KzeTnVr1c= -golang.org/x/net v0.19.0/go.mod h1:CfAk/cbD4CthTvqiEl8NpboMuiuOYsAr/7NOjZJtv1U= +golang.org/x/net v0.25.0 h1:d/OCCoBEUq33pjydKrGQhw7IlUPI2Oylr+8qLx49kac= +golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= @@ -386,8 +407,8 @@ golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4Iltr golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20210514164344-f6687ab2804c/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/oauth2 v0.0.0-20220223155221-ee480838109b/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc= -golang.org/x/oauth2 v0.7.0 h1:qe6s0zUXlPX80/dITx3440hWZ7GwMwgDDyrSGTPJG/g= -golang.org/x/oauth2 v0.7.0/go.mod h1:hPLQkd9LyjfXTiRohC/41GhcFqxisoUQ99sCUOHO9x4= +golang.org/x/oauth2 v0.20.0 h1:4mQdhULixXKP1rwYBW0vAijoXnkTG0BLCDRzfe1idMo= +golang.org/x/oauth2 v0.20.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -414,6 +435,7 @@ golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -443,12 +465,12 @@ golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220114195835-da31bd327af9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.15.0 h1:h48lPFYpsTvQJZF4EKyI4aLHaev3CxivZmv7yZig9pc= -golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.20.0 h1:Od9JTbYCk261bKm4M/mw7AklTlFYIa0bIp9BgSm1S8Y= +golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= -golang.org/x/term v0.15.0 h1:y/Oo/a/q3IXu26lQgl04j/gjuBDOBlx7X6Om1j2CPW4= -golang.org/x/term v0.15.0/go.mod h1:BDl952bC7+uMoWR75FIrCDx79TPU9oHkTZ9yRbYOrX0= +golang.org/x/term v0.20.0 h1:VnkxpohqXaOBYJtBmEppKUG6mXpi+4O6purfc2+sMhw= +golang.org/x/term v0.20.0/go.mod h1:8UkIAJTvZgivsXaD6/pH6U9ecQzZ45awqEOzuCvwpFY= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= @@ -456,8 +478,8 @@ golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= -golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= -golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= +golang.org/x/text v0.15.0 h1:h1V/4gjBv8v9cjcR6+AR5+/cIYK5N/WAgiv4xlsEtAk= +golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= @@ -535,8 +557,6 @@ google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7 google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0= google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= -google.golang.org/appengine v1.6.7 h1:FZR1q0exgwxzPzp/aF+VccGrSfxfPpkBqjIIEq3ru6c= -google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= @@ -567,6 +587,10 @@ google.golang.org/genproto v0.0.0-20200729003335-053ba62fc06f/go.mod h1:FWY/as6D google.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20201019141844-1ed22bb0c154/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto/googleapis/api v0.0.0-20240520151616-dc85e6b867a5 h1:P8OJ/WCl/Xo4E4zoe4/bifHpSmmKwARqyqE4nW6J2GQ= +google.golang.org/genproto/googleapis/api v0.0.0-20240520151616-dc85e6b867a5/go.mod h1:RGnPtTG7r4i8sPlNyDeikXF99hMM+hN6QMm4ooG9g2g= +google.golang.org/genproto/googleapis/rpc v0.0.0-20240515191416-fc5f0ca64291 h1:AgADTJarZTBqgjiUzRgfaBchgYB3/WFTC80GPwsMcRI= +google.golang.org/genproto/googleapis/rpc v0.0.0-20240515191416-fc5f0ca64291/go.mod h1:EfXuqaE1J41VCDicxHzUDm+8rk+7ZdXzHV0IhO/I6s0= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= @@ -579,6 +603,8 @@ google.golang.org/grpc v1.28.0/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKa google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk= google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= +google.golang.org/grpc v1.64.0 h1:KH3VH9y/MgNQg1dE7b3XfVK0GsPSIzJwdF617gUSbvY= +google.golang.org/grpc v1.64.0/go.mod h1:oxjF8E3FBnjp+/gVFYdWacaLDx9na1aqy9oovLpxQYg= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= @@ -591,14 +617,15 @@ google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGj google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= -google.golang.org/protobuf v1.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGmI= -google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= +google.golang.org/protobuf v1.34.1 h1:9ddQBjfCyZPOHPUiPxpYESBLc+T8P3E+Vo4IbKZgFWg= +google.golang.org/protobuf v1.34.1/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f h1:BLraFXnmrev5lT+xlilqcH8XK9/i0At2xKjWk4p6zsU= gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc= diff --git a/main.go b/main.go index 08697433c..0f80e6864 100644 --- a/main.go +++ b/main.go @@ -17,6 +17,7 @@ limitations under the License. package main import ( + "errors" "flag" "fmt" "os" @@ -24,7 +25,7 @@ import ( "strings" "github.com/litmuschaos/chaos-operator/pkg/analytics" - "github.com/pkg/errors" + "github.com/litmuschaos/chaos-operator/pkg/telemetry" // Import all Kubernetes client auth plugins (e.g. Azure, GCP, OIDC, etc.) // to ensure that exec-entrypoint and run can make use of them. @@ -110,6 +111,20 @@ func main() { os.Exit(1) } + ctx := ctrl.SetupSignalHandler() + + // Set up Observability. + shutdown, err := telemetry.InitOTelSDK(ctx) + if err != nil { + setupLog.Error(err, "unable to set up observability") + os.Exit(1) + } + + // Handle shutdown properly so nothing leaks. + defer func() { + err = errors.Join(err, shutdown(ctx)) + }() + if err = (&controllers.ChaosEngineReconciler{ Client: mgr.GetClient(), Scheme: mgr.GetScheme(), @@ -130,7 +145,7 @@ func main() { } setupLog.Info("starting manager") - if err := mgr.Start(ctrl.SetupSignalHandler()); err != nil { + if err := mgr.Start(ctx); err != nil { setupLog.Error(err, "problem running manager") os.Exit(1) } diff --git a/pkg/telemetry/otel.go b/pkg/telemetry/otel.go new file mode 100644 index 000000000..3fd0dcc45 --- /dev/null +++ b/pkg/telemetry/otel.go @@ -0,0 +1,90 @@ +package telemetry + +import ( + "context" + "errors" + "os" + + log "github.com/sirupsen/logrus" + "go.opentelemetry.io/otel" + "go.opentelemetry.io/otel/exporters/otlp/otlptrace" + "go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc" + "go.opentelemetry.io/otel/propagation" + "go.opentelemetry.io/otel/sdk/resource" + sdktrace "go.opentelemetry.io/otel/sdk/trace" + semconv "go.opentelemetry.io/otel/semconv/v1.25.0" +) + +const OTELExporterOTLPEndpoint = "OTEL_EXPORTER_OTLP_ENDPOINT" +const OTELServiceName = "chaos_operator" + +func InitOTelSDK(ctx context.Context) (shutdown func(context.Context) error, err error) { + var shutdownFuncs []func(context.Context) error + + shutdown = func(ctx context.Context) error { + var err error + for _, fn := range shutdownFuncs { + err = errors.Join(err, fn(ctx)) + } + shutdownFuncs = nil + return err + } + + handleErr := func(inErr error) { + err = errors.Join(inErr, shutdown(ctx)) + } + + tracerProvider, err := newTracerProvider(ctx) + if err != nil { + handleErr(err) + return + } + + prop := newPropagator() + otel.SetTextMapPropagator(prop) + + shutdownFuncs = append(shutdownFuncs, tracerProvider.Shutdown) + otel.SetTracerProvider(tracerProvider) + + log.Info("OTel SDK initialized") + + // TODO: need to add metrics & logging provider + return +} + +func newPropagator() propagation.TextMapPropagator { + return propagation.NewCompositeTextMapPropagator( + propagation.TraceContext{}, + propagation.Baggage{}, + ) +} + +func newTracerProvider(ctx context.Context) (*sdktrace.TracerProvider, error) { + endpoint := os.Getenv(OTELExporterOTLPEndpoint) + + res, err := resource.New(ctx, + resource.WithAttributes( + semconv.ServiceNameKey.String(OTELServiceName), + ), + ) + traceExporter, err := otlptrace.New( + ctx, + otlptracegrpc.NewClient( + // TODO: add secure option + otlptracegrpc.WithInsecure(), + otlptracegrpc.WithEndpoint(endpoint), + ), + ) + if err != nil { + return nil, err + } + + batchSpanProcessor := sdktrace.NewBatchSpanProcessor(traceExporter) + tracerProvider := sdktrace.NewTracerProvider( + sdktrace.WithSampler(sdktrace.AlwaysSample()), + sdktrace.WithResource(res), + sdktrace.WithSpanProcessor(batchSpanProcessor), + ) + + return tracerProvider, nil +} diff --git a/pkg/telemetry/tracing.go b/pkg/telemetry/tracing.go new file mode 100644 index 000000000..79abc1938 --- /dev/null +++ b/pkg/telemetry/tracing.go @@ -0,0 +1,63 @@ +package telemetry + +import ( + "context" + "encoding/json" + "github.com/litmuschaos/chaos-operator/api/litmuschaos/v1alpha1" + log "github.com/sirupsen/logrus" + "go.opentelemetry.io/otel" + "go.opentelemetry.io/otel/attribute" + "go.opentelemetry.io/otel/propagation" +) + +const ( + // TracerName is the name of the tracer + TracerName = "litmuschaos.io/chaos-operator" +) + +// InitSpanContext initialize tracing by creating the root span and injecting the +// spanContext is propagated through annotations in the CR +func InitSpanContext(ctx context.Context, engine *v1alpha1.ChaosEngine) context.Context { + tracerProvider := otel.GetTracerProvider() + pro := otel.GetTextMapPropagator() + + spanContext := make(map[string]string) + + // Create a new root span since there was no parent spanContext provided through annotations + ctxWithTrace, span := tracerProvider.Tracer(TracerName).Start(ctx, "ChaosEngine:Reconciler") + defer span.End() + span.SetAttributes(attribute.String("chaos_engine.name", engine.Name), attribute.String("chaos_engine.namespace", engine.Namespace)) + + pro.Inject(ctxWithTrace, propagation.MapCarrier(spanContext)) + + log.Debug("got tracing carrier", spanContext) + if len(spanContext) == 0 { + log.Debug("tracerProvider doesn't provide a traceId, tracing is disabled") + return ctx + } + return ctxWithTrace +} + +// GetMarshalledSpanFromContext Extract spanContext from the context and return it as json encoded string +func GetMarshalledSpanFromContext(ctx context.Context) string { + carrier := make(map[string]string) + pro := otel.GetTextMapPropagator() + + pro.Inject(ctx, propagation.MapCarrier(carrier)) + + if len(carrier) == 0 { + log.Error("spanContext not present in the context, unable to marshall") + return "" + } + + marshalled, err := json.Marshal(carrier) + if err != nil { + log.Error("unable to marshal span context, err: %s", err) + return "" + } + if len(marshalled) >= 1024 { + log.Error("marshalled span context is too large, unable to marshall") + return "" + } + return string(marshalled) +}