From 267ed8257591fdbf248a11e1a486125f81f03400 Mon Sep 17 00:00:00 2001 From: Mateusz Urbanek Date: Mon, 24 Jun 2024 14:37:23 +0200 Subject: [PATCH] [feat] add distributed tracing using OTEL (#372) * feat: added distributed tracing using OTEL Autoexport Signed-off-by: Mateusz Urbanek * docs: added tracing chapter to docs Signed-off-by: Mateusz Urbanek * feat: add gowrap to tools Signed-off-by: Mateusz Urbanek * feat: add tracing to linodeclient Signed-off-by: Mateusz Urbanek * fix: simplify the generated code signature Signed-off-by: Mateusz Urbanek * fixup! fix: simplify the generated code signature * fixup! feat: added distributed tracing using OTEL Autoexport * test: add smoke tests to few parts of code Signed-off-by: Mateusz Urbanek * chore: add codecov config file Signed-off-by: Mateusz Urbanek --------- Signed-off-by: Mateusz Urbanek --- .golangci.yml | 2 +- Dockerfile | 4 + Makefile | 14 +- Tiltfile | 4 +- api/v1alpha1/linodecluster_webhook.go | 2 + api/v1alpha1/linodemachine_webhook.go | 2 + .../linodeobjectstoragebucket_webhook.go | 2 + api/v1alpha1/linodevpc_webhook.go | 2 + api/v1alpha1/webhook_helpers.go | 13 +- api/v1alpha2/linodecluster_webhook.go | 2 + api/v1alpha2/webhook_helpers.go | 10 +- cloud/scope/common.go | 23 +- cloud/scope/machine.go | 5 +- cloud/scope/vpc.go | 5 +- cmd/main.go | 148 +++- cmd/main_test.go | 33 + codecov.yml | 2 + docs/src/topics/distributed-tracing.md | 75 ++ go.mod | 35 +- go.sum | 82 +- hack/templates/opentelemetry.go.gotpl | 52 ++ observability/tracing/tracing.go | 62 ++ observability/tracing/tracing_test.go | 58 ++ observability/wrappers/interfaces.go | 35 + .../wrappers/linodeclient/linodeclient.gen.go | 756 ++++++++++++++++++ .../wrappers/reconciler/reconciler.gen.go | 223 ++++++ 26 files changed, 1580 insertions(+), 71 deletions(-) create mode 100644 cmd/main_test.go create mode 100644 codecov.yml create mode 100644 docs/src/topics/distributed-tracing.md create mode 100644 hack/templates/opentelemetry.go.gotpl create mode 100644 observability/tracing/tracing.go create mode 100644 observability/tracing/tracing_test.go create mode 100644 observability/wrappers/interfaces.go create mode 100644 observability/wrappers/linodeclient/linodeclient.gen.go create mode 100644 observability/wrappers/reconciler/reconciler.gen.go diff --git a/.golangci.yml b/.golangci.yml index cf9f142ae..f866b5bb4 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -154,7 +154,6 @@ linters: # - goerr113 - gofmt - goimports - - gomnd # - gocyclo - goprintffuncname - gosec @@ -165,6 +164,7 @@ linters: - maintidx - makezero - misspell + - mnd - nestif - nilerr - nilnil diff --git a/Dockerfile b/Dockerfile index b6c216aa4..c81352bec 100644 --- a/Dockerfile +++ b/Dockerfile @@ -18,6 +18,7 @@ COPY api/ api/ COPY clients/ clients/ COPY controller/ controller/ COPY cloud/ cloud/ +COPY observability/ observability/ COPY util/ util/ COPY version/ version/ @@ -35,4 +36,7 @@ WORKDIR / COPY --from=builder /workspace/manager . USER 65532:65532 +# By default disable traces exporter +ENV OTEL_TRACES_EXPORTER=none + ENTRYPOINT ["/manager"] diff --git a/Makefile b/Makefile index be9ea1739..92d4d1afe 100644 --- a/Makefile +++ b/Makefile @@ -85,7 +85,8 @@ generate-manifests: controller-gen ## Generate WebhookConfiguration, ClusterRole $(CONTROLLER_GEN) rbac:roleName=manager-role crd webhook paths="./..." output:crd:artifacts:config=config/crd/bases .PHONY: generate-code -generate-code: controller-gen ## Generate code containing DeepCopy, DeepCopyInto, and DeepCopyObject method implementations. +generate-code: controller-gen gowrap ## Generate code containing DeepCopy, DeepCopyInto, and DeepCopyObject method implementations. + go generate ./... $(CONTROLLER_GEN) object:headerFile="hack/boilerplate.go.txt" paths="./..." .PHONY: generate-mock @@ -306,6 +307,7 @@ HUSKY ?= $(LOCALBIN)/husky NILAWAY ?= $(LOCALBIN)/nilaway GOVULNC ?= $(LOCALBIN)/govulncheck MOCKGEN ?= $(LOCALBIN)/mockgen +GOWRAP ?= $(CACHE_BIN)/gowrap ## Tool Versions KUSTOMIZE_VERSION ?= v5.4.1 @@ -320,9 +322,10 @@ HUSKY_VERSION ?= v0.2.16 NILAWAY_VERSION ?= latest GOVULNC_VERSION ?= v1.1.1 MOCKGEN_VERSION ?= v0.4.0 +GOWRAP_VERSION ?= latest .PHONY: tools -tools: $(KUSTOMIZE) $(CTLPTL) $(CLUSTERCTL) $(CONTROLLER_GEN) $(TILT) $(KIND) $(CHAINSAW) $(ENVTEST) $(HUSKY) $(NILAWAY) $(GOVULNC) $(MOCKGEN) +tools: $(KUSTOMIZE) $(CTLPTL) $(CLUSTERCTL) $(CONTROLLER_GEN) $(TILT) $(KIND) $(CHAINSAW) $(ENVTEST) $(HUSKY) $(NILAWAY) $(GOVULNC) $(MOCKGEN) $(GOWRAP) .PHONY: kustomize @@ -375,7 +378,7 @@ $(CHAINSAW): $(CACHE_BIN) .PHONY: envtest envtest: $(ENVTEST) ## Download setup-envtest locally if necessary. -$(ENVTEST): $(LOCALBIN) +$(ENVTEST): $(CACHE_BIN) GOBIN=$(CACHE_BIN) go install sigs.k8s.io/controller-runtime/tools/setup-envtest@latest .PHONY: husky @@ -400,3 +403,8 @@ mockgen: $(MOCKGEN) ## Download mockgen locally if necessary. $(MOCKGEN): $(LOCALBIN) GOBIN=$(LOCALBIN) go install go.uber.org/mock/mockgen@$(MOCKGEN_VERSION) +.PHONY: gowrap +gowrap: $(GOWRAP) ## Download gowrap locally if necessary. +$(GOWRAP): $(CACHE_BIN) + GOBIN=$(CACHE_BIN) go install github.com/hexdigest/gowrap/cmd/gowrap@$(GOWRAP_VERSION) + diff --git a/Tiltfile b/Tiltfile index ee313a831..d14733413 100644 --- a/Tiltfile +++ b/Tiltfile @@ -105,7 +105,7 @@ if debug == "true": "capl-compile", 'GOOS=linux CGO_ENABLED=0 go build -gcflags "-N -l" -ldflags="-X github.com/linode/cluster-api-provider-linode/version.version=$VERSION" -a -o bin/manager ./cmd/main.go', deps=["./main.go", "./start.go", "vendor", "go.mod", "go.sum", "./api", "./cloud", "./cmd", "./controller", - "./util", "./version",], + "./observability", "./util", "./version",], labels=["CAPL"], ) docker_build_with_restart( @@ -147,7 +147,7 @@ if os.getenv("SKIP_DOCKER_BUILD", "false") != "true" and debug != "true": "docker.io/linode/cluster-api-provider-linode", context=".", only=("Dockerfile", "Makefile", "vendor", "go.mod", "go.sum", - "./api", "./clients", "./cloud", "./cmd", "./controller", "./util", "./version",), + "./api", "./clients", "./cloud", "./cmd", "./controller", "./observability", "./util", "./version"), build_args={"VERSION": os.getenv("VERSION", "")}, ) diff --git a/api/v1alpha1/linodecluster_webhook.go b/api/v1alpha1/linodecluster_webhook.go index 47e529b23..f9de6061f 100644 --- a/api/v1alpha1/linodecluster_webhook.go +++ b/api/v1alpha1/linodecluster_webhook.go @@ -74,6 +74,7 @@ func (r *LinodeCluster) ValidateDelete() (admission.Warnings, error) { } func (r *LinodeCluster) validateLinodeCluster(ctx context.Context, client LinodeClient) error { + // TODO: instrument with tracing, might need refactor to preserve readibility var errs field.ErrorList if err := r.validateLinodeClusterSpec(ctx, client); err != nil { @@ -89,6 +90,7 @@ func (r *LinodeCluster) validateLinodeCluster(ctx context.Context, client Linode } func (r *LinodeCluster) validateLinodeClusterSpec(ctx context.Context, client LinodeClient) field.ErrorList { + // TODO: instrument with tracing, might need refactor to preserve readibility var errs field.ErrorList if err := validateRegion(ctx, client, r.Spec.Region, field.NewPath("spec").Child("region")); err != nil { diff --git a/api/v1alpha1/linodemachine_webhook.go b/api/v1alpha1/linodemachine_webhook.go index 8c818bb0a..1f6b12c6a 100644 --- a/api/v1alpha1/linodemachine_webhook.go +++ b/api/v1alpha1/linodemachine_webhook.go @@ -94,6 +94,7 @@ func (r *LinodeMachine) ValidateDelete() (admission.Warnings, error) { } func (r *LinodeMachine) validateLinodeMachine(ctx context.Context, client LinodeClient) error { + // TODO: instrument with tracing, might need refactor to preserve readibility var errs field.ErrorList if err := r.validateLinodeMachineSpec(ctx, client); err != nil { @@ -109,6 +110,7 @@ func (r *LinodeMachine) validateLinodeMachine(ctx context.Context, client Linode } func (r *LinodeMachine) validateLinodeMachineSpec(ctx context.Context, client LinodeClient) field.ErrorList { + // TODO: instrument with tracing, might need refactor to preserve readibility var errs field.ErrorList if err := validateRegion(ctx, client, r.Spec.Region, field.NewPath("spec").Child("region")); err != nil { diff --git a/api/v1alpha1/linodeobjectstoragebucket_webhook.go b/api/v1alpha1/linodeobjectstoragebucket_webhook.go index 5bf1146f4..2e03a88c6 100644 --- a/api/v1alpha1/linodeobjectstoragebucket_webhook.go +++ b/api/v1alpha1/linodeobjectstoragebucket_webhook.go @@ -81,6 +81,7 @@ func (r *LinodeObjectStorageBucket) ValidateDelete() (admission.Warnings, error) } func (r *LinodeObjectStorageBucket) validateLinodeObjectStorageBucket(ctx context.Context, client LinodeClient) error { + // TODO: instrument with tracing, might need refactor to preserve readibility var errs field.ErrorList if err := r.validateLinodeObjectStorageBucketSpec(ctx, client); err != nil { @@ -96,6 +97,7 @@ func (r *LinodeObjectStorageBucket) validateLinodeObjectStorageBucket(ctx contex } func (r *LinodeObjectStorageBucket) validateLinodeObjectStorageBucketSpec(ctx context.Context, client LinodeClient) field.ErrorList { + // TODO: instrument with tracing, might need refactor to preserve readibility var errs field.ErrorList if err := validateObjectStorageCluster(ctx, client, r.Spec.Cluster, field.NewPath("spec").Child("cluster")); err != nil { diff --git a/api/v1alpha1/linodevpc_webhook.go b/api/v1alpha1/linodevpc_webhook.go index 81099bff7..29202a519 100644 --- a/api/v1alpha1/linodevpc_webhook.go +++ b/api/v1alpha1/linodevpc_webhook.go @@ -113,6 +113,7 @@ func (r *LinodeVPC) ValidateDelete() (admission.Warnings, error) { } func (r *LinodeVPC) validateLinodeVPC(ctx context.Context, client LinodeClient) error { + // TODO: instrument with tracing, might need refactor to preserve readibility var errs field.ErrorList if err := r.validateLinodeVPCSpec(ctx, client); err != nil { @@ -128,6 +129,7 @@ func (r *LinodeVPC) validateLinodeVPC(ctx context.Context, client LinodeClient) } func (r *LinodeVPC) validateLinodeVPCSpec(ctx context.Context, client LinodeClient) field.ErrorList { + // TODO: instrument with tracing, might need refactor to preserve readibility var errs field.ErrorList if err := validateRegion(ctx, client, r.Spec.Region, field.NewPath("spec").Child("region"), LinodeVPCCapability); err != nil { diff --git a/api/v1alpha1/webhook_helpers.go b/api/v1alpha1/webhook_helpers.go index 4552bbe7a..058f91ba6 100644 --- a/api/v1alpha1/webhook_helpers.go +++ b/api/v1alpha1/webhook_helpers.go @@ -11,6 +11,8 @@ import ( "github.com/linode/linodego" "k8s.io/apimachinery/pkg/util/validation/field" + "github.com/linode/cluster-api-provider-linode/observability/wrappers/linodeclient" + . "github.com/linode/cluster-api-provider-linode/clients" ) @@ -21,12 +23,19 @@ const ( defaultClientTimeout = time.Second * 10 ) +func mkptr[T any](v T) *T { + return &v +} + var ( // defaultLinodeClient is an unauthenticated Linode client - defaultLinodeClient = linodego.NewClient(&http.Client{Timeout: defaultClientTimeout}) + defaultLinodeClient = linodeclient.NewLinodeClientWithTracing( + mkptr(linodego.NewClient(&http.Client{Timeout: defaultClientTimeout})), + ) ) func validateRegion(ctx context.Context, client LinodeClient, id string, path *field.Path, capabilities ...string) *field.Error { + // TODO: instrument with tracing, might need refactor to preserve readibility region, err := client.GetRegion(ctx, id) if err != nil { return field.NotFound(path, id) @@ -42,6 +51,7 @@ func validateRegion(ctx context.Context, client LinodeClient, id string, path *f } func validateLinodeType(ctx context.Context, client LinodeClient, id string, path *field.Path) (*linodego.LinodeType, *field.Error) { + // TODO: instrument with tracing, might need refactor to preserve readibility plan, err := client.GetType(ctx, id) if err != nil { return nil, field.NotFound(path, id) @@ -61,6 +71,7 @@ func validateLinodeType(ctx context.Context, client LinodeClient, id string, pat // [Clusters List]: https://www.linode.com/docs/api/object-storage/#clusters-list // [Cluster View]: https://www.linode.com/docs/api/object-storage/#cluster-view func validateObjectStorageCluster(ctx context.Context, client LinodeClient, id string, path *field.Path) *field.Error { + // TODO: instrument with tracing, might need refactor to preserve readibility //nolint:gocritic // prefer no escapes cexp := regexp.MustCompile("^(([[:lower:]]+-)*[[:lower:]]+)-[[:digit:]]+$") if !cexp.MatchString(id) { diff --git a/api/v1alpha2/linodecluster_webhook.go b/api/v1alpha2/linodecluster_webhook.go index 042222608..602e86610 100644 --- a/api/v1alpha2/linodecluster_webhook.go +++ b/api/v1alpha2/linodecluster_webhook.go @@ -74,6 +74,7 @@ func (r *LinodeCluster) ValidateDelete() (admission.Warnings, error) { } func (r *LinodeCluster) validateLinodeCluster(ctx context.Context, client LinodeClient) error { + // TODO: instrument with tracing, might need refactor to preserve readibility var errs field.ErrorList if err := r.validateLinodeClusterSpec(ctx, client); err != nil { @@ -89,6 +90,7 @@ func (r *LinodeCluster) validateLinodeCluster(ctx context.Context, client Linode } func (r *LinodeCluster) validateLinodeClusterSpec(ctx context.Context, client LinodeClient) field.ErrorList { + // TODO: instrument with tracing, might need refactor to preserve readibility var errs field.ErrorList if err := validateRegion(ctx, client, r.Spec.Region, field.NewPath("spec").Child("region")); err != nil { diff --git a/api/v1alpha2/webhook_helpers.go b/api/v1alpha2/webhook_helpers.go index 326072da9..31a1f6ef5 100644 --- a/api/v1alpha2/webhook_helpers.go +++ b/api/v1alpha2/webhook_helpers.go @@ -26,6 +26,8 @@ import ( "github.com/linode/linodego" "k8s.io/apimachinery/pkg/util/validation/field" + "github.com/linode/cluster-api-provider-linode/observability/wrappers/linodeclient" + . "github.com/linode/cluster-api-provider-linode/clients" ) @@ -36,9 +38,15 @@ const ( defaultClientTimeout = time.Second * 10 ) +func mkptr[T any](v T) *T { + return &v +} + var ( // defaultLinodeClient is an unauthenticated Linode client - defaultLinodeClient = linodego.NewClient(&http.Client{Timeout: defaultClientTimeout}) + defaultLinodeClient = linodeclient.NewLinodeClientWithTracing( + mkptr(linodego.NewClient(&http.Client{Timeout: defaultClientTimeout})), + ) ) func validateRegion(ctx context.Context, client LinodeClient, id string, path *field.Path, capabilities ...string) *field.Error { diff --git a/cloud/scope/common.go b/cloud/scope/common.go index acc76553f..770291811 100644 --- a/cloud/scope/common.go +++ b/cloud/scope/common.go @@ -14,6 +14,7 @@ import ( "sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil" + "github.com/linode/cluster-api-provider-linode/observability/wrappers/linodeclient" "github.com/linode/cluster-api-provider-linode/version" . "github.com/linode/cluster-api-provider-linode/clients" @@ -24,7 +25,19 @@ const ( defaultClientTimeout = time.Second * 10 ) -func CreateLinodeClient(apiKey string, timeout time.Duration) (*linodego.Client, error) { +type Option struct { + set func(client *linodego.Client) +} + +func WithRetryCount(c int) Option { + return Option{ + set: func(client *linodego.Client) { + client.SetRetryCount(c) + }, + } +} + +func CreateLinodeClient(apiKey string, timeout time.Duration, opts ...Option) (LinodeClient, error) { if apiKey == "" { return nil, errors.New("missing Linode API key") } @@ -41,7 +54,13 @@ func CreateLinodeClient(apiKey string, timeout time.Duration) (*linodego.Client, linodeClient.SetUserAgent(fmt.Sprintf("CAPL/%s", version.GetVersion())) - return &linodeClient, nil + for _, opt := range opts { + opt.set(&linodeClient) + } + + return linodeclient.NewLinodeClientWithTracing( + &linodeClient, + ), nil } func getCredentialDataFromRef(ctx context.Context, crClient K8sClient, credentialsRef corev1.SecretReference, defaultNamespace string) ([]byte, error) { diff --git a/cloud/scope/machine.go b/cloud/scope/machine.go index 033bf8c3f..3c6284521 100644 --- a/cloud/scope/machine.go +++ b/cloud/scope/machine.go @@ -84,11 +84,12 @@ func NewMachineScope(ctx context.Context, apiKey string, params MachineScopePara } apiKey = string(data) } - linodeClient, err := CreateLinodeClient(apiKey, defaultClientTimeout) + linodeClient, err := CreateLinodeClient(apiKey, defaultClientTimeout, + WithRetryCount(0), + ) if err != nil { return nil, fmt.Errorf("failed to create linode client: %w", err) } - linodeClient.SetRetryCount(0) helper, err := patch.NewHelper(params.LinodeMachine, params.Client) if err != nil { diff --git a/cloud/scope/vpc.go b/cloud/scope/vpc.go index 5dd181782..5e92aef30 100644 --- a/cloud/scope/vpc.go +++ b/cloud/scope/vpc.go @@ -67,11 +67,12 @@ func NewVPCScope(ctx context.Context, apiKey string, params VPCScopeParams) (*VP } apiKey = string(data) } - linodeClient, err := CreateLinodeClient(apiKey, defaultClientTimeout) + linodeClient, err := CreateLinodeClient(apiKey, defaultClientTimeout, + WithRetryCount(0), + ) if err != nil { return nil, fmt.Errorf("failed to create linode client: %w", err) } - linodeClient.SetRetryCount(0) helper, err := patch.NewHelper(params.LinodeVPC, params.Client) if err != nil { diff --git a/cmd/main.go b/cmd/main.go index a05251636..eb264caa3 100644 --- a/cmd/main.go +++ b/cmd/main.go @@ -1,5 +1,5 @@ /* -Copyright 2023 Akamai Technologies, Inc. +Copyright 2023-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. @@ -17,11 +17,17 @@ limitations under the License. package main import ( + "context" "errors" "flag" "fmt" "os" + "strings" + "sync" + "time" + "go.opentelemetry.io/otel/sdk/resource" + semconv "go.opentelemetry.io/otel/semconv/v1.17.0" "k8s.io/apimachinery/pkg/runtime" utilruntime "k8s.io/apimachinery/pkg/util/runtime" clientgoscheme "k8s.io/client-go/kubernetes/scheme" @@ -35,6 +41,8 @@ import ( infrastructurev1alpha1 "github.com/linode/cluster-api-provider-linode/api/v1alpha1" infrastructurev1alpha2 "github.com/linode/cluster-api-provider-linode/api/v1alpha2" controller2 "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" @@ -49,6 +57,13 @@ var ( setupLog = ctrl.Log.WithName("setup") ) +const ( + controllerName = "cluster-api-provider-linode.linode.com" + gracePeriod = 5 * time.Second + envK8sNodeName = "K8S_NODE_NAME" + envK8sPodName = "K8S_POD_NAME" +) + func init() { utilruntime.Must(clientgoscheme.AddToScheme(scheme)) utilruntime.Must(capi.AddToScheme(scheme)) @@ -114,41 +129,52 @@ func main() { os.Exit(1) } - if err = (&controller2.LinodeClusterReconciler{ - Client: mgr.GetClient(), - Recorder: mgr.GetEventRecorderFor("LinodeClusterReconciler"), - WatchFilterValue: clusterWatchFilter, - LinodeApiKey: linodeToken, - }).SetupWithManager(mgr); err != nil { + if err = reconciler.NewReconcilerWithTracing( + &controller2.LinodeClusterReconciler{ + Client: mgr.GetClient(), + Recorder: mgr.GetEventRecorderFor("LinodeClusterReconciler"), + WatchFilterValue: clusterWatchFilter, + LinodeApiKey: linodeToken, + }, + ).SetupWithManager(mgr); err != nil { setupLog.Error(err, "unable to create controller", "controller", "LinodeCluster") os.Exit(1) } - if err = (&controller2.LinodeMachineReconciler{ - Client: mgr.GetClient(), - Scheme: mgr.GetScheme(), - Recorder: mgr.GetEventRecorderFor("LinodeMachineReconciler"), - WatchFilterValue: machineWatchFilter, - LinodeApiKey: linodeToken, - }).SetupWithManager(mgr); err != nil { + + if err = reconciler.NewReconcilerWithTracing( + &controller2.LinodeMachineReconciler{ + Client: mgr.GetClient(), + Scheme: mgr.GetScheme(), + Recorder: mgr.GetEventRecorderFor("LinodeMachineReconciler"), + WatchFilterValue: machineWatchFilter, + LinodeApiKey: linodeToken, + }, + ).SetupWithManager(mgr); err != nil { setupLog.Error(err, "unable to create controller", "controller", "LinodeMachine") os.Exit(1) } - if err = (&controller2.LinodeVPCReconciler{ - Client: mgr.GetClient(), - Recorder: mgr.GetEventRecorderFor("LinodeVPCReconciler"), - WatchFilterValue: clusterWatchFilter, - LinodeApiKey: linodeToken, - }).SetupWithManager(mgr); err != nil { + + if err = reconciler.NewReconcilerWithTracing( + &controller2.LinodeVPCReconciler{ + Client: mgr.GetClient(), + Recorder: mgr.GetEventRecorderFor("LinodeVPCReconciler"), + WatchFilterValue: clusterWatchFilter, + LinodeApiKey: linodeToken, + }, + ).SetupWithManager(mgr); err != nil { setupLog.Error(err, "unable to create controller", "controller", "LinodeVPC") os.Exit(1) } - if err = (&controller2.LinodeObjectStorageBucketReconciler{ - Client: mgr.GetClient(), - Logger: ctrl.Log.WithName("LinodeObjectStorageBucketReconciler"), - Recorder: mgr.GetEventRecorderFor("LinodeObjectStorageBucketReconciler"), - WatchFilterValue: objectStorageBucketWatchFilter, - LinodeApiKey: linodeToken, - }).SetupWithManager(mgr); err != nil { + + if err = reconciler.NewReconcilerWithTracing( + &controller2.LinodeObjectStorageBucketReconciler{ + Client: mgr.GetClient(), + Logger: ctrl.Log.WithName("LinodeObjectStorageBucketReconciler"), + Recorder: mgr.GetEventRecorderFor("LinodeObjectStorageBucketReconciler"), + WatchFilterValue: objectStorageBucketWatchFilter, + LinodeApiKey: linodeToken, + }, + ).SetupWithManager(mgr); err != nil { setupLog.Error(err, "unable to create controller", "controller", "LinodeObjectStorageBucket") os.Exit(1) } @@ -166,8 +192,18 @@ func main() { os.Exit(1) } - setupLog.Info("starting manager") - if err := mgr.Start(ctrl.SetupSignalHandler()); err != nil { + ctx := ctrl.SetupSignalHandler() + + // closure for mgr.Start, so we defers are running + run := func(ctx context.Context) error { + o11yShutdown := setupObservabillity(ctx) + defer o11yShutdown() + + setupLog.Info("starting manager") + return mgr.Start(ctx) + } + + if err := run(ctx); err != nil { setupLog.Error(err, "problem running manager") os.Exit(1) } @@ -196,3 +232,57 @@ func setupWebhooks(mgr manager.Manager) { os.Exit(1) } } + +func setupObservabillity(ctx context.Context) func() { + node := os.Getenv(envK8sNodeName) + pod := os.Getenv(envK8sPodName) + + res := resource.NewWithAttributes( + semconv.SchemaURL, + semconv.ServiceName(controllerName), + semconv.ServiceVersion(version.GetVersion()), + semconv.K8SPodName(pod), + semconv.K8SNodeName(node), + ) + + tracingShutdown, err := tracing.Setup(ctx, res) + if err != nil { + setupLog.Error(err, "failed to setup tracing") + } + + attrs := []any{} + + for _, kv := range os.Environ() { + k, v, ok := strings.Cut(kv, "=") + if ok && strings.HasPrefix(k, "OTEL_") { + attrs = append(attrs, k, v) + } + } + + setupLog.Info("opentelemetry configuration applied", + attrs..., + ) + + return func() { + timeout := 25 * time.Second //nolint:mnd // 2.5x default OTLP timeout + + ctx, cancel := context.WithTimeout(context.WithoutCancel(ctx), timeout) + defer cancel() + + wg := &sync.WaitGroup{} + + if tracingShutdown != nil { + wg.Add(1) + + go func() { + defer wg.Done() + + if err := tracingShutdown(ctx); err != nil { + setupLog.Error(err, "failed to shutdown tracing") + } + }() + } + + wg.Wait() + } +} diff --git a/cmd/main_test.go b/cmd/main_test.go new file mode 100644 index 000000000..828ff6996 --- /dev/null +++ b/cmd/main_test.go @@ -0,0 +1,33 @@ +/* +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 main + +import ( + "context" + "testing" + "time" +) + +func TestSetupObservabillity(t *testing.T) { + t.Parallel() + + ctx, cancel := context.WithTimeout(context.Background(), time.Second) + defer cancel() + + shutdown := setupObservabillity(ctx) + shutdown() +} diff --git a/codecov.yml b/codecov.yml new file mode 100644 index 000000000..23972b979 --- /dev/null +++ b/codecov.yml @@ -0,0 +1,2 @@ +ignore: + - "**/*.gen.go" diff --git a/docs/src/topics/distributed-tracing.md b/docs/src/topics/distributed-tracing.md new file mode 100644 index 000000000..a9d318a9a --- /dev/null +++ b/docs/src/topics/distributed-tracing.md @@ -0,0 +1,75 @@ +# Distributed Tracing + +Distributed tracing is a method used to monitor and track requests as they propagate through various services within a distributed system. In the context of Cluster API Provider Linode (CAPL), distributed tracing can help in identifying performance bottlenecks, debugging issues, and gaining insights into the behavior of the CAPL manager. + +## What is Distributed Tracing? + +Distributed tracing involves capturing trace data as requests flow through different components of a system. Each trace contains information about the request, including the start and end time, as well as any interactions with other services. This trace data is invaluable for understanding the system's performance and diagnosing issues. + +## Why Instrument CAPL with OpenTelemetry? + +OpenTelemetry is a collection of tools, APIs, and SDKs used to instrument, generate, collect, and export telemetry data (such as traces, metrics, and logs) to help understand software performance and behavior. By instrumenting the CAPL manager with OpenTelemetry, you can achieve the following benefits: + +- **Enhanced Observability**: Gain detailed insights into the internal workings of the CAPL manager. +- **Performance Monitoring**: Identify and address performance bottlenecks within your Kubernetes clusters. +- **Troubleshooting**: Easier debugging of issues by tracing requests through the entire lifecycle. +- **Integration**: Seamlessly integrate with various observability backends that support OpenTelemetry. + +## How to Instrument CAPL with OpenTelemetry + +To instrument the CAPL manager with OpenTelemetry, follow these steps: + +1. **Install Tracing Backend**: Deploy a supported OpenTelemetry backend to collect, process, and export telemetry data. This can be [Zipkin](https://zipkin.io/), [Jaeger](https://www.jaegertracing.io/), or any other collector supporting OpenTelemetry format. Refer to the respective documentation for setup instructions and configuration options. + +2. **Configure OpenTelemetry**: Set the necessary environment variables to configure OpenTelemetry for the CAPL manager. The primary environment variable is `OTEL_TRACES_EXPORTER`, which specifies the exporter for trace data. + +### Default Configuration + +By default, the `OTEL_TRACES_EXPORTER` is set to `none`. This means no trace data will be exported. To enable tracing, you need to set this variable to an appropriate exporter, such as `otlp` for OpenTelemetry Protocol. + +To customize the CAPL manager deployment and add extra environment variables, you can use Kustomize. Kustomize is a configuration management tool that allows you to customize Kubernetes resources declaratively. + +1. **Create a Kustomization File**: Create a `kustomization.yaml` file in your project directory. + + + +```yaml +# kustomization.yaml +resources: + - manager.yaml + +patchesStrategicMerge: + - add-otel-env-vars.yaml +``` + +2. **Create a Patch File**: Create a patch file, `add-otel-env-vars.yaml`, to add the necessary environment variables. + +```yaml +# add-otel-env-vars.yaml +apiVersion: apps/v1 +kind: Deployment +metadata: + name: controller-manager +spec: + template: + spec: + containers: + - name: manager + env: + - name: OTEL_TRACES_EXPORTER + value: "otlp" + - name: OTEL_EXPORTER_OTLP_ENDPOINT + value: "http://localhost:4317" +``` + +3. **Apply the Customization**: Use Kustomize to apply the changes to your CAPL manager deployment. + +```sh +kubectl apply -k . +``` + +## Conclusion + +Instrumenting the CAPL manager with OpenTelemetry provides powerful insights into CAPI system's performance and behavior. By setting up distributed tracing, you can enhance observability, improve performance monitoring, and streamline troubleshooting. Using Kustomize, you can easily customize the deployment configuration to include necessary environment variables for OpenTelemetry. + +For more detailed configuration options and examples, refer to the [OpenTelemetry documentation](https://opentelemetry.io/docs/specs/otel/configuration/sdk-environment-variables/). diff --git a/go.mod b/go.mod index 6747f500e..827c06705 100644 --- a/go.mod +++ b/go.mod @@ -12,6 +12,10 @@ require ( github.com/onsi/ginkgo/v2 v2.19.0 github.com/onsi/gomega v1.33.1 github.com/stretchr/testify v1.9.0 + go.opentelemetry.io/contrib/exporters/autoexport v0.52.0 + go.opentelemetry.io/otel v1.27.0 + go.opentelemetry.io/otel/sdk v1.27.0 + go.opentelemetry.io/otel/trace v1.27.0 go.uber.org/automaxprocs v1.5.3 go.uber.org/mock v0.4.0 go4.org/netipx v0.0.0-20231129151722-fdeea329fbba @@ -29,11 +33,13 @@ require ( require ( github.com/beorn7/perks v1.0.1 // indirect github.com/blang/semver/v4 v4.0.0 // indirect - github.com/cespare/xxhash/v2 v2.2.0 // indirect + github.com/cenkalti/backoff/v4 v4.3.0 // indirect + github.com/cespare/xxhash/v2 v2.3.0 // indirect github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect github.com/emicklei/go-restful/v3 v3.11.0 // indirect github.com/evanphx/json-patch/v5 v5.9.0 // indirect github.com/fsnotify/fsnotify v1.7.0 // indirect + github.com/go-logr/stdr v1.2.2 // indirect github.com/go-logr/zapr v1.3.0 // indirect github.com/go-openapi/jsonpointer v0.19.6 // indirect github.com/go-openapi/jsonreference v0.20.2 // indirect @@ -47,21 +53,33 @@ require ( github.com/google/gnostic-models v0.6.8 // indirect github.com/google/gofuzz v1.2.0 // indirect github.com/google/pprof v0.0.0-20240424215950-a892ee059fd6 // indirect + github.com/grpc-ecosystem/grpc-gateway/v2 v2.20.0 // indirect github.com/imdario/mergo v0.3.13 // indirect github.com/josharian/intern v1.0.0 // indirect github.com/json-iterator/go v1.1.12 // indirect github.com/mailru/easyjson v0.7.7 // indirect - github.com/matttproud/golang_protobuf_extensions/v2 v2.0.0 // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/reflect2 v1.0.2 // indirect github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect github.com/pkg/errors v0.9.1 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect - github.com/prometheus/client_golang v1.18.0 // indirect - github.com/prometheus/client_model v0.5.0 // indirect - github.com/prometheus/common v0.45.0 // indirect - github.com/prometheus/procfs v0.12.0 // indirect + github.com/prometheus/client_golang v1.19.1 // indirect + github.com/prometheus/client_model v0.6.1 // indirect + github.com/prometheus/common v0.53.0 // indirect + github.com/prometheus/procfs v0.15.0 // indirect github.com/spf13/pflag v1.0.5 // indirect + go.opentelemetry.io/contrib/bridges/prometheus v0.52.0 // indirect + go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.27.0 // indirect + go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp v1.27.0 // indirect + go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.27.0 // indirect + go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.27.0 // indirect + go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.27.0 // indirect + go.opentelemetry.io/otel/exporters/prometheus v0.49.0 // indirect + go.opentelemetry.io/otel/exporters/stdout/stdoutmetric v1.27.0 // indirect + go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.27.0 // indirect + go.opentelemetry.io/otel/metric v1.27.0 // indirect + go.opentelemetry.io/otel/sdk/metric v1.27.0 // indirect + go.opentelemetry.io/proto/otlp v1.2.0 // indirect go.uber.org/multierr v1.11.0 // indirect go.uber.org/zap v1.26.0 // indirect golang.org/x/exp v0.0.0-20230905200255-921286631fa9 // indirect @@ -72,7 +90,10 @@ require ( golang.org/x/time v0.5.0 // indirect golang.org/x/tools v0.21.0 // indirect gomodules.xyz/jsonpatch/v2 v2.4.0 // 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-20240520151616-dc85e6b867a5 // 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/ini.v1 v1.67.0 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect diff --git a/go.sum b/go.sum index 188ca437d..2d4610078 100644 --- a/go.sum +++ b/go.sum @@ -14,8 +14,10 @@ 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/blang/semver/v4 v4.0.0 h1:1PFHFE6yCCTv8C1TeyNNarDzntLi7wMI5i/pzqYIsAM= github.com/blang/semver/v4 v4.0.0/go.mod h1:IbckMUScFkM3pff0VJDNKRiT6TG/YpiHIM2yvyW5YoQ= -github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44= -github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +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/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs= +github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/coredns/caddy v1.1.0 h1:ezvsPrT/tA/7pYDBZxu0cT0VmWk75AfIaf6GSYCNMf0= github.com/coredns/caddy v1.1.0/go.mod h1:A6ntJQlAWuQfFlsd9hvigKbo2WS0VUs2l1e2F+BawD4= github.com/coredns/corefile-migration v1.0.21 h1:W/DCETrHDiFo0Wj03EyMkaQ9fwsmSgqTCQDHpceaSsE= @@ -35,8 +37,11 @@ github.com/evanphx/json-patch/v5 v5.9.0 h1:kcBlZQbplgElYIlo/n1hJbls2z/1awpXxpRi0 github.com/evanphx/json-patch/v5 v5.9.0/go.mod h1:VNkHZ/282BpEyt/tObQO8s5CMPmYYq14uClGH4abBuQ= github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA= github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM= +github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= 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.3.0 h1:XGdV8XW8zdwFiwOA2Dryh1gj2KRQyOOoNmBy4EplIcQ= github.com/go-logr/zapr v1.3.0/go.mod h1:YKepepNBd1u/oyhd/yQmtjVXmm9uML4IXUgMOwR8/Gg= github.com/go-openapi/jsonpointer v0.19.6 h1:eCs3fxoIi3Wh6vtgmLTOjdhSpiqphQ+DaPn38N2ZdrE= @@ -71,6 +76,8 @@ github.com/google/pprof v0.0.0-20240424215950-a892ee059fd6 h1:k7nVchz72niMH6YLQN github.com/google/pprof v0.0.0-20240424215950-a892ee059fd6/go.mod h1:kf6iHlnVGwgKolg33glAes7Yg/8iWP8ukqeldJSO7jw= 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/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/huandu/xstrings v1.3.3 h1:/Gcsuc1x8JVbJ9/rlye4xZnVAbEkGauT8lbebqcQws4= github.com/huandu/xstrings v1.3.3/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE= github.com/imdario/mergo v0.3.13 h1:lFzP57bqS/wsqKssCGmtLAb8A0wKjLGrve2q3PPVcBk= @@ -94,8 +101,6 @@ github.com/linode/linodego v1.35.0 h1:rIhUeCHBLEDlkoRnOTwzSGzljQ3ksXwLxacmXnrV+D github.com/linode/linodego v1.35.0/go.mod h1:JxuhOEAMfSxun6RU5/MgTKH2GGTmFrhKRj3wL1NFin0= github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0= github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= -github.com/matttproud/golang_protobuf_extensions/v2 v2.0.0 h1:jWpvCLoY8Z/e3VKvlsiIGKtc+UG6U5vzxaoagmhXfyg= -github.com/matttproud/golang_protobuf_extensions/v2 v2.0.0/go.mod h1:QUyp042oQthUoa9bqDv0ER0wrtXnBruoNd7aNjkbP+k= github.com/mitchellh/copystructure v1.2.0 h1:vpKXTN4ewci03Vljg/q9QvCGUDttBOGBIa15WveJJGw= github.com/mitchellh/copystructure v1.2.0/go.mod h1:qLl+cE2AmVv+CoeAwDPye/v+N2HKCj9FbZEVFJRxO9s= github.com/mitchellh/reflectwalk v1.0.2 h1:G2LzWKi524PWgd3mLHV8Y5k7s6XUvT0Gef6zxSIeXaQ= @@ -119,16 +124,16 @@ github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZb github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/prashantv/gostub v1.1.0 h1:BTyx3RfQjRHnUWaGF9oQos79AlQ5k8WNktv7VGvVH4g= github.com/prashantv/gostub v1.1.0/go.mod h1:A5zLQHz7ieHGG7is6LLXLz7I8+3LZzsrV0P1IAHhP5U= -github.com/prometheus/client_golang v1.18.0 h1:HzFfmkOzH5Q8L8G+kSJKUx5dtG87sewO+FoDDqP5Tbk= -github.com/prometheus/client_golang v1.18.0/go.mod h1:T+GXkCk5wSJyOqMIzVgvvjFDlkOQntgjkJWKrN5txjA= -github.com/prometheus/client_model v0.5.0 h1:VQw1hfvPvk3Uv6Qf29VrPF32JB6rtbgI6cYPYQjL0Qw= -github.com/prometheus/client_model v0.5.0/go.mod h1:dTiFglRmd66nLR9Pv9f0mZi7B7fk5Pm3gvsjB5tr+kI= -github.com/prometheus/common v0.45.0 h1:2BGz0eBc2hdMDLnO/8n0jeB3oPrt2D08CekT0lneoxM= -github.com/prometheus/common v0.45.0/go.mod h1:YJmSTw9BoKxJplESWWxlbyttQR4uaEcGyv9MZjVOJsY= -github.com/prometheus/procfs v0.12.0 h1:jluTpSng7V9hY0O2R9DzzJHYb2xULk9VTR1V1R/k6Bo= -github.com/prometheus/procfs v0.12.0/go.mod h1:pcuDEFsWDnvcgNzo4EEweacyhjeA9Zk3cnaOZAZEfOo= -github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ= -github.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncjaFoBhdsK/akog= +github.com/prometheus/client_golang v1.19.1 h1:wZWJDwK+NameRJuPGDhlnFgx8e8HN3XHQeLaYJFJBOE= +github.com/prometheus/client_golang v1.19.1/go.mod h1:mP78NwGzrVks5S2H6ab8+ZZGJLZUq1hoULYBAYBw1Ho= +github.com/prometheus/client_model v0.6.1 h1:ZKSh/rekM+n3CeS952MLRAdFwIKqeY8b62p8ais2e9E= +github.com/prometheus/client_model v0.6.1/go.mod h1:OrxVMOVHjw3lKMa8+x6HeMGkHMQyHDk9E3jmP2AmGiY= +github.com/prometheus/common v0.53.0 h1:U2pL9w9nmJwJDa4qqLQ3ZaePJ6ZTwt7cMD3AG3+aLCE= +github.com/prometheus/common v0.53.0/go.mod h1:BrxBKv3FWBIGXw89Mg1AeBq7FSyRzXWI3l3e7W3RN5U= +github.com/prometheus/procfs v0.15.0 h1:A82kmvXJq2jTu5YUhSGNlYoxh85zLnKgPz4bMZgI5Ek= +github.com/prometheus/procfs v0.15.0/go.mod h1:Y0RJ/Y5g5wJpkTisOtqwDSo4HwhGmLB4VQSw2sQJLHk= +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/shopspring/decimal v1.3.1 h1:2Usl1nmF/WZucqkFZhnfFYxxxu8LG21F6nPQBE5gKV8= github.com/shopspring/decimal v1.3.1/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o= github.com/spf13/cast v1.6.0 h1:GEiTHELF+vaR5dhz3VqZfFSzZjYbgeKDpBxQVS4GYJ0= @@ -149,6 +154,40 @@ github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8 github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= +go.opentelemetry.io/collector/pdata v1.7.0 h1:/WNsBbE6KM3TTPUb9v/5B7IDqnDkgf8GyFhVJJqu7II= +go.opentelemetry.io/collector/pdata v1.7.0/go.mod h1:ehCBBA5GoFrMZkwyZAKGY/lAVSgZf6rzUt3p9mddmPU= +go.opentelemetry.io/contrib/bridges/prometheus v0.52.0 h1:NNkEjNcUXeNcxDTNLyyAmFHefByhj8YU1AojgcPqbfs= +go.opentelemetry.io/contrib/bridges/prometheus v0.52.0/go.mod h1:Dv7d2yUvusfblvi9qMQby+youF09GiUVWRWkdogrDtE= +go.opentelemetry.io/contrib/exporters/autoexport v0.52.0 h1:G/AGl5O78ZKHs63Rl65P1HyZfDnTyxjv8r7dbdZ9fB0= +go.opentelemetry.io/contrib/exporters/autoexport v0.52.0/go.mod h1:WoVWPZjJ7EB5Z9aROW1DZuRIoFEemxmhCdZJlcjY2AE= +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/otlpmetric/otlpmetricgrpc v1.27.0 h1:bFgvUr3/O4PHj3VQcFEuYKvRZJX1SJDQ+11JXuSB3/w= +go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.27.0/go.mod h1:xJntEd2KL6Qdg5lwp97HMLQDVeAhrYxmzFseAMDPQ8I= +go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp v1.27.0 h1:CIHWikMsN3wO+wq1Tp5VGdVRTcON+DmOJSfDjXypKOc= +go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp v1.27.0/go.mod h1:TNupZ6cxqyFEpLXAZW7On+mLFL0/g0TE3unIYL91xWc= +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/exporters/otlp/otlptrace/otlptracehttp v1.27.0 h1:QY7/0NeRPKlzusf40ZE4t1VlMKbqSNT7cJRYzWuja0s= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.27.0/go.mod h1:HVkSiDhTM9BoUJU8qE6j2eSWLLXvi1USXjyd2BXT8PY= +go.opentelemetry.io/otel/exporters/prometheus v0.49.0 h1:Er5I1g/YhfYv9Affk9nJLfH/+qCCVVg1f2R9AbJfqDQ= +go.opentelemetry.io/otel/exporters/prometheus v0.49.0/go.mod h1:KfQ1wpjf3zsHjzP149P4LyAwWRupc6c7t1ZJ9eXpKQM= +go.opentelemetry.io/otel/exporters/stdout/stdoutmetric v1.27.0 h1:/jlt1Y8gXWiHG9FBx6cJaIC5hYx5Fe64nC8w5Cylt/0= +go.opentelemetry.io/otel/exporters/stdout/stdoutmetric v1.27.0/go.mod h1:bmToOGOBZ4hA9ghphIc1PAf66VA8KOtsuy3+ScStG20= +go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.27.0 h1:/0YaXu3755A/cFbtXp+21lkXgI0QE5avTWA2HjU9/WE= +go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.27.0/go.mod h1:m7SFxp0/7IxmJPLIY3JhOcU9CoFzDaCPL6xxQIxhA+o= +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/sdk/metric v1.27.0 h1:5uGNOlpXi+Hbo/DRoI31BSb1v+OGcpv2NemcCrOL8gI= +go.opentelemetry.io/otel/sdk/metric v1.27.0/go.mod h1:we7jJVrYN2kh3mVBlswtPU22K0SA+769l93J6bsyvqw= +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/automaxprocs v1.5.3 h1:kWazyxZUrS3Gs4qUpbwo5kEIMGe/DAvi5Z4tl2NW4j8= go.uber.org/automaxprocs v1.5.3/go.mod h1:eRbA25aqJrxAbsLO0xy5jVwPt7FQnRgjW+efnwa1WM0= go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= @@ -239,13 +278,14 @@ golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8T golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= gomodules.xyz/jsonpatch/v2 v2.4.0 h1:Ci3iUJyx9UeRx7CeFN8ARgGbkESwJK+KB9lLcWxY/Zw= gomodules.xyz/jsonpatch/v2 v2.4.0/go.mod h1:AH3dM2RI6uoBZxn3LVrfvJ3E0/9dG4cSrbuBJT4moAY= -google.golang.org/genproto v0.0.0-20231106174013-bbf56f31fb17 h1:wpZ8pe2x1Q3f2KyT5f8oP/fa9rHAKgFPr/HZdNuS+PQ= -google.golang.org/genproto/googleapis/api v0.0.0-20231106174013-bbf56f31fb17 h1:JpwMPBpFN3uKhdaekDpiNlImDdkUAyiJ6ez/uxGaUSo= -google.golang.org/genproto/googleapis/api v0.0.0-20231106174013-bbf56f31fb17/go.mod h1:0xJLfVdJqpAPl8tDg1ujOCGzx6LFLttXT5NhllGOXY4= -google.golang.org/genproto/googleapis/rpc v0.0.0-20231120223509-83a465c0220f h1:ultW7fxlIvee4HYrtnaRPon9HpEgFk5zYpmfMgtKB5I= -google.golang.org/genproto/googleapis/rpc v0.0.0-20231120223509-83a465c0220f/go.mod h1:L9KNLi232K1/xB6f7AlSX692koaRnKaWSR0stBki0Yc= -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/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-20240520151616-dc85e6b867a5 h1:Q2RxlXqh1cgzzUgV261vBO2jI5R/3DD1J2pM0nI4NhU= +google.golang.org/genproto/googleapis/rpc v0.0.0-20240520151616-dc85e6b867a5/go.mod h1:EfXuqaE1J41VCDicxHzUDm+8rk+7ZdXzHV0IhO/I6s0= +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 v1.34.1 h1:9ddQBjfCyZPOHPUiPxpYESBLc+T8P3E+Vo4IbKZgFWg= +google.golang.org/protobuf v1.34.1/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/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= diff --git a/hack/templates/opentelemetry.go.gotpl b/hack/templates/opentelemetry.go.gotpl new file mode 100644 index 000000000..92411d6bf --- /dev/null +++ b/hack/templates/opentelemetry.go.gotpl @@ -0,0 +1,52 @@ +{{.Import}} + +import ( + "github.com/linode/cluster-api-provider-linode/observability/tracing" + "go.opentelemetry.io/otel" + "go.opentelemetry.io/otel/attribute" + "go.opentelemetry.io/otel/trace" +) + +{{ $decorator := (or .Vars.DecoratorName (printf "%sWithTracing" .Interface.Name)) }} + +// {{$decorator}} implements {{.Interface.Type}} interface instrumented with opentracing spans +type {{$decorator}} struct { + {{.Interface.Type}} + _spanDecorator func(span trace.Span, params, results map[string]interface{}) +} + +// New{{$decorator}} returns {{$decorator}} +func New{{$decorator}} (base {{.Interface.Type}}, spanDecorator ...func(span trace.Span, params, results map[string]interface{})) {{$decorator}} { + d := {{$decorator}} { + {{.Interface.Name}}: base, + } + + if len(spanDecorator) > 0 && spanDecorator[0] != nil { + d._spanDecorator = spanDecorator[0] + } + + return d +} + +{{range $method := .Interface.Methods}} + {{if $method.AcceptsContext}} + // {{$method.Name}} implements {{$.Interface.Type}} +func (_d {{$decorator}}) {{$method.Declaration}} { + ctx, _span := tracing.Start(ctx, "{{$.Interface.Type}}.{{$method.Name}}") + defer func() { + if _d._spanDecorator != nil { + _d._spanDecorator(_span, {{$method.ParamsMap}}, {{$method.ResultsMap}}) + }{{- if $method.ReturnsError}} else if err != nil { + _span.RecordError(err) + _span.SetAttributes( + attribute.String("event", "error"), + attribute.String("message", err.Error()), + ) + } + {{end}} + _span.End() + }() + {{$method.Pass (printf "_d.%s." $.Interface.Name) }} +} + {{end}} +{{end}} diff --git a/observability/tracing/tracing.go b/observability/tracing/tracing.go new file mode 100644 index 000000000..e6b8832a8 --- /dev/null +++ b/observability/tracing/tracing.go @@ -0,0 +1,62 @@ +/* +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 tracing + +import ( + "context" + + "go.opentelemetry.io/contrib/exporters/autoexport" + "go.opentelemetry.io/otel" + "go.opentelemetry.io/otel/propagation" + "go.opentelemetry.io/otel/sdk/resource" + sdktrace "go.opentelemetry.io/otel/sdk/trace" + "go.opentelemetry.io/otel/trace" +) + +const ( + tracerName = "github.com/linode/cluster-api-provider-linode/observability/tracing" + defaultSamplingRatio = 1 +) + +// Setup sets up the OpenTelemetry tracer provider. +func Setup(ctx context.Context, res *resource.Resource) (func(context.Context) error, error) { + exporter, err := autoexport.NewSpanExporter(ctx) + if err != nil { + return nil, err + } + + options := []sdktrace.TracerProviderOption{ + sdktrace.WithBatcher(exporter), + } + if res != nil { + options = append(options, sdktrace.WithResource(res)) + } + + tp := sdktrace.NewTracerProvider(options...) + otel.SetTracerProvider(tp) + + // set global propagator to tracecontext (the default is no-op). + otel.SetTextMapPropagator(propagation.TraceContext{}) + + // Shutdown will flush any remaining spans and shut down the exporter. + return tp.Shutdown, nil +} + +// Start starts a new span with the given name. +func Start(ctx context.Context, name string) (context.Context, trace.Span) { + return otel.Tracer(tracerName).Start(ctx, name) //nolint:spancheck // wrapper for start, user is respobsible for handling that span. +} diff --git a/observability/tracing/tracing_test.go b/observability/tracing/tracing_test.go new file mode 100644 index 000000000..616387a99 --- /dev/null +++ b/observability/tracing/tracing_test.go @@ -0,0 +1,58 @@ +/* +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 tracing + +import ( + "context" + "testing" + "time" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "go.opentelemetry.io/otel/sdk/resource" +) + +func TestSetup(t *testing.T) { + t.Parallel() + + for name, tc := range map[string]struct { + env map[string]string + resource *resource.Resource + }{ + "smoke": { + env: make(map[string]string), + resource: resource.Default(), + }, + } { + tc := tc + + t.Run(name, func(t *testing.T) { + for k, v := range tc.env { + t.Setenv(k, v) + } + + ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) + defer cancel() + + shutdown, err := Setup(ctx, tc.resource) + require.NoError(t, err) + + err = shutdown(ctx) + assert.NoError(t, err) + }) + } +} diff --git a/observability/wrappers/interfaces.go b/observability/wrappers/interfaces.go new file mode 100644 index 000000000..5bac0a445 --- /dev/null +++ b/observability/wrappers/interfaces.go @@ -0,0 +1,35 @@ +/* +Copyright 2023 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 ( + "context" + + ctrl "sigs.k8s.io/controller-runtime" + "sigs.k8s.io/controller-runtime/pkg/client" + "sigs.k8s.io/controller-runtime/pkg/manager" +) + +type Reconciler interface { + client.Reader + client.Writer + client.StatusClient + client.SubResourceClientConstructor + + Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) + SetupWithManager(mgr manager.Manager) error +} diff --git a/observability/wrappers/linodeclient/linodeclient.gen.go b/observability/wrappers/linodeclient/linodeclient.gen.go new file mode 100644 index 000000000..f3140af90 --- /dev/null +++ b/observability/wrappers/linodeclient/linodeclient.gen.go @@ -0,0 +1,756 @@ +// Code generated by gowrap. DO NOT EDIT. +// template: ../../../hack/templates/opentelemetry.go.gotpl +// gowrap: http://github.com/hexdigest/gowrap + +package linodeclient + +//go:generate gowrap gen -p github.com/linode/cluster-api-provider-linode/clients -i LinodeClient -t ../../../hack/templates/opentelemetry.go.gotpl -o linodeclient.gen.go -l "" + +import ( + "context" + + "github.com/linode/cluster-api-provider-linode/clients" + "github.com/linode/cluster-api-provider-linode/observability/tracing" + "github.com/linode/linodego" + "go.opentelemetry.io/otel/attribute" + "go.opentelemetry.io/otel/trace" +) + +// LinodeClientWithTracing implements clients.LinodeClient interface instrumented with opentracing spans +type LinodeClientWithTracing struct { + clients.LinodeClient + _spanDecorator func(span trace.Span, params, results map[string]interface{}) +} + +// NewLinodeClientWithTracing returns LinodeClientWithTracing +func NewLinodeClientWithTracing(base clients.LinodeClient, spanDecorator ...func(span trace.Span, params, results map[string]interface{})) LinodeClientWithTracing { + d := LinodeClientWithTracing{ + LinodeClient: base, + } + + if len(spanDecorator) > 0 && spanDecorator[0] != nil { + d._spanDecorator = spanDecorator[0] + } + + return d +} + +// BootInstance implements clients.LinodeClient +func (_d LinodeClientWithTracing) BootInstance(ctx context.Context, linodeID int, configID int) (err error) { + ctx, _span := tracing.Start(ctx, "clients.LinodeClient.BootInstance") + defer func() { + if _d._spanDecorator != nil { + _d._spanDecorator(_span, map[string]interface{}{ + "ctx": ctx, + "linodeID": linodeID, + "configID": configID}, 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.LinodeClient.BootInstance(ctx, linodeID, configID) +} + +// CreateInstance implements clients.LinodeClient +func (_d LinodeClientWithTracing) CreateInstance(ctx context.Context, opts linodego.InstanceCreateOptions) (ip1 *linodego.Instance, err error) { + ctx, _span := tracing.Start(ctx, "clients.LinodeClient.CreateInstance") + defer func() { + if _d._spanDecorator != nil { + _d._spanDecorator(_span, map[string]interface{}{ + "ctx": ctx, + "opts": opts}, map[string]interface{}{ + "ip1": ip1, + "err": err}) + } else if err != nil { + _span.RecordError(err) + _span.SetAttributes( + attribute.String("event", "error"), + attribute.String("message", err.Error()), + ) + } + + _span.End() + }() + return _d.LinodeClient.CreateInstance(ctx, opts) +} + +// CreateInstanceDisk implements clients.LinodeClient +func (_d LinodeClientWithTracing) CreateInstanceDisk(ctx context.Context, linodeID int, opts linodego.InstanceDiskCreateOptions) (ip1 *linodego.InstanceDisk, err error) { + ctx, _span := tracing.Start(ctx, "clients.LinodeClient.CreateInstanceDisk") + defer func() { + if _d._spanDecorator != nil { + _d._spanDecorator(_span, map[string]interface{}{ + "ctx": ctx, + "linodeID": linodeID, + "opts": opts}, map[string]interface{}{ + "ip1": ip1, + "err": err}) + } else if err != nil { + _span.RecordError(err) + _span.SetAttributes( + attribute.String("event", "error"), + attribute.String("message", err.Error()), + ) + } + + _span.End() + }() + return _d.LinodeClient.CreateInstanceDisk(ctx, linodeID, opts) +} + +// CreateNodeBalancer implements clients.LinodeClient +func (_d LinodeClientWithTracing) CreateNodeBalancer(ctx context.Context, opts linodego.NodeBalancerCreateOptions) (np1 *linodego.NodeBalancer, err error) { + ctx, _span := tracing.Start(ctx, "clients.LinodeClient.CreateNodeBalancer") + defer func() { + if _d._spanDecorator != nil { + _d._spanDecorator(_span, map[string]interface{}{ + "ctx": ctx, + "opts": opts}, map[string]interface{}{ + "np1": np1, + "err": err}) + } else if err != nil { + _span.RecordError(err) + _span.SetAttributes( + attribute.String("event", "error"), + attribute.String("message", err.Error()), + ) + } + + _span.End() + }() + return _d.LinodeClient.CreateNodeBalancer(ctx, opts) +} + +// CreateNodeBalancerConfig implements clients.LinodeClient +func (_d LinodeClientWithTracing) CreateNodeBalancerConfig(ctx context.Context, nodebalancerID int, opts linodego.NodeBalancerConfigCreateOptions) (np1 *linodego.NodeBalancerConfig, err error) { + ctx, _span := tracing.Start(ctx, "clients.LinodeClient.CreateNodeBalancerConfig") + defer func() { + if _d._spanDecorator != nil { + _d._spanDecorator(_span, map[string]interface{}{ + "ctx": ctx, + "nodebalancerID": nodebalancerID, + "opts": opts}, map[string]interface{}{ + "np1": np1, + "err": err}) + } else if err != nil { + _span.RecordError(err) + _span.SetAttributes( + attribute.String("event", "error"), + attribute.String("message", err.Error()), + ) + } + + _span.End() + }() + return _d.LinodeClient.CreateNodeBalancerConfig(ctx, nodebalancerID, opts) +} + +// CreateNodeBalancerNode implements clients.LinodeClient +func (_d LinodeClientWithTracing) CreateNodeBalancerNode(ctx context.Context, nodebalancerID int, configID int, opts linodego.NodeBalancerNodeCreateOptions) (np1 *linodego.NodeBalancerNode, err error) { + ctx, _span := tracing.Start(ctx, "clients.LinodeClient.CreateNodeBalancerNode") + defer func() { + if _d._spanDecorator != nil { + _d._spanDecorator(_span, map[string]interface{}{ + "ctx": ctx, + "nodebalancerID": nodebalancerID, + "configID": configID, + "opts": opts}, map[string]interface{}{ + "np1": np1, + "err": err}) + } else if err != nil { + _span.RecordError(err) + _span.SetAttributes( + attribute.String("event", "error"), + attribute.String("message", err.Error()), + ) + } + + _span.End() + }() + return _d.LinodeClient.CreateNodeBalancerNode(ctx, nodebalancerID, configID, opts) +} + +// CreateObjectStorageBucket implements clients.LinodeClient +func (_d LinodeClientWithTracing) CreateObjectStorageBucket(ctx context.Context, opts linodego.ObjectStorageBucketCreateOptions) (op1 *linodego.ObjectStorageBucket, err error) { + ctx, _span := tracing.Start(ctx, "clients.LinodeClient.CreateObjectStorageBucket") + defer func() { + if _d._spanDecorator != nil { + _d._spanDecorator(_span, map[string]interface{}{ + "ctx": ctx, + "opts": opts}, map[string]interface{}{ + "op1": op1, + "err": err}) + } else if err != nil { + _span.RecordError(err) + _span.SetAttributes( + attribute.String("event", "error"), + attribute.String("message", err.Error()), + ) + } + + _span.End() + }() + return _d.LinodeClient.CreateObjectStorageBucket(ctx, opts) +} + +// CreateObjectStorageKey implements clients.LinodeClient +func (_d LinodeClientWithTracing) CreateObjectStorageKey(ctx context.Context, opts linodego.ObjectStorageKeyCreateOptions) (op1 *linodego.ObjectStorageKey, err error) { + ctx, _span := tracing.Start(ctx, "clients.LinodeClient.CreateObjectStorageKey") + defer func() { + if _d._spanDecorator != nil { + _d._spanDecorator(_span, map[string]interface{}{ + "ctx": ctx, + "opts": opts}, map[string]interface{}{ + "op1": op1, + "err": err}) + } else if err != nil { + _span.RecordError(err) + _span.SetAttributes( + attribute.String("event", "error"), + attribute.String("message", err.Error()), + ) + } + + _span.End() + }() + return _d.LinodeClient.CreateObjectStorageKey(ctx, opts) +} + +// CreateStackscript implements clients.LinodeClient +func (_d LinodeClientWithTracing) CreateStackscript(ctx context.Context, opts linodego.StackscriptCreateOptions) (sp1 *linodego.Stackscript, err error) { + ctx, _span := tracing.Start(ctx, "clients.LinodeClient.CreateStackscript") + defer func() { + if _d._spanDecorator != nil { + _d._spanDecorator(_span, map[string]interface{}{ + "ctx": ctx, + "opts": opts}, map[string]interface{}{ + "sp1": sp1, + "err": err}) + } else if err != nil { + _span.RecordError(err) + _span.SetAttributes( + attribute.String("event", "error"), + attribute.String("message", err.Error()), + ) + } + + _span.End() + }() + return _d.LinodeClient.CreateStackscript(ctx, opts) +} + +// CreateVPC implements clients.LinodeClient +func (_d LinodeClientWithTracing) CreateVPC(ctx context.Context, opts linodego.VPCCreateOptions) (vp1 *linodego.VPC, err error) { + ctx, _span := tracing.Start(ctx, "clients.LinodeClient.CreateVPC") + defer func() { + if _d._spanDecorator != nil { + _d._spanDecorator(_span, map[string]interface{}{ + "ctx": ctx, + "opts": opts}, map[string]interface{}{ + "vp1": vp1, + "err": err}) + } else if err != nil { + _span.RecordError(err) + _span.SetAttributes( + attribute.String("event", "error"), + attribute.String("message", err.Error()), + ) + } + + _span.End() + }() + return _d.LinodeClient.CreateVPC(ctx, opts) +} + +// DeleteInstance implements clients.LinodeClient +func (_d LinodeClientWithTracing) DeleteInstance(ctx context.Context, linodeID int) (err error) { + ctx, _span := tracing.Start(ctx, "clients.LinodeClient.DeleteInstance") + defer func() { + if _d._spanDecorator != nil { + _d._spanDecorator(_span, map[string]interface{}{ + "ctx": ctx, + "linodeID": linodeID}, 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.LinodeClient.DeleteInstance(ctx, linodeID) +} + +// DeleteNodeBalancer implements clients.LinodeClient +func (_d LinodeClientWithTracing) DeleteNodeBalancer(ctx context.Context, nodebalancerID int) (err error) { + ctx, _span := tracing.Start(ctx, "clients.LinodeClient.DeleteNodeBalancer") + defer func() { + if _d._spanDecorator != nil { + _d._spanDecorator(_span, map[string]interface{}{ + "ctx": ctx, + "nodebalancerID": nodebalancerID}, 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.LinodeClient.DeleteNodeBalancer(ctx, nodebalancerID) +} + +// DeleteNodeBalancerNode implements clients.LinodeClient +func (_d LinodeClientWithTracing) DeleteNodeBalancerNode(ctx context.Context, nodebalancerID int, configID int, nodeID int) (err error) { + ctx, _span := tracing.Start(ctx, "clients.LinodeClient.DeleteNodeBalancerNode") + defer func() { + if _d._spanDecorator != nil { + _d._spanDecorator(_span, map[string]interface{}{ + "ctx": ctx, + "nodebalancerID": nodebalancerID, + "configID": configID, + "nodeID": nodeID}, 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.LinodeClient.DeleteNodeBalancerNode(ctx, nodebalancerID, configID, nodeID) +} + +// DeleteObjectStorageKey implements clients.LinodeClient +func (_d LinodeClientWithTracing) DeleteObjectStorageKey(ctx context.Context, keyID int) (err error) { + ctx, _span := tracing.Start(ctx, "clients.LinodeClient.DeleteObjectStorageKey") + defer func() { + if _d._spanDecorator != nil { + _d._spanDecorator(_span, map[string]interface{}{ + "ctx": ctx, + "keyID": keyID}, 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.LinodeClient.DeleteObjectStorageKey(ctx, keyID) +} + +// DeleteVPC implements clients.LinodeClient +func (_d LinodeClientWithTracing) DeleteVPC(ctx context.Context, vpcID int) (err error) { + ctx, _span := tracing.Start(ctx, "clients.LinodeClient.DeleteVPC") + defer func() { + if _d._spanDecorator != nil { + _d._spanDecorator(_span, map[string]interface{}{ + "ctx": ctx, + "vpcID": vpcID}, 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.LinodeClient.DeleteVPC(ctx, vpcID) +} + +// GetImage implements clients.LinodeClient +func (_d LinodeClientWithTracing) GetImage(ctx context.Context, imageID string) (ip1 *linodego.Image, err error) { + ctx, _span := tracing.Start(ctx, "clients.LinodeClient.GetImage") + defer func() { + if _d._spanDecorator != nil { + _d._spanDecorator(_span, map[string]interface{}{ + "ctx": ctx, + "imageID": imageID}, map[string]interface{}{ + "ip1": ip1, + "err": err}) + } else if err != nil { + _span.RecordError(err) + _span.SetAttributes( + attribute.String("event", "error"), + attribute.String("message", err.Error()), + ) + } + + _span.End() + }() + return _d.LinodeClient.GetImage(ctx, imageID) +} + +// GetInstance implements clients.LinodeClient +func (_d LinodeClientWithTracing) GetInstance(ctx context.Context, linodeID int) (ip1 *linodego.Instance, err error) { + ctx, _span := tracing.Start(ctx, "clients.LinodeClient.GetInstance") + defer func() { + if _d._spanDecorator != nil { + _d._spanDecorator(_span, map[string]interface{}{ + "ctx": ctx, + "linodeID": linodeID}, map[string]interface{}{ + "ip1": ip1, + "err": err}) + } else if err != nil { + _span.RecordError(err) + _span.SetAttributes( + attribute.String("event", "error"), + attribute.String("message", err.Error()), + ) + } + + _span.End() + }() + return _d.LinodeClient.GetInstance(ctx, linodeID) +} + +// GetInstanceDisk implements clients.LinodeClient +func (_d LinodeClientWithTracing) GetInstanceDisk(ctx context.Context, linodeID int, diskID int) (ip1 *linodego.InstanceDisk, err error) { + ctx, _span := tracing.Start(ctx, "clients.LinodeClient.GetInstanceDisk") + defer func() { + if _d._spanDecorator != nil { + _d._spanDecorator(_span, map[string]interface{}{ + "ctx": ctx, + "linodeID": linodeID, + "diskID": diskID}, map[string]interface{}{ + "ip1": ip1, + "err": err}) + } else if err != nil { + _span.RecordError(err) + _span.SetAttributes( + attribute.String("event", "error"), + attribute.String("message", err.Error()), + ) + } + + _span.End() + }() + return _d.LinodeClient.GetInstanceDisk(ctx, linodeID, diskID) +} + +// GetInstanceIPAddresses implements clients.LinodeClient +func (_d LinodeClientWithTracing) GetInstanceIPAddresses(ctx context.Context, linodeID int) (ip1 *linodego.InstanceIPAddressResponse, err error) { + ctx, _span := tracing.Start(ctx, "clients.LinodeClient.GetInstanceIPAddresses") + defer func() { + if _d._spanDecorator != nil { + _d._spanDecorator(_span, map[string]interface{}{ + "ctx": ctx, + "linodeID": linodeID}, map[string]interface{}{ + "ip1": ip1, + "err": err}) + } else if err != nil { + _span.RecordError(err) + _span.SetAttributes( + attribute.String("event", "error"), + attribute.String("message", err.Error()), + ) + } + + _span.End() + }() + return _d.LinodeClient.GetInstanceIPAddresses(ctx, linodeID) +} + +// GetObjectStorageBucket implements clients.LinodeClient +func (_d LinodeClientWithTracing) GetObjectStorageBucket(ctx context.Context, cluster string, label string) (op1 *linodego.ObjectStorageBucket, err error) { + ctx, _span := tracing.Start(ctx, "clients.LinodeClient.GetObjectStorageBucket") + defer func() { + if _d._spanDecorator != nil { + _d._spanDecorator(_span, map[string]interface{}{ + "ctx": ctx, + "cluster": cluster, + "label": label}, map[string]interface{}{ + "op1": op1, + "err": err}) + } else if err != nil { + _span.RecordError(err) + _span.SetAttributes( + attribute.String("event", "error"), + attribute.String("message", err.Error()), + ) + } + + _span.End() + }() + return _d.LinodeClient.GetObjectStorageBucket(ctx, cluster, label) +} + +// GetObjectStorageKey implements clients.LinodeClient +func (_d LinodeClientWithTracing) GetObjectStorageKey(ctx context.Context, keyID int) (op1 *linodego.ObjectStorageKey, err error) { + ctx, _span := tracing.Start(ctx, "clients.LinodeClient.GetObjectStorageKey") + defer func() { + if _d._spanDecorator != nil { + _d._spanDecorator(_span, map[string]interface{}{ + "ctx": ctx, + "keyID": keyID}, map[string]interface{}{ + "op1": op1, + "err": err}) + } else if err != nil { + _span.RecordError(err) + _span.SetAttributes( + attribute.String("event", "error"), + attribute.String("message", err.Error()), + ) + } + + _span.End() + }() + return _d.LinodeClient.GetObjectStorageKey(ctx, keyID) +} + +// GetRegion implements clients.LinodeClient +func (_d LinodeClientWithTracing) GetRegion(ctx context.Context, regionID string) (rp1 *linodego.Region, err error) { + ctx, _span := tracing.Start(ctx, "clients.LinodeClient.GetRegion") + defer func() { + if _d._spanDecorator != nil { + _d._spanDecorator(_span, map[string]interface{}{ + "ctx": ctx, + "regionID": regionID}, map[string]interface{}{ + "rp1": rp1, + "err": err}) + } else if err != nil { + _span.RecordError(err) + _span.SetAttributes( + attribute.String("event", "error"), + attribute.String("message", err.Error()), + ) + } + + _span.End() + }() + return _d.LinodeClient.GetRegion(ctx, regionID) +} + +// GetType implements clients.LinodeClient +func (_d LinodeClientWithTracing) GetType(ctx context.Context, typeID string) (lp1 *linodego.LinodeType, err error) { + ctx, _span := tracing.Start(ctx, "clients.LinodeClient.GetType") + defer func() { + if _d._spanDecorator != nil { + _d._spanDecorator(_span, map[string]interface{}{ + "ctx": ctx, + "typeID": typeID}, map[string]interface{}{ + "lp1": lp1, + "err": err}) + } else if err != nil { + _span.RecordError(err) + _span.SetAttributes( + attribute.String("event", "error"), + attribute.String("message", err.Error()), + ) + } + + _span.End() + }() + return _d.LinodeClient.GetType(ctx, typeID) +} + +// GetVPC implements clients.LinodeClient +func (_d LinodeClientWithTracing) GetVPC(ctx context.Context, vpcID int) (vp1 *linodego.VPC, err error) { + ctx, _span := tracing.Start(ctx, "clients.LinodeClient.GetVPC") + defer func() { + if _d._spanDecorator != nil { + _d._spanDecorator(_span, map[string]interface{}{ + "ctx": ctx, + "vpcID": vpcID}, map[string]interface{}{ + "vp1": vp1, + "err": err}) + } else if err != nil { + _span.RecordError(err) + _span.SetAttributes( + attribute.String("event", "error"), + attribute.String("message", err.Error()), + ) + } + + _span.End() + }() + return _d.LinodeClient.GetVPC(ctx, vpcID) +} + +// ListInstanceConfigs implements clients.LinodeClient +func (_d LinodeClientWithTracing) ListInstanceConfigs(ctx context.Context, linodeID int, opts *linodego.ListOptions) (ia1 []linodego.InstanceConfig, err error) { + ctx, _span := tracing.Start(ctx, "clients.LinodeClient.ListInstanceConfigs") + defer func() { + if _d._spanDecorator != nil { + _d._spanDecorator(_span, map[string]interface{}{ + "ctx": ctx, + "linodeID": linodeID, + "opts": opts}, map[string]interface{}{ + "ia1": ia1, + "err": err}) + } else if err != nil { + _span.RecordError(err) + _span.SetAttributes( + attribute.String("event", "error"), + attribute.String("message", err.Error()), + ) + } + + _span.End() + }() + return _d.LinodeClient.ListInstanceConfigs(ctx, linodeID, opts) +} + +// ListInstances implements clients.LinodeClient +func (_d LinodeClientWithTracing) ListInstances(ctx context.Context, opts *linodego.ListOptions) (ia1 []linodego.Instance, err error) { + ctx, _span := tracing.Start(ctx, "clients.LinodeClient.ListInstances") + defer func() { + if _d._spanDecorator != nil { + _d._spanDecorator(_span, map[string]interface{}{ + "ctx": ctx, + "opts": opts}, map[string]interface{}{ + "ia1": ia1, + "err": err}) + } else if err != nil { + _span.RecordError(err) + _span.SetAttributes( + attribute.String("event", "error"), + attribute.String("message", err.Error()), + ) + } + + _span.End() + }() + return _d.LinodeClient.ListInstances(ctx, opts) +} + +// ListNodeBalancers implements clients.LinodeClient +func (_d LinodeClientWithTracing) ListNodeBalancers(ctx context.Context, opts *linodego.ListOptions) (na1 []linodego.NodeBalancer, err error) { + ctx, _span := tracing.Start(ctx, "clients.LinodeClient.ListNodeBalancers") + defer func() { + if _d._spanDecorator != nil { + _d._spanDecorator(_span, map[string]interface{}{ + "ctx": ctx, + "opts": opts}, map[string]interface{}{ + "na1": na1, + "err": err}) + } else if err != nil { + _span.RecordError(err) + _span.SetAttributes( + attribute.String("event", "error"), + attribute.String("message", err.Error()), + ) + } + + _span.End() + }() + return _d.LinodeClient.ListNodeBalancers(ctx, opts) +} + +// ListStackscripts implements clients.LinodeClient +func (_d LinodeClientWithTracing) ListStackscripts(ctx context.Context, opts *linodego.ListOptions) (sa1 []linodego.Stackscript, err error) { + ctx, _span := tracing.Start(ctx, "clients.LinodeClient.ListStackscripts") + defer func() { + if _d._spanDecorator != nil { + _d._spanDecorator(_span, map[string]interface{}{ + "ctx": ctx, + "opts": opts}, map[string]interface{}{ + "sa1": sa1, + "err": err}) + } else if err != nil { + _span.RecordError(err) + _span.SetAttributes( + attribute.String("event", "error"), + attribute.String("message", err.Error()), + ) + } + + _span.End() + }() + return _d.LinodeClient.ListStackscripts(ctx, opts) +} + +// ListVPCs implements clients.LinodeClient +func (_d LinodeClientWithTracing) ListVPCs(ctx context.Context, opts *linodego.ListOptions) (va1 []linodego.VPC, err error) { + ctx, _span := tracing.Start(ctx, "clients.LinodeClient.ListVPCs") + defer func() { + if _d._spanDecorator != nil { + _d._spanDecorator(_span, map[string]interface{}{ + "ctx": ctx, + "opts": opts}, map[string]interface{}{ + "va1": va1, + "err": err}) + } else if err != nil { + _span.RecordError(err) + _span.SetAttributes( + attribute.String("event", "error"), + attribute.String("message", err.Error()), + ) + } + + _span.End() + }() + return _d.LinodeClient.ListVPCs(ctx, opts) +} + +// ResizeInstanceDisk implements clients.LinodeClient +func (_d LinodeClientWithTracing) ResizeInstanceDisk(ctx context.Context, linodeID int, diskID int, size int) (err error) { + ctx, _span := tracing.Start(ctx, "clients.LinodeClient.ResizeInstanceDisk") + defer func() { + if _d._spanDecorator != nil { + _d._spanDecorator(_span, map[string]interface{}{ + "ctx": ctx, + "linodeID": linodeID, + "diskID": diskID, + "size": size}, 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.LinodeClient.ResizeInstanceDisk(ctx, linodeID, diskID, size) +} + +// UpdateInstanceConfig implements clients.LinodeClient +func (_d LinodeClientWithTracing) UpdateInstanceConfig(ctx context.Context, linodeID int, configID int, opts linodego.InstanceConfigUpdateOptions) (ip1 *linodego.InstanceConfig, err error) { + ctx, _span := tracing.Start(ctx, "clients.LinodeClient.UpdateInstanceConfig") + defer func() { + if _d._spanDecorator != nil { + _d._spanDecorator(_span, map[string]interface{}{ + "ctx": ctx, + "linodeID": linodeID, + "configID": configID, + "opts": opts}, map[string]interface{}{ + "ip1": ip1, + "err": err}) + } else if err != nil { + _span.RecordError(err) + _span.SetAttributes( + attribute.String("event", "error"), + attribute.String("message", err.Error()), + ) + } + + _span.End() + }() + return _d.LinodeClient.UpdateInstanceConfig(ctx, linodeID, configID, opts) +} diff --git a/observability/wrappers/reconciler/reconciler.gen.go b/observability/wrappers/reconciler/reconciler.gen.go new file mode 100644 index 000000000..593fa3dd6 --- /dev/null +++ b/observability/wrappers/reconciler/reconciler.gen.go @@ -0,0 +1,223 @@ +// 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...) +}