diff --git a/server/controllers/events/events_controller.go b/server/controllers/events/events_controller.go index 7164d10da7..f12c3b5d45 100644 --- a/server/controllers/events/events_controller.go +++ b/server/controllers/events/events_controller.go @@ -20,6 +20,7 @@ import ( "strings" "github.com/google/go-github/v31/github" + stats "github.com/lyft/gostats" "github.com/mcdafydd/go-azuredevops/azuredevops" "github.com/microcosm-cc/bluemonday" "github.com/pkg/errors" @@ -48,6 +49,7 @@ type VCSEventsController struct { CommandRunner events.CommandRunner PullCleaner events.PullCleaner Logger logging.SimpleLogging + Scope stats.Scope Parser events.EventParsing CommentParser events.CommentParsing ApplyDisabled bool @@ -136,6 +138,16 @@ func (e *VCSEventsController) Post(w http.ResponseWriter, r *http.Request) { e.respond(w, logging.Debug, http.StatusBadRequest, "Ignoring request") } +type HttpError struct { + err error + code int +} + +type HttpResponse struct { + body string + err HttpError +} + func (e *VCSEventsController) handleGithubPost(w http.ResponseWriter, r *http.Request) { // Validate the request against the optional webhook secret. payload, err := e.GithubRequestValidator.Validate(r, e.GithubWebhookSecret) @@ -143,20 +155,41 @@ func (e *VCSEventsController) handleGithubPost(w http.ResponseWriter, r *http.Re e.respond(w, logging.Warn, http.StatusBadRequest, err.Error()) return } - e.Logger.Debug("request valid") githubReqID := "X-Github-Delivery=" + r.Header.Get("X-Github-Delivery") + logger := e.Logger.With("gh-request-id", githubReqID) + scope := e.Scope.Scope("github.event") + + logger.Debug("request valid") + event, _ := github.ParseWebHook(github.WebHookType(r), payload) + + var resp HttpResponse + switch event := event.(type) { case *github.IssueCommentEvent: - e.Logger.Debug("handling as comment event") - e.HandleGithubCommentEvent(w, event, githubReqID) + resp = e.HandleGithubCommentEvent(event, githubReqID, logger) + scope = scope.Scope(fmt.Sprintf("comment.%s", *event.Action)) case *github.PullRequestEvent: - e.Logger.Debug("handling as pull request event") - e.HandleGithubPullRequestEvent(w, event, githubReqID) + resp = e.HandleGithubPullRequestEvent(logger, event, githubReqID) + scope = scope.Scope(fmt.Sprintf("pr.%s", *event.Action)) default: - e.respond(w, logging.Debug, http.StatusOK, "Ignoring unsupported event %s", githubReqID) + resp = HttpResponse{ + body: fmt.Sprintf("Ignoring unsupported event %s", githubReqID), + } } + + if resp.err.code != 0 { + logger.Err("error handling gh post code: %d err: %s", resp.err.code, resp.err.err.Error()) + scope.NewCounter(fmt.Sprintf("error_%d", resp.err.code)).Inc() + w.WriteHeader(resp.err.code) + fmt.Fprintln(w, resp.err.err.Error()) + return + } + + scope.NewCounter(fmt.Sprintf("success_%d", http.StatusOK)).Inc() + w.WriteHeader(http.StatusOK) + fmt.Fprintln(w, resp.body) } func (e *VCSEventsController) handleBitbucketCloudPost(w http.ResponseWriter, r *http.Request) { @@ -247,21 +280,29 @@ func (e *VCSEventsController) handleAzureDevopsPost(w http.ResponseWriter, r *ht // HandleGithubCommentEvent handles comment events from GitHub where Atlantis // commands can come from. It's exported to make testing easier. -func (e *VCSEventsController) HandleGithubCommentEvent(w http.ResponseWriter, event *github.IssueCommentEvent, githubReqID string) { +func (e *VCSEventsController) HandleGithubCommentEvent(event *github.IssueCommentEvent, githubReqID string, logger logging.SimpleLogging) HttpResponse { if event.GetAction() != "created" { - e.respond(w, logging.Debug, http.StatusOK, "Ignoring comment event since action was not created %s", githubReqID) - return + return HttpResponse{ + body: fmt.Sprintf("Ignoring comment event since action was not created %s", githubReqID), + } } baseRepo, user, pullNum, err := e.Parser.ParseGithubIssueCommentEvent(event) + + wrapped := errors.Wrapf(err, "Failed parsing event: %s", githubReqID) if err != nil { - e.respond(w, logging.Error, http.StatusBadRequest, "Failed parsing event: %v %s", err, githubReqID) - return + return HttpResponse{ + body: wrapped.Error(), + err: HttpError{ + code: http.StatusBadRequest, + err: wrapped, + }, + } } // We pass in nil for maybeHeadRepo because the head repo data isn't // available in the GithubIssueComment event. - e.handleCommentEvent(w, baseRepo, nil, nil, user, pullNum, event.Comment.GetBody(), models.Github) + return e.handleCommentEvent(logger, baseRepo, nil, nil, user, pullNum, event.Comment.GetBody(), models.Github) } // HandleBitbucketCloudCommentEvent handles comment events from Bitbucket. @@ -271,7 +312,18 @@ func (e *VCSEventsController) HandleBitbucketCloudCommentEvent(w http.ResponseWr e.respond(w, logging.Error, http.StatusBadRequest, "Error parsing pull data: %s %s=%s", err, bitbucketCloudRequestIDHeader, reqID) return } - e.handleCommentEvent(w, baseRepo, &headRepo, &pull, user, pull.Num, comment, models.BitbucketCloud) + resp := e.handleCommentEvent(e.Logger, baseRepo, &headRepo, &pull, user, pull.Num, comment, models.BitbucketCloud) + + //TODO: move this to the outer most function similar to github + lvl := logging.Debug + code := http.StatusOK + msg := resp.body + if resp.err.code != 0 { + lvl = logging.Error + code = resp.err.code + msg = resp.err.err.Error() + } + e.respond(w, lvl, code, msg) } // HandleBitbucketServerCommentEvent handles comment events from Bitbucket. @@ -281,7 +333,18 @@ func (e *VCSEventsController) HandleBitbucketServerCommentEvent(w http.ResponseW e.respond(w, logging.Error, http.StatusBadRequest, "Error parsing pull data: %s %s=%s", err, bitbucketCloudRequestIDHeader, reqID) return } - e.handleCommentEvent(w, baseRepo, &headRepo, &pull, user, pull.Num, comment, models.BitbucketCloud) + resp := e.handleCommentEvent(e.Logger, baseRepo, &headRepo, &pull, user, pull.Num, comment, models.BitbucketCloud) + + //TODO: move this to the outer most function similar to github + lvl := logging.Debug + code := http.StatusOK + msg := resp.body + if resp.err.code != 0 { + lvl = logging.Error + code = resp.err.code + msg = resp.err.err.Error() + } + e.respond(w, lvl, code, msg) } func (e *VCSEventsController) handleBitbucketCloudPullRequestEvent(w http.ResponseWriter, eventType string, body []byte, reqID string) { @@ -292,7 +355,18 @@ func (e *VCSEventsController) handleBitbucketCloudPullRequestEvent(w http.Respon } pullEventType := e.Parser.GetBitbucketCloudPullEventType(eventType) e.Logger.Info("identified event as type %q", pullEventType.String()) - e.handlePullRequestEvent(w, baseRepo, headRepo, pull, user, pullEventType) + resp := e.handlePullRequestEvent(e.Logger, baseRepo, headRepo, pull, user, pullEventType) + + //TODO: move this to the outer most function similar to github + lvl := logging.Debug + code := http.StatusOK + msg := resp.body + if resp.err.code != 0 { + lvl = logging.Error + code = resp.err.code + msg = resp.err.err.Error() + } + e.respond(w, lvl, code, msg) } func (e *VCSEventsController) handleBitbucketServerPullRequestEvent(w http.ResponseWriter, eventType string, body []byte, reqID string) { @@ -303,23 +377,40 @@ func (e *VCSEventsController) handleBitbucketServerPullRequestEvent(w http.Respo } pullEventType := e.Parser.GetBitbucketServerPullEventType(eventType) e.Logger.Info("identified event as type %q", pullEventType.String()) - e.handlePullRequestEvent(w, baseRepo, headRepo, pull, user, pullEventType) + resp := e.handlePullRequestEvent(e.Logger, baseRepo, headRepo, pull, user, pullEventType) + + //TODO: move this to the outer most function similar to github + lvl := logging.Debug + code := http.StatusOK + msg := resp.body + if resp.err.code != 0 { + lvl = logging.Error + code = resp.err.code + msg = resp.err.err.Error() + } + e.respond(w, lvl, code, msg) } // HandleGithubPullRequestEvent will delete any locks associated with the pull // request if the event is a pull request closed event. It's exported to make // testing easier. -func (e *VCSEventsController) HandleGithubPullRequestEvent(w http.ResponseWriter, pullEvent *github.PullRequestEvent, githubReqID string) { +func (e *VCSEventsController) HandleGithubPullRequestEvent(logger logging.SimpleLogging, pullEvent *github.PullRequestEvent, githubReqID string) HttpResponse { pull, pullEventType, baseRepo, headRepo, user, err := e.Parser.ParseGithubPullEvent(pullEvent) if err != nil { - e.respond(w, logging.Error, http.StatusBadRequest, "Error parsing pull data: %s %s", err, githubReqID) - return + wrapped := errors.Wrapf(err, "Error parsing pull data: %s %s", err, githubReqID) + return HttpResponse{ + body: wrapped.Error(), + err: HttpError{ + code: http.StatusBadRequest, + err: wrapped, + }, + } } - e.Logger.Info("identified event as type %q", pullEventType.String()) - e.handlePullRequestEvent(w, baseRepo, headRepo, pull, user, pullEventType) + logger.Debug("identified event as type %q", pullEventType.String()) + return e.handlePullRequestEvent(logger, baseRepo, headRepo, pull, user, pullEventType) } -func (e *VCSEventsController) handlePullRequestEvent(w http.ResponseWriter, baseRepo models.Repo, headRepo models.Repo, pull models.PullRequest, user models.User, eventType models.PullRequestEventType) { +func (e *VCSEventsController) handlePullRequestEvent(logger logging.SimpleLogging, baseRepo models.Repo, headRepo models.Repo, pull models.PullRequest, user models.User, eventType models.PullRequestEventType) HttpResponse { if !e.RepoAllowlistChecker.IsAllowlisted(baseRepo.FullName, baseRepo.VCSHost.Hostname) { // If the repo isn't allowlisted and we receive an opened pull request // event we comment back on the pull request that the repo isn't @@ -328,10 +419,16 @@ func (e *VCSEventsController) handlePullRequestEvent(w http.ResponseWriter, base if eventType == models.OpenedPullEvent { e.commentNotAllowlisted(baseRepo, pull.Num) } - e.respond(w, logging.Debug, http.StatusForbidden, - "Ignoring pull request event from non-allowlisted repo \"%s/%s\"", - baseRepo.VCSHost.Hostname, baseRepo.FullName) - return + + err := errors.New(fmt.Sprintf("Pull request event from non-allowlisted repo \"%s/%s\"", baseRepo.VCSHost.Hostname, baseRepo.FullName)) + + return HttpResponse{ + body: err.Error(), + err: HttpError{ + code: http.StatusForbidden, + err: err, + }, + } } switch eventType { @@ -341,30 +438,37 @@ func (e *VCSEventsController) handlePullRequestEvent(w http.ResponseWriter, base // Respond with success and then actually execute the command asynchronously. // We use a goroutine so that this function returns and the connection is // closed. - fmt.Fprintln(w, "Processing...") - - e.Logger.Info("executing autoplan") if !e.TestingMode { go e.CommandRunner.RunAutoplanCommand(baseRepo, headRepo, pull, user) } else { // When testing we want to wait for everything to complete. e.CommandRunner.RunAutoplanCommand(baseRepo, headRepo, pull, user) } - return + return HttpResponse{ + body: "Processing...", + } case models.ClosedPullEvent: // If the pull request was closed, we delete locks. if err := e.PullCleaner.CleanUpPull(baseRepo, pull); err != nil { - e.respond(w, logging.Error, http.StatusInternalServerError, "Error cleaning pull request: %s", err) - return + return HttpResponse{ + body: err.Error(), + err: HttpError{ + code: http.StatusForbidden, + err: err, + }, + } + } + logger.Info("deleted locks and workspace for repo %s, pull %d", baseRepo.FullName, pull.Num) + return HttpResponse{ + body: "Pull request cleaned successfully", } - e.Logger.Info("deleted locks and workspace for repo %s, pull %d", baseRepo.FullName, pull.Num) - fmt.Fprintln(w, "Pull request cleaned successfully") - return case models.OtherPullEvent: // Else we ignore the event. - e.respond(w, logging.Debug, http.StatusOK, "Ignoring non-actionable pull request event") - return + return HttpResponse{ + body: "Ignoring non-actionable pull request event", + } } + return HttpResponse{} } func (e *VCSEventsController) handleGitlabPost(w http.ResponseWriter, r *http.Request) { @@ -400,10 +504,21 @@ func (e *VCSEventsController) HandleGitlabCommentEvent(w http.ResponseWriter, ev e.respond(w, logging.Error, http.StatusBadRequest, "Error parsing webhook: %s", err) return } - e.handleCommentEvent(w, baseRepo, &headRepo, nil, user, event.MergeRequest.IID, event.ObjectAttributes.Note, models.Gitlab) + resp := e.handleCommentEvent(e.Logger, baseRepo, &headRepo, nil, user, event.MergeRequest.IID, event.ObjectAttributes.Note, models.Gitlab) + + //TODO: move this to the outer most function similar to github + lvl := logging.Debug + code := http.StatusOK + msg := resp.body + if resp.err.code != 0 { + lvl = logging.Error + code = resp.err.code + msg = resp.err.err.Error() + } + e.respond(w, lvl, code, msg) } -func (e *VCSEventsController) handleCommentEvent(w http.ResponseWriter, baseRepo models.Repo, maybeHeadRepo *models.Repo, maybePull *models.PullRequest, user models.User, pullNum int, comment string, vcsHost models.VCSHostType) { +func (e *VCSEventsController) handleCommentEvent(logger logging.SimpleLogging, baseRepo models.Repo, maybeHeadRepo *models.Repo, maybePull *models.PullRequest, user models.User, pullNum int, comment string, vcsHost models.VCSHostType) HttpResponse { parseResult := e.CommentParser.Parse(comment, vcsHost) if parseResult.Ignore { truncated := comment @@ -411,17 +526,25 @@ func (e *VCSEventsController) handleCommentEvent(w http.ResponseWriter, baseRepo if len(truncated) > truncateLen { truncated = comment[:truncateLen] + "..." } - e.respond(w, logging.Debug, http.StatusOK, "Ignoring non-command comment: %q", truncated) - return + return HttpResponse{ + body: fmt.Sprintf("Ignoring non-command comment: %q", truncated), + } } - e.Logger.Info("parsed comment as %s", parseResult.Command) + logger.Info("parsed comment as %s", parseResult.Command) // At this point we know it's a command we're not supposed to ignore, so now // we check if this repo is allowed to run commands in the first place. if !e.RepoAllowlistChecker.IsAllowlisted(baseRepo.FullName, baseRepo.VCSHost.Hostname) { e.commentNotAllowlisted(baseRepo, pullNum) - e.respond(w, logging.Warn, http.StatusForbidden, "Repo not allowlisted") - return + + err := errors.New("Repo not allowlisted") + return HttpResponse{ + body: err.Error(), + err: HttpError{ + err: err, + code: http.StatusForbidden, + }, + } } // If the command isn't valid or doesn't require processing, ex. @@ -430,14 +553,14 @@ func (e *VCSEventsController) handleCommentEvent(w http.ResponseWriter, baseRepo // variable to comment back on the pull request. if parseResult.CommentResponse != "" { if err := e.VCSClient.CreateComment(baseRepo, pullNum, parseResult.CommentResponse, ""); err != nil { - e.Logger.Err("unable to comment on pull request: %s", err) + logger.Err("unable to comment on pull request: %s", err) + } + return HttpResponse{ + body: "Commenting back on pull request", } - e.respond(w, logging.Info, http.StatusOK, "Commenting back on pull request") - return } - e.Logger.Debug("executing command") - fmt.Fprintln(w, "Processing...") + logger.Debug("executing command") if !e.TestingMode { // Respond with success and then actually execute the command asynchronously. // We use a goroutine so that this function returns and the connection is @@ -447,6 +570,10 @@ func (e *VCSEventsController) handleCommentEvent(w http.ResponseWriter, baseRepo // When testing we want to wait for everything to complete. e.CommandRunner.RunCommentCommand(baseRepo, maybeHeadRepo, maybePull, user, pullNum, parseResult.Command) } + + return HttpResponse{ + body: "Processing...", + } } // HandleGitlabMergeRequestEvent will delete any locks associated with the pull @@ -459,7 +586,18 @@ func (e *VCSEventsController) HandleGitlabMergeRequestEvent(w http.ResponseWrite return } e.Logger.Info("identified event as type %q", pullEventType.String()) - e.handlePullRequestEvent(w, baseRepo, headRepo, pull, user, pullEventType) + resp := e.handlePullRequestEvent(e.Logger, baseRepo, headRepo, pull, user, pullEventType) + + //TODO: move this to the outer most function similar to github + lvl := logging.Debug + code := http.StatusOK + msg := resp.body + if resp.err.code != 0 { + lvl = logging.Error + code = resp.err.code + msg = resp.err.err.Error() + } + e.respond(w, lvl, code, msg) } // HandleAzureDevopsPullRequestCommentedEvent handles comment events from Azure DevOps where Atlantis @@ -491,7 +629,18 @@ func (e *VCSEventsController) HandleAzureDevopsPullRequestCommentedEvent(w http. e.respond(w, logging.Error, http.StatusBadRequest, "Error parsing pull request repository field: %s; %s", err, azuredevopsReqID) return } - e.handleCommentEvent(w, baseRepo, nil, nil, user, resource.PullRequest.GetPullRequestID(), string(strippedComment), models.AzureDevops) + resp := e.handleCommentEvent(e.Logger, baseRepo, nil, nil, user, resource.PullRequest.GetPullRequestID(), string(strippedComment), models.AzureDevops) + + //TODO: move this to the outer most function similar to github + lvl := logging.Debug + code := http.StatusOK + msg := resp.body + if resp.err.code != 0 { + lvl = logging.Error + code = resp.err.code + msg = resp.err.err.Error() + } + e.respond(w, lvl, code, msg) } // HandleAzureDevopsPullRequestEvent will delete any locks associated with the pull @@ -521,7 +670,18 @@ func (e *VCSEventsController) HandleAzureDevopsPullRequestEvent(w http.ResponseW return } e.Logger.Info("identified event as type %q", pullEventType.String()) - e.handlePullRequestEvent(w, baseRepo, headRepo, pull, user, pullEventType) + resp := e.handlePullRequestEvent(e.Logger, baseRepo, headRepo, pull, user, pullEventType) + + //TODO: move this to the outer most function similar to github + lvl := logging.Debug + code := http.StatusOK + msg := resp.body + if resp.err.code != 0 { + lvl = logging.Error + code = resp.err.code + msg = resp.err.err.Error() + } + e.respond(w, lvl, code, msg) } // supportsHost returns true if h is in e.SupportedVCSHosts and false otherwise. diff --git a/server/controllers/events/events_controller_e2e_test.go b/server/controllers/events/events_controller_e2e_test.go index c73709149a..f491d6b082 100644 --- a/server/controllers/events/events_controller_e2e_test.go +++ b/server/controllers/events/events_controller_e2e_test.go @@ -1085,6 +1085,7 @@ func setupE2E(t *testing.T, repoDir string) (events_controllers.VCSEventsControl LogStreamResourceCleaner: projectCmdOutputHandler, }, Logger: logger, + Scope: statsScope, Parser: eventParser, CommentParser: commentParser, GithubWebhookSecret: nil, diff --git a/server/controllers/events/events_controller_test.go b/server/controllers/events/events_controller_test.go index 260ffc7340..f67640028e 100644 --- a/server/controllers/events/events_controller_test.go +++ b/server/controllers/events/events_controller_test.go @@ -26,6 +26,7 @@ import ( "testing" "github.com/google/go-github/v31/github" + stats "github.com/lyft/gostats" . "github.com/petergtz/pegomock" events_controllers "github.com/runatlantis/atlantis/server/controllers/events" "github.com/runatlantis/atlantis/server/controllers/events/mocks" @@ -200,6 +201,7 @@ func TestPost_GitlabCommentNotAllowlisted(t *testing.T) { vcsClient := vcsmocks.NewMockClient() e := events_controllers.VCSEventsController{ Logger: logging.NewNoopLogger(t), + Scope: stats.NewStore(stats.NewNullSink(), false).Scope("null"), CommentParser: &events.CommentParser{}, GitlabRequestParserValidator: &events_controllers.DefaultGitlabRequestParserValidator{}, Parser: &events.EventParser{}, @@ -228,6 +230,7 @@ func TestPost_GitlabCommentNotAllowlistedWithSilenceErrors(t *testing.T) { vcsClient := vcsmocks.NewMockClient() e := events_controllers.VCSEventsController{ Logger: logging.NewNoopLogger(t), + Scope: stats.NewStore(stats.NewNullSink(), false).Scope("null"), CommentParser: &events.CommentParser{}, GitlabRequestParserValidator: &events_controllers.DefaultGitlabRequestParserValidator{}, Parser: &events.EventParser{}, @@ -257,6 +260,7 @@ func TestPost_GithubCommentNotAllowlisted(t *testing.T) { vcsClient := vcsmocks.NewMockClient() e := events_controllers.VCSEventsController{ Logger: logging.NewNoopLogger(t), + Scope: stats.NewStore(stats.NewNullSink(), false).Scope("null"), GithubRequestValidator: &events_controllers.DefaultGithubRequestValidator{}, CommentParser: &events.CommentParser{}, Parser: &events.EventParser{}, @@ -286,6 +290,7 @@ func TestPost_GithubCommentNotAllowlistedWithSilenceErrors(t *testing.T) { vcsClient := vcsmocks.NewMockClient() e := events_controllers.VCSEventsController{ Logger: logging.NewNoopLogger(t), + Scope: stats.NewStore(stats.NewNullSink(), false).Scope("null"), GithubRequestValidator: &events_controllers.DefaultGithubRequestValidator{}, CommentParser: &events.CommentParser{}, Parser: &events.EventParser{}, @@ -413,7 +418,7 @@ func TestPost_GithubPullRequestNotAllowlisted(t *testing.T) { When(v.Validate(req, secret)).ThenReturn([]byte(event), nil) w := httptest.NewRecorder() e.Post(w, req) - ResponseContains(t, w, http.StatusForbidden, "Ignoring pull request event from non-allowlisted repo") + ResponseContains(t, w, http.StatusForbidden, "Pull request event from non-allowlisted repo") } func TestPost_GitlabMergeRequestNotAllowlisted(t *testing.T) { @@ -432,7 +437,7 @@ func TestPost_GitlabMergeRequestNotAllowlisted(t *testing.T) { w := httptest.NewRecorder() e.Post(w, req) - ResponseContains(t, w, http.StatusForbidden, "Ignoring pull request event from non-allowlisted repo") + ResponseContains(t, w, http.StatusForbidden, "Pull request event from non-allowlisted repo") } func TestPost_GithubPullRequestUnsupportedAction(t *testing.T) { @@ -484,6 +489,7 @@ func TestPost_AzureDevopsPullRequestIgnoreEvent(t *testing.T) { e := events_controllers.VCSEventsController{ TestingMode: true, Logger: logging.NewNoopLogger(t), + Scope: stats.NewStore(stats.NewNullSink(), false).Scope("null"), ApplyDisabled: false, AzureDevopsWebhookBasicUser: user, AzureDevopsWebhookBasicPassword: secret, @@ -648,6 +654,7 @@ func TestPost_BBServerPullClosed(t *testing.T) { SupportedVCSHosts: []models.VCSHostType{models.BitbucketServer}, VCSClient: nil, Logger: logging.NewNoopLogger(t), + Scope: stats.NewStore(stats.NewNullSink(), false).Scope("null"), } // Build HTTP request. @@ -768,6 +775,7 @@ func setup(t *testing.T) (events_controllers.VCSEventsController, *mocks.MockGit e := events_controllers.VCSEventsController{ TestingMode: true, Logger: logging.NewNoopLogger(t), + Scope: stats.NewStore(stats.NewNullSink(), false).Scope("null"), GithubRequestValidator: v, Parser: p, CommentParser: cp, diff --git a/server/server.go b/server/server.go index 19872f64a1..46394abd86 100644 --- a/server/server.go +++ b/server/server.go @@ -767,6 +767,7 @@ func NewServer(userConfig UserConfig, config Config) (*Server, error) { Parser: eventParser, CommentParser: commentParser, Logger: logger, + Scope: statsScope, ApplyDisabled: userConfig.DisableApply, GithubWebhookSecret: []byte(userConfig.GithubWebhookSecret), GithubRequestValidator: &events_controllers.DefaultGithubRequestValidator{},