feat: count github api calls

introduce metrics counter for github api calls

Signed-off-by: Mario Constanti <mario.constanti@mercedes-benz.com>
This commit is contained in:
Mario Constanti 2024-02-21 14:22:45 +01:00
parent 0faf0927bc
commit b36b5137b6
6 changed files with 268 additions and 3 deletions

19
metrics/github.go Normal file
View file

@ -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"})
)

View file

@ -50,7 +50,9 @@ func RegisterMetrics() error {
// runner instances
InstanceOperationCount,
InstanceOperationFailedCount,
// github
GithubOperationCount,
GithubOperationFailedCount,
// webhook metrics
WebhooksReceived,
)

View file

@ -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")
}

View file

@ -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

View file

@ -57,6 +57,10 @@ const (
// before we give up.
// TODO: make this configurable(?)
maxCreateAttempts = 5
metricsLabelEnterpriseScope = "Enterprise"
metricsLabelRepositoryScope = "Repository"
metricsLabelOrganizationScope = "Organization"
)
type keyMutex struct {

View file

@ -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