diff --git a/server/events/vcs/github_client.go b/server/events/vcs/github_client.go index 43e0deb1f7..0b5ed38c5d 100644 --- a/server/events/vcs/github_client.go +++ b/server/events/vcs/github_client.go @@ -17,6 +17,7 @@ import ( "context" "fmt" "strings" + "time" "github.com/runatlantis/atlantis/server/events/models" "github.com/runatlantis/atlantis/server/events/vcs/common" @@ -273,7 +274,25 @@ func (g *GithubClient) PullIsMergeable(repo models.Repo, pull models.PullRequest // GetPullRequest returns the pull request. func (g *GithubClient) GetPullRequest(repo models.Repo, num int) (*github.PullRequest, error) { - pull, _, err := g.client.PullRequests.Get(g.ctx, repo.Owner, repo.Name, num) + var err error + var pull *github.PullRequest + + // GitHub has started to return 404's here (#1019) even after they send the webhook. + // They've got some eventual consistency issues going on so we're just going + // to retry up to 3 times with a 1s sleep. + numRetries := 3 + retryDelay := 1 * time.Second + for i := 0; i < numRetries; i++ { + pull, _, err = g.client.PullRequests.Get(g.ctx, repo.Owner, repo.Name, num) + if err == nil { + return pull, nil + } + ghErr, ok := err.(*github.ErrorResponse) + if !ok || ghErr.Response.StatusCode != 404 { + return pull, err + } + time.Sleep(retryDelay) + } return pull, err } diff --git a/server/events/vcs/github_client_test.go b/server/events/vcs/github_client_test.go index 6c46fe07ea..dc56d79965 100644 --- a/server/events/vcs/github_client_test.go +++ b/server/events/vcs/github_client_test.go @@ -852,3 +852,48 @@ func TestGithubClient_SplitComments(t *testing.T) { Assert(t, strings.Contains(firstSplit, models.PlanCommand.String()), fmt.Sprintf("comment should contain the command name but was %q", firstSplit)) Assert(t, strings.Contains(secondSplit, "continued from previous comment"), fmt.Sprintf("comment should contain no reference to the command name but was %q", secondSplit)) } + +// Test that we retry the get pull request call if it 404s. +func TestGithubClient_Retry404(t *testing.T) { + var numCalls = 0 + + testServer := httptest.NewTLSServer( + http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + + switch r.Method + " " + r.RequestURI { + case "GET /api/v3/repos/runatlantis/atlantis/pulls/1": + defer r.Body.Close() // nolint: errcheck + numCalls++ + if numCalls < 3 { + w.WriteHeader(404) + } else { + w.WriteHeader(200) + } + return + default: + t.Errorf("got unexpected request at %q", r.RequestURI) + http.Error(w, "not found", http.StatusNotFound) + return + } + })) + + testServerURL, err := url.Parse(testServer.URL) + Ok(t, err) + client, err := vcs.NewGithubClient(testServerURL.Host, &vcs.GithubUserCredentials{"user", "pass"}, nil) + Ok(t, err) + defer disableSSLVerification()() + repo := models.Repo{ + FullName: "runatlantis/atlantis", + Owner: "runatlantis", + Name: "atlantis", + CloneURL: "", + SanitizedCloneURL: "", + VCSHost: models.VCSHost{ + Type: models.Github, + Hostname: "github.com", + }, + } + _, err = client.GetPullRequest(repo, 1) + Ok(t, err) + Equals(t, 3, numCalls) +}