From 1406907cd68214c7c6b199b7cac634fca47957e5 Mon Sep 17 00:00:00 2001 From: Kim Tao Date: Wed, 5 Feb 2025 17:19:05 -0500 Subject: [PATCH 1/4] Add a bunch of comments about modifying the GH app auth client --- agent/internal/client/base_client.go | 3 ++ model/githubapp/github_app_auth.go | 2 + model/githubapp/github_app_installation.go | 45 ++++++++++++++++++---- rest/route/agent.go | 7 ++++ 4 files changed, 50 insertions(+), 7 deletions(-) diff --git a/agent/internal/client/base_client.go b/agent/internal/client/base_client.go index 399100b6419..0e7c00d6423 100644 --- a/agent/internal/client/base_client.go +++ b/agent/internal/client/base_client.go @@ -941,6 +941,9 @@ func (c *baseCommunicator) CreateGitHubDynamicAccessToken(ctx context.Context, t path: fmt.Sprintf("task/%s/github_dynamic_access_token/%s/%s", td.ID, owner, repo), taskData: &td, } + // kim: NOTE: this doesn't call retryRequest but still times out requesting + // the route. Possibly it is getting 500 but still retrying due to HTTP + // client configuration resp, err := c.request(ctx, info, permissions) if err != nil { return "", nil, errors.Wrapf(err, "creating github dynamic access token for '%s/%s'", owner, repo) diff --git a/model/githubapp/github_app_auth.go b/model/githubapp/github_app_auth.go index 38fb7037353..fdbf647edb5 100644 --- a/model/githubapp/github_app_auth.go +++ b/model/githubapp/github_app_auth.go @@ -124,6 +124,8 @@ func (g *GithubAppAuth) CreateGitHubSenderInstallationToken(ctx context.Context, // CreateInstallationToken creates an installation token for the given // owner/repo. This is never cached, and should only be used in scenarios where // the token can be revoked at any time. +// kim: NOTE: this is the main logic used to generate dynamic access token. This +// package uses a custom GitHub HTTP client based on utility/rehttp. func (g *GithubAppAuth) CreateInstallationToken(ctx context.Context, owner, repo string, opts *github.InstallationTokenOptions) (string, *github.InstallationPermissions, error) { installationID, err := getInstallationID(ctx, g, owner, repo) if err != nil { diff --git a/model/githubapp/github_app_installation.go b/model/githubapp/github_app_installation.go index 52f493bf446..8570efb5b33 100644 --- a/model/githubapp/github_app_installation.go +++ b/model/githubapp/github_app_installation.go @@ -105,24 +105,55 @@ func (g *GitHubClient) Close() { // This function cannot be moved to thirdparty because it is needed to set up the environment. // Couple this with a defered call with Close() to clean up the client. func getGitHubClientForAuth(authFields *GithubAppAuth) (*GitHubClient, error) { - retryConf := utility.NewDefaultHTTPRetryConf() - retryConf.MaxDelay = GitHubRetryMaxDelay - retryConf.BaseDelay = GitHubRetryMinDelay - retryConf.MaxRetries = GitHubMaxRetries + // retryConf := utility.NewDefaultHTTPRetryConf() + // retryConf.MaxDelay = GitHubRetryMaxDelay + // retryConf.BaseDelay = GitHubRetryMinDelay + // retryConf.MaxRetries = GitHubMaxRetries key, err := jwt.ParseRSAPrivateKeyFromPEM(authFields.PrivateKey) if err != nil { return nil, errors.Wrap(err, "parsing private key") } - httpClient := utility.GetHTTPRetryableClient(retryConf) - itr := ghinstallation.NewAppsTransportFromPrivateKey(httpClient.Transport, authFields.AppID, key) - httpClient.Transport = itr + // kim: TODO: unsure if this is the same. Logically, it should be the same + // as before, but it wraps the GH auth transport in the rehttp transport, + // which means we can add custom retry logic on top of GH auth'd requests. + // httpClient := utility.GetHTTPClient(retryConf) + // kim: TODO: see if this can be wrapped in a rehttp.NewTransport, which + // accepts a RoundTripper. The transport returned from this already fulfills + // the RoundTripper interface. + // itr := ghinstallation.NewAppsTransportFromPrivateKey(httpClient.Transport, authFields.AppID, key) + // httpClient.Transport = itr + itr := ghinstallation.NewAppsTransportFromPrivateKey(utility.DefaultTransport(), authFields.AppID, key) + httpClient := utility.GetCustomHTTPRetryableClientWithTransport(itr, githubClientShouldRetry(), utility.RetryHTTPDelay(utility.RetryOptions{ + MinDelay: GitHubRetryMinDelay, + MaxDelay: GitHubRetryMaxDelay, + MaxAttempts: GitHubMaxRetries + 1, + })) + client := github.NewClient(httpClient) wrappedClient := GitHubClient{Client: client} return &wrappedClient, nil } +func githubClientShouldRetry() utility.HTTPRetryFunction { + return func(index int, req *http.Request, resp *http.Response, err error) bool { + // kim: TODO: add custom logic for retrying on GitHub app errors. May + // unfortunately require reading the body depending on how the response + // is returned. + // Found errors: + // * EOF + // * read: connection reset by peer + // * 504 We couldn't respond to your request in time. Sorry about that. Please try resubmitting your request and contact us if the problem persists. + // * 403 You have exceeded a secondary rate limit. Please wait a few minutes before you try again. If you reach out to GitHub Support for help, please include the request ID + // Note: may have to read the response body. Then either retry or + // restore the response body and pass it along to the caller. Seems like + // most code here doesn't use the response body anyways, so it's + // probably fine to restore it. + return false + } +} + // getInstallationIDFromGitHub returns an installation ID from GitHub given an owner and a repo. // This function cannot be moved to thirdparty because it is needed to set up the environment. func getInstallationIDFromGitHub(ctx context.Context, authFields *GithubAppAuth, owner, repo string) (int64, error) { diff --git a/rest/route/agent.go b/rest/route/agent.go index c193ee87d9e..255657025dc 100644 --- a/rest/route/agent.go +++ b/rest/route/agent.go @@ -1648,6 +1648,13 @@ func (h *createGitHubDynamicAccessToken) Run(ctx context.Context) gimlet.Respond Permissions: permissions, }) if err != nil { + // kim: NOTE: it's not so easy to determine if the error is due to user + // error or if it's due to GitHub sucking, and we still have to retry + // when GitHub temporarily sucks. Alternative difficult possibility is + // to keep this as-is with returning 500 and somehow integrate the + // agent's client so it logs HTTP errors (HTTP client doesn't support + // this natively). + // See logs: https://mongodb.splunkcloud.com/en-US/app/search/search?q=search%20index%3Devergreen%20%2Frest%2Fv2%2Ftask%2F*%2Fgithub_dynamic_access_token%20AND%20status%3D500%20AND%20method%3DPOST%20AND%20%22request%20encountered%20internal%20error%22%20%0A%7C%20stats%20count%20by%20error.message&display.page.search.mode=verbose&dispatch.sample_ratio=1&workload_pool=&earliest=-30d%40d&latest=now&display.page.search.tab=statistics&display.general.type=statistics&sid=1738789170.18231874 return gimlet.MakeJSONInternalErrorResponder(errors.Wrapf(err, "creating installation token for '%s/%s'", h.owner, h.repo)) } if token == "" { From 771fb6b3a9b7e610a80a0df3ab13f4c0c10dccf0 Mon Sep 17 00:00:00 2001 From: Kim Tao Date: Thu, 6 Feb 2025 13:29:34 -0500 Subject: [PATCH 2/4] Add HC span and Splunk logging for GH client errors --- model/githubapp/github_app_auth.go | 24 +++++++- model/githubapp/github_app_installation.go | 70 ++++++++++++++++++++++ model/githubapp/otel.go | 12 ++++ 3 files changed, 104 insertions(+), 2 deletions(-) create mode 100644 model/githubapp/otel.go diff --git a/model/githubapp/github_app_auth.go b/model/githubapp/github_app_auth.go index fdbf647edb5..02534581bab 100644 --- a/model/githubapp/github_app_auth.go +++ b/model/githubapp/github_app_auth.go @@ -7,6 +7,15 @@ import ( "github.com/evergreen-ci/evergreen" "github.com/google/go-github/v52/github" "github.com/pkg/errors" + "go.opentelemetry.io/otel/attribute" + "go.opentelemetry.io/otel/trace" +) + +const ( + githubAppEndpointAttribute = "evergreen.githubapp.endpoint" + githubAppAttemptAttribute = "evergreen.githubapp.attempt" + githubAppURLAttribute = "evergreen.githubapp.url" + githubAppMethodAttribute = "evergreen.githubapp.method" ) // GithubAppAuth holds the appId and privateKey for the github app associated with the project. @@ -143,6 +152,13 @@ func (g *GithubAppAuth) CreateInstallationToken(ctx context.Context, owner, repo // createInstallationTokenForID returns an installation token from GitHub given an installation ID. // This function cannot be moved to thirdparty because it is needed to set up the environment. func (g *GithubAppAuth) createInstallationTokenForID(ctx context.Context, installationID int64, opts *github.InstallationTokenOptions) (string, *github.InstallationPermissions, error) { + // kim: TODO: test HC span creation in staging + caller := "CreateInstallationToken" + ctx, span := tracer.Start(ctx, caller, trace.WithAttributes( + attribute.String(githubAppEndpointAttribute, caller), + )) + defer span.End() + client, err := getGitHubClientForAuth(g) if err != nil { return "", nil, errors.Wrap(err, "getting GitHub client for token creation") @@ -152,12 +168,16 @@ func (g *GithubAppAuth) createInstallationTokenForID(ctx context.Context, instal token, resp, err := client.Apps.CreateInstallationToken(ctx, installationID, opts) if resp != nil { defer resp.Body.Close() + span.SetAttributes(attribute.Int("status", resp.StatusCode)) } if err != nil { - return "", nil, errors.Wrapf(err, "creating installation token for installation id: '%d'", installationID) + span.SetAttributes(attribute.String("err", err.Error())) + return "", nil, errors.Wrapf(err, "creating installation token for installation id: %d", installationID) } if token == nil { - return "", nil, errors.Errorf("Installation token for installation 'id': %d not found", installationID) + err := errors.Errorf("Installation token for installation 'id': %d not found", installationID) + span.SetAttributes(attribute.String("err", err.Error())) + return "", nil, err } return token.GetToken(), token.GetPermissions(), nil diff --git a/model/githubapp/github_app_installation.go b/model/githubapp/github_app_installation.go index 8570efb5b33..d5e6d0f9f1c 100644 --- a/model/githubapp/github_app_installation.go +++ b/model/githubapp/github_app_installation.go @@ -2,6 +2,7 @@ package githubapp import ( "context" + "io" "net/http" "sync" "time" @@ -11,9 +12,12 @@ import ( "github.com/evergreen-ci/utility" "github.com/golang-jwt/jwt" "github.com/google/go-github/v52/github" + "github.com/mongodb/grip" + "github.com/mongodb/grip/message" "github.com/pkg/errors" "go.mongodb.org/mongo-driver/bson" "go.mongodb.org/mongo-driver/mongo/options" + "go.opentelemetry.io/otel/attribute" ) const ( @@ -105,6 +109,7 @@ func (g *GitHubClient) Close() { // This function cannot be moved to thirdparty because it is needed to set up the environment. // Couple this with a defered call with Close() to clean up the client. func getGitHubClientForAuth(authFields *GithubAppAuth) (*GitHubClient, error) { + // kim: TODO: remove once replaced with retry func // retryConf := utility.NewDefaultHTTPRetryConf() // retryConf.MaxDelay = GitHubRetryMaxDelay // retryConf.BaseDelay = GitHubRetryMinDelay @@ -137,7 +142,15 @@ func getGitHubClientForAuth(authFields *GithubAppAuth) (*GitHubClient, error) { } func githubClientShouldRetry() utility.HTTPRetryFunction { + defaultRetryableStatuses := utility.NewDefaultHTTPRetryConf().Statuses + return func(index int, req *http.Request, resp *http.Response, err error) bool { + _, span := tracer.Start(req.Context(), "githubClientShouldRetry") + defer span.End() + + span.SetAttributes(attribute.Int(githubAppAttemptAttribute, index)) + span.SetAttributes(attribute.String(githubAppURLAttribute, req.URL.String())) + span.SetAttributes(attribute.String(githubAppMethodAttribute, req.Method)) // kim: TODO: add custom logic for retrying on GitHub app errors. May // unfortunately require reading the body depending on how the response // is returned. @@ -150,6 +163,63 @@ func githubClientShouldRetry() utility.HTTPRetryFunction { // restore the response body and pass it along to the caller. Seems like // most code here doesn't use the response body anyways, so it's // probably fine to restore it. + + // kim: TODO: add Splunk logs + // kim: TODO: add testing for Splunk logging + + makeLogMsg := func(extraFields map[string]any) message.Fields { + msg := message.Fields{ + "url": req.URL.String(), + "method": req.Method, + "attempt": index, + "op": "githubClientShouldRetry", + } + for k, v := range extraFields { + msg[k] = v + } + return msg + } + + if err != nil { + if errors.Is(err, io.EOF) || errors.Is(err, io.ErrUnexpectedEOF) { + return true + } + if utility.IsTemporaryError(err) { + return true + } + + // TODO (DEVPROD-13567): retry in situations where there's no + // response but the error is still retryable (e.g. connection reset + // by peer). + + grip.Error(message.WrapError(err, makeLogMsg(map[string]any{ + "message": "GitHub endpoint encountered unretryable error", + }))) + + return false + } + + if resp == nil { + grip.Error(message.WrapError(err, makeLogMsg(map[string]any{ + "message": "GitHub app endpoint returned nil response", + }))) + return true + } + + for _, statusCode := range defaultRetryableStatuses { + if resp.StatusCode == statusCode { + return true + } + } + + // TODO (DEVPROD-13567): retry when response from GitHub is non-OK due + // to a transient problem that is still retryable (e.g. secondary rate + // limit exceeded). + grip.ErrorWhen(resp.StatusCode >= http.StatusBadRequest, makeLogMsg(map[string]any{ + "message": "GitHub app endpoint returned response but is not retryable", + "status_code": resp.StatusCode, + })) + return false } } diff --git a/model/githubapp/otel.go b/model/githubapp/otel.go new file mode 100644 index 00000000000..2d075092b92 --- /dev/null +++ b/model/githubapp/otel.go @@ -0,0 +1,12 @@ +package githubapp + +import ( + "fmt" + + "github.com/evergreen-ci/evergreen" + "go.opentelemetry.io/otel" +) + +var packageName = fmt.Sprintf("%s%s", evergreen.PackageName, "/model/githubapp") + +var tracer = otel.GetTracerProvider().Tracer(packageName) From febab6a360a33a3e2503792feb6df6e096300fa7 Mon Sep 17 00:00:00 2001 From: Kim Tao Date: Thu, 6 Feb 2025 13:30:27 -0500 Subject: [PATCH 3/4] Clean up --- agent/internal/client/base_client.go | 3 --- model/githubapp/github_app_auth.go | 3 --- model/githubapp/github_app_installation.go | 30 ---------------------- rest/route/agent.go | 7 ----- 4 files changed, 43 deletions(-) diff --git a/agent/internal/client/base_client.go b/agent/internal/client/base_client.go index 0e7c00d6423..399100b6419 100644 --- a/agent/internal/client/base_client.go +++ b/agent/internal/client/base_client.go @@ -941,9 +941,6 @@ func (c *baseCommunicator) CreateGitHubDynamicAccessToken(ctx context.Context, t path: fmt.Sprintf("task/%s/github_dynamic_access_token/%s/%s", td.ID, owner, repo), taskData: &td, } - // kim: NOTE: this doesn't call retryRequest but still times out requesting - // the route. Possibly it is getting 500 but still retrying due to HTTP - // client configuration resp, err := c.request(ctx, info, permissions) if err != nil { return "", nil, errors.Wrapf(err, "creating github dynamic access token for '%s/%s'", owner, repo) diff --git a/model/githubapp/github_app_auth.go b/model/githubapp/github_app_auth.go index 02534581bab..67cfb425d8f 100644 --- a/model/githubapp/github_app_auth.go +++ b/model/githubapp/github_app_auth.go @@ -133,8 +133,6 @@ func (g *GithubAppAuth) CreateGitHubSenderInstallationToken(ctx context.Context, // CreateInstallationToken creates an installation token for the given // owner/repo. This is never cached, and should only be used in scenarios where // the token can be revoked at any time. -// kim: NOTE: this is the main logic used to generate dynamic access token. This -// package uses a custom GitHub HTTP client based on utility/rehttp. func (g *GithubAppAuth) CreateInstallationToken(ctx context.Context, owner, repo string, opts *github.InstallationTokenOptions) (string, *github.InstallationPermissions, error) { installationID, err := getInstallationID(ctx, g, owner, repo) if err != nil { @@ -152,7 +150,6 @@ func (g *GithubAppAuth) CreateInstallationToken(ctx context.Context, owner, repo // createInstallationTokenForID returns an installation token from GitHub given an installation ID. // This function cannot be moved to thirdparty because it is needed to set up the environment. func (g *GithubAppAuth) createInstallationTokenForID(ctx context.Context, installationID int64, opts *github.InstallationTokenOptions) (string, *github.InstallationPermissions, error) { - // kim: TODO: test HC span creation in staging caller := "CreateInstallationToken" ctx, span := tracer.Start(ctx, caller, trace.WithAttributes( attribute.String(githubAppEndpointAttribute, caller), diff --git a/model/githubapp/github_app_installation.go b/model/githubapp/github_app_installation.go index d5e6d0f9f1c..1b2229f9da4 100644 --- a/model/githubapp/github_app_installation.go +++ b/model/githubapp/github_app_installation.go @@ -109,26 +109,11 @@ func (g *GitHubClient) Close() { // This function cannot be moved to thirdparty because it is needed to set up the environment. // Couple this with a defered call with Close() to clean up the client. func getGitHubClientForAuth(authFields *GithubAppAuth) (*GitHubClient, error) { - // kim: TODO: remove once replaced with retry func - // retryConf := utility.NewDefaultHTTPRetryConf() - // retryConf.MaxDelay = GitHubRetryMaxDelay - // retryConf.BaseDelay = GitHubRetryMinDelay - // retryConf.MaxRetries = GitHubMaxRetries - key, err := jwt.ParseRSAPrivateKeyFromPEM(authFields.PrivateKey) if err != nil { return nil, errors.Wrap(err, "parsing private key") } - // kim: TODO: unsure if this is the same. Logically, it should be the same - // as before, but it wraps the GH auth transport in the rehttp transport, - // which means we can add custom retry logic on top of GH auth'd requests. - // httpClient := utility.GetHTTPClient(retryConf) - // kim: TODO: see if this can be wrapped in a rehttp.NewTransport, which - // accepts a RoundTripper. The transport returned from this already fulfills - // the RoundTripper interface. - // itr := ghinstallation.NewAppsTransportFromPrivateKey(httpClient.Transport, authFields.AppID, key) - // httpClient.Transport = itr itr := ghinstallation.NewAppsTransportFromPrivateKey(utility.DefaultTransport(), authFields.AppID, key) httpClient := utility.GetCustomHTTPRetryableClientWithTransport(itr, githubClientShouldRetry(), utility.RetryHTTPDelay(utility.RetryOptions{ MinDelay: GitHubRetryMinDelay, @@ -151,21 +136,6 @@ func githubClientShouldRetry() utility.HTTPRetryFunction { span.SetAttributes(attribute.Int(githubAppAttemptAttribute, index)) span.SetAttributes(attribute.String(githubAppURLAttribute, req.URL.String())) span.SetAttributes(attribute.String(githubAppMethodAttribute, req.Method)) - // kim: TODO: add custom logic for retrying on GitHub app errors. May - // unfortunately require reading the body depending on how the response - // is returned. - // Found errors: - // * EOF - // * read: connection reset by peer - // * 504 We couldn't respond to your request in time. Sorry about that. Please try resubmitting your request and contact us if the problem persists. - // * 403 You have exceeded a secondary rate limit. Please wait a few minutes before you try again. If you reach out to GitHub Support for help, please include the request ID - // Note: may have to read the response body. Then either retry or - // restore the response body and pass it along to the caller. Seems like - // most code here doesn't use the response body anyways, so it's - // probably fine to restore it. - - // kim: TODO: add Splunk logs - // kim: TODO: add testing for Splunk logging makeLogMsg := func(extraFields map[string]any) message.Fields { msg := message.Fields{ diff --git a/rest/route/agent.go b/rest/route/agent.go index 255657025dc..c193ee87d9e 100644 --- a/rest/route/agent.go +++ b/rest/route/agent.go @@ -1648,13 +1648,6 @@ func (h *createGitHubDynamicAccessToken) Run(ctx context.Context) gimlet.Respond Permissions: permissions, }) if err != nil { - // kim: NOTE: it's not so easy to determine if the error is due to user - // error or if it's due to GitHub sucking, and we still have to retry - // when GitHub temporarily sucks. Alternative difficult possibility is - // to keep this as-is with returning 500 and somehow integrate the - // agent's client so it logs HTTP errors (HTTP client doesn't support - // this natively). - // See logs: https://mongodb.splunkcloud.com/en-US/app/search/search?q=search%20index%3Devergreen%20%2Frest%2Fv2%2Ftask%2F*%2Fgithub_dynamic_access_token%20AND%20status%3D500%20AND%20method%3DPOST%20AND%20%22request%20encountered%20internal%20error%22%20%0A%7C%20stats%20count%20by%20error.message&display.page.search.mode=verbose&dispatch.sample_ratio=1&workload_pool=&earliest=-30d%40d&latest=now&display.page.search.tab=statistics&display.general.type=statistics&sid=1738789170.18231874 return gimlet.MakeJSONInternalErrorResponder(errors.Wrapf(err, "creating installation token for '%s/%s'", h.owner, h.repo)) } if token == "" { From 13322f70557052774cd92852771d0d1fda0ad82f Mon Sep 17 00:00:00 2001 From: Kim Tao Date: Thu, 6 Feb 2025 13:39:53 -0500 Subject: [PATCH 4/4] Clean up span attributes to follow conventions --- model/githubapp/github_app_auth.go | 18 ++++++++++-------- model/githubapp/github_app_installation.go | 5 +++-- 2 files changed, 13 insertions(+), 10 deletions(-) diff --git a/model/githubapp/github_app_auth.go b/model/githubapp/github_app_auth.go index 67cfb425d8f..250e9e9f123 100644 --- a/model/githubapp/github_app_auth.go +++ b/model/githubapp/github_app_auth.go @@ -12,10 +12,12 @@ import ( ) const ( - githubAppEndpointAttribute = "evergreen.githubapp.endpoint" - githubAppAttemptAttribute = "evergreen.githubapp.attempt" - githubAppURLAttribute = "evergreen.githubapp.url" - githubAppMethodAttribute = "evergreen.githubapp.method" + githubAppEndpointAttribute = "evergreen.githubapp.endpoint" + githubAppAttemptAttribute = "evergreen.githubapp.attempt" + githubAppURLAttribute = "evergreen.githubapp.url" + githubAppErrorAttribute = "evergreen.githubapp.error" + githubAppMethodAttribute = "evergreen.githubapp.method" + githubAppStatusCodeAttribute = "evergreen.githubapp.status_code" ) // GithubAppAuth holds the appId and privateKey for the github app associated with the project. @@ -150,7 +152,7 @@ func (g *GithubAppAuth) CreateInstallationToken(ctx context.Context, owner, repo // createInstallationTokenForID returns an installation token from GitHub given an installation ID. // This function cannot be moved to thirdparty because it is needed to set up the environment. func (g *GithubAppAuth) createInstallationTokenForID(ctx context.Context, installationID int64, opts *github.InstallationTokenOptions) (string, *github.InstallationPermissions, error) { - caller := "CreateInstallationToken" + const caller = "CreateInstallationToken" ctx, span := tracer.Start(ctx, caller, trace.WithAttributes( attribute.String(githubAppEndpointAttribute, caller), )) @@ -165,15 +167,15 @@ func (g *GithubAppAuth) createInstallationTokenForID(ctx context.Context, instal token, resp, err := client.Apps.CreateInstallationToken(ctx, installationID, opts) if resp != nil { defer resp.Body.Close() - span.SetAttributes(attribute.Int("status", resp.StatusCode)) + span.SetAttributes(attribute.Int(githubAppStatusCodeAttribute, resp.StatusCode)) } if err != nil { - span.SetAttributes(attribute.String("err", err.Error())) + span.SetAttributes(attribute.String(githubAppErrorAttribute, err.Error())) return "", nil, errors.Wrapf(err, "creating installation token for installation id: %d", installationID) } if token == nil { err := errors.Errorf("Installation token for installation 'id': %d not found", installationID) - span.SetAttributes(attribute.String("err", err.Error())) + span.SetAttributes(attribute.String(githubAppErrorAttribute, err.Error())) return "", nil, err } diff --git a/model/githubapp/github_app_installation.go b/model/githubapp/github_app_installation.go index 1b2229f9da4..2b7e1d24c98 100644 --- a/model/githubapp/github_app_installation.go +++ b/model/githubapp/github_app_installation.go @@ -130,7 +130,8 @@ func githubClientShouldRetry() utility.HTTPRetryFunction { defaultRetryableStatuses := utility.NewDefaultHTTPRetryConf().Statuses return func(index int, req *http.Request, resp *http.Response, err error) bool { - _, span := tracer.Start(req.Context(), "githubClientShouldRetry") + const op = "githubClientShouldRetry" + _, span := tracer.Start(req.Context(), op) defer span.End() span.SetAttributes(attribute.Int(githubAppAttemptAttribute, index)) @@ -142,7 +143,7 @@ func githubClientShouldRetry() utility.HTTPRetryFunction { "url": req.URL.String(), "method": req.Method, "attempt": index, - "op": "githubClientShouldRetry", + "op": op, } for k, v := range extraFields { msg[k] = v