From e83dc0c7f84e9613b180b5e84d5519e11f77a3d5 Mon Sep 17 00:00:00 2001 From: Mateusz Urbanek Date: Mon, 29 Jul 2024 15:49:59 +0200 Subject: [PATCH] [improvement] improve span propagation to ensure Reconcile is always a top level span parent (#387) * fix: improve span propagation Signed-off-by: Mateusz Urbanek * feat: add extra info to span via default decorators Signed-off-by: Mateusz Urbanek * test: add tests for helpers Signed-off-by: Mateusz Urbanek * chore: add to placementgroups Signed-off-by: Mateusz Urbanek --------- Signed-off-by: Mateusz Urbanek --- api/v1alpha1/webhook_helpers.go | 1 + api/v1alpha2/webhook_helpers.go | 1 + cloud/scope/common.go | 1 + cmd/main.go | 75 +++--- controller/linodecluster_controller.go | 14 +- controller/linodemachine_controller.go | 20 +- .../linodemachine_controller_helpers.go | 8 +- .../linodeobjectstoragebucket_controller.go | 18 +- controller/linodeplacementgroup_controller.go | 18 +- controller/linodevpc_controller.go | 18 +- hack/templates/opentelemetry.go.gotpl | 4 +- observability/wrappers/helpers.go | 39 +++ observability/wrappers/helpers_test.go | 121 ++++++++++ observability/wrappers/interfaces.go | 18 +- .../wrappers/linodeclient/decorator.go | 157 ++++++++++++ .../wrappers/linodeclient/linodeclient.gen.go | 176 ++++++++++---- .../wrappers/reconciler/reconciler.gen.go | 223 ------------------ .../wrappers/runtimeclient/decorator.go | 66 ++++++ .../runtimeclient/runtimeclient.gen.go | 213 +++++++++++++++++ .../wrappers/runtimereconciler/decorator.go | 52 ++++ .../runtimereconciler.gen.go | 61 +++++ 21 files changed, 956 insertions(+), 348 deletions(-) create mode 100644 observability/wrappers/helpers.go create mode 100644 observability/wrappers/helpers_test.go create mode 100644 observability/wrappers/linodeclient/decorator.go delete mode 100644 observability/wrappers/reconciler/reconciler.gen.go create mode 100644 observability/wrappers/runtimeclient/decorator.go create mode 100644 observability/wrappers/runtimeclient/runtimeclient.gen.go create mode 100644 observability/wrappers/runtimereconciler/decorator.go create mode 100644 observability/wrappers/runtimereconciler/runtimereconciler.gen.go diff --git a/api/v1alpha1/webhook_helpers.go b/api/v1alpha1/webhook_helpers.go index 561ce5edf..316466acd 100644 --- a/api/v1alpha1/webhook_helpers.go +++ b/api/v1alpha1/webhook_helpers.go @@ -28,6 +28,7 @@ var ( // defaultLinodeClient is an unauthenticated Linode client defaultLinodeClient = linodeclient.NewLinodeClientWithTracing( ptr.To(linodego.NewClient(&http.Client{Timeout: defaultClientTimeout})), + linodeclient.DefaultDecorator(), ) ) diff --git a/api/v1alpha2/webhook_helpers.go b/api/v1alpha2/webhook_helpers.go index 06fbab7b9..c07957e5d 100644 --- a/api/v1alpha2/webhook_helpers.go +++ b/api/v1alpha2/webhook_helpers.go @@ -43,6 +43,7 @@ var ( // defaultLinodeClient is an unauthenticated Linode client defaultLinodeClient = linodeclient.NewLinodeClientWithTracing( ptr.To(linodego.NewClient(&http.Client{Timeout: defaultClientTimeout})), + linodeclient.DefaultDecorator(), ) ) diff --git a/cloud/scope/common.go b/cloud/scope/common.go index 9e1526134..d1cc998a8 100644 --- a/cloud/scope/common.go +++ b/cloud/scope/common.go @@ -67,6 +67,7 @@ func CreateLinodeClient(apiKey string, timeout time.Duration, opts ...Option) (L return linodeclient.NewLinodeClientWithTracing( &linodeClient, + linodeclient.DefaultDecorator(), ), nil } diff --git a/cmd/main.go b/cmd/main.go index e6af1b550..55efa284d 100644 --- a/cmd/main.go +++ b/cmd/main.go @@ -43,7 +43,6 @@ import ( infrastructurev1alpha2 "github.com/linode/cluster-api-provider-linode/api/v1alpha2" "github.com/linode/cluster-api-provider-linode/controller" "github.com/linode/cluster-api-provider-linode/observability/tracing" - "github.com/linode/cluster-api-provider-linode/observability/wrappers/reconciler" "github.com/linode/cluster-api-provider-linode/version" _ "go.uber.org/automaxprocs" @@ -164,64 +163,54 @@ func main() { os.Exit(1) } - if err = reconciler.NewReconcilerWithTracing( - &controller.LinodeClusterReconciler{ - Client: mgr.GetClient(), - Recorder: mgr.GetEventRecorderFor("LinodeClusterReconciler"), - WatchFilterValue: clusterWatchFilter, - LinodeApiKey: linodeToken, - }, - ).SetupWithManager(mgr, crcontroller.Options{MaxConcurrentReconciles: linodeClusterConcurrency}); err != nil { + if err = (&controller.LinodeClusterReconciler{ + Client: mgr.GetClient(), + Recorder: mgr.GetEventRecorderFor("LinodeClusterReconciler"), + WatchFilterValue: clusterWatchFilter, + LinodeApiKey: linodeToken, + }).SetupWithManager(mgr, crcontroller.Options{MaxConcurrentReconciles: linodeClusterConcurrency}); err != nil { setupLog.Error(err, "unable to create controller", "controller", "LinodeCluster") os.Exit(1) } - if err = reconciler.NewReconcilerWithTracing( - &controller.LinodeMachineReconciler{ - Client: mgr.GetClient(), - Recorder: mgr.GetEventRecorderFor("LinodeMachineReconciler"), - WatchFilterValue: machineWatchFilter, - LinodeApiKey: linodeToken, - LinodeDNSAPIKey: linodeDNSToken, - }, - ).SetupWithManager(mgr, crcontroller.Options{MaxConcurrentReconciles: linodeMachineConcurrency}); err != nil { + if err = (&controller.LinodeMachineReconciler{ + Client: mgr.GetClient(), + Recorder: mgr.GetEventRecorderFor("LinodeMachineReconciler"), + WatchFilterValue: machineWatchFilter, + LinodeApiKey: linodeToken, + LinodeDNSAPIKey: linodeDNSToken, + }).SetupWithManager(mgr, crcontroller.Options{MaxConcurrentReconciles: linodeMachineConcurrency}); err != nil { setupLog.Error(err, "unable to create controller", "controller", "LinodeMachine") os.Exit(1) } - if err = reconciler.NewReconcilerWithTracing( - &controller.LinodeVPCReconciler{ - Client: mgr.GetClient(), - Recorder: mgr.GetEventRecorderFor("LinodeVPCReconciler"), - WatchFilterValue: clusterWatchFilter, - LinodeApiKey: linodeToken, - }, - ).SetupWithManager(mgr, crcontroller.Options{MaxConcurrentReconciles: linodeVPCConcurrency}); err != nil { + if err = (&controller.LinodeVPCReconciler{ + Client: mgr.GetClient(), + Recorder: mgr.GetEventRecorderFor("LinodeVPCReconciler"), + WatchFilterValue: clusterWatchFilter, + LinodeApiKey: linodeToken, + }).SetupWithManager(mgr, crcontroller.Options{MaxConcurrentReconciles: linodeVPCConcurrency}); err != nil { setupLog.Error(err, "unable to create controller", "controller", "LinodeVPC") os.Exit(1) } - if err = reconciler.NewReconcilerWithTracing( - &controller.LinodeObjectStorageBucketReconciler{ - Client: mgr.GetClient(), - Logger: ctrl.Log.WithName("LinodeObjectStorageBucketReconciler"), - Recorder: mgr.GetEventRecorderFor("LinodeObjectStorageBucketReconciler"), - WatchFilterValue: objectStorageBucketWatchFilter, - LinodeApiKey: linodeToken, - }, - ).SetupWithManager(mgr, crcontroller.Options{MaxConcurrentReconciles: linodeObjectStorageBucketConcurrency}); err != nil { + if err = (&controller.LinodeObjectStorageBucketReconciler{ + Client: mgr.GetClient(), + Logger: ctrl.Log.WithName("LinodeObjectStorageBucketReconciler"), + Recorder: mgr.GetEventRecorderFor("LinodeObjectStorageBucketReconciler"), + WatchFilterValue: objectStorageBucketWatchFilter, + LinodeApiKey: linodeToken, + }).SetupWithManager(mgr, crcontroller.Options{MaxConcurrentReconciles: linodeObjectStorageBucketConcurrency}); err != nil { setupLog.Error(err, "unable to create controller", "controller", "LinodeObjectStorageBucket") os.Exit(1) } - if err = reconciler.NewReconcilerWithTracing( - &controller.LinodePlacementGroupReconciler{ - Client: mgr.GetClient(), - Recorder: mgr.GetEventRecorderFor("LinodePlacementGroupReconciler"), - WatchFilterValue: clusterWatchFilter, - LinodeApiKey: linodeToken, - }, - ).SetupWithManager(mgr, crcontroller.Options{MaxConcurrentReconciles: linodePlacementGroupConcurrency}); err != nil { + if err = (&controller.LinodePlacementGroupReconciler{ + Client: mgr.GetClient(), + Recorder: mgr.GetEventRecorderFor("LinodePlacementGroupReconciler"), + WatchFilterValue: clusterWatchFilter, + LinodeApiKey: linodeToken, + }).SetupWithManager(mgr, crcontroller.Options{MaxConcurrentReconciles: linodePlacementGroupConcurrency}); err != nil { setupLog.Error(err, "unable to create controller", "controller", "LinodePlacementGroup") os.Exit(1) } diff --git a/controller/linodecluster_controller.go b/controller/linodecluster_controller.go index 270753819..dfe77f0af 100644 --- a/controller/linodecluster_controller.go +++ b/controller/linodecluster_controller.go @@ -43,6 +43,8 @@ import ( infrav1alpha2 "github.com/linode/cluster-api-provider-linode/api/v1alpha2" "github.com/linode/cluster-api-provider-linode/cloud/scope" "github.com/linode/cluster-api-provider-linode/cloud/services" + wrappedruntimeclient "github.com/linode/cluster-api-provider-linode/observability/wrappers/runtimeclient" + wrappedruntimereconciler "github.com/linode/cluster-api-provider-linode/observability/wrappers/runtimereconciler" "github.com/linode/cluster-api-provider-linode/util" "github.com/linode/cluster-api-provider-linode/util/reconciler" ) @@ -69,13 +71,13 @@ func (r *LinodeClusterReconciler) Reconcile(ctx context.Context, req ctrl.Reques logger := ctrl.LoggerFrom(ctx).WithName("LinodeClusterReconciler").WithValues("name", req.NamespacedName.String()) linodeCluster := &infrav1alpha2.LinodeCluster{} - if err := r.Client.Get(ctx, req.NamespacedName, linodeCluster); err != nil { + if err := r.TracedClient().Get(ctx, req.NamespacedName, linodeCluster); err != nil { logger.Info("Failed to fetch Linode cluster", "error", err.Error()) return ctrl.Result{}, client.IgnoreNotFound(err) } - cluster, err := kutil.GetOwnerCluster(ctx, r.Client, linodeCluster.ObjectMeta) + cluster, err := kutil.GetOwnerCluster(ctx, r.TracedClient(), linodeCluster.ObjectMeta) if err != nil { logger.Info("Failed to get owner cluster", "error", err.Error()) @@ -91,7 +93,7 @@ func (r *LinodeClusterReconciler) Reconcile(ctx context.Context, req ctrl.Reques ctx, r.LinodeApiKey, scope.ClusterScopeParams{ - Client: r.Client, + Client: r.TracedClient(), Cluster: cluster, LinodeCluster: linodeCluster, }, @@ -300,10 +302,14 @@ func (r *LinodeClusterReconciler) SetupWithManager(mgr ctrl.Manager, options crc kutil.ClusterToInfrastructureMapFunc(context.TODO(), infrav1alpha2.GroupVersion.WithKind("LinodeCluster"), mgr.GetClient(), &infrav1alpha2.LinodeCluster{}), ), builder.WithPredicates(predicates.ClusterUnpausedAndInfrastructureReady(mgr.GetLogger())), - ).Complete(r) + ).Complete(wrappedruntimereconciler.NewRuntimeReconcilerWithTracing(r, wrappedruntimereconciler.DefaultDecorator())) if err != nil { return fmt.Errorf("failed to build controller: %w", err) } return nil } + +func (r *LinodeClusterReconciler) TracedClient() client.Client { + return wrappedruntimeclient.NewRuntimeClientWithTracing(r.Client, wrappedruntimereconciler.DefaultDecorator()) +} diff --git a/controller/linodemachine_controller.go b/controller/linodemachine_controller.go index f41548e57..ec51844b9 100644 --- a/controller/linodemachine_controller.go +++ b/controller/linodemachine_controller.go @@ -48,6 +48,8 @@ import ( infrav1alpha2 "github.com/linode/cluster-api-provider-linode/api/v1alpha2" "github.com/linode/cluster-api-provider-linode/cloud/scope" "github.com/linode/cluster-api-provider-linode/cloud/services" + wrappedruntimeclient "github.com/linode/cluster-api-provider-linode/observability/wrappers/runtimeclient" + wrappedruntimereconciler "github.com/linode/cluster-api-provider-linode/observability/wrappers/runtimereconciler" "github.com/linode/cluster-api-provider-linode/util" "github.com/linode/cluster-api-provider-linode/util/reconciler" ) @@ -118,7 +120,7 @@ func (r *LinodeMachineReconciler) Reconcile(ctx context.Context, req ctrl.Reques log := ctrl.LoggerFrom(ctx).WithName("LinodeMachineReconciler").WithValues("name", req.NamespacedName.String()) linodeMachine := &infrav1alpha2.LinodeMachine{} - if err := r.Client.Get(ctx, req.NamespacedName, linodeMachine); err != nil { + if err := r.TracedClient().Get(ctx, req.NamespacedName, linodeMachine); err != nil { if err = client.IgnoreNotFound(err); err != nil { log.Error(err, "Failed to fetch LinodeMachine") } @@ -142,7 +144,7 @@ func (r *LinodeMachineReconciler) Reconcile(ctx context.Context, req ctrl.Reques r.LinodeApiKey, r.LinodeDNSAPIKey, scope.MachineScopeParams{ - Client: r.Client, + Client: r.TracedClient(), Cluster: cluster, Machine: machine, LinodeCluster: &infrav1alpha2.LinodeCluster{}, @@ -221,7 +223,7 @@ func (r *LinodeMachineReconciler) reconcile( Name: machineScope.Cluster.Spec.InfrastructureRef.Name, } - if err := r.Client.Get(ctx, linodeClusterKey, machineScope.LinodeCluster); err != nil { + if err := r.Get(ctx, linodeClusterKey, machineScope.LinodeCluster); err != nil { if err = client.IgnoreNotFound(err); err != nil { return ctrl.Result{}, fmt.Errorf("get linodecluster %q: %w", linodeClusterKey, err) } @@ -740,7 +742,11 @@ func (r *LinodeMachineReconciler) reconcileDelete( // SetupWithManager sets up the controller with the Manager. func (r *LinodeMachineReconciler) SetupWithManager(mgr ctrl.Manager, options crcontroller.Options) error { - linodeMachineMapper, err := kutil.ClusterToTypedObjectsMapper(r.Client, &infrav1alpha2.LinodeMachineList{}, mgr.GetScheme()) + linodeMachineMapper, err := kutil.ClusterToTypedObjectsMapper( + r.TracedClient(), + &infrav1alpha2.LinodeMachineList{}, + mgr.GetScheme(), + ) if err != nil { return fmt.Errorf("failed to create mapper for LinodeMachines: %w", err) } @@ -762,10 +768,14 @@ func (r *LinodeMachineReconciler) SetupWithManager(mgr ctrl.Manager, options crc builder.WithPredicates(predicates.ClusterUnpausedAndInfrastructureReady(mgr.GetLogger())), ). WithEventFilter(predicates.ResourceNotPausedAndHasFilterLabel(mgr.GetLogger(), r.WatchFilterValue)). - Complete(r) + Complete(wrappedruntimereconciler.NewRuntimeReconcilerWithTracing(r, wrappedruntimereconciler.DefaultDecorator())) if err != nil { return fmt.Errorf("failed to build controller: %w", err) } return nil } + +func (r *LinodeMachineReconciler) TracedClient() client.Client { + return wrappedruntimeclient.NewRuntimeClientWithTracing(r.Client, wrappedruntimeclient.DefaultDecorator()) +} diff --git a/controller/linodemachine_controller_helpers.go b/controller/linodemachine_controller_helpers.go index addb386b8..f44a03a45 100644 --- a/controller/linodemachine_controller_helpers.go +++ b/controller/linodemachine_controller_helpers.go @@ -180,7 +180,7 @@ func (r *LinodeMachineReconciler) buildInstanceAddrs(ctx context.Context, machin } func (r *LinodeMachineReconciler) getOwnerMachine(ctx context.Context, linodeMachine infrav1alpha2.LinodeMachine, log logr.Logger) (*clusterv1.Machine, error) { - machine, err := kutil.GetOwnerMachine(ctx, r.Client, linodeMachine.ObjectMeta) + machine, err := kutil.GetOwnerMachine(ctx, r.TracedClient(), linodeMachine.ObjectMeta) if err != nil { if err = client.IgnoreNotFound(err); err != nil { log.Error(err, "Failed to fetch owner machine") @@ -212,7 +212,7 @@ func (r *LinodeMachineReconciler) getOwnerMachine(ctx context.Context, linodeMac } func (r *LinodeMachineReconciler) getClusterFromMetadata(ctx context.Context, machine clusterv1.Machine, log logr.Logger) (*clusterv1.Cluster, error) { - cluster, err := kutil.GetClusterFromMetadata(ctx, r.Client, machine.ObjectMeta) + cluster, err := kutil.GetClusterFromMetadata(ctx, r.TracedClient(), machine.ObjectMeta) if err != nil { if err = client.IgnoreNotFound(err); err != nil { log.Error(err, "Failed to fetch cluster by label") @@ -254,7 +254,7 @@ func (r *LinodeMachineReconciler) linodeClusterToLinodeMachines(logger logr.Logg return nil } - cluster, err := kutil.GetOwnerCluster(ctx, r.Client, linodeCluster.ObjectMeta) + cluster, err := kutil.GetOwnerCluster(ctx, r.TracedClient(), linodeCluster.ObjectMeta) switch { case apierrors.IsNotFound(err) || cluster == nil: logger.Info("Cluster for LinodeCluster not found, skipping mapping") @@ -281,7 +281,7 @@ func (r *LinodeMachineReconciler) requestsForCluster(ctx context.Context, namesp labels := map[string]string{clusterv1.ClusterNameLabel: name} machineList := clusterv1.MachineList{} - if err := r.Client.List(ctx, &machineList, client.InNamespace(namespace), client.MatchingLabels(labels)); err != nil { + if err := r.TracedClient().List(ctx, &machineList, client.InNamespace(namespace), client.MatchingLabels(labels)); err != nil { return nil, err } diff --git a/controller/linodeobjectstoragebucket_controller.go b/controller/linodeobjectstoragebucket_controller.go index eb7f88d01..4041af71c 100644 --- a/controller/linodeobjectstoragebucket_controller.go +++ b/controller/linodeobjectstoragebucket_controller.go @@ -44,6 +44,8 @@ import ( infrav1alpha1 "github.com/linode/cluster-api-provider-linode/api/v1alpha1" "github.com/linode/cluster-api-provider-linode/cloud/scope" "github.com/linode/cluster-api-provider-linode/cloud/services" + wrappedruntimeclient "github.com/linode/cluster-api-provider-linode/observability/wrappers/runtimeclient" + wrappedruntimereconciler "github.com/linode/cluster-api-provider-linode/observability/wrappers/runtimereconciler" "github.com/linode/cluster-api-provider-linode/util" "github.com/linode/cluster-api-provider-linode/util/reconciler" ) @@ -81,7 +83,7 @@ func (r *LinodeObjectStorageBucketReconciler) Reconcile(ctx context.Context, req logger := r.Logger.WithValues("name", req.NamespacedName.String()) objectStorageBucket := &infrav1alpha1.LinodeObjectStorageBucket{} - if err := r.Client.Get(ctx, req.NamespacedName, objectStorageBucket); err != nil { + if err := r.TracedClient().Get(ctx, req.NamespacedName, objectStorageBucket); err != nil { if err = client.IgnoreNotFound(err); err != nil { logger.Error(err, "Failed to fetch LinodeObjectStorageBucket", "name", req.NamespacedName.String()) } @@ -93,7 +95,7 @@ func (r *LinodeObjectStorageBucketReconciler) Reconcile(ctx context.Context, req ctx, r.LinodeApiKey, scope.ObjectStorageBucketScopeParams{ - Client: r.Client, + Client: r.TracedClient(), Bucket: objectStorageBucket, Logger: &logger, }, @@ -259,7 +261,11 @@ func (r *LinodeObjectStorageBucketReconciler) reconcileDelete(ctx context.Contex // SetupWithManager sets up the controller with the Manager. func (r *LinodeObjectStorageBucketReconciler) SetupWithManager(mgr ctrl.Manager, options crcontroller.Options) error { - linodeObjectStorageBucketMapper, err := kutil.ClusterToTypedObjectsMapper(r.Client, &infrav1alpha1.LinodeObjectStorageBucketList{}, mgr.GetScheme()) + linodeObjectStorageBucketMapper, err := kutil.ClusterToTypedObjectsMapper( + r.TracedClient(), + &infrav1alpha1.LinodeObjectStorageBucketList{}, + mgr.GetScheme(), + ) if err != nil { return fmt.Errorf("failed to create mapper for LinodeObjectStorageBuckets: %w", err) } @@ -276,10 +282,14 @@ func (r *LinodeObjectStorageBucketReconciler) SetupWithManager(mgr ctrl.Manager, &clusterv1.Cluster{}, handler.EnqueueRequestsFromMapFunc(linodeObjectStorageBucketMapper), builder.WithPredicates(predicates.ClusterUnpausedAndInfrastructureReady(mgr.GetLogger())), - ).Complete(r) + ).Complete(wrappedruntimereconciler.NewRuntimeReconcilerWithTracing(r, wrappedruntimereconciler.DefaultDecorator())) if err != nil { return fmt.Errorf("failed to build controller: %w", err) } return nil } + +func (r *LinodeObjectStorageBucketReconciler) TracedClient() client.Client { + return wrappedruntimeclient.NewRuntimeClientWithTracing(r.Client, wrappedruntimeclient.DefaultDecorator()) +} diff --git a/controller/linodeplacementgroup_controller.go b/controller/linodeplacementgroup_controller.go index 0bd7b2b6a..43e4f1d0f 100644 --- a/controller/linodeplacementgroup_controller.go +++ b/controller/linodeplacementgroup_controller.go @@ -45,6 +45,8 @@ import ( infrav1alpha2 "github.com/linode/cluster-api-provider-linode/api/v1alpha2" "github.com/linode/cluster-api-provider-linode/cloud/scope" + wrappedruntimeclient "github.com/linode/cluster-api-provider-linode/observability/wrappers/runtimeclient" + wrappedruntimereconciler "github.com/linode/cluster-api-provider-linode/observability/wrappers/runtimereconciler" "github.com/linode/cluster-api-provider-linode/util" "github.com/linode/cluster-api-provider-linode/util/reconciler" ) @@ -76,7 +78,7 @@ func (r *LinodePlacementGroupReconciler) Reconcile(ctx context.Context, req ctrl log := ctrl.LoggerFrom(ctx).WithName("LinodePlacementGroupReconciler").WithValues("name", req.NamespacedName.String()) linodeplacementgroup := &infrav1alpha2.LinodePlacementGroup{} - if err := r.Client.Get(ctx, req.NamespacedName, linodeplacementgroup); err != nil { + if err := r.TracedClient().Get(ctx, req.NamespacedName, linodeplacementgroup); err != nil { if err = client.IgnoreNotFound(err); err != nil { log.Error(err, "Failed to fetch LinodePlacementGroup") } @@ -88,7 +90,7 @@ func (r *LinodePlacementGroupReconciler) Reconcile(ctx context.Context, req ctrl ctx, r.LinodeApiKey, scope.PlacementGroupScopeParams{ - Client: r.Client, + Client: r.TracedClient(), LinodePlacementGroup: linodeplacementgroup, }, ) @@ -291,7 +293,11 @@ func (r *LinodePlacementGroupReconciler) reconcileDelete(ctx context.Context, lo // //nolint:dupl // this is same as Placement Group, worth making generic later. func (r *LinodePlacementGroupReconciler) SetupWithManager(mgr ctrl.Manager, options crcontroller.Options) error { - linodePlacementGroupMapper, err := kutil.ClusterToTypedObjectsMapper(r.Client, &infrav1alpha2.LinodePlacementGroupList{}, mgr.GetScheme()) + linodePlacementGroupMapper, err := kutil.ClusterToTypedObjectsMapper( + r.TracedClient(), + &infrav1alpha2.LinodePlacementGroupList{}, + mgr.GetScheme(), + ) if err != nil { return fmt.Errorf("failed to create mapper for LinodePlacementGroups: %w", err) } @@ -312,10 +318,14 @@ func (r *LinodePlacementGroupReconciler) SetupWithManager(mgr ctrl.Manager, opti &clusterv1.Cluster{}, handler.EnqueueRequestsFromMapFunc(linodePlacementGroupMapper), builder.WithPredicates(predicates.ClusterUnpausedAndInfrastructureReady(mgr.GetLogger())), - ).Complete(r) + ).Complete(wrappedruntimereconciler.NewRuntimeReconcilerWithTracing(r, wrappedruntimereconciler.DefaultDecorator())) if err != nil { return fmt.Errorf("failed to build controller: %w", err) } return nil } + +func (r *LinodePlacementGroupReconciler) TracedClient() client.Client { + return wrappedruntimeclient.NewRuntimeClientWithTracing(r.Client, wrappedruntimeclient.DefaultDecorator()) +} diff --git a/controller/linodevpc_controller.go b/controller/linodevpc_controller.go index fc1c22457..f8ee801b5 100644 --- a/controller/linodevpc_controller.go +++ b/controller/linodevpc_controller.go @@ -44,6 +44,8 @@ import ( infrav1alpha2 "github.com/linode/cluster-api-provider-linode/api/v1alpha2" "github.com/linode/cluster-api-provider-linode/cloud/scope" + wrappedruntimeclient "github.com/linode/cluster-api-provider-linode/observability/wrappers/runtimeclient" + wrappedruntimereconciler "github.com/linode/cluster-api-provider-linode/observability/wrappers/runtimereconciler" "github.com/linode/cluster-api-provider-linode/util" "github.com/linode/cluster-api-provider-linode/util/reconciler" ) @@ -82,7 +84,7 @@ func (r *LinodeVPCReconciler) Reconcile(ctx context.Context, req ctrl.Request) ( log := ctrl.LoggerFrom(ctx).WithName("LinodeVPCReconciler").WithValues("name", req.NamespacedName.String()) linodeVPC := &infrav1alpha2.LinodeVPC{} - if err := r.Client.Get(ctx, req.NamespacedName, linodeVPC); err != nil { + if err := r.TracedClient().Get(ctx, req.NamespacedName, linodeVPC); err != nil { if err = client.IgnoreNotFound(err); err != nil { log.Error(err, "Failed to fetch LinodeVPC") } @@ -94,7 +96,7 @@ func (r *LinodeVPCReconciler) Reconcile(ctx context.Context, req ctrl.Request) ( ctx, r.LinodeApiKey, scope.VPCScopeParams{ - Client: r.Client, + Client: r.TracedClient(), LinodeVPC: linodeVPC, }, ) @@ -321,7 +323,11 @@ func (r *LinodeVPCReconciler) reconcileDelete(ctx context.Context, logger logr.L // //nolint:dupl // this is same as Placement Group, worth making generic later. func (r *LinodeVPCReconciler) SetupWithManager(mgr ctrl.Manager, options crcontroller.Options) error { - linodeVPCMapper, err := kutil.ClusterToTypedObjectsMapper(r.Client, &infrav1alpha2.LinodeVPCList{}, mgr.GetScheme()) + linodeVPCMapper, err := kutil.ClusterToTypedObjectsMapper( + r.TracedClient(), + &infrav1alpha2.LinodeVPCList{}, + mgr.GetScheme(), + ) if err != nil { return fmt.Errorf("failed to create mapper for LinodeVPCs: %w", err) } @@ -342,10 +348,14 @@ func (r *LinodeVPCReconciler) SetupWithManager(mgr ctrl.Manager, options crcontr &clusterv1.Cluster{}, handler.EnqueueRequestsFromMapFunc(linodeVPCMapper), builder.WithPredicates(predicates.ClusterUnpausedAndInfrastructureReady(mgr.GetLogger())), - ).Complete(r) + ).Complete(wrappedruntimereconciler.NewRuntimeReconcilerWithTracing(r, wrappedruntimereconciler.DefaultDecorator())) if err != nil { return fmt.Errorf("failed to build controller: %w", err) } return nil } + +func (r *LinodeVPCReconciler) TracedClient() client.Client { + return wrappedruntimeclient.NewRuntimeClientWithTracing(r.Client, wrappedruntimeclient.DefaultDecorator()) +} diff --git a/hack/templates/opentelemetry.go.gotpl b/hack/templates/opentelemetry.go.gotpl index 92411d6bf..7d6b888ff 100644 --- a/hack/templates/opentelemetry.go.gotpl +++ b/hack/templates/opentelemetry.go.gotpl @@ -36,7 +36,9 @@ func (_d {{$decorator}}) {{$method.Declaration}} { defer func() { if _d._spanDecorator != nil { _d._spanDecorator(_span, {{$method.ParamsMap}}, {{$method.ResultsMap}}) - }{{- if $method.ReturnsError}} else if err != nil { + } + {{if $method.ReturnsError}} + if err != nil { _span.RecordError(err) _span.SetAttributes( attribute.String("event", "error"), diff --git a/observability/wrappers/helpers.go b/observability/wrappers/helpers.go new file mode 100644 index 000000000..42d0134a9 --- /dev/null +++ b/observability/wrappers/helpers.go @@ -0,0 +1,39 @@ +/* +Copyright 2024 Akamai Technologies, Inc. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package wrappers + +func GetValue[T any](m map[string]any, key string) (T, bool) { + var zero T + + if val, ok := m[key]; ok { + if val, ok := val.(T); ok { + return val, true + } + } + + return zero, false +} + +func Optional[T any](val *T) T { + var zero T + + if val != nil { + return *val + } + + return zero +} diff --git a/observability/wrappers/helpers_test.go b/observability/wrappers/helpers_test.go new file mode 100644 index 000000000..9f76d3bc4 --- /dev/null +++ b/observability/wrappers/helpers_test.go @@ -0,0 +1,121 @@ +/* +Copyright 2024 Akamai Technologies, Inc. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package wrappers + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestOptional(t *testing.T) { + t.Parallel() + + t.Run("int", func(t *testing.T) { + t.Parallel() + + val := 1 + + ret := Optional[int](&val) + assert.Equal(t, val, ret) + + ret = Optional[int](nil) + assert.Equal(t, 0, ret) + }) + + t.Run("string", func(t *testing.T) { + t.Parallel() + + val := "foo" + + ret := Optional[string](&val) + assert.Equal(t, val, ret) + + ret = Optional[string](nil) + assert.Equal(t, "", ret) + }) + + t.Run("slice", func(t *testing.T) { + t.Parallel() + + val := []string{"foo", "bar"} + + ret := Optional[[]string](&val) + assert.Equal(t, val, ret) + + ret = Optional[[]string](nil) + assert.Equal(t, []string(nil), ret) + }) +} + +func TestGetValue(t *testing.T) { + t.Parallel() + + t.Run("int", func(t *testing.T) { + t.Parallel() + + key := "key" + val := 3 + valueMap := map[string]any{key: val} + + ret, ok := GetValue[int](valueMap, key) + assert.True(t, ok) + assert.Equal(t, val, ret) + + _, ok = GetValue[int](valueMap, "foo") + assert.False(t, ok) + + _, ok = GetValue[string](valueMap, key) + assert.False(t, ok) + }) + + t.Run("string", func(t *testing.T) { + t.Parallel() + + key := "key" + val := "val" + valueMap := map[string]any{key: val} + + ret, ok := GetValue[string](valueMap, key) + assert.True(t, ok) + assert.Equal(t, val, ret) + + _, ok = GetValue[string](valueMap, "foo") + assert.False(t, ok) + + _, ok = GetValue[int](valueMap, key) + assert.False(t, ok) + }) + + t.Run("slice", func(t *testing.T) { + t.Parallel() + + key := "key" + val := []string{"foo", "bar"} + valueMap := map[string]any{key: val} + + ret, ok := GetValue[[]string](valueMap, key) + assert.True(t, ok) + assert.Equal(t, val, ret) + + _, ok = GetValue[[]string](valueMap, "foo") + assert.False(t, ok) + + _, ok = GetValue[int](valueMap, key) + assert.False(t, ok) + }) +} diff --git a/observability/wrappers/interfaces.go b/observability/wrappers/interfaces.go index 13751bcac..70a28f5b3 100644 --- a/observability/wrappers/interfaces.go +++ b/observability/wrappers/interfaces.go @@ -17,20 +17,14 @@ limitations under the License. package wrappers import ( - "context" - - ctrl "sigs.k8s.io/controller-runtime" "sigs.k8s.io/controller-runtime/pkg/client" - "sigs.k8s.io/controller-runtime/pkg/controller" - "sigs.k8s.io/controller-runtime/pkg/manager" + "sigs.k8s.io/controller-runtime/pkg/reconcile" ) -type Reconciler interface { - client.Reader - client.Writer - client.StatusClient - client.SubResourceClientConstructor +type RuntimeReconciler interface { + reconcile.Reconciler +} - Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) - SetupWithManager(mgr manager.Manager, options controller.Options) error +type RuntimeClient interface { + client.Client } diff --git a/observability/wrappers/linodeclient/decorator.go b/observability/wrappers/linodeclient/decorator.go new file mode 100644 index 000000000..eff89d5d3 --- /dev/null +++ b/observability/wrappers/linodeclient/decorator.go @@ -0,0 +1,157 @@ +/* +Copyright 2024 Akamai Technologies, Inc. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package linodeclient + +import ( + "github.com/linode/linodego" + "go.opentelemetry.io/otel/attribute" + "go.opentelemetry.io/otel/trace" + + "github.com/linode/cluster-api-provider-linode/observability/wrappers" +) + +const ( + NodebalancerIDParam = "nodebalancerID" + ConfigIDParam = "configID" + DomainIDParam = "domainID" + DiskIDParam = "diskID" + NodeIDParam = "nodeID" + DomainRecordIDParam = "domainRecordID" + LinodeIDParam = "linodeID" + KeyIDParam = "keyID" + VpcIDParam = "vpcID" + ImageIDParam = "imageID" + ClusterParam = "cluster" + LabelParam = "label" + RegionIDParam = "regionID" + TypeIDParam = "typeID" + SizeParam = "size" + RecordReqParam = "recordReq" +) + +func DefaultDecorator() func(span trace.Span, params, results map[string]interface{}) { //nolint:cyclop,gocognit // TODO: refactor this + return func(span trace.Span, params, results map[string]interface{}) { + attr := []attribute.KeyValue{} + + if val, ok := wrappers.GetValue[int](params, NodebalancerIDParam); ok { + attr = append(attr, + attribute.Int("req.nodebalancer_id", val), + ) + } + + if val, ok := wrappers.GetValue[int](params, ConfigIDParam); ok { + attr = append(attr, + attribute.Int("req.config_id", val), + ) + } + + if val, ok := wrappers.GetValue[int](params, DomainIDParam); ok { + attr = append(attr, + attribute.Int("req.domain_id", val), + ) + } + + if val, ok := wrappers.GetValue[int](params, DiskIDParam); ok { + attr = append(attr, + attribute.Int("req.disk_id", val), + ) + } + + if val, ok := wrappers.GetValue[int](params, NodeIDParam); ok { + attr = append(attr, + attribute.Int("req.node_id", val), + ) + } + + if val, ok := wrappers.GetValue[int](params, DomainRecordIDParam); ok { + attr = append(attr, + attribute.Int("req.domain_record_id", val), + ) + } + + if val, ok := wrappers.GetValue[int](params, LinodeIDParam); ok { + attr = append(attr, + attribute.Int("req.linode_id", val), + ) + } + + if val, ok := wrappers.GetValue[int](params, KeyIDParam); ok { + attr = append(attr, + attribute.Int("req.key_id", val), + ) + } + + if val, ok := wrappers.GetValue[int](params, VpcIDParam); ok { + attr = append(attr, + attribute.Int("req.vpc_id", val), + ) + } + + if val, ok := wrappers.GetValue[int](params, SizeParam); ok { + attr = append(attr, + attribute.Int("req.size", val), + ) + } + + if val, ok := wrappers.GetValue[string](params, ImageIDParam); ok { + attr = append(attr, + attribute.String("req.image_id", val), + ) + } + + if val, ok := wrappers.GetValue[string](params, ClusterParam); ok { + attr = append(attr, + attribute.String("req.cluster", val), + ) + } + + if val, ok := wrappers.GetValue[string](params, LabelParam); ok { + attr = append(attr, + attribute.String("req.label", val), + ) + } + + if val, ok := wrappers.GetValue[string](params, RegionIDParam); ok { + attr = append(attr, + attribute.String("req.region_id", val), + ) + } + + if val, ok := wrappers.GetValue[string](params, TypeIDParam); ok { + attr = append(attr, + attribute.String("req.type_id", val), + ) + } + + if val, ok := wrappers.GetValue[linodego.DomainRecordUpdateOptions](params, RecordReqParam); ok { + attr = append(attr, + attribute.String("req.domain_record_update_options.name", val.Name), + attribute.String("req.domain_record_update_options.target", val.Target), + attribute.String("req.domain_record_update_options.type", string(val.Type)), + attribute.String("req.domain_record_update_options.protocol", wrappers.Optional(val.Protocol)), + attribute.String("req.domain_record_update_options.service", wrappers.Optional(val.Service)), + attribute.String("req.domain_record_update_options.tag", wrappers.Optional(val.Tag)), + attribute.Int("req.domain_record_update_options.ttl_sec", val.TTLSec), + attribute.Int("req.domain_record_update_options.port", wrappers.Optional(val.Port)), + attribute.Int("req.domain_record_update_options.priority", wrappers.Optional(val.Priority)), + attribute.Int("req.domain_record_update_options.weight", wrappers.Optional(val.Weight)), + ) + } + + span.SetAttributes(attr...) + } +} diff --git a/observability/wrappers/linodeclient/linodeclient.gen.go b/observability/wrappers/linodeclient/linodeclient.gen.go index ca7c1eb4f..726cfe89b 100644 --- a/observability/wrappers/linodeclient/linodeclient.gen.go +++ b/observability/wrappers/linodeclient/linodeclient.gen.go @@ -46,7 +46,9 @@ func (_d LinodeClientWithTracing) AssignPlacementGroupLinodes(ctx context.Contex "options": options}, map[string]interface{}{ "pp1": pp1, "err": err}) - } else if err != nil { + } + + if err != nil { _span.RecordError(err) _span.SetAttributes( attribute.String("event", "error"), @@ -69,7 +71,9 @@ func (_d LinodeClientWithTracing) BootInstance(ctx context.Context, linodeID int "linodeID": linodeID, "configID": configID}, map[string]interface{}{ "err": err}) - } else if err != nil { + } + + if err != nil { _span.RecordError(err) _span.SetAttributes( attribute.String("event", "error"), @@ -93,7 +97,9 @@ func (_d LinodeClientWithTracing) CreateDomainRecord(ctx context.Context, domain "recordReq": recordReq}, map[string]interface{}{ "dp1": dp1, "err": err}) - } else if err != nil { + } + + if err != nil { _span.RecordError(err) _span.SetAttributes( attribute.String("event", "error"), @@ -116,7 +122,9 @@ func (_d LinodeClientWithTracing) CreateInstance(ctx context.Context, opts linod "opts": opts}, map[string]interface{}{ "ip1": ip1, "err": err}) - } else if err != nil { + } + + if err != nil { _span.RecordError(err) _span.SetAttributes( attribute.String("event", "error"), @@ -140,7 +148,9 @@ func (_d LinodeClientWithTracing) CreateInstanceDisk(ctx context.Context, linode "opts": opts}, map[string]interface{}{ "ip1": ip1, "err": err}) - } else if err != nil { + } + + if err != nil { _span.RecordError(err) _span.SetAttributes( attribute.String("event", "error"), @@ -163,7 +173,9 @@ func (_d LinodeClientWithTracing) CreateNodeBalancer(ctx context.Context, opts l "opts": opts}, map[string]interface{}{ "np1": np1, "err": err}) - } else if err != nil { + } + + if err != nil { _span.RecordError(err) _span.SetAttributes( attribute.String("event", "error"), @@ -187,7 +199,9 @@ func (_d LinodeClientWithTracing) CreateNodeBalancerConfig(ctx context.Context, "opts": opts}, map[string]interface{}{ "np1": np1, "err": err}) - } else if err != nil { + } + + if err != nil { _span.RecordError(err) _span.SetAttributes( attribute.String("event", "error"), @@ -212,7 +226,9 @@ func (_d LinodeClientWithTracing) CreateNodeBalancerNode(ctx context.Context, no "opts": opts}, map[string]interface{}{ "np1": np1, "err": err}) - } else if err != nil { + } + + if err != nil { _span.RecordError(err) _span.SetAttributes( attribute.String("event", "error"), @@ -235,7 +251,9 @@ func (_d LinodeClientWithTracing) CreateObjectStorageBucket(ctx context.Context, "opts": opts}, map[string]interface{}{ "op1": op1, "err": err}) - } else if err != nil { + } + + if err != nil { _span.RecordError(err) _span.SetAttributes( attribute.String("event", "error"), @@ -258,7 +276,9 @@ func (_d LinodeClientWithTracing) CreateObjectStorageKey(ctx context.Context, op "opts": opts}, map[string]interface{}{ "op1": op1, "err": err}) - } else if err != nil { + } + + if err != nil { _span.RecordError(err) _span.SetAttributes( attribute.String("event", "error"), @@ -281,7 +301,9 @@ func (_d LinodeClientWithTracing) CreatePlacementGroup(ctx context.Context, opts "opts": opts}, map[string]interface{}{ "pp1": pp1, "err": err}) - } else if err != nil { + } + + if err != nil { _span.RecordError(err) _span.SetAttributes( attribute.String("event", "error"), @@ -304,7 +326,9 @@ func (_d LinodeClientWithTracing) CreateStackscript(ctx context.Context, opts li "opts": opts}, map[string]interface{}{ "sp1": sp1, "err": err}) - } else if err != nil { + } + + if err != nil { _span.RecordError(err) _span.SetAttributes( attribute.String("event", "error"), @@ -327,7 +351,9 @@ func (_d LinodeClientWithTracing) CreateVPC(ctx context.Context, opts linodego.V "opts": opts}, map[string]interface{}{ "vp1": vp1, "err": err}) - } else if err != nil { + } + + if err != nil { _span.RecordError(err) _span.SetAttributes( attribute.String("event", "error"), @@ -350,7 +376,9 @@ func (_d LinodeClientWithTracing) DeleteDomainRecord(ctx context.Context, domain "domainID": domainID, "domainRecordID": domainRecordID}, map[string]interface{}{ "err": err}) - } else if err != nil { + } + + if err != nil { _span.RecordError(err) _span.SetAttributes( attribute.String("event", "error"), @@ -372,7 +400,9 @@ func (_d LinodeClientWithTracing) DeleteInstance(ctx context.Context, linodeID i "ctx": ctx, "linodeID": linodeID}, map[string]interface{}{ "err": err}) - } else if err != nil { + } + + if err != nil { _span.RecordError(err) _span.SetAttributes( attribute.String("event", "error"), @@ -394,7 +424,9 @@ func (_d LinodeClientWithTracing) DeleteNodeBalancer(ctx context.Context, nodeba "ctx": ctx, "nodebalancerID": nodebalancerID}, map[string]interface{}{ "err": err}) - } else if err != nil { + } + + if err != nil { _span.RecordError(err) _span.SetAttributes( attribute.String("event", "error"), @@ -418,7 +450,9 @@ func (_d LinodeClientWithTracing) DeleteNodeBalancerNode(ctx context.Context, no "configID": configID, "nodeID": nodeID}, map[string]interface{}{ "err": err}) - } else if err != nil { + } + + if err != nil { _span.RecordError(err) _span.SetAttributes( attribute.String("event", "error"), @@ -440,7 +474,9 @@ func (_d LinodeClientWithTracing) DeleteObjectStorageKey(ctx context.Context, ke "ctx": ctx, "keyID": keyID}, map[string]interface{}{ "err": err}) - } else if err != nil { + } + + if err != nil { _span.RecordError(err) _span.SetAttributes( attribute.String("event", "error"), @@ -462,7 +498,9 @@ func (_d LinodeClientWithTracing) DeletePlacementGroup(ctx context.Context, id i "ctx": ctx, "id": id}, map[string]interface{}{ "err": err}) - } else if err != nil { + } + + if err != nil { _span.RecordError(err) _span.SetAttributes( attribute.String("event", "error"), @@ -484,7 +522,9 @@ func (_d LinodeClientWithTracing) DeleteVPC(ctx context.Context, vpcID int) (err "ctx": ctx, "vpcID": vpcID}, map[string]interface{}{ "err": err}) - } else if err != nil { + } + + if err != nil { _span.RecordError(err) _span.SetAttributes( attribute.String("event", "error"), @@ -507,7 +547,9 @@ func (_d LinodeClientWithTracing) GetImage(ctx context.Context, imageID string) "imageID": imageID}, map[string]interface{}{ "ip1": ip1, "err": err}) - } else if err != nil { + } + + if err != nil { _span.RecordError(err) _span.SetAttributes( attribute.String("event", "error"), @@ -530,7 +572,9 @@ func (_d LinodeClientWithTracing) GetInstance(ctx context.Context, linodeID int) "linodeID": linodeID}, map[string]interface{}{ "ip1": ip1, "err": err}) - } else if err != nil { + } + + if err != nil { _span.RecordError(err) _span.SetAttributes( attribute.String("event", "error"), @@ -554,7 +598,9 @@ func (_d LinodeClientWithTracing) GetInstanceDisk(ctx context.Context, linodeID "diskID": diskID}, map[string]interface{}{ "ip1": ip1, "err": err}) - } else if err != nil { + } + + if err != nil { _span.RecordError(err) _span.SetAttributes( attribute.String("event", "error"), @@ -577,7 +623,9 @@ func (_d LinodeClientWithTracing) GetInstanceIPAddresses(ctx context.Context, li "linodeID": linodeID}, map[string]interface{}{ "ip1": ip1, "err": err}) - } else if err != nil { + } + + if err != nil { _span.RecordError(err) _span.SetAttributes( attribute.String("event", "error"), @@ -600,7 +648,9 @@ func (_d LinodeClientWithTracing) GetNodeBalancer(ctx context.Context, nodebalan "nodebalancerID": nodebalancerID}, map[string]interface{}{ "np1": np1, "err": err}) - } else if err != nil { + } + + if err != nil { _span.RecordError(err) _span.SetAttributes( attribute.String("event", "error"), @@ -624,7 +674,9 @@ func (_d LinodeClientWithTracing) GetNodeBalancerConfig(ctx context.Context, nod "configID": configID}, map[string]interface{}{ "np1": np1, "err": err}) - } else if err != nil { + } + + if err != nil { _span.RecordError(err) _span.SetAttributes( attribute.String("event", "error"), @@ -648,7 +700,9 @@ func (_d LinodeClientWithTracing) GetObjectStorageBucket(ctx context.Context, re "label": label}, map[string]interface{}{ "op1": op1, "err": err}) - } else if err != nil { + } + + if err != nil { _span.RecordError(err) _span.SetAttributes( attribute.String("event", "error"), @@ -671,7 +725,9 @@ func (_d LinodeClientWithTracing) GetObjectStorageKey(ctx context.Context, keyID "keyID": keyID}, map[string]interface{}{ "op1": op1, "err": err}) - } else if err != nil { + } + + if err != nil { _span.RecordError(err) _span.SetAttributes( attribute.String("event", "error"), @@ -694,7 +750,9 @@ func (_d LinodeClientWithTracing) GetPlacementGroup(ctx context.Context, id int) "id": id}, map[string]interface{}{ "pp1": pp1, "err": err}) - } else if err != nil { + } + + if err != nil { _span.RecordError(err) _span.SetAttributes( attribute.String("event", "error"), @@ -717,7 +775,9 @@ func (_d LinodeClientWithTracing) GetRegion(ctx context.Context, regionID string "regionID": regionID}, map[string]interface{}{ "rp1": rp1, "err": err}) - } else if err != nil { + } + + if err != nil { _span.RecordError(err) _span.SetAttributes( attribute.String("event", "error"), @@ -740,7 +800,9 @@ func (_d LinodeClientWithTracing) GetType(ctx context.Context, typeID string) (l "typeID": typeID}, map[string]interface{}{ "lp1": lp1, "err": err}) - } else if err != nil { + } + + if err != nil { _span.RecordError(err) _span.SetAttributes( attribute.String("event", "error"), @@ -763,7 +825,9 @@ func (_d LinodeClientWithTracing) GetVPC(ctx context.Context, vpcID int) (vp1 *l "vpcID": vpcID}, map[string]interface{}{ "vp1": vp1, "err": err}) - } else if err != nil { + } + + if err != nil { _span.RecordError(err) _span.SetAttributes( attribute.String("event", "error"), @@ -787,7 +851,9 @@ func (_d LinodeClientWithTracing) ListDomainRecords(ctx context.Context, domainI "opts": opts}, map[string]interface{}{ "da1": da1, "err": err}) - } else if err != nil { + } + + if err != nil { _span.RecordError(err) _span.SetAttributes( attribute.String("event", "error"), @@ -810,7 +876,9 @@ func (_d LinodeClientWithTracing) ListDomains(ctx context.Context, opts *linodeg "opts": opts}, map[string]interface{}{ "da1": da1, "err": err}) - } else if err != nil { + } + + if err != nil { _span.RecordError(err) _span.SetAttributes( attribute.String("event", "error"), @@ -834,7 +902,9 @@ func (_d LinodeClientWithTracing) ListInstanceConfigs(ctx context.Context, linod "opts": opts}, map[string]interface{}{ "ia1": ia1, "err": err}) - } else if err != nil { + } + + if err != nil { _span.RecordError(err) _span.SetAttributes( attribute.String("event", "error"), @@ -857,7 +927,9 @@ func (_d LinodeClientWithTracing) ListInstances(ctx context.Context, opts *linod "opts": opts}, map[string]interface{}{ "ia1": ia1, "err": err}) - } else if err != nil { + } + + if err != nil { _span.RecordError(err) _span.SetAttributes( attribute.String("event", "error"), @@ -880,7 +952,9 @@ func (_d LinodeClientWithTracing) ListPlacementGroups(ctx context.Context, optio "options": options}, map[string]interface{}{ "pa1": pa1, "err": err}) - } else if err != nil { + } + + if err != nil { _span.RecordError(err) _span.SetAttributes( attribute.String("event", "error"), @@ -903,7 +977,9 @@ func (_d LinodeClientWithTracing) ListStackscripts(ctx context.Context, opts *li "opts": opts}, map[string]interface{}{ "sa1": sa1, "err": err}) - } else if err != nil { + } + + if err != nil { _span.RecordError(err) _span.SetAttributes( attribute.String("event", "error"), @@ -926,7 +1002,9 @@ func (_d LinodeClientWithTracing) ListVPCs(ctx context.Context, opts *linodego.L "opts": opts}, map[string]interface{}{ "va1": va1, "err": err}) - } else if err != nil { + } + + if err != nil { _span.RecordError(err) _span.SetAttributes( attribute.String("event", "error"), @@ -950,7 +1028,9 @@ func (_d LinodeClientWithTracing) ResizeInstanceDisk(ctx context.Context, linode "diskID": diskID, "size": size}, map[string]interface{}{ "err": err}) - } else if err != nil { + } + + if err != nil { _span.RecordError(err) _span.SetAttributes( attribute.String("event", "error"), @@ -974,7 +1054,9 @@ func (_d LinodeClientWithTracing) UnassignPlacementGroupLinodes(ctx context.Cont "options": options}, map[string]interface{}{ "pp1": pp1, "err": err}) - } else if err != nil { + } + + if err != nil { _span.RecordError(err) _span.SetAttributes( attribute.String("event", "error"), @@ -999,7 +1081,9 @@ func (_d LinodeClientWithTracing) UpdateDomainRecord(ctx context.Context, domain "recordReq": recordReq}, map[string]interface{}{ "dp1": dp1, "err": err}) - } else if err != nil { + } + + if err != nil { _span.RecordError(err) _span.SetAttributes( attribute.String("event", "error"), @@ -1024,7 +1108,9 @@ func (_d LinodeClientWithTracing) UpdateInstanceConfig(ctx context.Context, lino "opts": opts}, map[string]interface{}{ "ip1": ip1, "err": err}) - } else if err != nil { + } + + if err != nil { _span.RecordError(err) _span.SetAttributes( attribute.String("event", "error"), @@ -1048,7 +1134,9 @@ func (_d LinodeClientWithTracing) UpdatePlacementGroup(ctx context.Context, id i "options": options}, map[string]interface{}{ "pp1": pp1, "err": err}) - } else if err != nil { + } + + if err != nil { _span.RecordError(err) _span.SetAttributes( attribute.String("event", "error"), diff --git a/observability/wrappers/reconciler/reconciler.gen.go b/observability/wrappers/reconciler/reconciler.gen.go deleted file mode 100644 index 593fa3dd6..000000000 --- a/observability/wrappers/reconciler/reconciler.gen.go +++ /dev/null @@ -1,223 +0,0 @@ -// Code generated by gowrap. DO NOT EDIT. -// template: ../../../hack/templates/opentelemetry.go.gotpl -// gowrap: http://github.com/hexdigest/gowrap - -package reconciler - -//go:generate gowrap gen -p github.com/linode/cluster-api-provider-linode/observability/wrappers -i Reconciler -t ../../../hack/templates/opentelemetry.go.gotpl -o reconciler.gen.go -l "" - -import ( - "context" - - "github.com/linode/cluster-api-provider-linode/observability/tracing" - "github.com/linode/cluster-api-provider-linode/observability/wrappers" - "go.opentelemetry.io/otel/attribute" - "go.opentelemetry.io/otel/trace" - ctrl "sigs.k8s.io/controller-runtime" - "sigs.k8s.io/controller-runtime/pkg/client" -) - -// ReconcilerWithTracing implements wrappers.Reconciler interface instrumented with opentracing spans -type ReconcilerWithTracing struct { - wrappers.Reconciler - _spanDecorator func(span trace.Span, params, results map[string]interface{}) -} - -// NewReconcilerWithTracing returns ReconcilerWithTracing -func NewReconcilerWithTracing(base wrappers.Reconciler, spanDecorator ...func(span trace.Span, params, results map[string]interface{})) ReconcilerWithTracing { - d := ReconcilerWithTracing{ - Reconciler: base, - } - - if len(spanDecorator) > 0 && spanDecorator[0] != nil { - d._spanDecorator = spanDecorator[0] - } - - return d -} - -// Create implements wrappers.Reconciler -func (_d ReconcilerWithTracing) Create(ctx context.Context, obj client.Object, opts ...client.CreateOption) (err error) { - ctx, _span := tracing.Start(ctx, "wrappers.Reconciler.Create") - defer func() { - if _d._spanDecorator != nil { - _d._spanDecorator(_span, map[string]interface{}{ - "ctx": ctx, - "obj": obj, - "opts": opts}, map[string]interface{}{ - "err": err}) - } else if err != nil { - _span.RecordError(err) - _span.SetAttributes( - attribute.String("event", "error"), - attribute.String("message", err.Error()), - ) - } - - _span.End() - }() - return _d.Reconciler.Create(ctx, obj, opts...) -} - -// Delete implements wrappers.Reconciler -func (_d ReconcilerWithTracing) Delete(ctx context.Context, obj client.Object, opts ...client.DeleteOption) (err error) { - ctx, _span := tracing.Start(ctx, "wrappers.Reconciler.Delete") - defer func() { - if _d._spanDecorator != nil { - _d._spanDecorator(_span, map[string]interface{}{ - "ctx": ctx, - "obj": obj, - "opts": opts}, map[string]interface{}{ - "err": err}) - } else if err != nil { - _span.RecordError(err) - _span.SetAttributes( - attribute.String("event", "error"), - attribute.String("message", err.Error()), - ) - } - - _span.End() - }() - return _d.Reconciler.Delete(ctx, obj, opts...) -} - -// DeleteAllOf implements wrappers.Reconciler -func (_d ReconcilerWithTracing) DeleteAllOf(ctx context.Context, obj client.Object, opts ...client.DeleteAllOfOption) (err error) { - ctx, _span := tracing.Start(ctx, "wrappers.Reconciler.DeleteAllOf") - defer func() { - if _d._spanDecorator != nil { - _d._spanDecorator(_span, map[string]interface{}{ - "ctx": ctx, - "obj": obj, - "opts": opts}, map[string]interface{}{ - "err": err}) - } else if err != nil { - _span.RecordError(err) - _span.SetAttributes( - attribute.String("event", "error"), - attribute.String("message", err.Error()), - ) - } - - _span.End() - }() - return _d.Reconciler.DeleteAllOf(ctx, obj, opts...) -} - -// Get implements wrappers.Reconciler -func (_d ReconcilerWithTracing) Get(ctx context.Context, key client.ObjectKey, obj client.Object, opts ...client.GetOption) (err error) { - ctx, _span := tracing.Start(ctx, "wrappers.Reconciler.Get") - defer func() { - if _d._spanDecorator != nil { - _d._spanDecorator(_span, map[string]interface{}{ - "ctx": ctx, - "key": key, - "obj": obj, - "opts": opts}, map[string]interface{}{ - "err": err}) - } else if err != nil { - _span.RecordError(err) - _span.SetAttributes( - attribute.String("event", "error"), - attribute.String("message", err.Error()), - ) - } - - _span.End() - }() - return _d.Reconciler.Get(ctx, key, obj, opts...) -} - -// List implements wrappers.Reconciler -func (_d ReconcilerWithTracing) List(ctx context.Context, list client.ObjectList, opts ...client.ListOption) (err error) { - ctx, _span := tracing.Start(ctx, "wrappers.Reconciler.List") - defer func() { - if _d._spanDecorator != nil { - _d._spanDecorator(_span, map[string]interface{}{ - "ctx": ctx, - "list": list, - "opts": opts}, map[string]interface{}{ - "err": err}) - } else if err != nil { - _span.RecordError(err) - _span.SetAttributes( - attribute.String("event", "error"), - attribute.String("message", err.Error()), - ) - } - - _span.End() - }() - return _d.Reconciler.List(ctx, list, opts...) -} - -// Patch implements wrappers.Reconciler -func (_d ReconcilerWithTracing) Patch(ctx context.Context, obj client.Object, patch client.Patch, opts ...client.PatchOption) (err error) { - ctx, _span := tracing.Start(ctx, "wrappers.Reconciler.Patch") - defer func() { - if _d._spanDecorator != nil { - _d._spanDecorator(_span, map[string]interface{}{ - "ctx": ctx, - "obj": obj, - "patch": patch, - "opts": opts}, map[string]interface{}{ - "err": err}) - } else if err != nil { - _span.RecordError(err) - _span.SetAttributes( - attribute.String("event", "error"), - attribute.String("message", err.Error()), - ) - } - - _span.End() - }() - return _d.Reconciler.Patch(ctx, obj, patch, opts...) -} - -// Reconcile implements wrappers.Reconciler -func (_d ReconcilerWithTracing) Reconcile(ctx context.Context, req ctrl.Request) (r1 ctrl.Result, err error) { - ctx, _span := tracing.Start(ctx, "wrappers.Reconciler.Reconcile") - defer func() { - if _d._spanDecorator != nil { - _d._spanDecorator(_span, map[string]interface{}{ - "ctx": ctx, - "req": req}, map[string]interface{}{ - "r1": r1, - "err": err}) - } else if err != nil { - _span.RecordError(err) - _span.SetAttributes( - attribute.String("event", "error"), - attribute.String("message", err.Error()), - ) - } - - _span.End() - }() - return _d.Reconciler.Reconcile(ctx, req) -} - -// Update implements wrappers.Reconciler -func (_d ReconcilerWithTracing) Update(ctx context.Context, obj client.Object, opts ...client.UpdateOption) (err error) { - ctx, _span := tracing.Start(ctx, "wrappers.Reconciler.Update") - defer func() { - if _d._spanDecorator != nil { - _d._spanDecorator(_span, map[string]interface{}{ - "ctx": ctx, - "obj": obj, - "opts": opts}, map[string]interface{}{ - "err": err}) - } else if err != nil { - _span.RecordError(err) - _span.SetAttributes( - attribute.String("event", "error"), - attribute.String("message", err.Error()), - ) - } - - _span.End() - }() - return _d.Reconciler.Update(ctx, obj, opts...) -} diff --git a/observability/wrappers/runtimeclient/decorator.go b/observability/wrappers/runtimeclient/decorator.go new file mode 100644 index 000000000..b5f6299de --- /dev/null +++ b/observability/wrappers/runtimeclient/decorator.go @@ -0,0 +1,66 @@ +/* +Copyright 2024 Akamai Technologies, Inc. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package runtimeclient + +import ( + "go.opentelemetry.io/otel/attribute" + "go.opentelemetry.io/otel/trace" + "k8s.io/apimachinery/pkg/types" + "sigs.k8s.io/controller-runtime/pkg/client" + + "github.com/linode/cluster-api-provider-linode/observability/wrappers" +) + +const ( + KeyParam = "key" + ListParam = "list" // ignored + ObjParam = "obj" + OptsParam = "opts" // ignored + PatchParam = "patch" +) + +func DefaultDecorator() func(span trace.Span, params, results map[string]interface{}) { + return func(span trace.Span, params, results map[string]interface{}) { + attr := []attribute.KeyValue{} + + if obj, ok := wrappers.GetValue[client.Object](params, ObjParam); ok { + attr = append(attr, + attribute.String("object.metadata.name", obj.GetName()), + attribute.String("object.metadata.namespace", obj.GetNamespace()), + attribute.String("object.metadata.uid", string(obj.GetUID())), + attribute.String("object.group", obj.GetObjectKind().GroupVersionKind().Group), + attribute.String("object.version", obj.GetObjectKind().GroupVersionKind().Version), + attribute.String("object.kind", obj.GetObjectKind().GroupVersionKind().Kind), + ) + } + + if key, ok := wrappers.GetValue[types.NamespacedName](params, KeyParam); ok { + attr = append(attr, + attribute.String("key.metadata.name", key.Name), + attribute.String("key.metadata.namespace", key.Namespace), + ) + } + + if patch, ok := wrappers.GetValue[client.Patch](params, KeyParam); ok { + attr = append(attr, + attribute.String("patch.type", string(patch.Type())), + ) + } + + span.SetAttributes(attr...) + } +} diff --git a/observability/wrappers/runtimeclient/runtimeclient.gen.go b/observability/wrappers/runtimeclient/runtimeclient.gen.go new file mode 100644 index 000000000..15fe249cd --- /dev/null +++ b/observability/wrappers/runtimeclient/runtimeclient.gen.go @@ -0,0 +1,213 @@ +// Code generated by gowrap. DO NOT EDIT. +// template: ../../../hack/templates/opentelemetry.go.gotpl +// gowrap: http://github.com/hexdigest/gowrap + +package runtimeclient + +//go:generate gowrap gen -p github.com/linode/cluster-api-provider-linode/observability/wrappers -i RuntimeClient -t ../../../hack/templates/opentelemetry.go.gotpl -o runtimeclient.gen.go -l "" + +import ( + "context" + + "github.com/linode/cluster-api-provider-linode/observability/tracing" + "github.com/linode/cluster-api-provider-linode/observability/wrappers" + "go.opentelemetry.io/otel/attribute" + "go.opentelemetry.io/otel/trace" + "sigs.k8s.io/controller-runtime/pkg/client" +) + +// RuntimeClientWithTracing implements wrappers.RuntimeClient interface instrumented with opentracing spans +type RuntimeClientWithTracing struct { + wrappers.RuntimeClient + _spanDecorator func(span trace.Span, params, results map[string]interface{}) +} + +// NewRuntimeClientWithTracing returns RuntimeClientWithTracing +func NewRuntimeClientWithTracing(base wrappers.RuntimeClient, spanDecorator ...func(span trace.Span, params, results map[string]interface{})) RuntimeClientWithTracing { + d := RuntimeClientWithTracing{ + RuntimeClient: base, + } + + if len(spanDecorator) > 0 && spanDecorator[0] != nil { + d._spanDecorator = spanDecorator[0] + } + + return d +} + +// Create implements wrappers.RuntimeClient +func (_d RuntimeClientWithTracing) Create(ctx context.Context, obj client.Object, opts ...client.CreateOption) (err error) { + ctx, _span := tracing.Start(ctx, "wrappers.RuntimeClient.Create") + defer func() { + if _d._spanDecorator != nil { + _d._spanDecorator(_span, map[string]interface{}{ + "ctx": ctx, + "obj": obj, + "opts": opts}, map[string]interface{}{ + "err": err}) + } + + if err != nil { + _span.RecordError(err) + _span.SetAttributes( + attribute.String("event", "error"), + attribute.String("message", err.Error()), + ) + } + + _span.End() + }() + return _d.RuntimeClient.Create(ctx, obj, opts...) +} + +// Delete implements wrappers.RuntimeClient +func (_d RuntimeClientWithTracing) Delete(ctx context.Context, obj client.Object, opts ...client.DeleteOption) (err error) { + ctx, _span := tracing.Start(ctx, "wrappers.RuntimeClient.Delete") + defer func() { + if _d._spanDecorator != nil { + _d._spanDecorator(_span, map[string]interface{}{ + "ctx": ctx, + "obj": obj, + "opts": opts}, map[string]interface{}{ + "err": err}) + } + + if err != nil { + _span.RecordError(err) + _span.SetAttributes( + attribute.String("event", "error"), + attribute.String("message", err.Error()), + ) + } + + _span.End() + }() + return _d.RuntimeClient.Delete(ctx, obj, opts...) +} + +// DeleteAllOf implements wrappers.RuntimeClient +func (_d RuntimeClientWithTracing) DeleteAllOf(ctx context.Context, obj client.Object, opts ...client.DeleteAllOfOption) (err error) { + ctx, _span := tracing.Start(ctx, "wrappers.RuntimeClient.DeleteAllOf") + defer func() { + if _d._spanDecorator != nil { + _d._spanDecorator(_span, map[string]interface{}{ + "ctx": ctx, + "obj": obj, + "opts": opts}, map[string]interface{}{ + "err": err}) + } + + if err != nil { + _span.RecordError(err) + _span.SetAttributes( + attribute.String("event", "error"), + attribute.String("message", err.Error()), + ) + } + + _span.End() + }() + return _d.RuntimeClient.DeleteAllOf(ctx, obj, opts...) +} + +// Get implements wrappers.RuntimeClient +func (_d RuntimeClientWithTracing) Get(ctx context.Context, key client.ObjectKey, obj client.Object, opts ...client.GetOption) (err error) { + ctx, _span := tracing.Start(ctx, "wrappers.RuntimeClient.Get") + defer func() { + if _d._spanDecorator != nil { + _d._spanDecorator(_span, map[string]interface{}{ + "ctx": ctx, + "key": key, + "obj": obj, + "opts": opts}, map[string]interface{}{ + "err": err}) + } + + if err != nil { + _span.RecordError(err) + _span.SetAttributes( + attribute.String("event", "error"), + attribute.String("message", err.Error()), + ) + } + + _span.End() + }() + return _d.RuntimeClient.Get(ctx, key, obj, opts...) +} + +// List implements wrappers.RuntimeClient +func (_d RuntimeClientWithTracing) List(ctx context.Context, list client.ObjectList, opts ...client.ListOption) (err error) { + ctx, _span := tracing.Start(ctx, "wrappers.RuntimeClient.List") + defer func() { + if _d._spanDecorator != nil { + _d._spanDecorator(_span, map[string]interface{}{ + "ctx": ctx, + "list": list, + "opts": opts}, map[string]interface{}{ + "err": err}) + } + + if err != nil { + _span.RecordError(err) + _span.SetAttributes( + attribute.String("event", "error"), + attribute.String("message", err.Error()), + ) + } + + _span.End() + }() + return _d.RuntimeClient.List(ctx, list, opts...) +} + +// Patch implements wrappers.RuntimeClient +func (_d RuntimeClientWithTracing) Patch(ctx context.Context, obj client.Object, patch client.Patch, opts ...client.PatchOption) (err error) { + ctx, _span := tracing.Start(ctx, "wrappers.RuntimeClient.Patch") + defer func() { + if _d._spanDecorator != nil { + _d._spanDecorator(_span, map[string]interface{}{ + "ctx": ctx, + "obj": obj, + "patch": patch, + "opts": opts}, map[string]interface{}{ + "err": err}) + } + + if err != nil { + _span.RecordError(err) + _span.SetAttributes( + attribute.String("event", "error"), + attribute.String("message", err.Error()), + ) + } + + _span.End() + }() + return _d.RuntimeClient.Patch(ctx, obj, patch, opts...) +} + +// Update implements wrappers.RuntimeClient +func (_d RuntimeClientWithTracing) Update(ctx context.Context, obj client.Object, opts ...client.UpdateOption) (err error) { + ctx, _span := tracing.Start(ctx, "wrappers.RuntimeClient.Update") + defer func() { + if _d._spanDecorator != nil { + _d._spanDecorator(_span, map[string]interface{}{ + "ctx": ctx, + "obj": obj, + "opts": opts}, map[string]interface{}{ + "err": err}) + } + + if err != nil { + _span.RecordError(err) + _span.SetAttributes( + attribute.String("event", "error"), + attribute.String("message", err.Error()), + ) + } + + _span.End() + }() + return _d.RuntimeClient.Update(ctx, obj, opts...) +} diff --git a/observability/wrappers/runtimereconciler/decorator.go b/observability/wrappers/runtimereconciler/decorator.go new file mode 100644 index 000000000..b0fe2f318 --- /dev/null +++ b/observability/wrappers/runtimereconciler/decorator.go @@ -0,0 +1,52 @@ +/* +Copyright 2024 Akamai Technologies, Inc. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package runtimereconciler + +import ( + "go.opentelemetry.io/otel/attribute" + "go.opentelemetry.io/otel/trace" + "sigs.k8s.io/controller-runtime/pkg/reconcile" + + "github.com/linode/cluster-api-provider-linode/observability/wrappers" +) + +const ( + RequestParam = "r1" + ResultParam = "r2" +) + +func DefaultDecorator() func(span trace.Span, params, results map[string]interface{}) { + return func(span trace.Span, params, results map[string]interface{}) { + attr := []attribute.KeyValue{} + + if req, ok := wrappers.GetValue[reconcile.Request](params, RequestParam); ok { + attr = append(attr, + attribute.String("request.name", req.Name), + attribute.String("request.namespace", req.Namespace), + ) + } + + if res, ok := wrappers.GetValue[reconcile.Result](params, RequestParam); ok { + attr = append(attr, + attribute.Bool("result.requeue", res.Requeue), + attribute.String("result.requeue_after", res.RequeueAfter.String()), + ) + } + + span.SetAttributes(attr...) + } +} diff --git a/observability/wrappers/runtimereconciler/runtimereconciler.gen.go b/observability/wrappers/runtimereconciler/runtimereconciler.gen.go new file mode 100644 index 000000000..c2534c031 --- /dev/null +++ b/observability/wrappers/runtimereconciler/runtimereconciler.gen.go @@ -0,0 +1,61 @@ +// Code generated by gowrap. DO NOT EDIT. +// template: ../../../hack/templates/opentelemetry.go.gotpl +// gowrap: http://github.com/hexdigest/gowrap + +package runtimereconciler + +//go:generate gowrap gen -p github.com/linode/cluster-api-provider-linode/observability/wrappers -i RuntimeReconciler -t ../../../hack/templates/opentelemetry.go.gotpl -o runtimereconciler.gen.go -l "" + +import ( + "context" + + "github.com/linode/cluster-api-provider-linode/observability/tracing" + "github.com/linode/cluster-api-provider-linode/observability/wrappers" + "go.opentelemetry.io/otel/attribute" + "go.opentelemetry.io/otel/trace" + "sigs.k8s.io/controller-runtime/pkg/reconcile" +) + +// RuntimeReconcilerWithTracing implements wrappers.RuntimeReconciler interface instrumented with opentracing spans +type RuntimeReconcilerWithTracing struct { + wrappers.RuntimeReconciler + _spanDecorator func(span trace.Span, params, results map[string]interface{}) +} + +// NewRuntimeReconcilerWithTracing returns RuntimeReconcilerWithTracing +func NewRuntimeReconcilerWithTracing(base wrappers.RuntimeReconciler, spanDecorator ...func(span trace.Span, params, results map[string]interface{})) RuntimeReconcilerWithTracing { + d := RuntimeReconcilerWithTracing{ + RuntimeReconciler: base, + } + + if len(spanDecorator) > 0 && spanDecorator[0] != nil { + d._spanDecorator = spanDecorator[0] + } + + return d +} + +// Reconcile implements wrappers.RuntimeReconciler +func (_d RuntimeReconcilerWithTracing) Reconcile(ctx context.Context, r1 reconcile.Request) (r2 reconcile.Result, err error) { + ctx, _span := tracing.Start(ctx, "wrappers.RuntimeReconciler.Reconcile") + defer func() { + if _d._spanDecorator != nil { + _d._spanDecorator(_span, map[string]interface{}{ + "ctx": ctx, + "r1": r1}, map[string]interface{}{ + "r2": r2, + "err": err}) + } + + if err != nil { + _span.RecordError(err) + _span.SetAttributes( + attribute.String("event", "error"), + attribute.String("message", err.Error()), + ) + } + + _span.End() + }() + return _d.RuntimeReconciler.Reconcile(ctx, r1) +}