From 11bf64b937572422320c87a891d98cd52618e9f1 Mon Sep 17 00:00:00 2001 From: Mauricio Araujo Date: Thu, 4 Apr 2024 16:53:36 -0400 Subject: [PATCH] Add telemetry to all metronome methods and endpoints (#4503) --- api/server/handlers/billing/credits.go | 2 +- api/server/handlers/billing/list.go | 2 +- api/server/handlers/project/create.go | 2 +- api/server/handlers/project/delete.go | 7 +++- internal/billing/metronome.go | 49 +++++++++++++++++--------- 5 files changed, 42 insertions(+), 20 deletions(-) diff --git a/api/server/handlers/billing/credits.go b/api/server/handlers/billing/credits.go index 033370971f..fc4ef06e69 100644 --- a/api/server/handlers/billing/credits.go +++ b/api/server/handlers/billing/credits.go @@ -42,7 +42,7 @@ func (c *GetCreditsHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { return } - credits, err := c.Config().BillingManager.MetronomeClient.GetCustomerCredits(proj.UsageID) + credits, err := c.Config().BillingManager.MetronomeClient.GetCustomerCredits(ctx, proj.UsageID) if err != nil { err := telemetry.Error(ctx, span, err, "error getting credits") c.HandleAPIError(w, r, apierrors.NewErrInternal(err)) diff --git a/api/server/handlers/billing/list.go b/api/server/handlers/billing/list.go index ac386fc0f4..04f65e5784 100644 --- a/api/server/handlers/billing/list.go +++ b/api/server/handlers/billing/list.go @@ -86,7 +86,7 @@ func (c *CheckPaymentEnabledHandler) ServeHTTP(w http.ResponseWriter, r *http.Re if c.Config().ServerConf.MetronomeAPIKey != "" && c.Config().ServerConf.PorterCloudPlanID != "" && proj.GetFeatureFlag(models.MetronomeEnabled, c.Config().LaunchDarklyClient) && proj.UsageID == uuid.Nil { - customerID, customerPlanID, err := c.Config().BillingManager.MetronomeClient.CreateCustomerWithPlan(user.CompanyName, proj.Name, proj.ID, proj.BillingID, c.Config().ServerConf.PorterCloudPlanID) + customerID, customerPlanID, err := c.Config().BillingManager.MetronomeClient.CreateCustomerWithPlan(ctx, user.CompanyName, proj.Name, proj.ID, proj.BillingID, c.Config().ServerConf.PorterCloudPlanID) if err != nil { err = telemetry.Error(ctx, span, err, "error creating Metronome customer") c.HandleAPIError(w, r, apierrors.NewErrInternal(err)) diff --git a/api/server/handlers/project/create.go b/api/server/handlers/project/create.go index 90eb1a0dfa..e131e3de3a 100644 --- a/api/server/handlers/project/create.go +++ b/api/server/handlers/project/create.go @@ -99,7 +99,7 @@ func (p *ProjectCreateHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) // Create Metronome customer and add to starter plan if p.Config().ServerConf.MetronomeAPIKey != "" && p.Config().ServerConf.PorterCloudPlanID != "" && proj.GetFeatureFlag(models.MetronomeEnabled, p.Config().LaunchDarklyClient) { - customerID, customerPlanID, err := p.Config().BillingManager.MetronomeClient.CreateCustomerWithPlan(user.CompanyName, proj.Name, proj.ID, proj.BillingID, p.Config().ServerConf.PorterCloudPlanID) + customerID, customerPlanID, err := p.Config().BillingManager.MetronomeClient.CreateCustomerWithPlan(ctx, user.CompanyName, proj.Name, proj.ID, proj.BillingID, p.Config().ServerConf.PorterCloudPlanID) if err != nil { err = telemetry.Error(ctx, span, err, "error creating Metronome customer") p.HandleAPIError(w, r, apierrors.NewErrInternal(err)) diff --git a/api/server/handlers/project/delete.go b/api/server/handlers/project/delete.go index b4da45030f..90f09c439c 100644 --- a/api/server/handlers/project/delete.go +++ b/api/server/handlers/project/delete.go @@ -94,13 +94,18 @@ func (p *ProjectDeleteHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) if p.Config().ServerConf.MetronomeAPIKey != "" && p.Config().ServerConf.PorterCloudPlanID != "" && proj.GetFeatureFlag(models.MetronomeEnabled, p.Config().LaunchDarklyClient) { - err = p.Config().BillingManager.MetronomeClient.EndCustomerPlan(proj.UsageID, proj.UsagePlanID) + err = p.Config().BillingManager.MetronomeClient.EndCustomerPlan(ctx, proj.UsageID, proj.UsagePlanID) if err != nil { e := "error ending billing plan" err = telemetry.Error(ctx, span, err, e) p.HandleAPIError(w, r, apierrors.NewErrPassThroughToClient(err, http.StatusInternalServerError)) return } + telemetry.WithAttributes(span, + telemetry.AttributeKV{Key: "project-id", Value: proj.ID}, + telemetry.AttributeKV{Key: "usage-id", Value: proj.UsageID}, + telemetry.AttributeKV{Key: "usage-plan-id", Value: proj.UsagePlanID}, + ) } deletedProject, err := p.Repo().Project().DeleteProject(proj) diff --git a/internal/billing/metronome.go b/internal/billing/metronome.go index 5a6c3d853c..a5a385f398 100644 --- a/internal/billing/metronome.go +++ b/internal/billing/metronome.go @@ -2,6 +2,7 @@ package billing import ( "bytes" + "context" "encoding/json" "fmt" "net/http" @@ -11,6 +12,7 @@ import ( "github.com/google/uuid" "github.com/porter-dev/porter/api/types" + "github.com/porter-dev/porter/internal/telemetry" ) const ( @@ -34,7 +36,10 @@ func NewMetronomeClient(metronomeApiKey string) MetronomeClient { } // createCustomer will create the customer in Metronome -func (m MetronomeClient) createCustomer(orgName string, projectName string, projectID uint, billingID string) (customerID uuid.UUID, err error) { +func (m MetronomeClient) createCustomer(ctx context.Context, orgName string, projectName string, projectID uint, billingID string) (customerID uuid.UUID, err error) { + ctx, span := telemetry.NewSpan(ctx, "create-metronome-customer") + defer span.End() + path := "customers" projIDStr := strconv.FormatUint(uint64(projectID), 10) @@ -56,15 +61,18 @@ func (m MetronomeClient) createCustomer(orgName string, projectName string, proj err = post(path, m.ApiKey, customer, &result) if err != nil { - return customerID, err + return customerID, telemetry.Error(ctx, span, err, "error creating customer") } return result.Data.ID, nil } // addCustomerPlan will start the customer on the given plan -func (m MetronomeClient) addCustomerPlan(customerID uuid.UUID, planID uuid.UUID) (customerPlanID uuid.UUID, err error) { +func (m MetronomeClient) addCustomerPlan(ctx context.Context, customerID uuid.UUID, planID uuid.UUID) (customerPlanID uuid.UUID, err error) { + ctx, span := telemetry.NewSpan(ctx, "add-metronome-customer-plan") + defer span.End() + if customerID == uuid.Nil || planID == uuid.Nil { - return customerPlanID, fmt.Errorf("customer or plan id empty") + return customerPlanID, telemetry.Error(ctx, span, err, "customer or plan id empty") } path := fmt.Sprintf("/customers/%s/plans/add", customerID) @@ -87,33 +95,39 @@ func (m MetronomeClient) addCustomerPlan(customerID uuid.UUID, planID uuid.UUID) err = post(path, m.ApiKey, req, &result) if err != nil { - return customerPlanID, err + return customerPlanID, telemetry.Error(ctx, span, err, "failed to add customer to plan") } return result.Data.CustomerPlanID, nil } // CreateCustomerWithPlan will create the customer in Metronome and immediately add it to the plan -func (m MetronomeClient) CreateCustomerWithPlan(orgName string, projectName string, projectID uint, billingID string, planID string) (customerID uuid.UUID, customerPlanID uuid.UUID, err error) { +func (m MetronomeClient) CreateCustomerWithPlan(ctx context.Context, orgName string, projectName string, projectID uint, billingID string, planID string) (customerID uuid.UUID, customerPlanID uuid.UUID, err error) { + ctx, span := telemetry.NewSpan(ctx, "add-metronome-customer-plan") + defer span.End() + porterCloudPlanID, err := uuid.Parse(planID) if err != nil { - return customerID, customerPlanID, fmt.Errorf("error parsing starter plan id: %w", err) + return customerID, customerPlanID, telemetry.Error(ctx, span, err, "error parsing starter plan id") } - customerID, err = m.createCustomer(orgName, projectName, projectID, billingID) + customerID, err = m.createCustomer(ctx, orgName, projectName, projectID, billingID) if err != nil { - return customerID, customerPlanID, err + return customerID, customerPlanID, telemetry.Error(ctx, span, err, "error while creatinc customer with plan") } - customerPlanID, err = m.addCustomerPlan(customerID, porterCloudPlanID) + customerPlanID, err = m.addCustomerPlan(ctx, customerID, porterCloudPlanID) return customerID, customerPlanID, err } // EndCustomerPlan will immediately end the plan for the given customer -func (m MetronomeClient) EndCustomerPlan(customerID uuid.UUID, customerPlanID uuid.UUID) (err error) { +func (m MetronomeClient) EndCustomerPlan(ctx context.Context, customerID uuid.UUID, customerPlanID uuid.UUID) (err error) { + ctx, span := telemetry.NewSpan(ctx, "end-metronome-customer-plan") + defer span.End() + if customerID == uuid.Nil || customerPlanID == uuid.Nil { - return fmt.Errorf("customer or customer plan id empty") + return telemetry.Error(ctx, span, err, "customer or customer plan id empty") } path := fmt.Sprintf("/customers/%s/plans/%s/end", customerID, customerPlanID) @@ -129,16 +143,19 @@ func (m MetronomeClient) EndCustomerPlan(customerID uuid.UUID, customerPlanID uu err = post(path, m.ApiKey, req, nil) if err != nil { - return err + return telemetry.Error(ctx, span, err, "failed to end customer plan") } return nil } // GetCustomerCredits will return the first credit grant for the customer -func (m MetronomeClient) GetCustomerCredits(customerID uuid.UUID) (credits int64, err error) { +func (m MetronomeClient) GetCustomerCredits(ctx context.Context, customerID uuid.UUID) (credits int64, err error) { + ctx, span := telemetry.NewSpan(ctx, "get-customer-credits") + defer span.End() + if customerID == uuid.Nil { - return credits, fmt.Errorf("customer id empty") + return credits, telemetry.Error(ctx, span, err, "customer id empty") } path := "credits/listGrants" @@ -155,7 +172,7 @@ func (m MetronomeClient) GetCustomerCredits(customerID uuid.UUID) (credits int64 err = post(path, m.ApiKey, req, &result) if err != nil { - return credits, err + return credits, telemetry.Error(ctx, span, err, "failed to get customer credits") } return result.Data[0].Balance.IncludingPending, nil