Skip to content

Commit

Permalink
Merge pull request #17 from zapier/error-handling-nats
Browse files Browse the repository at this point in the history
Improve error handling and prevent unrecoverable situations
  • Loading branch information
davidwin93 authored Jan 19, 2023
2 parents 1e69521 + 39081c8 commit 8d6fbf3
Show file tree
Hide file tree
Showing 13 changed files with 426 additions and 207 deletions.
7 changes: 5 additions & 2 deletions pkg/comment_actions/parsing.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,12 @@ package comment_actions

import (
"errors"
"fmt"
"strings"

"github.com/jessevdk/go-flags"
"github.com/rs/zerolog/log"
"strings"
"github.com/zapier/tfbuddy/pkg/utils"
)

var (
Expand Down Expand Up @@ -36,7 +39,7 @@ func ParseCommentCommand(noteBody string) (*CommentOpts, error) {
_, err := flags.ParseArgs(opts, words)
if err != nil {
log.Error().Err(err).Msg("error parsing comment as command")
return nil, errors.New("could not parse comment as command")
return nil, fmt.Errorf("could not parse comment as command. %w", utils.ErrPermanent)
}

if opts.Args.Agent == "terraform" || opts.Args.Agent == "atlantis" {
Expand Down
21 changes: 11 additions & 10 deletions pkg/git/git_repo.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import (
"github.com/go-git/go-git/v5/plumbing"
"github.com/go-git/go-git/v5/plumbing/object"
githttp "github.com/go-git/go-git/v5/plumbing/transport/http"
"github.com/zapier/tfbuddy/pkg/utils"
)

type Repository struct {
Expand All @@ -36,7 +37,7 @@ func (gr *Repository) FetchUpstreamBranch(branch string) error {
Auth: gr.authentication,
})
if err != nil && err.Error() != git.NoErrAlreadyUpToDate.Error() {
return err
return utils.CreatePermanentError(err)
}
return nil
}
Expand All @@ -45,51 +46,51 @@ func (gr *Repository) GetMergeBase(oldest, newest string) (string, error) {
for _, rev := range []string{oldest, newest} {
hash, err := gr.ResolveRevision(plumbing.Revision(rev))
if err != nil {
return "", err
return "", utils.CreatePermanentError(err)
}
hashes = append(hashes, hash)
}
var commits []*object.Commit
for _, hash := range hashes {
commit, err := gr.CommitObject(*hash)
if err != nil {
return "", err
return "", utils.CreatePermanentError(err)
}
commits = append(commits, commit)
}
res, err := commits[0].MergeBase(commits[1])
if err != nil {
return "", err
return "", utils.CreatePermanentError(err)
}

if len(res) > 0 {
println(res)
return res[0].Hash.String(), nil

}
return "", fmt.Errorf("could not find merge base")
return "", utils.CreatePermanentError(fmt.Errorf("could not find merge base"))
}
func (gr *Repository) GetModifiedFileNamesBetweenCommits(oldest, newest string) ([]string, error) {

oldestSha, err := gr.ResolveRevision(plumbing.Revision(oldest))
if err != nil {
return nil, err
return nil, utils.CreatePermanentError(err)
}
newestSha, err := gr.ResolveRevision(plumbing.Revision(newest))
if err != nil {
return nil, err
return nil, utils.CreatePermanentError(err)
}
oldestCommit, err := gr.CommitObject(*oldestSha)
if err != nil {
return nil, err
return nil, utils.CreatePermanentError(err)
}
newestCommit, err := gr.CommitObject(*newestSha)
if err != nil {
return nil, err
return nil, utils.CreatePermanentError(err)
}
patch, err := oldestCommit.Patch(newestCommit)
if err != nil {
return nil, err
return nil, utils.CreatePermanentError(err)
}
filePatches := patch.FilePatches()

Expand Down
120 changes: 71 additions & 49 deletions pkg/github/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,19 +2,21 @@ package github

import (
"context"
"errors"
"fmt"
"os"
"path/filepath"
"strings"
"time"

"github.com/cenkalti/backoff/v4"
"github.com/go-git/go-git/v5"
"github.com/go-git/go-git/v5/plumbing"
"github.com/go-git/go-git/v5/plumbing/protocol/packp/sideband"
githttp "github.com/go-git/go-git/v5/plumbing/transport/http"
gogithub "github.com/google/go-github/v49/github"
"github.com/rs/zerolog/log"
zgit "github.com/zapier/tfbuddy/pkg/git"
"github.com/zapier/tfbuddy/pkg/utils"
"github.com/zapier/tfbuddy/pkg/vcs"
"golang.org/x/oauth2"
)
Expand All @@ -28,6 +30,14 @@ type Client struct {
token string
}

const DefaultMaxRetries = 3

func createBackOffWithRetries() backoff.BackOff {
exp := backoff.NewExponentialBackOff()
exp.MaxElapsedTime = 30 * time.Second
return backoff.WithMaxRetries(exp, DefaultMaxRetries)

}
func NewGithubClient() *Client {
token := os.Getenv("GITHUB_TOKEN")
ctx := context.Background()
Expand Down Expand Up @@ -78,17 +88,19 @@ func (c *Client) GetRepoFile(fullName string, file string, ref string) ([]byte,
if err != nil {
return nil, err
}
fileContent, _, _, err := c.client.Repositories.GetContents(c.ctx, parts[0], parts[1], file, &gogithub.RepositoryContentGetOptions{Ref: ref})
if err != nil {
return nil, err
}
return backoff.RetryWithData(func() ([]byte, error) {
fileContent, _, _, err := c.client.Repositories.GetContents(c.ctx, parts[0], parts[1], file, &gogithub.RepositoryContentGetOptions{Ref: ref})
if err != nil {
return nil, utils.CreatePermanentError(err)
}

contents, err := fileContent.GetContent()
if err != nil {
return nil, err
}
contents, err := fileContent.GetContent()
if err != nil {
return nil, utils.CreatePermanentError(err)
}

return []byte(contents), nil
return []byte(contents), nil
}, createBackOffWithRetries())
}

func (c *Client) GetMergeRequestModifiedFiles(prID int, fullName string) ([]string, error) {
Expand All @@ -102,19 +114,21 @@ func (c *Client) GetMergeRequestModifiedFiles(prID int, fullName string) ([]stri
}

if pr.GetChangedFiles() > 0 {
parts, err := splitFullName(fullName)
if err != nil {
return nil, err
}
files, _, err := c.client.PullRequests.ListFiles(c.ctx, parts[0], parts[1], prID, &opts)
if err != nil {
return nil, err
}
modifiedFiles := make([]string, len(files))
for i, file := range files {
modifiedFiles[i] = file.GetFilename()
}
return modifiedFiles, nil
return backoff.RetryWithData(func() ([]string, error) {
parts, err := splitFullName(fullName)
if err != nil {
return nil, utils.CreatePermanentError(err)
}
files, _, err := c.client.PullRequests.ListFiles(c.ctx, parts[0], parts[1], prID, &opts)
if err != nil {
return nil, utils.CreatePermanentError(err)
}
modifiedFiles := make([]string, len(files))
for i, file := range files {
modifiedFiles[i] = file.GetFilename()
}
return modifiedFiles, nil
}, createBackOffWithRetries())
}
return []string{}, nil
}
Expand All @@ -129,7 +143,7 @@ func (c *Client) CloneMergeRequest(project string, mr vcs.MR, dest string) (vcs.

repo, _, err := c.client.Repositories.Get(context.Background(), parts[0], parts[1])
if err != nil {
return nil, err
return nil, utils.CreatePermanentError(err)
}
log.Debug().Msg(*repo.CloneURL)
ref := plumbing.NewBranchReferenceName(mr.GetSourceBranch())
Expand Down Expand Up @@ -208,50 +222,58 @@ func (c *Client) GetPipelinesForCommit(projectWithNS string, commitSHA string) (
func (c *Client) GetIssue(owner *gogithub.User, repo string, issueId int) (*gogithub.Issue, error) {
owName, err := ResolveOwnerName(owner)
if err != nil {
return nil, err
return nil, utils.CreatePermanentError(err)
}
iss, _, err := c.client.Issues.Get(context.Background(), owName, repo, issueId)
return iss, err
return backoff.RetryWithData(func() (*gogithub.Issue, error) {
iss, _, err := c.client.Issues.Get(context.Background(), owName, repo, issueId)
return iss, utils.CreatePermanentError(err)
}, createBackOffWithRetries())
}

func (c *Client) GetPullRequest(fullName string, prID int) (*GithubPR, error) {
parts, err := splitFullName(fullName)
if err != nil {
return nil, err
}
pr, _, err := c.client.PullRequests.Get(c.ctx, parts[0], parts[1], prID)
return &GithubPR{pr}, err
return backoff.RetryWithData(func() (*GithubPR, error) {
pr, _, err := c.client.PullRequests.Get(c.ctx, parts[0], parts[1], prID)
return &GithubPR{pr}, utils.CreatePermanentError(err)
}, createBackOffWithRetries())
}

// PostIssueComment adds a comment to an existing Pull Request
func (c *Client) PostIssueComment(prId int, fullName string, body string) (*gogithub.IssueComment, error) {
projectParts, err := splitFullName(fullName)
if err != nil {
return nil, err
}
comment := &gogithub.IssueComment{
Body: String(body),
}
iss, _, err := c.client.Issues.CreateComment(context.Background(), projectParts[0], projectParts[1], prId, comment)
if err != nil {
log.Error().Err(err).Msg("github client: could not post issue comment")
return nil, utils.CreatePermanentError(err)
}
return backoff.RetryWithData(func() (*gogithub.IssueComment, error) {
comment := &gogithub.IssueComment{
Body: String(body),
}
iss, _, err := c.client.Issues.CreateComment(context.Background(), projectParts[0], projectParts[1], prId, comment)
if err != nil {
log.Error().Err(err).Msg("github client: could not post issue comment")
}

return iss, err
return iss, utils.CreatePermanentError(err)
}, createBackOffWithRetries())
}

// PostPullRequestComment adds a review comment to an existing PullRequest
func (c *Client) PostPullRequestComment(owner, repo string, prId int, body string) error {
// TODO: this is broken
comment := &gogithub.PullRequestComment{
//InReplyTo: nil,
Body: String(body),
}
_, _, err := c.client.PullRequests.CreateComment(c.ctx, owner, repo, int(prId), comment)
if err != nil {
log.Error().Err(err).Msg("could not post pull request comment")
}
return err
return backoff.Retry(func() error {
comment := &gogithub.PullRequestComment{
//InReplyTo: nil,
Body: String(body),
}
_, _, err := c.client.PullRequests.CreateComment(c.ctx, owner, repo, int(prId), comment)
if err != nil {
log.Error().Err(err).Msg("could not post pull request comment")
}
return utils.CreatePermanentError(err)
}, createBackOffWithRetries())
}

func String(str string) *string {
Expand All @@ -266,7 +288,7 @@ func ResolveOwnerName(owner *gogithub.User) (string, error) {
name = owner.Login
if name == nil {
log.Error().Msg("owner name/login is nil")
return "", errors.New("owner name/login is nil")
return "", fmt.Errorf("owner name/login is nil. %w", utils.ErrPermanent)
}
}
return *name, nil
Expand All @@ -275,7 +297,7 @@ func ResolveOwnerName(owner *gogithub.User) (string, error) {
func splitFullName(fullName string) ([]string, error) {
parts := strings.Split(fullName, "/")
if len(parts) != 2 {
return nil, fmt.Errorf("github client: invalid repo format")
return nil, fmt.Errorf("github client: invalid repo format. %w", utils.ErrPermanent)
}
return parts, nil
}
Expand Down
16 changes: 15 additions & 1 deletion pkg/github/hooks/stream_worker.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,24 @@ import (
"github.com/zapier/tfbuddy/pkg/comment_actions"
"github.com/zapier/tfbuddy/pkg/github"
"github.com/zapier/tfbuddy/pkg/tfc_trigger"
"github.com/zapier/tfbuddy/pkg/utils"
)

func (h *GithubHooksHandler) processIssueCommentEvent(msg *GithubIssueCommentEventMsg) error {
var commentErr error
defer func() {
if r := recover(); r != nil {
log.Error().Msgf("Unrecoverable error in issue comment event processing %v", r)
commentErr = nil
}
}()
commentErr = h.processIssueComment(msg)
return utils.EmitPermanentError(commentErr, func(err error) {
log.Error().Msgf("got a permanent error attempting to process comment event: %s", err.Error())
})
}

func (h *GithubHooksHandler) processIssueComment(msg *GithubIssueCommentEventMsg) error {
if msg == nil || msg.payload == nil {
return errors.New("msg is nil")
}
Expand Down Expand Up @@ -102,7 +117,6 @@ func (h *GithubHooksHandler) processIssueCommentEvent(msg *GithubIssueCommentEve
return nil
}
return tfError

}

func (h *GithubHooksHandler) postPullRequestComment(event *gogithub.IssueCommentEvent, body string) error {
Expand Down
Loading

0 comments on commit 8d6fbf3

Please sign in to comment.