From b36b5137b6c9cc9237341d2907a0cc1c94e98321 Mon Sep 17 00:00:00 2001 From: Mario Constanti Date: Wed, 21 Feb 2024 14:22:45 +0100 Subject: [PATCH] feat: count github api calls introduce metrics counter for github api calls Signed-off-by: Mario Constanti --- metrics/github.go | 19 +++++++ metrics/metrics.go | 4 +- runner/pool/enterprise.go | 60 +++++++++++++++++++++ runner/pool/organization.go | 101 +++++++++++++++++++++++++++++++++++- runner/pool/pool.go | 4 ++ runner/pool/repository.go | 83 ++++++++++++++++++++++++++++- 6 files changed, 268 insertions(+), 3 deletions(-) create mode 100644 metrics/github.go diff --git a/metrics/github.go b/metrics/github.go new file mode 100644 index 00000000..c4f043a1 --- /dev/null +++ b/metrics/github.go @@ -0,0 +1,19 @@ +package metrics + +import "github.com/prometheus/client_golang/prometheus" + +var ( + GithubOperationCount = prometheus.NewCounterVec(prometheus.CounterOpts{ + Namespace: metricsNamespace, + Subsystem: metricsGithubSubsystem, + Name: "operation_total", + Help: "Total number of github operation attempts", + }, []string{"operation", "scope"}) + + GithubOperationFailedCount = prometheus.NewCounterVec(prometheus.CounterOpts{ + Namespace: metricsNamespace, + Subsystem: metricsGithubSubsystem, + Name: "operation_failed_total", + Help: "Total number of failed github operation attempts", + }, []string{"operation", "scope"}) +) diff --git a/metrics/metrics.go b/metrics/metrics.go index 43c0c294..62bdf2c2 100644 --- a/metrics/metrics.go +++ b/metrics/metrics.go @@ -50,7 +50,9 @@ func RegisterMetrics() error { // runner instances InstanceOperationCount, InstanceOperationFailedCount, - + // github + GithubOperationCount, + GithubOperationFailedCount, // webhook metrics WebhooksReceived, ) diff --git a/runner/pool/enterprise.go b/runner/pool/enterprise.go index f03947b5..cf318c65 100644 --- a/runner/pool/enterprise.go +++ b/runner/pool/enterprise.go @@ -13,6 +13,7 @@ import ( runnerErrors "github.com/cloudbase/garm-provider-common/errors" commonParams "github.com/cloudbase/garm-provider-common/params" dbCommon "github.com/cloudbase/garm/database/common" + "github.com/cloudbase/garm/metrics" "github.com/cloudbase/garm/params" "github.com/cloudbase/garm/runner/common" "github.com/cloudbase/garm/util" @@ -85,8 +86,16 @@ func (r *enterprise) findRunnerGroupByName(ctx context.Context, name string) (*g } for { + metrics.GithubOperationCount.WithLabelValues( + "ListOrganizationRunnerGroups", // label: operation + metricsLabelEnterpriseScope, // label: scope + ).Inc() runnerGroups, ghResp, err := r.ghcEnterpriseCli.ListRunnerGroups(r.ctx, r.cfg.Name, &opts) if err != nil { + metrics.GithubOperationFailedCount.WithLabelValues( + "ListOrganizationRunnerGroups", // label: operation + metricsLabelEnterpriseScope, // label: scope + ).Inc() if ghResp != nil && ghResp.StatusCode == http.StatusUnauthorized { return nil, errors.Wrap(runnerErrors.ErrUnauthorized, "fetching runners") } @@ -123,8 +132,16 @@ func (r *enterprise) GetJITConfig(ctx context.Context, instance string, pool par // TODO(gabriel-samfira): Should we make this configurable? WorkFolder: github.String("_work"), } + metrics.GithubOperationCount.WithLabelValues( + "GenerateEnterpriseJITConfig", // label: operation + metricsLabelEnterpriseScope, // label: scope + ).Inc() jitConfig, resp, err := r.ghcEnterpriseCli.GenerateEnterpriseJITConfig(ctx, r.cfg.Name, &req) if err != nil { + metrics.GithubOperationFailedCount.WithLabelValues( + "GenerateEnterpriseJITConfig", // label: operation + metricsLabelEnterpriseScope, // label: scope + ).Inc() if resp != nil && resp.StatusCode == http.StatusUnauthorized { return nil, nil, fmt.Errorf("failed to get JIT config: %w", err) } @@ -134,7 +151,17 @@ func (r *enterprise) GetJITConfig(ctx context.Context, instance string, pool par runner = jitConfig.Runner defer func() { if err != nil && runner != nil { + metrics.GithubOperationCount.WithLabelValues( + "RemoveRunner", // label: operation + metricsLabelEnterpriseScope, // label: scope + ).Inc() _, innerErr := r.ghcEnterpriseCli.RemoveRunner(r.ctx, r.cfg.Name, runner.GetID()) + if innerErr != nil { + metrics.GithubOperationFailedCount.WithLabelValues( + "RemoveRunner", // label: operation + metricsLabelEnterpriseScope, // label: scope + ).Inc() + } slog.With(slog.Any("error", innerErr)).ErrorContext( ctx, "failed to remove runner", "runner_id", runner.GetID(), "organization", r.cfg.Name) @@ -166,8 +193,16 @@ func (r *enterprise) GetRunnerInfoFromWorkflow(job params.WorkflowJob) (params.R if err := r.ValidateOwner(job); err != nil { return params.RunnerInfo{}, errors.Wrap(err, "validating owner") } + metrics.GithubOperationCount.WithLabelValues( + "GetWorkflowJobByID", // label: operation + metricsLabelEnterpriseScope, // label: scope + ).Inc() workflow, ghResp, err := r.ghcli.GetWorkflowJobByID(r.ctx, job.Repository.Owner.Login, job.Repository.Name, job.WorkflowJob.ID) if err != nil { + metrics.GithubOperationFailedCount.WithLabelValues( + "GetWorkflowJobByID", // label: operation + metricsLabelEnterpriseScope, // label: scope + ).Inc() if ghResp != nil && ghResp.StatusCode == http.StatusUnauthorized { return params.RunnerInfo{}, errors.Wrap(runnerErrors.ErrUnauthorized, "fetching workflow info") } @@ -212,8 +247,16 @@ func (r *enterprise) GetGithubRunners() ([]*github.Runner, error) { var allRunners []*github.Runner for { + metrics.GithubOperationCount.WithLabelValues( + "ListRunners", // label: operation + metricsLabelEnterpriseScope, // label: scope + ).Inc() runners, ghResp, err := r.ghcEnterpriseCli.ListRunners(r.ctx, r.cfg.Name, &opts) if err != nil { + metrics.GithubOperationFailedCount.WithLabelValues( + "ListRunners", // label: operation + metricsLabelEnterpriseScope, // label: scope + ).Inc() if ghResp != nil && ghResp.StatusCode == http.StatusUnauthorized { return nil, errors.Wrap(runnerErrors.ErrUnauthorized, "fetching runners") } @@ -231,8 +274,16 @@ func (r *enterprise) GetGithubRunners() ([]*github.Runner, error) { func (r *enterprise) FetchTools() ([]commonParams.RunnerApplicationDownload, error) { r.mux.Lock() defer r.mux.Unlock() + metrics.GithubOperationCount.WithLabelValues( + "ListRunnerApplicationDownloads", // label: operation + metricsLabelEnterpriseScope, // label: scope + ).Inc() tools, ghResp, err := r.ghcEnterpriseCli.ListRunnerApplicationDownloads(r.ctx, r.cfg.Name) if err != nil { + metrics.GithubOperationFailedCount.WithLabelValues( + "ListRunnerApplicationDownloads", // label: operation + metricsLabelEnterpriseScope, // label: scope + ).Inc() if ghResp != nil && ghResp.StatusCode == http.StatusUnauthorized { return nil, errors.Wrap(runnerErrors.ErrUnauthorized, "fetching runners") } @@ -275,9 +326,18 @@ func (r *enterprise) JwtToken() string { } func (r *enterprise) GetGithubRegistrationToken() (string, error) { + metrics.GithubOperationCount.WithLabelValues( + "CreateRegistrationToken", // label: operation + metricsLabelEnterpriseScope, // label: scope + ).Inc() + tk, ghResp, err := r.ghcEnterpriseCli.CreateRegistrationToken(r.ctx, r.cfg.Name) if err != nil { + metrics.GithubOperationFailedCount.WithLabelValues( + "CreateRegistrationToken", // label: operation + metricsLabelEnterpriseScope, // label: scope + ).Inc() if ghResp != nil && ghResp.StatusCode == http.StatusUnauthorized { return "", errors.Wrap(runnerErrors.ErrUnauthorized, "fetching registration token") } diff --git a/runner/pool/organization.go b/runner/pool/organization.go index 0f534508..3de5a9b1 100644 --- a/runner/pool/organization.go +++ b/runner/pool/organization.go @@ -27,6 +27,7 @@ import ( runnerErrors "github.com/cloudbase/garm-provider-common/errors" commonParams "github.com/cloudbase/garm-provider-common/params" dbCommon "github.com/cloudbase/garm/database/common" + "github.com/cloudbase/garm/metrics" "github.com/cloudbase/garm/params" "github.com/cloudbase/garm/runner/common" "github.com/cloudbase/garm/util" @@ -97,8 +98,16 @@ func (r *organization) findRunnerGroupByName(ctx context.Context, name string) ( } for { + metrics.GithubOperationCount.WithLabelValues( + "ListOrganizationRunnerGroups", // label: operation + metricsLabelOrganizationScope, // label: scope + ).Inc() runnerGroups, ghResp, err := r.ghcli.ListOrganizationRunnerGroups(r.ctx, r.cfg.Name, &opts) if err != nil { + metrics.GithubOperationFailedCount.WithLabelValues( + "ListOrganizationRunnerGroups", // label: operation + metricsLabelOrganizationScope, // label: scope + ).Inc() if ghResp != nil && ghResp.StatusCode == http.StatusUnauthorized { return nil, errors.Wrap(runnerErrors.ErrUnauthorized, "fetching runners") } @@ -135,8 +144,16 @@ func (r *organization) GetJITConfig(ctx context.Context, instance string, pool p // TODO(gabriel-samfira): Should we make this configurable? WorkFolder: github.String("_work"), } + metrics.GithubOperationCount.WithLabelValues( + "GenerateOrgJITConfig", // label: operation + metricsLabelOrganizationScope, // label: scope + ).Inc() jitConfig, resp, err := r.ghcli.GenerateOrgJITConfig(ctx, r.cfg.Name, &req) if err != nil { + metrics.GithubOperationFailedCount.WithLabelValues( + "GenerateOrgJITConfig", // label: operation + metricsLabelOrganizationScope, // label: scope + ).Inc() if resp != nil && resp.StatusCode == http.StatusUnauthorized { return nil, nil, fmt.Errorf("failed to get JIT config: %w", err) } @@ -146,7 +163,17 @@ func (r *organization) GetJITConfig(ctx context.Context, instance string, pool p runner = jitConfig.GetRunner() defer func() { if err != nil && runner != nil { + metrics.GithubOperationCount.WithLabelValues( + "RemoveOrganizationRunner", // label: operation + metricsLabelOrganizationScope, // label: scope + ).Inc() _, innerErr := r.ghcli.RemoveOrganizationRunner(r.ctx, r.cfg.Name, runner.GetID()) + if innerErr != nil { + metrics.GithubOperationFailedCount.WithLabelValues( + "RemoveOrganizationRunner", // label: operation + metricsLabelOrganizationScope, // label: scope + ).Inc() + } slog.With(slog.Any("error", innerErr)).ErrorContext( ctx, "failed to remove runner", "runner_id", runner.GetID(), "organization", r.cfg.Name) @@ -178,8 +205,16 @@ func (r *organization) GetRunnerInfoFromWorkflow(job params.WorkflowJob) (params if err := r.ValidateOwner(job); err != nil { return params.RunnerInfo{}, errors.Wrap(err, "validating owner") } + metrics.GithubOperationCount.WithLabelValues( + "GetWorkflowJobByID", // label: operation + metricsLabelOrganizationScope, // label: scope + ).Inc() workflow, ghResp, err := r.ghcli.GetWorkflowJobByID(r.ctx, job.Organization.Login, job.Repository.Name, job.WorkflowJob.ID) if err != nil { + metrics.GithubOperationFailedCount.WithLabelValues( + "GetWorkflowJobByID", // label: operation + metricsLabelOrganizationScope, // label: scope + ).Inc() if ghResp != nil && ghResp.StatusCode == http.StatusUnauthorized { return params.RunnerInfo{}, errors.Wrap(runnerErrors.ErrUnauthorized, "fetching workflow info") } @@ -223,8 +258,16 @@ func (r *organization) GetGithubRunners() ([]*github.Runner, error) { var allRunners []*github.Runner for { + metrics.GithubOperationCount.WithLabelValues( + "ListOrganizationRunners", // label: operation + metricsLabelOrganizationScope, // label: scope + ).Inc() runners, ghResp, err := r.ghcli.ListOrganizationRunners(r.ctx, r.cfg.Name, &opts) if err != nil { + metrics.GithubOperationFailedCount.WithLabelValues( + "ListOrganizationRunners", // label: operation + metricsLabelOrganizationScope, // label: scope + ).Inc() if ghResp != nil && ghResp.StatusCode == http.StatusUnauthorized { return nil, errors.Wrap(runnerErrors.ErrUnauthorized, "fetching runners") } @@ -243,8 +286,16 @@ func (r *organization) GetGithubRunners() ([]*github.Runner, error) { func (r *organization) FetchTools() ([]commonParams.RunnerApplicationDownload, error) { r.mux.Lock() defer r.mux.Unlock() + metrics.GithubOperationCount.WithLabelValues( + "ListOrganizationRunnerApplicationDownloads", // label: operation + metricsLabelOrganizationScope, // label: scope + ).Inc() tools, ghResp, err := r.ghcli.ListOrganizationRunnerApplicationDownloads(r.ctx, r.cfg.Name) if err != nil { + metrics.GithubOperationFailedCount.WithLabelValues( + "ListOrganizationRunnerApplicationDownloads", // label: operation + metricsLabelOrganizationScope, // label: scope + ).Inc() if ghResp != nil && ghResp.StatusCode == http.StatusUnauthorized { return nil, errors.Wrap(runnerErrors.ErrUnauthorized, "fetching tools") } @@ -267,7 +318,21 @@ func (r *organization) FetchDbInstances() ([]params.Instance, error) { } func (r *organization) RemoveGithubRunner(runnerID int64) (*github.Response, error) { - return r.ghcli.RemoveOrganizationRunner(r.ctx, r.cfg.Name, runnerID) + metrics.GithubOperationCount.WithLabelValues( + "RemoveRunner", // label: operation + metricsLabelOrganizationScope, // label: scope + ).Inc() + + ghResp, err := r.ghcli.RemoveOrganizationRunner(r.ctx, r.cfg.Name, runnerID) + if err != nil { + metrics.GithubOperationFailedCount.WithLabelValues( + "RemoveRunner", // label: operation + metricsLabelOrganizationScope, // label: scope + ).Inc() + return nil, err + } + + return ghResp, nil } func (r *organization) ListPools() ([]params.Pool, error) { @@ -340,8 +405,16 @@ func (r *organization) listHooks(ctx context.Context) ([]*github.Hook, error) { } var allHooks []*github.Hook for { + metrics.GithubOperationCount.WithLabelValues( + "ListOrgHooks", // label: operation + metricsLabelOrganizationScope, // label: scope + ).Inc() hooks, ghResp, err := r.ghcli.ListOrgHooks(ctx, r.cfg.Name, &opts) if err != nil { + metrics.GithubOperationFailedCount.WithLabelValues( + "ListOrgHooks", // label: operation + metricsLabelOrganizationScope, // label: scope + ).Inc() if ghResp != nil && ghResp.StatusCode == http.StatusNotFound { return nil, runnerErrors.NewBadRequestError("organization not found or your PAT does not have access to manage webhooks") } @@ -366,12 +439,30 @@ func (r *organization) InstallHook(ctx context.Context, req *github.Hook) (param return params.HookInfo{}, errors.Wrap(err, "validating hook request") } + metrics.GithubOperationCount.WithLabelValues( + "CreateOrgHook", // label: operation + metricsLabelOrganizationScope, // label: scope + ).Inc() + hook, _, err := r.ghcli.CreateOrgHook(ctx, r.cfg.Name, req) if err != nil { + metrics.GithubOperationFailedCount.WithLabelValues( + "CreateOrgHook", // label: operation + metricsLabelOrganizationScope, // label: scope + ).Inc() return params.HookInfo{}, errors.Wrap(err, "creating organization hook") } + metrics.GithubOperationCount.WithLabelValues( + "PingOrgHook", // label: operation + metricsLabelOrganizationScope, // label: scope + ).Inc() + if _, err := r.ghcli.PingOrgHook(ctx, r.cfg.Name, hook.GetID()); err != nil { + metrics.GithubOperationFailedCount.WithLabelValues( + "PingOrgHook", // label: operation + metricsLabelOrganizationScope, // label: scope + ).Inc() slog.With(slog.Any("error", err)).ErrorContext(ctx, "failed to ping hook", "hook_id", hook.GetID()) } @@ -386,8 +477,16 @@ func (r *organization) UninstallHook(ctx context.Context, url string) error { for _, hook := range allHooks { if hook.Config["url"] == url { + metrics.GithubOperationCount.WithLabelValues( + "DeleteOrgHook", // label: operation + metricsLabelOrganizationScope, // label: scope + ).Inc() _, err = r.ghcli.DeleteOrgHook(ctx, r.cfg.Name, hook.GetID()) if err != nil { + metrics.GithubOperationFailedCount.WithLabelValues( + "DeleteOrgHook", // label: operation + metricsLabelOrganizationScope, // label: scope + ).Inc() return errors.Wrap(err, "deleting hook") } return nil diff --git a/runner/pool/pool.go b/runner/pool/pool.go index b15e3677..8e69ff03 100644 --- a/runner/pool/pool.go +++ b/runner/pool/pool.go @@ -57,6 +57,10 @@ const ( // before we give up. // TODO: make this configurable(?) maxCreateAttempts = 5 + + metricsLabelEnterpriseScope = "Enterprise" + metricsLabelRepositoryScope = "Repository" + metricsLabelOrganizationScope = "Organization" ) type keyMutex struct { diff --git a/runner/pool/repository.go b/runner/pool/repository.go index 0505eece..c21e49a0 100644 --- a/runner/pool/repository.go +++ b/runner/pool/repository.go @@ -27,6 +27,7 @@ import ( runnerErrors "github.com/cloudbase/garm-provider-common/errors" commonParams "github.com/cloudbase/garm-provider-common/params" dbCommon "github.com/cloudbase/garm/database/common" + "github.com/cloudbase/garm/metrics" "github.com/cloudbase/garm/params" "github.com/cloudbase/garm/runner/common" "github.com/cloudbase/garm/util" @@ -110,7 +111,17 @@ func (r *repository) GetJITConfig(ctx context.Context, instance string, pool par defer func() { if err != nil && runner != nil { + metrics.GithubOperationCount.WithLabelValues( + "RemoveRunner", // label: operation + metricsLabelRepositoryScope, // label: scope + ).Inc() _, innerErr := r.ghcli.RemoveRunner(r.ctx, r.cfg.Owner, r.cfg.Name, runner.GetID()) + if innerErr != nil { + metrics.GithubOperationFailedCount.WithLabelValues( + "RemoveRunner", // label: operation + metricsLabelRepositoryScope, // label: scope + ).Inc() + } slog.With(slog.Any("error", innerErr)).ErrorContext( ctx, "failed to remove runner", "runner_id", runner.GetID(), @@ -144,8 +155,16 @@ func (r *repository) GetRunnerInfoFromWorkflow(job params.WorkflowJob) (params.R if err := r.ValidateOwner(job); err != nil { return params.RunnerInfo{}, errors.Wrap(err, "validating owner") } + metrics.GithubOperationCount.WithLabelValues( + "GetWorkflowJobByID", // label: operation + metricsLabelRepositoryScope, // label: scope + ).Inc() workflow, ghResp, err := r.ghcli.GetWorkflowJobByID(r.ctx, job.Repository.Owner.Login, job.Repository.Name, job.WorkflowJob.ID) if err != nil { + metrics.GithubOperationFailedCount.WithLabelValues( + "GetWorkflowJobByID", // label: operation + metricsLabelRepositoryScope, // label: scope + ).Inc() if ghResp != nil && ghResp.StatusCode == http.StatusUnauthorized { return params.RunnerInfo{}, errors.Wrap(runnerErrors.ErrUnauthorized, "fetching workflow info") } @@ -189,8 +208,16 @@ func (r *repository) GetGithubRunners() ([]*github.Runner, error) { var allRunners []*github.Runner for { + metrics.GithubOperationCount.WithLabelValues( + "ListRunners", // label: operation + metricsLabelRepositoryScope, // label: scope + ).Inc() runners, ghResp, err := r.ghcli.ListRunners(r.ctx, r.cfg.Owner, r.cfg.Name, &opts) if err != nil { + metrics.GithubOperationFailedCount.WithLabelValues( + "ListRunners", // label: operation + metricsLabelRepositoryScope, // label: scope + ).Inc() if ghResp != nil && ghResp.StatusCode == http.StatusUnauthorized { return nil, errors.Wrap(runnerErrors.ErrUnauthorized, "fetching runners") } @@ -209,8 +236,16 @@ func (r *repository) GetGithubRunners() ([]*github.Runner, error) { func (r *repository) FetchTools() ([]commonParams.RunnerApplicationDownload, error) { r.mux.Lock() defer r.mux.Unlock() + metrics.GithubOperationCount.WithLabelValues( + "ListRunnerApplicationDownloads", // label: operation + metricsLabelRepositoryScope, // label: scope + ).Inc() tools, ghResp, err := r.ghcli.ListRunnerApplicationDownloads(r.ctx, r.cfg.Owner, r.cfg.Name) if err != nil { + metrics.GithubOperationFailedCount.WithLabelValues( + "ListRunnerApplicationDownloads", // label: operation + metricsLabelRepositoryScope, // label: scope + ).Inc() if ghResp != nil && ghResp.StatusCode == http.StatusUnauthorized { return nil, errors.Wrap(runnerErrors.ErrUnauthorized, "fetching tools") } @@ -233,7 +268,20 @@ func (r *repository) FetchDbInstances() ([]params.Instance, error) { } func (r *repository) RemoveGithubRunner(runnerID int64) (*github.Response, error) { - return r.ghcli.RemoveRunner(r.ctx, r.cfg.Owner, r.cfg.Name, runnerID) + metrics.GithubOperationCount.WithLabelValues( + "RemoveRunner", // label: operation + metricsLabelRepositoryScope, // label: scope + ).Inc() + ghResp, err := r.ghcli.RemoveRunner(r.ctx, r.cfg.Owner, r.cfg.Name, runnerID) + if err != nil { + metrics.GithubOperationFailedCount.WithLabelValues( + "RemoveRunner", // label: operation + metricsLabelRepositoryScope, // label: scope + ).Inc() + return nil, err + } + + return ghResp, nil } func (r *repository) ListPools() ([]params.Pool, error) { @@ -253,9 +301,17 @@ func (r *repository) JwtToken() string { } func (r *repository) GetGithubRegistrationToken() (string, error) { + metrics.GithubOperationCount.WithLabelValues( + "CreateRegistrationToken", // label: operation + metricsLabelRepositoryScope, // label: scope + ).Inc() tk, ghResp, err := r.ghcli.CreateRegistrationToken(r.ctx, r.cfg.Owner, r.cfg.Name) if err != nil { + metrics.GithubOperationFailedCount.WithLabelValues( + "CreateRegistrationToken", // label: operation + metricsLabelRepositoryScope, // label: scope + ).Inc() if ghResp != nil && ghResp.StatusCode == http.StatusUnauthorized { return "", errors.Wrap(runnerErrors.ErrUnauthorized, "fetching token") } @@ -305,8 +361,16 @@ func (r *repository) listHooks(ctx context.Context) ([]*github.Hook, error) { } var allHooks []*github.Hook for { + metrics.GithubOperationCount.WithLabelValues( + "ListRepoHooks", // label: operation + metricsLabelRepositoryScope, // label: scope + ).Inc() hooks, ghResp, err := r.ghcli.ListRepoHooks(ctx, r.cfg.Owner, r.cfg.Name, &opts) if err != nil { + metrics.GithubOperationCount.WithLabelValues( + "ListRepoHooks", // label: operation + metricsLabelRepositoryScope, // label: scope + ).Inc() if ghResp != nil && ghResp.StatusCode == http.StatusNotFound { return nil, runnerErrors.NewBadRequestError("repository not found or your PAT does not have access to manage webhooks") } @@ -336,7 +400,16 @@ func (r *repository) InstallHook(ctx context.Context, req *github.Hook) (params. return params.HookInfo{}, errors.Wrap(err, "creating repository hook") } + metrics.GithubOperationCount.WithLabelValues( + "PingRepoHook", // label: operation + metricsLabelRepositoryScope, // label: scope + ).Inc() + if _, err := r.ghcli.PingRepoHook(ctx, r.cfg.Owner, r.cfg.Name, hook.GetID()); err != nil { + metrics.GithubOperationFailedCount.WithLabelValues( + "PingRepoHook", // label: operation + metricsLabelRepositoryScope, // label: scope + ).Inc() slog.With(slog.Any("error", err)).ErrorContext( ctx, "failed to ping hook", "hook_id", hook.GetID(), @@ -355,8 +428,16 @@ func (r *repository) UninstallHook(ctx context.Context, url string) error { for _, hook := range allHooks { if hook.Config["url"] == url { + metrics.GithubOperationCount.WithLabelValues( + "DeleteRepoHook", // label: operation + metricsLabelRepositoryScope, // label: scope + ).Inc() _, err = r.ghcli.DeleteRepoHook(ctx, r.cfg.Owner, r.cfg.Name, hook.GetID()) if err != nil { + metrics.GithubOperationFailedCount.WithLabelValues( + "DeleteRepoHook", // label: operation + metricsLabelRepositoryScope, // label: scope + ).Inc() return errors.Wrap(err, "deleting hook") } return nil