Skip to content

Commit

Permalink
Merge pull request #87 from runatlantis/repo-host-refactor
Browse files Browse the repository at this point in the history
Move vcs host type (github or gitlab) into `models` package
  • Loading branch information
lkysow authored Mar 28, 2018
2 parents 2058acf + f21cf09 commit 5d1722b
Show file tree
Hide file tree
Showing 31 changed files with 339 additions and 341 deletions.
2 changes: 1 addition & 1 deletion server/events/apply_executor.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ type ApplyExecutor struct {
// Execute executes apply for the ctx.
func (a *ApplyExecutor) Execute(ctx *CommandContext) CommandResponse {
if a.RequireApproval {
approved, err := a.VCSClient.PullIsApproved(ctx.BaseRepo, ctx.Pull, ctx.VCSHost)
approved, err := a.VCSClient.PullIsApproved(ctx.BaseRepo, ctx.Pull)
if err != nil {
return CommandResponse{Error: errors.Wrap(err, "checking if pull request was approved")}
}
Expand Down
3 changes: 0 additions & 3 deletions server/events/command_context.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@ package events

import (
"github.com/runatlantis/atlantis/server/events/models"
"github.com/runatlantis/atlantis/server/events/vcs"
"github.com/runatlantis/atlantis/server/logging"
)

Expand All @@ -34,6 +33,4 @@ type CommandContext struct {
User models.User
Command *Command
Log *logging.SimpleLogger
// VCSHost is the host that the command came from.
VCSHost vcs.Host
}
28 changes: 15 additions & 13 deletions server/events/command_handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ type CommandRunner interface {
// ExecuteCommand is the first step after a command request has been parsed.
// It handles gathering additional information needed to execute the command
// and then calling the appropriate services to finish executing the command.
ExecuteCommand(baseRepo models.Repo, headRepo models.Repo, user models.User, pullNum int, cmd *Command, vcsHost vcs.Host)
ExecuteCommand(baseRepo models.Repo, headRepo models.Repo, user models.User, pullNum int, cmd *Command)
}

//go:generate pegomock generate -m --use-experimental-model-gen --package mocks -o mocks/mock_github_pull_getter.go GithubPullGetter
Expand Down Expand Up @@ -73,20 +73,23 @@ type CommandHandler struct {
}

// ExecuteCommand executes the command.
// If vcsHost is GitHub, we don't use headRepo and instead make an API call
// If the repo is from GitHub, we don't use headRepo and instead make an API call
// to get the headRepo. This is because the caller is unable to pass in a
// headRepo since there's not enough data available on the initial webhook
// payload.
func (c *CommandHandler) ExecuteCommand(baseRepo models.Repo, headRepo models.Repo, user models.User, pullNum int, cmd *Command, vcsHost vcs.Host) {
func (c *CommandHandler) ExecuteCommand(baseRepo models.Repo, headRepo models.Repo, user models.User, pullNum int, cmd *Command) {
log := c.buildLogger(baseRepo.FullName, pullNum)

var err error
var pull models.PullRequest
if vcsHost == vcs.Github {
switch baseRepo.VCSHost.Type {
case models.Github:
pull, headRepo, err = c.getGithubData(baseRepo, pullNum)
} else if vcsHost == vcs.Gitlab {
case models.Gitlab:
pull, err = c.getGitlabData(baseRepo.FullName, pullNum)
default:
err = errors.New("Unknown VCS type, this is a bug!")
}

log := c.buildLogger(baseRepo.FullName, pullNum)
if err != nil {
log.Err(err.Error())
return
Expand All @@ -97,7 +100,6 @@ func (c *CommandHandler) ExecuteCommand(baseRepo models.Repo, headRepo models.Re
Pull: pull,
HeadRepo: headRepo,
Command: cmd,
VCSHost: vcsHost,
BaseRepo: baseRepo,
}
c.run(ctx)
Expand Down Expand Up @@ -147,17 +149,17 @@ func (c *CommandHandler) run(ctx *CommandContext) {

if !c.AllowForkPRs && ctx.HeadRepo.Owner != ctx.BaseRepo.Owner {
ctx.Log.Info("command was run on a fork pull request which is disallowed")
c.VCSClient.CreateComment(ctx.BaseRepo, ctx.Pull.Num, fmt.Sprintf("Atlantis commands can't be run on fork pull requests. To enable, set --%s", c.AllowForkPRsFlag), ctx.VCSHost) // nolint: errcheck
c.VCSClient.CreateComment(ctx.BaseRepo, ctx.Pull.Num, fmt.Sprintf("Atlantis commands can't be run on fork pull requests. To enable, set --%s", c.AllowForkPRsFlag)) // nolint: errcheck
return
}

if ctx.Pull.State != models.Open {
ctx.Log.Info("command was run on closed pull request")
c.VCSClient.CreateComment(ctx.BaseRepo, ctx.Pull.Num, "Atlantis commands can't be run on closed pull requests", ctx.VCSHost) // nolint: errcheck
c.VCSClient.CreateComment(ctx.BaseRepo, ctx.Pull.Num, "Atlantis commands can't be run on closed pull requests") // nolint: errcheck
return
}

if err := c.CommitStatusUpdater.Update(ctx.BaseRepo, ctx.Pull, vcs.Pending, ctx.Command, ctx.VCSHost); err != nil {
if err := c.CommitStatusUpdater.Update(ctx.BaseRepo, ctx.Pull, vcs.Pending, ctx.Command); err != nil {
ctx.Log.Warn("unable to update commit status: %s", err)
}
if !c.AtlantisWorkspaceLocker.TryLock(ctx.BaseRepo.FullName, ctx.Command.Workspace, ctx.Pull.Num) {
Expand Down Expand Up @@ -197,15 +199,15 @@ func (c *CommandHandler) updatePull(ctx *CommandContext, res CommandResponse) {
ctx.Log.Warn("unable to update commit status: %s", err)
}
comment := c.MarkdownRenderer.Render(res, ctx.Command.Name, ctx.Log.History.String(), ctx.Command.Verbose)
c.VCSClient.CreateComment(ctx.BaseRepo, ctx.Pull.Num, comment, ctx.VCSHost) // nolint: errcheck
c.VCSClient.CreateComment(ctx.BaseRepo, ctx.Pull.Num, comment) // nolint: errcheck
}

// logPanics logs and creates a comment on the pull request for panics.
func (c *CommandHandler) logPanics(ctx *CommandContext) {
if err := recover(); err != nil {
stack := recovery.Stack(3)
c.VCSClient.CreateComment(ctx.BaseRepo, ctx.Pull.Num, // nolint: errcheck
fmt.Sprintf("**Error: goroutine panic. This is a bug.**\n```\n%s\n%s```", err, stack), ctx.VCSHost)
fmt.Sprintf("**Error: goroutine panic. This is a bug.**\n```\n%s\n%s```", err, stack))
ctx.Log.Err("PANIC: %s\n%s", err, stack)
}
}
82 changes: 41 additions & 41 deletions server/events/command_handler_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -78,52 +78,52 @@ func TestExecuteCommand_LogPanics(t *testing.T) {
setup(t)
ch.AllowForkPRs = true // Lets us get to the panic code.
defer func() { ch.AllowForkPRs = false }()
When(ghStatus.Update(fixtures.Repo, fixtures.Pull, vcs.Pending, nil, vcs.Github)).ThenPanic("panic")
ch.ExecuteCommand(fixtures.Repo, fixtures.Repo, fixtures.User, 1, nil, vcs.Github)
_, _, comment, _ := vcsClient.VerifyWasCalledOnce().CreateComment(matchers.AnyModelsRepo(), AnyInt(), AnyString(), matchers.AnyVcsHost()).GetCapturedArguments()
When(ghStatus.Update(fixtures.GithubRepo, fixtures.Pull, vcs.Pending, nil)).ThenPanic("panic")
ch.ExecuteCommand(fixtures.GithubRepo, fixtures.GithubRepo, fixtures.User, 1, nil)
_, _, comment := vcsClient.VerifyWasCalledOnce().CreateComment(matchers.AnyModelsRepo(), AnyInt(), AnyString()).GetCapturedArguments()
Assert(t, strings.Contains(comment, "Error: goroutine panic"), "comment should be about a goroutine panic")
}

func TestExecuteCommand_NoGithubPullGetter(t *testing.T) {
t.Log("if CommandHandler was constructed with a nil GithubPullGetter an error should be logged")
setup(t)
ch.GithubPullGetter = nil
ch.ExecuteCommand(fixtures.Repo, fixtures.Repo, fixtures.User, 1, nil, vcs.Github)
ch.ExecuteCommand(fixtures.GithubRepo, fixtures.GithubRepo, fixtures.User, 1, nil)
Equals(t, "[ERROR] runatlantis/atlantis#1: Atlantis not configured to support GitHub\n", logBytes.String())
}

func TestExecuteCommand_NoGitlabMergeGetter(t *testing.T) {
t.Log("if CommandHandler was constructed with a nil GitlabMergeRequestGetter an error should be logged")
setup(t)
ch.GitlabMergeRequestGetter = nil
ch.ExecuteCommand(fixtures.Repo, fixtures.Repo, fixtures.User, 1, nil, vcs.Gitlab)
ch.ExecuteCommand(fixtures.GitlabRepo, fixtures.GitlabRepo, fixtures.User, 1, nil)
Equals(t, "[ERROR] runatlantis/atlantis#1: Atlantis not configured to support GitLab\n", logBytes.String())
}

func TestExecuteCommand_GithubPullErr(t *testing.T) {
t.Log("if getting the github pull request fails an error should be logged")
setup(t)
When(githubGetter.GetPullRequest(fixtures.Repo, fixtures.Pull.Num)).ThenReturn(nil, errors.New("err"))
ch.ExecuteCommand(fixtures.Repo, fixtures.Repo, fixtures.User, fixtures.Pull.Num, nil, vcs.Github)
When(githubGetter.GetPullRequest(fixtures.GithubRepo, fixtures.Pull.Num)).ThenReturn(nil, errors.New("err"))
ch.ExecuteCommand(fixtures.GithubRepo, fixtures.GithubRepo, fixtures.User, fixtures.Pull.Num, nil)
Equals(t, "[ERROR] runatlantis/atlantis#1: Making pull request API call to GitHub: err\n", logBytes.String())
}

func TestExecuteCommand_GitlabMergeRequestErr(t *testing.T) {
t.Log("if getting the gitlab merge request fails an error should be logged")
setup(t)
When(gitlabGetter.GetMergeRequest(fixtures.Repo.FullName, fixtures.Pull.Num)).ThenReturn(nil, errors.New("err"))
ch.ExecuteCommand(fixtures.Repo, fixtures.Repo, fixtures.User, fixtures.Pull.Num, nil, vcs.Gitlab)
When(gitlabGetter.GetMergeRequest(fixtures.GithubRepo.FullName, fixtures.Pull.Num)).ThenReturn(nil, errors.New("err"))
ch.ExecuteCommand(fixtures.GitlabRepo, fixtures.GitlabRepo, fixtures.User, fixtures.Pull.Num, nil)
Equals(t, "[ERROR] runatlantis/atlantis#1: Making merge request API call to GitLab: err\n", logBytes.String())
}

func TestExecuteCommand_GithubPullParseErr(t *testing.T) {
t.Log("if parsing the returned github pull request fails an error should be logged")
setup(t)
var pull github.PullRequest
When(githubGetter.GetPullRequest(fixtures.Repo, fixtures.Pull.Num)).ThenReturn(&pull, nil)
When(eventParsing.ParseGithubPull(&pull)).ThenReturn(fixtures.Pull, fixtures.Repo, errors.New("err"))
When(githubGetter.GetPullRequest(fixtures.GithubRepo, fixtures.Pull.Num)).ThenReturn(&pull, nil)
When(eventParsing.ParseGithubPull(&pull)).ThenReturn(fixtures.Pull, fixtures.GithubRepo, errors.New("err"))

ch.ExecuteCommand(fixtures.Repo, fixtures.Repo, fixtures.User, fixtures.Pull.Num, nil, vcs.Github)
ch.ExecuteCommand(fixtures.GithubRepo, fixtures.GithubRepo, fixtures.User, fixtures.Pull.Num, nil)
Equals(t, "[ERROR] runatlantis/atlantis#1: Extracting required fields from comment data: err\n", logBytes.String())
}

Expand All @@ -134,15 +134,15 @@ func TestExecuteCommand_ForkPRDisabled(t *testing.T) {
ch.AllowForkPRs = false // by default it's false so don't need to reset
var pull github.PullRequest
modelPull := models.PullRequest{State: models.Open}
When(githubGetter.GetPullRequest(fixtures.Repo, fixtures.Pull.Num)).ThenReturn(&pull, nil)
When(githubGetter.GetPullRequest(fixtures.GithubRepo, fixtures.Pull.Num)).ThenReturn(&pull, nil)

headRepo := fixtures.Repo
headRepo := fixtures.GithubRepo
headRepo.FullName = "forkrepo/atlantis"
headRepo.Owner = "forkrepo"
When(eventParsing.ParseGithubPull(&pull)).ThenReturn(modelPull, headRepo, nil)

ch.ExecuteCommand(fixtures.Repo, models.Repo{} /* this isn't used */, fixtures.User, fixtures.Pull.Num, nil, vcs.Github)
vcsClient.VerifyWasCalledOnce().CreateComment(fixtures.Repo, modelPull.Num, "Atlantis commands can't be run on fork pull requests. To enable, set --"+ch.AllowForkPRsFlag, vcs.Github)
ch.ExecuteCommand(fixtures.GithubRepo, models.Repo{} /* this isn't used */, fixtures.User, fixtures.Pull.Num, nil)
vcsClient.VerifyWasCalledOnce().CreateComment(fixtures.GithubRepo, modelPull.Num, "Atlantis commands can't be run on fork pull requests. To enable, set --"+ch.AllowForkPRsFlag)
}

func TestExecuteCommand_ClosedPull(t *testing.T) {
Expand All @@ -153,11 +153,11 @@ func TestExecuteCommand_ClosedPull(t *testing.T) {
State: github.String("closed"),
}
modelPull := models.PullRequest{State: models.Closed}
When(githubGetter.GetPullRequest(fixtures.Repo, fixtures.Pull.Num)).ThenReturn(pull, nil)
When(eventParsing.ParseGithubPull(pull)).ThenReturn(modelPull, fixtures.Repo, nil)
When(githubGetter.GetPullRequest(fixtures.GithubRepo, fixtures.Pull.Num)).ThenReturn(pull, nil)
When(eventParsing.ParseGithubPull(pull)).ThenReturn(modelPull, fixtures.GithubRepo, nil)

ch.ExecuteCommand(fixtures.Repo, fixtures.Repo, fixtures.User, fixtures.Pull.Num, nil, vcs.Github)
vcsClient.VerifyWasCalledOnce().CreateComment(fixtures.Repo, modelPull.Num, "Atlantis commands can't be run on closed pull requests", vcs.Github)
ch.ExecuteCommand(fixtures.GithubRepo, fixtures.GithubRepo, fixtures.User, fixtures.Pull.Num, nil)
vcsClient.VerifyWasCalledOnce().CreateComment(fixtures.GithubRepo, modelPull.Num, "Atlantis commands can't be run on closed pull requests")
}

func TestExecuteCommand_WorkspaceLocked(t *testing.T) {
Expand All @@ -171,19 +171,19 @@ func TestExecuteCommand_WorkspaceLocked(t *testing.T) {
Workspace: "workspace",
}

When(githubGetter.GetPullRequest(fixtures.Repo, fixtures.Pull.Num)).ThenReturn(pull, nil)
When(eventParsing.ParseGithubPull(pull)).ThenReturn(fixtures.Pull, fixtures.Repo, nil)
When(workspaceLocker.TryLock(fixtures.Repo.FullName, cmd.Workspace, fixtures.Pull.Num)).ThenReturn(false)
ch.ExecuteCommand(fixtures.Repo, fixtures.Repo, fixtures.User, fixtures.Pull.Num, &cmd, vcs.Github)
When(githubGetter.GetPullRequest(fixtures.GithubRepo, fixtures.Pull.Num)).ThenReturn(pull, nil)
When(eventParsing.ParseGithubPull(pull)).ThenReturn(fixtures.Pull, fixtures.GithubRepo, nil)
When(workspaceLocker.TryLock(fixtures.GithubRepo.FullName, cmd.Workspace, fixtures.Pull.Num)).ThenReturn(false)
ch.ExecuteCommand(fixtures.GithubRepo, fixtures.GithubRepo, fixtures.User, fixtures.Pull.Num, &cmd)

msg := "The workspace workspace is currently locked by another" +
" command that is running for this pull request." +
" Wait until the previous command is complete and try again."
ghStatus.VerifyWasCalledOnce().Update(fixtures.Repo, fixtures.Pull, vcs.Pending, &cmd, vcs.Github)
ghStatus.VerifyWasCalledOnce().Update(fixtures.GithubRepo, fixtures.Pull, vcs.Pending, &cmd)
_, response := ghStatus.VerifyWasCalledOnce().UpdateProjectResult(matchers.AnyPtrToEventsCommandContext(), matchers.AnyEventsCommandResponse()).GetCapturedArguments()
Equals(t, msg, response.Failure)
vcsClient.VerifyWasCalledOnce().CreateComment(fixtures.Repo, fixtures.Pull.Num,
"**Plan Failed**: "+msg+"\n\n", vcs.Github)
vcsClient.VerifyWasCalledOnce().CreateComment(fixtures.GithubRepo, fixtures.Pull.Num,
"**Plan Failed**: "+msg+"\n\n")
}

func TestExecuteCommand_FullRun(t *testing.T) {
Expand All @@ -198,23 +198,23 @@ func TestExecuteCommand_FullRun(t *testing.T) {
Name: c,
Workspace: "workspace",
}
When(githubGetter.GetPullRequest(fixtures.Repo, fixtures.Pull.Num)).ThenReturn(pull, nil)
When(eventParsing.ParseGithubPull(pull)).ThenReturn(fixtures.Pull, fixtures.Repo, nil)
When(workspaceLocker.TryLock(fixtures.Repo.FullName, cmd.Workspace, fixtures.Pull.Num)).ThenReturn(true)
When(githubGetter.GetPullRequest(fixtures.GithubRepo, fixtures.Pull.Num)).ThenReturn(pull, nil)
When(eventParsing.ParseGithubPull(pull)).ThenReturn(fixtures.Pull, fixtures.GithubRepo, nil)
When(workspaceLocker.TryLock(fixtures.GithubRepo.FullName, cmd.Workspace, fixtures.Pull.Num)).ThenReturn(true)
switch c {
case events.Plan:
When(planner.Execute(matchers.AnyPtrToEventsCommandContext())).ThenReturn(cmdResponse)
case events.Apply:
When(applier.Execute(matchers.AnyPtrToEventsCommandContext())).ThenReturn(cmdResponse)
}

ch.ExecuteCommand(fixtures.Repo, fixtures.Repo, fixtures.User, fixtures.Pull.Num, &cmd, vcs.Github)
ch.ExecuteCommand(fixtures.GithubRepo, fixtures.GithubRepo, fixtures.User, fixtures.Pull.Num, &cmd)

ghStatus.VerifyWasCalledOnce().Update(fixtures.Repo, fixtures.Pull, vcs.Pending, &cmd, vcs.Github)
ghStatus.VerifyWasCalledOnce().Update(fixtures.GithubRepo, fixtures.Pull, vcs.Pending, &cmd)
_, response := ghStatus.VerifyWasCalledOnce().UpdateProjectResult(matchers.AnyPtrToEventsCommandContext(), matchers.AnyEventsCommandResponse()).GetCapturedArguments()
Equals(t, cmdResponse, response)
vcsClient.VerifyWasCalledOnce().CreateComment(matchers.AnyModelsRepo(), AnyInt(), AnyString(), matchers.AnyVcsHost())
workspaceLocker.VerifyWasCalledOnce().Unlock(fixtures.Repo.FullName, cmd.Workspace, fixtures.Pull.Num)
vcsClient.VerifyWasCalledOnce().CreateComment(matchers.AnyModelsRepo(), AnyInt(), AnyString())
workspaceLocker.VerifyWasCalledOnce().Unlock(fixtures.GithubRepo.FullName, cmd.Workspace, fixtures.Pull.Num)
}
}

Expand All @@ -232,19 +232,19 @@ func TestExecuteCommand_ForkPREnabled(t *testing.T) {
Name: events.Plan,
Workspace: "workspace",
}
When(githubGetter.GetPullRequest(fixtures.Repo, fixtures.Pull.Num)).ThenReturn(&pull, nil)
headRepo := fixtures.Repo
When(githubGetter.GetPullRequest(fixtures.GithubRepo, fixtures.Pull.Num)).ThenReturn(&pull, nil)
headRepo := fixtures.GithubRepo
headRepo.FullName = "forkrepo/atlantis"
headRepo.Owner = "forkrepo"
When(eventParsing.ParseGithubPull(&pull)).ThenReturn(fixtures.Pull, headRepo, nil)
When(workspaceLocker.TryLock(fixtures.Repo.FullName, cmd.Workspace, fixtures.Pull.Num)).ThenReturn(true)
When(workspaceLocker.TryLock(fixtures.GithubRepo.FullName, cmd.Workspace, fixtures.Pull.Num)).ThenReturn(true)
When(planner.Execute(matchers.AnyPtrToEventsCommandContext())).ThenReturn(cmdResponse)

ch.ExecuteCommand(fixtures.Repo, models.Repo{} /* this isn't used */, fixtures.User, fixtures.Pull.Num, &cmd, vcs.Github)
ch.ExecuteCommand(fixtures.GithubRepo, models.Repo{} /* this isn't used */, fixtures.User, fixtures.Pull.Num, &cmd)

ghStatus.VerifyWasCalledOnce().Update(fixtures.Repo, fixtures.Pull, vcs.Pending, &cmd, vcs.Github)
ghStatus.VerifyWasCalledOnce().Update(fixtures.GithubRepo, fixtures.Pull, vcs.Pending, &cmd)
_, response := ghStatus.VerifyWasCalledOnce().UpdateProjectResult(matchers.AnyPtrToEventsCommandContext(), matchers.AnyEventsCommandResponse()).GetCapturedArguments()
Equals(t, cmdResponse, response)
vcsClient.VerifyWasCalledOnce().CreateComment(matchers.AnyModelsRepo(), AnyInt(), AnyString(), matchers.AnyVcsHost())
workspaceLocker.VerifyWasCalledOnce().Unlock(fixtures.Repo.FullName, cmd.Workspace, fixtures.Pull.Num)
vcsClient.VerifyWasCalledOnce().CreateComment(matchers.AnyModelsRepo(), AnyInt(), AnyString())
workspaceLocker.VerifyWasCalledOnce().Unlock(fixtures.GithubRepo.FullName, cmd.Workspace, fixtures.Pull.Num)
}
8 changes: 4 additions & 4 deletions server/events/comment_parser.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ import (
"path/filepath"
"strings"

"github.com/runatlantis/atlantis/server/events/vcs"
"github.com/runatlantis/atlantis/server/events/models"
"github.com/spf13/pflag"
)

Expand All @@ -39,7 +39,7 @@ const (
type CommentParsing interface {
// Parse attempts to parse a pull request comment to see if it's an Atlantis
// commmand.
Parse(comment string, vcsHost vcs.Host) CommentParseResult
Parse(comment string, vcsHost models.VCSHostType) CommentParseResult
}

// CommentParser implements CommentParsing
Expand Down Expand Up @@ -79,7 +79,7 @@ type CommentParseResult struct {
// - atlantis plan --verbose -- -key=value -key2 value2
//
// nolint: gocyclo
func (e *CommentParser) Parse(comment string, vcsHost vcs.Host) CommentParseResult {
func (e *CommentParser) Parse(comment string, vcsHost models.VCSHostType) CommentParseResult {
if multiLineRegex.MatchString(comment) {
return CommentParseResult{Ignore: true}
}
Expand All @@ -99,7 +99,7 @@ func (e *CommentParser) Parse(comment string, vcsHost vcs.Host) CommentParseResu
// Atlantis can be invoked using the name of the VCS host user we're
// running under. Need to be able to match against that user.
vcsUser := e.GithubUser
if vcsHost == vcs.Gitlab {
if vcsHost == models.Gitlab {
vcsUser = e.GitlabUser
}
executableNames := []string{"run", "atlantis", "@" + vcsUser}
Expand Down
Loading

0 comments on commit 5d1722b

Please sign in to comment.