mirror of
https://git.vectorsigma.ru/public/atlantis.git
synced 2026-07-28 23:08:26 +00:00
WIP
This commit is contained in:
@@ -35,6 +35,7 @@ type AtlantisWorkspace interface {
|
||||
// absolute path to the root of the cloned repo.
|
||||
Clone(log *logging.SimpleLogger, baseRepo models.Repo, headRepo models.Repo, p models.PullRequest, workspace string) (string, error)
|
||||
// GetWorkspace returns the path to the workspace for this repo and pull.
|
||||
// If workspace does not exist on disk, error will be of type os.IsNotExist.
|
||||
GetWorkspace(r models.Repo, p models.PullRequest, workspace string) (string, error)
|
||||
// Delete deletes the workspace for this repo and pull.
|
||||
Delete(r models.Repo, p models.PullRequest) error
|
||||
|
||||
@@ -30,6 +30,8 @@ import (
|
||||
type AtlantisWorkspaceLocker interface {
|
||||
// TryLock tries to acquire a lock for this repo, workspace and pull.
|
||||
TryLock(repoFullName string, workspace string, pullNum int) bool
|
||||
// TryLock2 tries to acquire a lock for this repo, workspace and pull.
|
||||
TryLock2(repoFullName string, workspace string, pullNum int) (func(), error)
|
||||
// Unlock deletes the lock for this repo, workspace and pull. If there was no
|
||||
// lock it will do nothing.
|
||||
Unlock(repoFullName, workspace string, pullNum int)
|
||||
@@ -48,6 +50,17 @@ func NewDefaultAtlantisWorkspaceLocker() *DefaultAtlantisWorkspaceLocker {
|
||||
}
|
||||
}
|
||||
|
||||
func (d *DefaultAtlantisWorkspaceLocker) TryLock2(repoFullName string, workspace string, pullNum int) (func(), error) {
|
||||
if !d.TryLock(repoFullName, workspace, pullNum) {
|
||||
return func() {}, fmt.Errorf("the %s workspace is currently locked by another"+
|
||||
" command that is running for this pull request–"+
|
||||
"wait until the previous command is complete and try again", workspace)
|
||||
}
|
||||
return func() {
|
||||
d.Unlock(repoFullName, workspace, pullNum)
|
||||
}, nil
|
||||
}
|
||||
|
||||
// TryLock returns true if a lock is acquired for this repo, pull and workspace and
|
||||
// false otherwise.
|
||||
func (d *DefaultAtlantisWorkspaceLocker) TryLock(repoFullName string, workspace string, pullNum int) bool {
|
||||
|
||||
@@ -30,7 +30,6 @@ type CommandContext struct {
|
||||
HeadRepo models.Repo
|
||||
Pull models.PullRequest
|
||||
// User is the user that triggered this command.
|
||||
User models.User
|
||||
Command *Command
|
||||
Log *logging.SimpleLogger
|
||||
User models.User
|
||||
Log *logging.SimpleLogger
|
||||
}
|
||||
|
||||
@@ -1,247 +0,0 @@
|
||||
// Copyright 2017 HootSuite Media Inc.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the License);
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an AS IS BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
// Modified hereafter by contributors to runatlantis/atlantis.
|
||||
//
|
||||
package events_test
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"log"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/google/go-github/github"
|
||||
. "github.com/petergtz/pegomock"
|
||||
"github.com/runatlantis/atlantis/server/events"
|
||||
"github.com/runatlantis/atlantis/server/events/mocks"
|
||||
"github.com/runatlantis/atlantis/server/events/mocks/matchers"
|
||||
"github.com/runatlantis/atlantis/server/events/models"
|
||||
"github.com/runatlantis/atlantis/server/events/models/fixtures"
|
||||
"github.com/runatlantis/atlantis/server/events/vcs"
|
||||
vcsmocks "github.com/runatlantis/atlantis/server/events/vcs/mocks"
|
||||
logmocks "github.com/runatlantis/atlantis/server/logging/mocks"
|
||||
. "github.com/runatlantis/atlantis/testing"
|
||||
)
|
||||
|
||||
var operator *mocks.MockPullRequestOperator
|
||||
var eventParsing *mocks.MockEventParsing
|
||||
var vcsClient *vcsmocks.MockClientProxy
|
||||
var ghStatus *mocks.MockCommitStatusUpdater
|
||||
var githubGetter *mocks.MockGithubPullGetter
|
||||
var gitlabGetter *mocks.MockGitlabMergeRequestGetter
|
||||
var workspaceLocker *mocks.MockAtlantisWorkspaceLocker
|
||||
var ch events.CommandHandler
|
||||
var logBytes *bytes.Buffer
|
||||
|
||||
func setup(t *testing.T) {
|
||||
RegisterMockTestingT(t)
|
||||
operator = mocks.NewMockPullRequestOperator()
|
||||
eventParsing = mocks.NewMockEventParsing()
|
||||
ghStatus = mocks.NewMockCommitStatusUpdater()
|
||||
workspaceLocker = mocks.NewMockAtlantisWorkspaceLocker()
|
||||
vcsClient = vcsmocks.NewMockClientProxy()
|
||||
githubGetter = mocks.NewMockGithubPullGetter()
|
||||
gitlabGetter = mocks.NewMockGitlabMergeRequestGetter()
|
||||
logger := logmocks.NewMockSimpleLogging()
|
||||
logBytes = new(bytes.Buffer)
|
||||
When(logger.Underlying()).ThenReturn(log.New(logBytes, "", 0))
|
||||
ch = events.CommandHandler{
|
||||
VCSClient: vcsClient,
|
||||
CommitStatusUpdater: ghStatus,
|
||||
EventParser: eventParsing,
|
||||
AtlantisWorkspaceLocker: workspaceLocker,
|
||||
MarkdownRenderer: &events.MarkdownRenderer{},
|
||||
GithubPullGetter: githubGetter,
|
||||
GitlabMergeRequestGetter: gitlabGetter,
|
||||
Logger: logger,
|
||||
AllowForkPRs: false,
|
||||
AllowForkPRsFlag: "allow-fork-prs-flag",
|
||||
PullRequestOperator: operator,
|
||||
}
|
||||
}
|
||||
|
||||
func TestExecuteCommand_LogPanics(t *testing.T) {
|
||||
t.Log("if there is a panic it is commented back on the pull request")
|
||||
setup(t)
|
||||
ch.AllowForkPRs = true // Lets us get to the panic code.
|
||||
defer func() { ch.AllowForkPRs = false }()
|
||||
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.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.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.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.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.GithubRepo, fixtures.Pull.Num)).ThenReturn(&pull, nil)
|
||||
When(eventParsing.ParseGithubPull(&pull)).ThenReturn(fixtures.Pull, fixtures.GithubRepo, errors.New("err"))
|
||||
|
||||
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())
|
||||
}
|
||||
|
||||
func TestExecuteCommand_ForkPRDisabled(t *testing.T) {
|
||||
t.Log("if a command is run on a forked pull request and this is disabled atlantis should" +
|
||||
" comment saying that this is not allowed")
|
||||
setup(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.GithubRepo, fixtures.Pull.Num)).ThenReturn(&pull, nil)
|
||||
|
||||
headRepo := fixtures.GithubRepo
|
||||
headRepo.FullName = "forkrepo/atlantis"
|
||||
headRepo.Owner = "forkrepo"
|
||||
When(eventParsing.ParseGithubPull(&pull)).ThenReturn(modelPull, headRepo, nil)
|
||||
|
||||
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) {
|
||||
t.Log("if a command is run on a closed pull request atlantis should" +
|
||||
" comment saying that this is not allowed")
|
||||
setup(t)
|
||||
pull := &github.PullRequest{
|
||||
State: github.String("closed"),
|
||||
}
|
||||
modelPull := models.PullRequest{State: models.Closed}
|
||||
When(githubGetter.GetPullRequest(fixtures.GithubRepo, fixtures.Pull.Num)).ThenReturn(pull, nil)
|
||||
When(eventParsing.ParseGithubPull(pull)).ThenReturn(modelPull, fixtures.GithubRepo, nil)
|
||||
|
||||
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) {
|
||||
t.Log("if the workspace is locked, should comment back on the pull")
|
||||
setup(t)
|
||||
pull := &github.PullRequest{
|
||||
State: github.String("closed"),
|
||||
}
|
||||
cmd := events.Command{
|
||||
Name: events.Plan,
|
||||
Workspace: "workspace",
|
||||
}
|
||||
|
||||
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.GithubRepo, fixtures.Pull, vcs.Pending, &cmd)
|
||||
_, response := ghStatus.VerifyWasCalledOnce().UpdateProjectResult(matchers.AnyPtrToEventsCommandContext(), matchers.AnyEventsCommandResponse()).GetCapturedArguments()
|
||||
Equals(t, msg, response.Failure)
|
||||
vcsClient.VerifyWasCalledOnce().CreateComment(fixtures.GithubRepo, fixtures.Pull.Num,
|
||||
"**Plan Failed**: "+msg+"\n\n")
|
||||
}
|
||||
|
||||
func TestExecuteCommand_FullRun(t *testing.T) {
|
||||
t.Log("when running a plan, apply should comment")
|
||||
pull := &github.PullRequest{
|
||||
State: github.String("closed"),
|
||||
}
|
||||
cmdResponse := events.CommandResponse{}
|
||||
for _, c := range []events.CommandName{events.Plan, events.Apply} {
|
||||
setup(t)
|
||||
cmd := events.Command{
|
||||
Name: c,
|
||||
Workspace: "workspace",
|
||||
}
|
||||
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(operator.PlanViaComment(matchers.AnyPtrToEventsCommandContext())).ThenReturn(cmdResponse)
|
||||
case events.Apply:
|
||||
When(operator.ApplyViaComment(matchers.AnyPtrToEventsCommandContext())).ThenReturn(cmdResponse)
|
||||
}
|
||||
|
||||
ch.ExecuteCommand(fixtures.GithubRepo, fixtures.GithubRepo, fixtures.User, fixtures.Pull.Num, &cmd)
|
||||
|
||||
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())
|
||||
workspaceLocker.VerifyWasCalledOnce().Unlock(fixtures.GithubRepo.FullName, cmd.Workspace, fixtures.Pull.Num)
|
||||
}
|
||||
}
|
||||
|
||||
func TestExecuteCommand_ForkPREnabled(t *testing.T) {
|
||||
t.Log("when running a plan on a fork PR, it should succeed")
|
||||
setup(t)
|
||||
|
||||
// Enable forked PRs.
|
||||
ch.AllowForkPRs = true
|
||||
defer func() { ch.AllowForkPRs = false }() // Reset after test.
|
||||
|
||||
var pull github.PullRequest
|
||||
cmdResponse := events.CommandResponse{}
|
||||
cmd := events.Command{
|
||||
Name: events.Plan,
|
||||
Workspace: "workspace",
|
||||
}
|
||||
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.GithubRepo.FullName, cmd.Workspace, fixtures.Pull.Num)).ThenReturn(true)
|
||||
When(operator.PlanViaComment(matchers.AnyPtrToEventsCommandContext())).ThenReturn(cmdResponse)
|
||||
|
||||
ch.ExecuteCommand(fixtures.GithubRepo, models.Repo{} /* this isn't used */, fixtures.User, fixtures.Pull.Num, &cmd)
|
||||
|
||||
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())
|
||||
workspaceLocker.VerifyWasCalledOnce().Unlock(fixtures.GithubRepo.FullName, cmd.Workspace, fixtures.Pull.Num)
|
||||
}
|
||||
@@ -13,8 +13,8 @@
|
||||
//
|
||||
package events
|
||||
|
||||
// CommandResponse is the result of running a Command.
|
||||
type CommandResponse struct {
|
||||
// CommandResult is the result of running a Command.
|
||||
type CommandResult struct {
|
||||
Error error
|
||||
Failure string
|
||||
ProjectResults []ProjectResult
|
||||
@@ -29,10 +29,11 @@ import (
|
||||
|
||||
// CommandRunner is the first step after a command request has been parsed.
|
||||
type CommandRunner interface {
|
||||
// ExecuteCommand is the first step after a command request has been parsed.
|
||||
// RunCommentCommand 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)
|
||||
RunCommentCommand(baseRepo models.Repo, maybeHeadRepo *models.Repo, user models.User, pullNum int, cmd *CommentCommand)
|
||||
RunAutoplanCommand(baseRepo models.Repo, headRepo models.Repo, pull models.PullRequest, user models.User)
|
||||
}
|
||||
|
||||
//go:generate pegomock generate -m --use-experimental-model-gen --package mocks -o mocks/mock_github_pull_getter.go GithubPullGetter
|
||||
@@ -51,14 +52,13 @@ type GitlabMergeRequestGetter interface {
|
||||
GetMergeRequest(repoFullName string, pullNum int) (*gitlab.MergeRequest, error)
|
||||
}
|
||||
|
||||
// CommandHandler is the first step when processing a comment command.
|
||||
type CommandHandler struct {
|
||||
// DefaultCommandRunner is the first step when processing a comment command.
|
||||
type DefaultCommandRunner struct {
|
||||
VCSClient vcs.ClientProxy
|
||||
GithubPullGetter GithubPullGetter
|
||||
GitlabMergeRequestGetter GitlabMergeRequestGetter
|
||||
CommitStatusUpdater CommitStatusUpdater
|
||||
EventParser EventParsing
|
||||
AtlantisWorkspaceLocker AtlantisWorkspaceLocker
|
||||
MarkdownRenderer *MarkdownRenderer
|
||||
Logger logging.SimpleLogging
|
||||
// AllowForkPRs controls whether we operate on pull requests from forks.
|
||||
@@ -66,17 +66,50 @@ type CommandHandler struct {
|
||||
// AllowForkPRsFlag is the name of the flag that controls fork PR's. We use
|
||||
// this in our error message back to the user on a forked PR so they know
|
||||
// how to enable this functionality.
|
||||
AllowForkPRsFlag string
|
||||
PullRequestOperator PullRequestOperator
|
||||
AllowForkPRsFlag string
|
||||
ProjectCommandBuilder ProjectCommandBuilder
|
||||
ProjectCommandRunner *ProjectCommandRunner
|
||||
}
|
||||
|
||||
// ExecuteCommand executes the command.
|
||||
// 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) {
|
||||
func (c *DefaultCommandRunner) RunAutoplanCommand(baseRepo models.Repo, headRepo models.Repo, pull models.PullRequest, user models.User) {
|
||||
log := c.buildLogger(baseRepo.FullName, pull.Num)
|
||||
ctx := &CommandContext{
|
||||
User: user,
|
||||
Log: log,
|
||||
Pull: pull,
|
||||
HeadRepo: headRepo,
|
||||
BaseRepo: baseRepo,
|
||||
}
|
||||
runFn := func() ([]ProjectResult, error) {
|
||||
projectCmds, err := c.ProjectCommandBuilder.BuildAutoplanCommands(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var results []ProjectResult
|
||||
for _, cmd := range projectCmds {
|
||||
res := c.ProjectCommandRunner.Plan(cmd)
|
||||
results = append(results, ProjectResult{
|
||||
ProjectCommandResult: res,
|
||||
Path: cmd.RepoRelPath,
|
||||
Workspace: cmd.Workspace,
|
||||
})
|
||||
}
|
||||
return results, nil
|
||||
}
|
||||
c.run(ctx, AutoplanCommand{}, runFn)
|
||||
}
|
||||
|
||||
// RunCommentCommand executes the command.
|
||||
// We take in a pointer for maybeHeadRepo because for some events there isn't
|
||||
// enough data to construct the Repo model and callers might want to wait until
|
||||
// the event is further validated before making an additional (potentially
|
||||
// wasteful) call to get the necessary data.
|
||||
func (c *DefaultCommandRunner) RunCommentCommand(baseRepo models.Repo, maybeHeadRepo *models.Repo, user models.User, pullNum int, cmd *CommentCommand) {
|
||||
log := c.buildLogger(baseRepo.FullName, pullNum)
|
||||
var headRepo models.Repo
|
||||
if maybeHeadRepo != nil {
|
||||
headRepo = *maybeHeadRepo
|
||||
}
|
||||
|
||||
var err error
|
||||
var pull models.PullRequest
|
||||
@@ -97,13 +130,38 @@ func (c *CommandHandler) ExecuteCommand(baseRepo models.Repo, headRepo models.Re
|
||||
Log: log,
|
||||
Pull: pull,
|
||||
HeadRepo: headRepo,
|
||||
Command: cmd,
|
||||
BaseRepo: baseRepo,
|
||||
}
|
||||
c.run(ctx)
|
||||
|
||||
runFn := func() ([]ProjectResult, error) {
|
||||
var result ProjectCommandResult
|
||||
switch cmd.Name {
|
||||
case Plan:
|
||||
projectCmd, err := c.ProjectCommandBuilder.BuildPlanCommand(ctx, cmd)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
result = c.ProjectCommandRunner.Plan(projectCmd)
|
||||
case Apply:
|
||||
projectCmd, err := c.ProjectCommandBuilder.BuildApplyCommand(ctx, cmd)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
result = c.ProjectCommandRunner.Apply(projectCmd)
|
||||
default:
|
||||
ctx.Log.Err("failed to determine desired command, neither plan nor apply")
|
||||
}
|
||||
return []ProjectResult{{
|
||||
Path: cmd.Dir,
|
||||
Workspace: cmd.Workspace,
|
||||
ProjectCommandResult: result,
|
||||
}}, nil
|
||||
}
|
||||
|
||||
c.run(ctx, cmd, runFn)
|
||||
}
|
||||
|
||||
func (c *CommandHandler) getGithubData(baseRepo models.Repo, pullNum int) (models.PullRequest, models.Repo, error) {
|
||||
func (c *DefaultCommandRunner) getGithubData(baseRepo models.Repo, pullNum int) (models.PullRequest, models.Repo, error) {
|
||||
if c.GithubPullGetter == nil {
|
||||
return models.PullRequest{}, models.Repo{}, errors.New("Atlantis not configured to support GitHub")
|
||||
}
|
||||
@@ -111,14 +169,14 @@ func (c *CommandHandler) getGithubData(baseRepo models.Repo, pullNum int) (model
|
||||
if err != nil {
|
||||
return models.PullRequest{}, models.Repo{}, errors.Wrap(err, "making pull request API call to GitHub")
|
||||
}
|
||||
pull, repo, err := c.EventParser.ParseGithubPull(ghPull)
|
||||
pull, _, headRepo, err := c.EventParser.ParseGithubPull(ghPull)
|
||||
if err != nil {
|
||||
return pull, repo, errors.Wrap(err, "extracting required fields from comment data")
|
||||
return pull, headRepo, errors.Wrap(err, "extracting required fields from comment data")
|
||||
}
|
||||
return pull, repo, nil
|
||||
return pull, headRepo, nil
|
||||
}
|
||||
|
||||
func (c *CommandHandler) getGitlabData(baseRepo models.Repo, pullNum int) (models.PullRequest, error) {
|
||||
func (c *DefaultCommandRunner) getGitlabData(baseRepo models.Repo, pullNum int) (models.PullRequest, error) {
|
||||
if c.GitlabMergeRequestGetter == nil {
|
||||
return models.PullRequest{}, errors.New("Atlantis not configured to support GitLab")
|
||||
}
|
||||
@@ -130,60 +188,47 @@ func (c *CommandHandler) getGitlabData(baseRepo models.Repo, pullNum int) (model
|
||||
return pull, nil
|
||||
}
|
||||
|
||||
func (c *CommandHandler) buildLogger(repoFullName string, pullNum int) *logging.SimpleLogger {
|
||||
func (c *DefaultCommandRunner) buildLogger(repoFullName string, pullNum int) *logging.SimpleLogger {
|
||||
src := fmt.Sprintf("%s#%d", repoFullName, pullNum)
|
||||
return logging.NewSimpleLogger(src, c.Logger.Underlying(), true, c.Logger.GetLevel())
|
||||
}
|
||||
|
||||
func (c *CommandHandler) run(ctx *CommandContext) {
|
||||
defer c.logPanics(ctx)
|
||||
|
||||
func (c *DefaultCommandRunner) validateCtxAndComment(ctx *CommandContext) bool {
|
||||
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)) // nolint: errcheck
|
||||
return
|
||||
return false
|
||||
}
|
||||
|
||||
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") // nolint: errcheck
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func (c *DefaultCommandRunner) run(ctx *CommandContext, command CommandInterface, commandRunner func() ([]ProjectResult, error)) {
|
||||
defer c.logPanics(ctx)
|
||||
|
||||
if !c.validateCtxAndComment(ctx) {
|
||||
return
|
||||
}
|
||||
|
||||
ctx.Log.Debug("updating commit status to pending")
|
||||
if err := c.CommitStatusUpdater.Update(ctx.BaseRepo, ctx.Pull, vcs.Pending, ctx.Command); err != nil {
|
||||
if err := c.CommitStatusUpdater.Update(ctx.BaseRepo, ctx.Pull, vcs.Pending, command.CommandName()); 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) {
|
||||
errMsg := fmt.Sprintf(
|
||||
"The %s workspace is currently locked by another"+
|
||||
" command that is running for this pull request."+
|
||||
" Wait until the previous command is complete and try again.",
|
||||
ctx.Command.Workspace)
|
||||
ctx.Log.Warn(errMsg)
|
||||
c.updatePull(ctx, CommandResponse{Failure: errMsg})
|
||||
|
||||
results, err := commandRunner()
|
||||
if err != nil {
|
||||
c.updatePull(ctx, command, CommandResult{Error: err})
|
||||
return
|
||||
}
|
||||
ctx.Log.Debug("successfully acquired workspace lock")
|
||||
defer c.AtlantisWorkspaceLocker.Unlock(ctx.BaseRepo.FullName, ctx.Command.Workspace, ctx.Pull.Num)
|
||||
|
||||
var cr CommandResponse
|
||||
switch ctx.Command.Name {
|
||||
case Plan:
|
||||
if ctx.Command.Autoplan {
|
||||
cr = c.PullRequestOperator.Autoplan(ctx)
|
||||
} else {
|
||||
cr = c.PullRequestOperator.PlanViaComment(ctx)
|
||||
}
|
||||
case Apply:
|
||||
cr = c.PullRequestOperator.ApplyViaComment(ctx)
|
||||
default:
|
||||
ctx.Log.Err("failed to determine desired command, neither plan nor apply")
|
||||
}
|
||||
c.updatePull(ctx, cr)
|
||||
c.updatePull(ctx, command, CommandResult{ProjectResults: results})
|
||||
}
|
||||
|
||||
func (c *CommandHandler) updatePull(ctx *CommandContext, res CommandResponse) {
|
||||
func (c *DefaultCommandRunner) updatePull(ctx *CommandContext, command CommandInterface, res CommandResult) {
|
||||
// Log if we got any errors or failures.
|
||||
if res.Error != nil {
|
||||
ctx.Log.Err(res.Error.Error())
|
||||
@@ -192,15 +237,15 @@ func (c *CommandHandler) updatePull(ctx *CommandContext, res CommandResponse) {
|
||||
}
|
||||
|
||||
// Update the pull request's status icon and comment back.
|
||||
if err := c.CommitStatusUpdater.UpdateProjectResult(ctx, res); err != nil {
|
||||
if err := c.CommitStatusUpdater.UpdateProjectResult(ctx, command.CommandName(), res); err != nil {
|
||||
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, ctx.Command.Autoplan)
|
||||
comment := c.MarkdownRenderer.Render(res, command.CommandName(), ctx.Log.History.String(), command.IsVerbose(), command.IsAutoplan())
|
||||
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) {
|
||||
func (c *DefaultCommandRunner) logPanics(ctx *CommandContext) {
|
||||
if err := recover(); err != nil {
|
||||
stack := recovery.Stack(3)
|
||||
c.VCSClient.CreateComment(ctx.BaseRepo, ctx.Pull.Num, // nolint: errcheck
|
||||
239
server/events/command_runner_test.go
Normal file
239
server/events/command_runner_test.go
Normal file
@@ -0,0 +1,239 @@
|
||||
// Copyright 2017 HootSuite Media Inc.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the License);
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an AS IS BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
// Modified hereafter by contributors to runatlantis/atlantis.
|
||||
//
|
||||
package events_test
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"log"
|
||||
"testing"
|
||||
|
||||
. "github.com/petergtz/pegomock"
|
||||
"github.com/runatlantis/atlantis/server/events"
|
||||
"github.com/runatlantis/atlantis/server/events/mocks"
|
||||
vcsmocks "github.com/runatlantis/atlantis/server/events/vcs/mocks"
|
||||
logmocks "github.com/runatlantis/atlantis/server/logging/mocks"
|
||||
//. "github.com/runatlantis/atlantis/testing"
|
||||
)
|
||||
|
||||
var projectCommandBuilder *mocks.MockProjectCommandBuilder
|
||||
var eventParsing *mocks.MockEventParsing
|
||||
var vcsClient *vcsmocks.MockClientProxy
|
||||
var ghStatus *mocks.MockCommitStatusUpdater
|
||||
var githubGetter *mocks.MockGithubPullGetter
|
||||
var gitlabGetter *mocks.MockGitlabMergeRequestGetter
|
||||
var workspaceLocker *mocks.MockAtlantisWorkspaceLocker
|
||||
var ch events.DefaultCommandRunner
|
||||
var logBytes *bytes.Buffer
|
||||
|
||||
func setup(t *testing.T) {
|
||||
RegisterMockTestingT(t)
|
||||
projectCommandBuilder = mocks.NewMockProjectCommandBuilder()
|
||||
eventParsing = mocks.NewMockEventParsing()
|
||||
ghStatus = mocks.NewMockCommitStatusUpdater()
|
||||
workspaceLocker = mocks.NewMockAtlantisWorkspaceLocker()
|
||||
vcsClient = vcsmocks.NewMockClientProxy()
|
||||
githubGetter = mocks.NewMockGithubPullGetter()
|
||||
gitlabGetter = mocks.NewMockGitlabMergeRequestGetter()
|
||||
logger := logmocks.NewMockSimpleLogging()
|
||||
logBytes = new(bytes.Buffer)
|
||||
When(logger.Underlying()).ThenReturn(log.New(logBytes, "", 0))
|
||||
ch = events.DefaultCommandRunner{
|
||||
VCSClient: vcsClient,
|
||||
CommitStatusUpdater: ghStatus,
|
||||
EventParser: eventParsing,
|
||||
MarkdownRenderer: &events.MarkdownRenderer{},
|
||||
GithubPullGetter: githubGetter,
|
||||
GitlabMergeRequestGetter: gitlabGetter,
|
||||
Logger: logger,
|
||||
AllowForkPRs: false,
|
||||
AllowForkPRsFlag: "allow-fork-prs-flag",
|
||||
ProjectCommandBuilder: projectCommandBuilder,
|
||||
}
|
||||
}
|
||||
|
||||
//func TestExecuteCommand_LogPanics(t *testing.T) {
|
||||
// t.Log("if there is a panic it is commented back on the pull request")
|
||||
// setup(t)
|
||||
// ch.AllowForkPRs = true // Lets us get to the panic code.
|
||||
// defer func() { ch.AllowForkPRs = false }()
|
||||
// When(ghStatus.Update(fixtures.GithubRepo, fixtures.Pull, vcs.Pending, events.Plan)).ThenPanic("panic")
|
||||
// ch.RunCommentCommand(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 DefaultCommandRunner was constructed with a nil GithubPullGetter an error should be logged")
|
||||
// setup(t)
|
||||
// ch.GithubPullGetter = nil
|
||||
// ch.RunCommentCommand(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 DefaultCommandRunner was constructed with a nil GitlabMergeRequestGetter an error should be logged")
|
||||
// setup(t)
|
||||
// ch.GitlabMergeRequestGetter = nil
|
||||
// ch.RunCommentCommand(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.GithubRepo, fixtures.Pull.Num)).ThenReturn(nil, errors.New("err"))
|
||||
// ch.RunCommentCommand(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.GithubRepo.FullName, fixtures.Pull.Num)).ThenReturn(nil, errors.New("err"))
|
||||
// ch.RunCommentCommand(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.GithubRepo, fixtures.Pull.Num)).ThenReturn(&pull, nil)
|
||||
// When(eventParsing.ParseGithubPull(&pull)).ThenReturn(fixtures.Pull, fixtures.GithubRepo, errors.New("err"))
|
||||
//
|
||||
// ch.RunCommentCommand(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())
|
||||
//}
|
||||
//
|
||||
//func TestExecuteCommand_ForkPRDisabled(t *testing.T) {
|
||||
// t.Log("if a command is run on a forked pull request and this is disabled atlantis should" +
|
||||
// " comment saying that this is not allowed")
|
||||
// setup(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.GithubRepo, fixtures.Pull.Num)).ThenReturn(&pull, nil)
|
||||
//
|
||||
// headRepo := fixtures.GithubRepo
|
||||
// headRepo.FullName = "forkrepo/atlantis"
|
||||
// headRepo.Owner = "forkrepo"
|
||||
// When(eventParsing.ParseGithubPull(&pull)).ThenReturn(modelPull, headRepo, nil)
|
||||
//
|
||||
// ch.RunCommentCommand(fixtures.GithubRepo, nil, 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) {
|
||||
// t.Log("if a command is run on a closed pull request atlantis should" +
|
||||
// " comment saying that this is not allowed")
|
||||
// setup(t)
|
||||
// pull := &github.PullRequest{
|
||||
// State: github.String("closed"),
|
||||
// }
|
||||
// modelPull := models.PullRequest{State: models.Closed}
|
||||
// When(githubGetter.GetPullRequest(fixtures.GithubRepo, fixtures.Pull.Num)).ThenReturn(pull, nil)
|
||||
// When(eventParsing.ParseGithubPull(pull)).ThenReturn(modelPull, fixtures.GithubRepo, nil)
|
||||
//
|
||||
// ch.RunCommentCommand(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) {
|
||||
// t.Log("if the workspace is locked, should comment back on the pull")
|
||||
// setup(t)
|
||||
// pull := &github.PullRequest{
|
||||
// State: github.String("closed"),
|
||||
// }
|
||||
// cmd := events.CommentCommand{
|
||||
// Name: events.Plan,
|
||||
// Workspace: "workspace",
|
||||
// }
|
||||
//
|
||||
// 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.RunCommentCommand(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.GithubRepo, fixtures.Pull, vcs.Pending, cmd.CommandName())
|
||||
// _, _, result := ghStatus.VerifyWasCalledOnce().UpdateProjectResult(matchers.AnyPtrToEventsCommandContext(), matchers.AnyEventsCommandName(), matchers.AnyEventsCommandResult()).GetCapturedArguments()
|
||||
// Equals(t, msg, result.Failure)
|
||||
// vcsClient.VerifyWasCalledOnce().CreateComment(fixtures.GithubRepo, fixtures.Pull.Num,
|
||||
// "**Plan Failed**: "+msg+"\n\n")
|
||||
//}
|
||||
//
|
||||
//func TestExecuteCommand_FullRun(t *testing.T) {
|
||||
// t.Log("when running a plan, apply should comment")
|
||||
// pull := &github.PullRequest{
|
||||
// State: github.String("closed"),
|
||||
// }
|
||||
// cmdResult := events.CommandResult{}
|
||||
// for _, c := range []events.CommandName{events.Plan, events.Apply} {
|
||||
// setup(t)
|
||||
// cmd := events.CommentCommand{
|
||||
// Name: c,
|
||||
// Workspace: "workspace",
|
||||
// }
|
||||
// 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(projectCommandBuilder.PlanViaComment(matchers.AnyPtrToEventsCommandContext())).ThenReturn(cmdResult)
|
||||
// case events.Apply:
|
||||
// When(projectCommandBuilder.ApplyViaComment(matchers.AnyPtrToEventsCommandContext())).ThenReturn(cmdResult)
|
||||
// }
|
||||
//
|
||||
// ch.RunCommentCommand(fixtures.GithubRepo, fixtures.GithubRepo, fixtures.User, fixtures.Pull.Num, &cmd)
|
||||
//
|
||||
// ghStatus.VerifyWasCalledOnce().Update(fixtures.GithubRepo, fixtures.Pull, vcs.Pending, &cmd)
|
||||
// _, response := ghStatus.VerifyWasCalledOnce().UpdateProjectResult(matchers.AnyPtrToEventsCommandContext(), matchers.AnyEventsCommandResult()).GetCapturedArguments()
|
||||
// Equals(t, cmdResult, response)
|
||||
// vcsClient.VerifyWasCalledOnce().CreateComment(matchers.AnyModelsRepo(), AnyInt(), AnyString())
|
||||
// workspaceLocker.VerifyWasCalledOnce().Unlock(fixtures.GithubRepo.FullName, cmd.Workspace, fixtures.Pull.Num)
|
||||
// }
|
||||
//}
|
||||
//
|
||||
//func TestExecuteCommand_ForkPREnabled(t *testing.T) {
|
||||
// t.Log("when running a plan on a fork PR, it should succeed")
|
||||
// setup(t)
|
||||
//
|
||||
// // Enable forked PRs.
|
||||
// ch.AllowForkPRs = true
|
||||
// defer func() { ch.AllowForkPRs = false }() // Reset after test.
|
||||
//
|
||||
// var pull github.PullRequest
|
||||
// cmdResponse := events.CommandResult{}
|
||||
// cmd := events.CommentCommand{
|
||||
// Name: events.Plan,
|
||||
// Workspace: "workspace",
|
||||
// }
|
||||
// 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.GithubRepo.FullName, cmd.Workspace, fixtures.Pull.Num)).ThenReturn(true)
|
||||
// When(projectCommandBuilder.PlanViaComment(matchers.AnyPtrToEventsCommandContext())).ThenReturn(cmdResponse)
|
||||
//
|
||||
// ch.RunCommentCommand(fixtures.GithubRepo, models.Repo{} /* this isn't used */, fixtures.User, fixtures.Pull.Num, &cmd)
|
||||
//
|
||||
// ghStatus.VerifyWasCalledOnce().Update(fixtures.GithubRepo, fixtures.Pull, vcs.Pending, &cmd)
|
||||
// _, response := ghStatus.VerifyWasCalledOnce().UpdateProjectResult(matchers.AnyPtrToEventsCommandContext(), matchers.AnyEventsCommandResult()).GetCapturedArguments()
|
||||
// Equals(t, cmdResponse, response)
|
||||
// vcsClient.VerifyWasCalledOnce().CreateComment(matchers.AnyModelsRepo(), AnyInt(), AnyString())
|
||||
// workspaceLocker.VerifyWasCalledOnce().Unlock(fixtures.GithubRepo.FullName, cmd.Workspace, fixtures.Pull.Num)
|
||||
//}
|
||||
@@ -59,7 +59,7 @@ type CommentParser struct {
|
||||
type CommentParseResult struct {
|
||||
// Command is the successfully parsed command. Will be nil if
|
||||
// CommentResponse or Ignore is set.
|
||||
Command *Command
|
||||
Command *CommentCommand
|
||||
// CommentResponse is set when we should respond immediately to the command
|
||||
// for example for atlantis help.
|
||||
CommentResponse string
|
||||
@@ -215,7 +215,7 @@ func (e *CommentParser) Parse(comment string, vcsHost models.VCSHostType) Commen
|
||||
}
|
||||
|
||||
return CommentParseResult{
|
||||
Command: NewCommand(dir, extraArgs, name, verbose, workspace, project, false),
|
||||
Command: NewCommand(dir, extraArgs, name, verbose, workspace, project),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -27,10 +27,10 @@ import (
|
||||
// the status to signify whether the plan/apply succeeds.
|
||||
type CommitStatusUpdater interface {
|
||||
// Update updates the status of the head commit of pull.
|
||||
Update(repo models.Repo, pull models.PullRequest, status vcs.CommitStatus, cmd *Command) error
|
||||
Update(repo models.Repo, pull models.PullRequest, status vcs.CommitStatus, command CommandName) error
|
||||
// UpdateProjectResult updates the status of the head commit given the
|
||||
// state of response.
|
||||
UpdateProjectResult(ctx *CommandContext, res CommandResponse) error
|
||||
UpdateProjectResult(ctx *CommandContext, commandName CommandName, res CommandResult) error
|
||||
}
|
||||
|
||||
// DefaultCommitStatusUpdater implements CommitStatusUpdater.
|
||||
@@ -39,13 +39,13 @@ type DefaultCommitStatusUpdater struct {
|
||||
}
|
||||
|
||||
// Update updates the commit status.
|
||||
func (d *DefaultCommitStatusUpdater) Update(repo models.Repo, pull models.PullRequest, status vcs.CommitStatus, cmd *Command) error {
|
||||
description := fmt.Sprintf("%s %s", strings.Title(cmd.Name.String()), strings.Title(status.String()))
|
||||
func (d *DefaultCommitStatusUpdater) Update(repo models.Repo, pull models.PullRequest, status vcs.CommitStatus, command CommandName) error {
|
||||
description := fmt.Sprintf("%s %s", strings.Title(command.String()), strings.Title(status.String()))
|
||||
return d.Client.UpdateStatus(repo, pull, status, description)
|
||||
}
|
||||
|
||||
// UpdateProjectResult updates the commit status based on the status of res.
|
||||
func (d *DefaultCommitStatusUpdater) UpdateProjectResult(ctx *CommandContext, res CommandResponse) error {
|
||||
func (d *DefaultCommitStatusUpdater) UpdateProjectResult(ctx *CommandContext, commandName CommandName, res CommandResult) error {
|
||||
var status vcs.CommitStatus
|
||||
if res.Error != nil || res.Failure != "" {
|
||||
status = vcs.Failed
|
||||
@@ -56,7 +56,7 @@ func (d *DefaultCommitStatusUpdater) UpdateProjectResult(ctx *CommandContext, re
|
||||
}
|
||||
status = d.worstStatus(statuses)
|
||||
}
|
||||
return d.Update(ctx.BaseRepo, ctx.Pull, status, ctx.Command)
|
||||
return d.Update(ctx.BaseRepo, ctx.Pull, status, commandName)
|
||||
}
|
||||
|
||||
func (d *DefaultCommitStatusUpdater) worstStatus(ss []vcs.CommitStatus) vcs.CommitStatus {
|
||||
|
||||
@@ -29,26 +29,12 @@ import (
|
||||
var repoModel = models.Repo{}
|
||||
var pullModel = models.PullRequest{}
|
||||
var status = vcs.Success
|
||||
var cmd = events.Command{
|
||||
Name: events.Plan,
|
||||
}
|
||||
|
||||
func TestStatus_String(t *testing.T) {
|
||||
cases := map[vcs.CommitStatus]string{
|
||||
vcs.Pending: "pending",
|
||||
vcs.Success: "success",
|
||||
vcs.Failed: "failed",
|
||||
}
|
||||
for k, v := range cases {
|
||||
Equals(t, v, k.String())
|
||||
}
|
||||
}
|
||||
|
||||
func TestUpdate(t *testing.T) {
|
||||
RegisterMockTestingT(t)
|
||||
client := mocks.NewMockClientProxy()
|
||||
s := events.DefaultCommitStatusUpdater{Client: client}
|
||||
err := s.Update(repoModel, pullModel, status, &cmd)
|
||||
err := s.Update(repoModel, pullModel, status, events.Plan)
|
||||
Ok(t, err)
|
||||
client.VerifyWasCalledOnce().UpdateStatus(repoModel, pullModel, status, "Plan Success")
|
||||
}
|
||||
@@ -58,11 +44,10 @@ func TestUpdateProjectResult_Error(t *testing.T) {
|
||||
ctx := &events.CommandContext{
|
||||
BaseRepo: repoModel,
|
||||
Pull: pullModel,
|
||||
Command: &events.Command{Name: events.Plan},
|
||||
}
|
||||
client := mocks.NewMockClientProxy()
|
||||
s := events.DefaultCommitStatusUpdater{Client: client}
|
||||
err := s.UpdateProjectResult(ctx, events.CommandResponse{Error: errors.New("err")})
|
||||
err := s.UpdateProjectResult(ctx, events.Plan, events.CommandResult{Error: errors.New("err")})
|
||||
Ok(t, err)
|
||||
client.VerifyWasCalledOnce().UpdateStatus(repoModel, pullModel, vcs.Failed, "Plan Failed")
|
||||
}
|
||||
@@ -72,23 +57,20 @@ func TestUpdateProjectResult_Failure(t *testing.T) {
|
||||
ctx := &events.CommandContext{
|
||||
BaseRepo: repoModel,
|
||||
Pull: pullModel,
|
||||
Command: &events.Command{Name: events.Plan},
|
||||
}
|
||||
client := mocks.NewMockClientProxy()
|
||||
s := events.DefaultCommitStatusUpdater{Client: client}
|
||||
err := s.UpdateProjectResult(ctx, events.CommandResponse{Failure: "failure"})
|
||||
err := s.UpdateProjectResult(ctx, events.Plan, events.CommandResult{Failure: "failure"})
|
||||
Ok(t, err)
|
||||
client.VerifyWasCalledOnce().UpdateStatus(repoModel, pullModel, vcs.Failed, "Plan Failed")
|
||||
}
|
||||
|
||||
func TestUpdateProjectResult(t *testing.T) {
|
||||
t.Log("should use worst status")
|
||||
RegisterMockTestingT(t)
|
||||
|
||||
ctx := &events.CommandContext{
|
||||
BaseRepo: repoModel,
|
||||
Pull: pullModel,
|
||||
Command: &events.Command{Name: events.Plan},
|
||||
}
|
||||
|
||||
cases := []struct {
|
||||
@@ -126,25 +108,31 @@ func TestUpdateProjectResult(t *testing.T) {
|
||||
}
|
||||
|
||||
for _, c := range cases {
|
||||
var results []events.ProjectResult
|
||||
for _, statusStr := range c.Statuses {
|
||||
var result events.ProjectResult
|
||||
switch statusStr {
|
||||
case "failure":
|
||||
result = events.ProjectResult{Failure: "failure"}
|
||||
case "error":
|
||||
result = events.ProjectResult{Error: errors.New("err")}
|
||||
default:
|
||||
result = events.ProjectResult{}
|
||||
t.Run(strings.Join(c.Statuses, "-"), func(t *testing.T) {
|
||||
var results []events.ProjectResult
|
||||
for _, statusStr := range c.Statuses {
|
||||
var result events.ProjectResult
|
||||
switch statusStr {
|
||||
case "failure":
|
||||
result = events.ProjectResult{
|
||||
ProjectCommandResult: events.ProjectCommandResult{Failure: "failure"},
|
||||
}
|
||||
case "error":
|
||||
result = events.ProjectResult{
|
||||
ProjectCommandResult: events.ProjectCommandResult{Error: errors.New("err")},
|
||||
}
|
||||
default:
|
||||
result = events.ProjectResult{}
|
||||
}
|
||||
results = append(results, result)
|
||||
}
|
||||
results = append(results, result)
|
||||
}
|
||||
resp := events.CommandResponse{ProjectResults: results}
|
||||
resp := events.CommandResult{ProjectResults: results}
|
||||
|
||||
client := mocks.NewMockClientProxy()
|
||||
s := events.DefaultCommitStatusUpdater{Client: client}
|
||||
err := s.UpdateProjectResult(ctx, resp)
|
||||
Ok(t, err)
|
||||
client.VerifyWasCalledOnce().UpdateStatus(repoModel, pullModel, c.Expected, "Plan "+strings.Title(c.Expected.String()))
|
||||
client := mocks.NewMockClientProxy()
|
||||
s := events.DefaultCommitStatusUpdater{Client: client}
|
||||
err := s.UpdateProjectResult(ctx, events.Plan, resp)
|
||||
Ok(t, err)
|
||||
client.VerifyWasCalledOnce().UpdateStatus(repoModel, pullModel, c.Expected, "Plan "+strings.Title(c.Expected.String()))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -34,7 +34,27 @@ var multiLineRegex = regexp.MustCompile(`.*\r?\n.+`)
|
||||
|
||||
//go:generate pegomock generate -m --use-experimental-model-gen --package mocks -o mocks/mock_event_parsing.go EventParsing
|
||||
|
||||
type Command struct {
|
||||
type CommandInterface interface {
|
||||
CommandName() CommandName
|
||||
IsVerbose() bool
|
||||
IsAutoplan() bool
|
||||
}
|
||||
|
||||
type AutoplanCommand struct{}
|
||||
|
||||
func (c AutoplanCommand) CommandName() CommandName {
|
||||
return Plan
|
||||
}
|
||||
|
||||
func (c AutoplanCommand) IsVerbose() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func (c AutoplanCommand) IsAutoplan() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
type CommentCommand struct {
|
||||
// Dir is the path relative to the repo root to run the command in.
|
||||
// Will never be an empty string and will never end in "/".
|
||||
Dir string
|
||||
@@ -44,20 +64,29 @@ type Command struct {
|
||||
Name CommandName
|
||||
Verbose bool
|
||||
Workspace string
|
||||
// Autoplan is true if the command is a plan command being executed in an
|
||||
// attempt to automatically run plan.
|
||||
Autoplan bool
|
||||
// ProjectName is the name of a project to run the command on. It refers to a
|
||||
// project specified in an atlantis.yaml file.
|
||||
ProjectName string
|
||||
}
|
||||
|
||||
func (c Command) String() string {
|
||||
return fmt.Sprintf("command=%q verbose=%t dir=%q workspace=%q project=%q autoplan=%t flags=%q", c.Name.String(), c.Verbose, c.Dir, c.Workspace, c.ProjectName, c.Autoplan, strings.Join(c.Flags, ","))
|
||||
func (c CommentCommand) CommandName() CommandName {
|
||||
return c.Name
|
||||
}
|
||||
|
||||
func (c CommentCommand) IsVerbose() bool {
|
||||
return c.Verbose
|
||||
}
|
||||
|
||||
func (c CommentCommand) IsAutoplan() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func (c CommentCommand) String() string {
|
||||
return fmt.Sprintf("command=%q verbose=%t dir=%q workspace=%q project=%q flags=%q", c.Name.String(), c.Verbose, c.Dir, c.Workspace, c.ProjectName, strings.Join(c.Flags, ","))
|
||||
}
|
||||
|
||||
// NewCommand constructs a Command, setting all missing fields to defaults.
|
||||
func NewCommand(dir string, flags []string, name CommandName, verbose bool, workspace string, project string, autoplan bool) *Command {
|
||||
func NewCommand(dir string, flags []string, name CommandName, verbose bool, workspace string, project string) *CommentCommand {
|
||||
// If dir was an empty string, this will return '.'.
|
||||
validDir := path.Clean(dir)
|
||||
if validDir == "/" {
|
||||
@@ -66,24 +95,27 @@ func NewCommand(dir string, flags []string, name CommandName, verbose bool, work
|
||||
if workspace == "" {
|
||||
workspace = DefaultWorkspace
|
||||
}
|
||||
return &Command{
|
||||
return &CommentCommand{
|
||||
Dir: validDir,
|
||||
Flags: flags,
|
||||
Name: name,
|
||||
Verbose: verbose,
|
||||
Workspace: workspace,
|
||||
Autoplan: autoplan,
|
||||
ProjectName: project,
|
||||
}
|
||||
}
|
||||
|
||||
type EventParsing interface {
|
||||
ParseGithubIssueCommentEvent(comment *github.IssueCommentEvent) (baseRepo models.Repo, user models.User, pullNum int, err error)
|
||||
// ParseGithubPull returns the pull request and head repo.
|
||||
ParseGithubPull(pull *github.PullRequest) (models.PullRequest, models.Repo, error)
|
||||
// ParseGithubPull returns the pull request, base repo and head repo.
|
||||
ParseGithubPull(pull *github.PullRequest) (models.PullRequest, models.Repo, models.Repo, error)
|
||||
// ParseGithubPullEvent returns the pull request, head repo and user that
|
||||
// caused the event. Base repo is available as a field on PullRequest.
|
||||
ParseGithubPullEvent(pullEvent *github.PullRequestEvent) (pull models.PullRequest, baseRepo models.Repo, headRepo models.Repo, user models.User, err error)
|
||||
ParseGithubRepo(ghRepo *github.Repository) (models.Repo, error)
|
||||
// ParseGitlabMergeEvent returns the pull request, base repo and head repo.
|
||||
ParseGitlabMergeEvent(event gitlab.MergeEvent) (models.PullRequest, models.Repo, models.Repo, error)
|
||||
// ParseGitlabMergeEvent returns the pull request, base repo, head repo and
|
||||
// user that caused the event.
|
||||
ParseGitlabMergeEvent(event gitlab.MergeEvent) (models.PullRequest, models.Repo, models.Repo, models.User, error)
|
||||
ParseGitlabMergeCommentEvent(event gitlab.MergeCommentEvent) (baseRepo models.Repo, headRepo models.Repo, user models.User, err error)
|
||||
ParseGitlabMergeRequest(mr *gitlab.MergeRequest, baseRepo models.Repo) models.PullRequest
|
||||
}
|
||||
@@ -116,38 +148,58 @@ func (e *EventParser) ParseGithubIssueCommentEvent(comment *github.IssueCommentE
|
||||
return
|
||||
}
|
||||
|
||||
func (e *EventParser) ParseGithubPull(pull *github.PullRequest) (models.PullRequest, models.Repo, error) {
|
||||
var pullModel models.PullRequest
|
||||
var headRepoModel models.Repo
|
||||
func (e *EventParser) ParseGithubPullEvent(pullEvent *github.PullRequestEvent) (models.PullRequest, models.Repo, models.Repo, models.User, error) {
|
||||
if pullEvent.PullRequest == nil {
|
||||
return models.PullRequest{}, models.Repo{}, models.Repo{}, models.User{}, errors.New("pull_request is null")
|
||||
}
|
||||
pull, baseRepo, headRepo, err := e.ParseGithubPull(pullEvent.PullRequest)
|
||||
if err != nil {
|
||||
return models.PullRequest{}, models.Repo{}, models.Repo{}, models.User{}, err
|
||||
}
|
||||
if pullEvent.Sender == nil {
|
||||
return models.PullRequest{}, models.Repo{}, models.Repo{}, models.User{}, errors.New("sender is null")
|
||||
}
|
||||
senderUsername := pullEvent.Sender.GetLogin()
|
||||
if senderUsername == "" {
|
||||
return models.PullRequest{}, models.Repo{}, models.Repo{}, models.User{}, errors.New("sender.login is null")
|
||||
}
|
||||
return pull, baseRepo, headRepo, models.User{Username: senderUsername}, nil
|
||||
}
|
||||
|
||||
func (e *EventParser) ParseGithubPull(pull *github.PullRequest) (pullModel models.PullRequest, baseRepo models.Repo, headRepo models.Repo, err error) {
|
||||
commit := pull.Head.GetSHA()
|
||||
if commit == "" {
|
||||
return pullModel, headRepoModel, errors.New("head.sha is null")
|
||||
err = errors.New("head.sha is null")
|
||||
return
|
||||
}
|
||||
url := pull.GetHTMLURL()
|
||||
if url == "" {
|
||||
return pullModel, headRepoModel, errors.New("html_url is null")
|
||||
err = errors.New("html_url is null")
|
||||
return
|
||||
}
|
||||
branch := pull.Head.GetRef()
|
||||
if branch == "" {
|
||||
return pullModel, headRepoModel, errors.New("head.ref is null")
|
||||
err = errors.New("head.ref is null")
|
||||
return
|
||||
}
|
||||
authorUsername := pull.User.GetLogin()
|
||||
if authorUsername == "" {
|
||||
return pullModel, headRepoModel, errors.New("user.login is null")
|
||||
err = errors.New("user.login is null")
|
||||
return
|
||||
}
|
||||
num := pull.GetNumber()
|
||||
if num == 0 {
|
||||
return pullModel, headRepoModel, errors.New("number is null")
|
||||
err = errors.New("number is null")
|
||||
return
|
||||
}
|
||||
|
||||
baseRepoModel, err := e.ParseGithubRepo(pull.Base.Repo)
|
||||
baseRepo, err = e.ParseGithubRepo(pull.Base.Repo)
|
||||
if err != nil {
|
||||
return pullModel, headRepoModel, err
|
||||
return
|
||||
}
|
||||
headRepoModel, err = e.ParseGithubRepo(pull.Head.Repo)
|
||||
headRepo, err = e.ParseGithubRepo(pull.Head.Repo)
|
||||
if err != nil {
|
||||
return pullModel, headRepoModel, err
|
||||
return
|
||||
}
|
||||
|
||||
pullState := models.Closed
|
||||
@@ -155,22 +207,23 @@ func (e *EventParser) ParseGithubPull(pull *github.PullRequest) (models.PullRequ
|
||||
pullState = models.Open
|
||||
}
|
||||
|
||||
return models.PullRequest{
|
||||
pullModel = models.PullRequest{
|
||||
Author: authorUsername,
|
||||
Branch: branch,
|
||||
HeadCommit: commit,
|
||||
URL: url,
|
||||
Num: num,
|
||||
State: pullState,
|
||||
BaseRepo: baseRepoModel,
|
||||
}, headRepoModel, nil
|
||||
BaseRepo: baseRepo,
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (e *EventParser) ParseGithubRepo(ghRepo *github.Repository) (models.Repo, error) {
|
||||
return models.NewRepo(models.Github, ghRepo.GetFullName(), ghRepo.GetCloneURL(), e.GithubUser, e.GithubToken)
|
||||
}
|
||||
|
||||
func (e *EventParser) ParseGitlabMergeEvent(event gitlab.MergeEvent) (models.PullRequest, models.Repo, models.Repo, error) {
|
||||
func (e *EventParser) ParseGitlabMergeEvent(event gitlab.MergeEvent) (models.PullRequest, models.Repo, models.Repo, models.User, error) {
|
||||
modelState := models.Closed
|
||||
if event.ObjectAttributes.State == gitlabPullOpened {
|
||||
modelState = models.Open
|
||||
@@ -180,11 +233,11 @@ func (e *EventParser) ParseGitlabMergeEvent(event gitlab.MergeEvent) (models.Pul
|
||||
|
||||
baseRepo, err := models.NewRepo(models.Gitlab, event.Project.PathWithNamespace, event.Project.GitHTTPURL, e.GitlabUser, e.GitlabToken)
|
||||
if err != nil {
|
||||
return models.PullRequest{}, models.Repo{}, models.Repo{}, err
|
||||
return models.PullRequest{}, models.Repo{}, models.Repo{}, models.User{}, err
|
||||
}
|
||||
headRepo, err := models.NewRepo(models.Gitlab, event.ObjectAttributes.Source.PathWithNamespace, event.ObjectAttributes.Source.GitHTTPURL, e.GitlabUser, e.GitlabToken)
|
||||
if err != nil {
|
||||
return models.PullRequest{}, models.Repo{}, models.Repo{}, err
|
||||
return models.PullRequest{}, models.Repo{}, models.Repo{}, models.User{}, err
|
||||
}
|
||||
|
||||
pull := models.PullRequest{
|
||||
@@ -197,7 +250,11 @@ func (e *EventParser) ParseGitlabMergeEvent(event gitlab.MergeEvent) (models.Pul
|
||||
BaseRepo: baseRepo,
|
||||
}
|
||||
|
||||
return pull, baseRepo, headRepo, err
|
||||
user := models.User{
|
||||
Username: event.User.Username,
|
||||
}
|
||||
|
||||
return pull, baseRepo, headRepo, user, err
|
||||
}
|
||||
|
||||
// ParseGitlabMergeCommentEvent creates Atlantis models out of a GitLab event.
|
||||
|
||||
@@ -102,34 +102,40 @@ func TestParseGithubIssueCommentEvent(t *testing.T) {
|
||||
Equals(t, *comment.Issue.Number, pullNum)
|
||||
}
|
||||
|
||||
func TestParseGithubPull(t *testing.T) {
|
||||
testPull := deepcopy.Copy(Pull).(github.PullRequest)
|
||||
testPull.Head.SHA = nil
|
||||
_, _, err := parser.ParseGithubPull(&testPull)
|
||||
ErrEquals(t, "head.sha is null", err)
|
||||
func TestParseGithubPullEvent(t *testing.T) {
|
||||
_, _, _, _, err := parser.ParseGithubPullEvent(&github.PullRequestEvent{})
|
||||
ErrEquals(t, "pull_request is null", err)
|
||||
|
||||
testPull = deepcopy.Copy(Pull).(github.PullRequest)
|
||||
testPull.HTMLURL = nil
|
||||
_, _, err = parser.ParseGithubPull(&testPull)
|
||||
testEvent := deepcopy.Copy(PullEvent).(github.PullRequestEvent)
|
||||
testEvent.PullRequest.HTMLURL = nil
|
||||
_, _, _, _, err = parser.ParseGithubPullEvent(&testEvent)
|
||||
ErrEquals(t, "html_url is null", err)
|
||||
|
||||
testPull = deepcopy.Copy(Pull).(github.PullRequest)
|
||||
testPull.Head.Ref = nil
|
||||
_, _, err = parser.ParseGithubPull(&testPull)
|
||||
ErrEquals(t, "head.ref is null", err)
|
||||
testEvent = deepcopy.Copy(PullEvent).(github.PullRequestEvent)
|
||||
testEvent.Sender = nil
|
||||
_, _, _, _, err = parser.ParseGithubPullEvent(&testEvent)
|
||||
ErrEquals(t, "sender is null", err)
|
||||
|
||||
testPull = deepcopy.Copy(Pull).(github.PullRequest)
|
||||
testPull.User.Login = nil
|
||||
_, _, err = parser.ParseGithubPull(&testPull)
|
||||
ErrEquals(t, "user.login is null", err)
|
||||
testEvent = deepcopy.Copy(PullEvent).(github.PullRequestEvent)
|
||||
testEvent.Sender.Login = nil
|
||||
_, _, _, _, err = parser.ParseGithubPullEvent(&testEvent)
|
||||
ErrEquals(t, "sender.login is null", err)
|
||||
|
||||
testPull = deepcopy.Copy(Pull).(github.PullRequest)
|
||||
testPull.Number = nil
|
||||
_, _, err = parser.ParseGithubPull(&testPull)
|
||||
ErrEquals(t, "number is null", err)
|
||||
|
||||
pullRes, _, err := parser.ParseGithubPull(&Pull)
|
||||
actPull, actBaseRepo, actHeadRepo, actUser, err := parser.ParseGithubPullEvent(&PullEvent)
|
||||
Ok(t, err)
|
||||
expBaseRepo := models.Repo{
|
||||
Owner: "owner",
|
||||
FullName: "owner/repo",
|
||||
CloneURL: "https://github-user:github-token@github.com/owner/repo.git",
|
||||
SanitizedCloneURL: Repo.GetCloneURL(),
|
||||
Name: "repo",
|
||||
VCSHost: models.VCSHost{
|
||||
Hostname: "github.com",
|
||||
Type: models.Github,
|
||||
},
|
||||
}
|
||||
Equals(t, expBaseRepo, actBaseRepo)
|
||||
Equals(t, expBaseRepo, actHeadRepo)
|
||||
Equals(t, models.PullRequest{
|
||||
URL: Pull.GetHTMLURL(),
|
||||
Author: Pull.User.GetLogin(),
|
||||
@@ -137,18 +143,61 @@ func TestParseGithubPull(t *testing.T) {
|
||||
HeadCommit: Pull.Head.GetSHA(),
|
||||
Num: Pull.GetNumber(),
|
||||
State: models.Open,
|
||||
BaseRepo: models.Repo{
|
||||
Owner: "owner",
|
||||
FullName: "owner/repo",
|
||||
CloneURL: "https://github-user:github-token@github.com/owner/repo.git",
|
||||
SanitizedCloneURL: Repo.GetCloneURL(),
|
||||
Name: "repo",
|
||||
VCSHost: models.VCSHost{
|
||||
Hostname: "github.com",
|
||||
Type: models.Github,
|
||||
},
|
||||
BaseRepo: expBaseRepo,
|
||||
}, actPull)
|
||||
Equals(t, models.User{Username: "user"}, actUser)
|
||||
}
|
||||
|
||||
func TestParseGithubPull(t *testing.T) {
|
||||
testPull := deepcopy.Copy(Pull).(github.PullRequest)
|
||||
testPull.Head.SHA = nil
|
||||
_, _, _, err := parser.ParseGithubPull(&testPull)
|
||||
ErrEquals(t, "head.sha is null", err)
|
||||
|
||||
testPull = deepcopy.Copy(Pull).(github.PullRequest)
|
||||
testPull.HTMLURL = nil
|
||||
_, _, _, err = parser.ParseGithubPull(&testPull)
|
||||
ErrEquals(t, "html_url is null", err)
|
||||
|
||||
testPull = deepcopy.Copy(Pull).(github.PullRequest)
|
||||
testPull.Head.Ref = nil
|
||||
_, _, _, err = parser.ParseGithubPull(&testPull)
|
||||
ErrEquals(t, "head.ref is null", err)
|
||||
|
||||
testPull = deepcopy.Copy(Pull).(github.PullRequest)
|
||||
testPull.User.Login = nil
|
||||
_, _, _, err = parser.ParseGithubPull(&testPull)
|
||||
ErrEquals(t, "user.login is null", err)
|
||||
|
||||
testPull = deepcopy.Copy(Pull).(github.PullRequest)
|
||||
testPull.Number = nil
|
||||
_, _, _, err = parser.ParseGithubPull(&testPull)
|
||||
ErrEquals(t, "number is null", err)
|
||||
|
||||
pullRes, actBaseRepo, actHeadRepo, err := parser.ParseGithubPull(&Pull)
|
||||
Ok(t, err)
|
||||
expBaseRepo := models.Repo{
|
||||
Owner: "owner",
|
||||
FullName: "owner/repo",
|
||||
CloneURL: "https://github-user:github-token@github.com/owner/repo.git",
|
||||
SanitizedCloneURL: Repo.GetCloneURL(),
|
||||
Name: "repo",
|
||||
VCSHost: models.VCSHost{
|
||||
Hostname: "github.com",
|
||||
Type: models.Github,
|
||||
},
|
||||
}
|
||||
Equals(t, models.PullRequest{
|
||||
URL: Pull.GetHTMLURL(),
|
||||
Author: Pull.User.GetLogin(),
|
||||
Branch: Pull.Head.GetRef(),
|
||||
HeadCommit: Pull.Head.GetSHA(),
|
||||
Num: Pull.GetNumber(),
|
||||
State: models.Open,
|
||||
BaseRepo: expBaseRepo,
|
||||
}, pullRes)
|
||||
Equals(t, expBaseRepo, actBaseRepo)
|
||||
Equals(t, expBaseRepo, actHeadRepo)
|
||||
}
|
||||
|
||||
func TestParseGitlabMergeEvent(t *testing.T) {
|
||||
@@ -156,10 +205,10 @@ func TestParseGitlabMergeEvent(t *testing.T) {
|
||||
var event *gitlab.MergeEvent
|
||||
err := json.Unmarshal([]byte(mergeEventJSON), &event)
|
||||
Ok(t, err)
|
||||
pull, repo, _, err := parser.ParseGitlabMergeEvent(*event)
|
||||
pull, actBaseRepo, actHeadRepo, actUser, err := parser.ParseGitlabMergeEvent(*event)
|
||||
Ok(t, err)
|
||||
|
||||
expRepo := models.Repo{
|
||||
expBaseRepo := models.Repo{
|
||||
FullName: "gitlabhq/gitlab-test",
|
||||
Name: "gitlab-test",
|
||||
SanitizedCloneURL: "https://example.com/gitlabhq/gitlab-test.git",
|
||||
@@ -178,14 +227,26 @@ func TestParseGitlabMergeEvent(t *testing.T) {
|
||||
HeadCommit: "da1560886d4f094c3e6c9ef40349f7d38b5d27d7",
|
||||
Branch: "ms-viewport",
|
||||
State: models.Open,
|
||||
BaseRepo: expRepo,
|
||||
BaseRepo: expBaseRepo,
|
||||
}, pull)
|
||||
|
||||
Equals(t, expRepo, repo)
|
||||
Equals(t, expBaseRepo, actBaseRepo)
|
||||
Equals(t, models.Repo{
|
||||
FullName: "awesome_space/awesome_project",
|
||||
Name: "awesome_project",
|
||||
SanitizedCloneURL: "http://example.com/awesome_space/awesome_project.git",
|
||||
Owner: "awesome_space",
|
||||
CloneURL: "http://gitlab-user:gitlab-token@example.com/awesome_space/awesome_project.git",
|
||||
VCSHost: models.VCSHost{
|
||||
Hostname: "example.com",
|
||||
Type: models.Gitlab,
|
||||
},
|
||||
}, actHeadRepo)
|
||||
Equals(t, models.User{Username: "root"}, actUser)
|
||||
|
||||
t.Log("If the state is closed, should set field correctly.")
|
||||
event.ObjectAttributes.State = "closed"
|
||||
pull, _, _, err = parser.ParseGitlabMergeEvent(*event)
|
||||
pull, _, _, _, err = parser.ParseGitlabMergeEvent(*event)
|
||||
Ok(t, err)
|
||||
Equals(t, models.Closed, pull.State)
|
||||
}
|
||||
@@ -283,20 +344,20 @@ func TestNewCommand_CleansDir(t *testing.T) {
|
||||
|
||||
for _, c := range cases {
|
||||
t.Run(c.Dir, func(t *testing.T) {
|
||||
cmd := events.NewCommand(c.Dir, nil, events.Plan, false, "workspace", "", false)
|
||||
cmd := events.NewCommand(c.Dir, nil, events.Plan, false, "workspace", "")
|
||||
Equals(t, c.ExpDir, cmd.Dir)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestNewCommand_EmptyWorkspace(t *testing.T) {
|
||||
cmd := events.NewCommand("dir", nil, events.Plan, false, "", "", false)
|
||||
cmd := events.NewCommand("dir", nil, events.Plan, false, "", "")
|
||||
Equals(t, "default", cmd.Workspace)
|
||||
}
|
||||
|
||||
func TestNewCommand_AllFieldsSet(t *testing.T) {
|
||||
cmd := events.NewCommand("dir", []string{"a", "b"}, events.Plan, true, "workspace", "project", false)
|
||||
Equals(t, events.Command{
|
||||
cmd := events.NewCommand("dir", []string{"a", "b"}, events.Plan, true, "workspace", "project")
|
||||
Equals(t, events.CommentCommand{
|
||||
Workspace: "workspace",
|
||||
Dir: "dir",
|
||||
Verbose: true,
|
||||
@@ -306,6 +367,52 @@ func TestNewCommand_AllFieldsSet(t *testing.T) {
|
||||
}, *cmd)
|
||||
}
|
||||
|
||||
func TestAutoplanCommand_CommandName(t *testing.T) {
|
||||
Equals(t, events.Plan, (events.AutoplanCommand{}).CommandName())
|
||||
}
|
||||
|
||||
func TestAutoplanCommand_IsVerbose(t *testing.T) {
|
||||
Equals(t, false, (events.AutoplanCommand{}).IsVerbose())
|
||||
}
|
||||
|
||||
func TestAutoplanCommand_IsAutoplan(t *testing.T) {
|
||||
Equals(t, true, (events.AutoplanCommand{}).IsAutoplan())
|
||||
}
|
||||
|
||||
func TestCommentCommand_CommandName(t *testing.T) {
|
||||
Equals(t, events.Plan, (events.CommentCommand{
|
||||
Name: events.Plan,
|
||||
}).CommandName())
|
||||
Equals(t, events.Apply, (events.CommentCommand{
|
||||
Name: events.Apply,
|
||||
}).CommandName())
|
||||
}
|
||||
|
||||
func TestCommentCommand_IsVerbose(t *testing.T) {
|
||||
Equals(t, false, (events.CommentCommand{
|
||||
Verbose: false,
|
||||
}).IsVerbose())
|
||||
Equals(t, true, (events.CommentCommand{
|
||||
Verbose: true,
|
||||
}).IsVerbose())
|
||||
}
|
||||
|
||||
func TestCommentCommand_IsAutoplan(t *testing.T) {
|
||||
Equals(t, false, (events.CommentCommand{}).IsAutoplan())
|
||||
}
|
||||
|
||||
func TestCommentCommand_String(t *testing.T) {
|
||||
exp := `command="plan" verbose=true dir="mydir" workspace="myworkspace" project="myproject" flags="flag1,flag2"`
|
||||
Equals(t, exp, (events.CommentCommand{
|
||||
Dir: "mydir",
|
||||
Flags: []string{"flag1", "flag2"},
|
||||
Name: events.Plan,
|
||||
Verbose: true,
|
||||
Workspace: "myworkspace",
|
||||
ProjectName: "myproject",
|
||||
}).String())
|
||||
}
|
||||
|
||||
var mergeEventJSON = `{
|
||||
"object_kind": "merge_request",
|
||||
"user": {
|
||||
|
||||
@@ -18,5 +18,5 @@ package events
|
||||
// Executor is the generic interface implemented by each command type:
|
||||
// help, plan, and apply.
|
||||
type Executor interface {
|
||||
Execute(ctx *CommandContext) CommandResponse
|
||||
Execute(ctx *CommandContext) CommandResult
|
||||
}
|
||||
|
||||
@@ -58,7 +58,7 @@ type ProjectResultTmplData struct {
|
||||
|
||||
// Render formats the data into a markdown string.
|
||||
// nolint: interfacer
|
||||
func (m *MarkdownRenderer) Render(res CommandResponse, cmdName CommandName, log string, verbose bool, autoplan bool) string {
|
||||
func (m *MarkdownRenderer) Render(res CommandResult, cmdName CommandName, log string, verbose bool, autoplan bool) string {
|
||||
commandStr := strings.Title(cmdName.String())
|
||||
common := CommonData{commandStr, verbose, log}
|
||||
if res.Error != nil {
|
||||
@@ -73,10 +73,10 @@ func (m *MarkdownRenderer) Render(res CommandResponse, cmdName CommandName, log
|
||||
return m.renderProjectResults(res.ProjectResults, common)
|
||||
}
|
||||
|
||||
func (m *MarkdownRenderer) renderProjectResults(pathResults []ProjectResult, common CommonData) string {
|
||||
func (m *MarkdownRenderer) renderProjectResults(results []ProjectResult, common CommonData) string {
|
||||
var resultsTmplData []ProjectResultTmplData
|
||||
|
||||
for _, result := range pathResults {
|
||||
for _, result := range results {
|
||||
resultData := ProjectResultTmplData{
|
||||
Workspace: result.Workspace,
|
||||
Dir: result.Path,
|
||||
|
||||
@@ -45,18 +45,20 @@ func TestRenderErr(t *testing.T) {
|
||||
|
||||
r := events.MarkdownRenderer{}
|
||||
for _, c := range cases {
|
||||
res := events.CommandResponse{
|
||||
Error: c.Error,
|
||||
}
|
||||
for _, verbose := range []bool{true, false} {
|
||||
t.Log("testing " + c.Description)
|
||||
s := r.Render(res, c.Command, "log", verbose, false)
|
||||
if !verbose {
|
||||
Equals(t, c.Expected, s)
|
||||
} else {
|
||||
Equals(t, c.Expected+"<details><summary>Log</summary>\n <p>\n\n```\nlog```\n</p></details>\n", s)
|
||||
t.Run(c.Description, func(t *testing.T) {
|
||||
res := events.CommandResult{
|
||||
Error: c.Error,
|
||||
}
|
||||
}
|
||||
for _, verbose := range []bool{true, false} {
|
||||
t.Log("testing " + c.Description)
|
||||
s := r.Render(res, c.Command, "log", verbose, false)
|
||||
if !verbose {
|
||||
Equals(t, c.Expected, s)
|
||||
} else {
|
||||
Equals(t, c.Expected+"<details><summary>Log</summary>\n <p>\n\n```\nlog```\n</p></details>\n", s)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -83,25 +85,27 @@ func TestRenderFailure(t *testing.T) {
|
||||
|
||||
r := events.MarkdownRenderer{}
|
||||
for _, c := range cases {
|
||||
res := events.CommandResponse{
|
||||
Failure: c.Failure,
|
||||
}
|
||||
for _, verbose := range []bool{true, false} {
|
||||
t.Log("testing " + c.Description)
|
||||
s := r.Render(res, c.Command, "log", verbose, false)
|
||||
if !verbose {
|
||||
Equals(t, c.Expected, s)
|
||||
} else {
|
||||
Equals(t, c.Expected+"<details><summary>Log</summary>\n <p>\n\n```\nlog```\n</p></details>\n", s)
|
||||
t.Run(c.Description, func(t *testing.T) {
|
||||
res := events.CommandResult{
|
||||
Failure: c.Failure,
|
||||
}
|
||||
}
|
||||
for _, verbose := range []bool{true, false} {
|
||||
t.Log("testing " + c.Description)
|
||||
s := r.Render(res, c.Command, "log", verbose, false)
|
||||
if !verbose {
|
||||
Equals(t, c.Expected, s)
|
||||
} else {
|
||||
Equals(t, c.Expected+"<details><summary>Log</summary>\n <p>\n\n```\nlog```\n</p></details>\n", s)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestRenderErrAndFailure(t *testing.T) {
|
||||
t.Log("if there is an error and a failure, the error should be printed")
|
||||
r := events.MarkdownRenderer{}
|
||||
res := events.CommandResponse{
|
||||
res := events.CommandResult{
|
||||
Error: errors.New("error"),
|
||||
Failure: "failure",
|
||||
}
|
||||
@@ -109,6 +113,15 @@ func TestRenderErrAndFailure(t *testing.T) {
|
||||
Equals(t, "**Plan Error**\n```\nerror\n```\n\n", s)
|
||||
}
|
||||
|
||||
func TestRenderAutoplanNoResults(t *testing.T) {
|
||||
// If there are no project results during an autoplan we should still comment
|
||||
// back because the user might expect some output.
|
||||
r := events.MarkdownRenderer{}
|
||||
res := events.CommandResult{}
|
||||
s := r.Render(res, events.Plan, "", false, true)
|
||||
Equals(t, "Ran `plan` in 0 projects because Atlantis detected no Terraform changes or could not determine where to run `plan`.\n\n", s)
|
||||
}
|
||||
|
||||
func TestRenderProjectResults(t *testing.T) {
|
||||
cases := []struct {
|
||||
Description string
|
||||
@@ -121,9 +134,11 @@ func TestRenderProjectResults(t *testing.T) {
|
||||
events.Plan,
|
||||
[]events.ProjectResult{
|
||||
{
|
||||
PlanSuccess: &events.PlanSuccess{
|
||||
TerraformOutput: "terraform-output",
|
||||
LockURL: "lock-url",
|
||||
ProjectCommandResult: events.ProjectCommandResult{
|
||||
PlanSuccess: &events.PlanSuccess{
|
||||
TerraformOutput: "terraform-output",
|
||||
LockURL: "lock-url",
|
||||
},
|
||||
},
|
||||
Workspace: "workspace",
|
||||
Path: "path",
|
||||
@@ -136,9 +151,11 @@ func TestRenderProjectResults(t *testing.T) {
|
||||
events.Apply,
|
||||
[]events.ProjectResult{
|
||||
{
|
||||
ApplySuccess: "success",
|
||||
Workspace: "workspace",
|
||||
Path: "path",
|
||||
ProjectCommandResult: events.ProjectCommandResult{
|
||||
ApplySuccess: "success",
|
||||
},
|
||||
Workspace: "workspace",
|
||||
Path: "path",
|
||||
},
|
||||
},
|
||||
"Ran Apply in dir: `path` workspace: `workspace`\n```diff\nsuccess\n```\n\n",
|
||||
@@ -150,17 +167,21 @@ func TestRenderProjectResults(t *testing.T) {
|
||||
{
|
||||
Workspace: "workspace",
|
||||
Path: "path",
|
||||
PlanSuccess: &events.PlanSuccess{
|
||||
TerraformOutput: "terraform-output",
|
||||
LockURL: "lock-url",
|
||||
ProjectCommandResult: events.ProjectCommandResult{
|
||||
PlanSuccess: &events.PlanSuccess{
|
||||
TerraformOutput: "terraform-output",
|
||||
LockURL: "lock-url",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Workspace: "workspace",
|
||||
Path: "path2",
|
||||
PlanSuccess: &events.PlanSuccess{
|
||||
TerraformOutput: "terraform-output2",
|
||||
LockURL: "lock-url2",
|
||||
ProjectCommandResult: events.ProjectCommandResult{
|
||||
PlanSuccess: &events.PlanSuccess{
|
||||
TerraformOutput: "terraform-output2",
|
||||
LockURL: "lock-url2",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -171,14 +192,18 @@ func TestRenderProjectResults(t *testing.T) {
|
||||
events.Apply,
|
||||
[]events.ProjectResult{
|
||||
{
|
||||
Path: "path",
|
||||
Workspace: "workspace",
|
||||
ApplySuccess: "success",
|
||||
Path: "path",
|
||||
Workspace: "workspace",
|
||||
ProjectCommandResult: events.ProjectCommandResult{
|
||||
ApplySuccess: "success",
|
||||
},
|
||||
},
|
||||
{
|
||||
Path: "path2",
|
||||
Workspace: "workspace",
|
||||
ApplySuccess: "success2",
|
||||
Path: "path2",
|
||||
Workspace: "workspace",
|
||||
ProjectCommandResult: events.ProjectCommandResult{
|
||||
ApplySuccess: "success2",
|
||||
},
|
||||
},
|
||||
},
|
||||
"Ran Apply for 2 projects:\n1. workspace: `workspace` path: `path`\n1. workspace: `workspace` path: `path2`\n\n### 1. workspace: `workspace` path: `path`\n```diff\nsuccess\n```\n---\n### 2. workspace: `workspace` path: `path2`\n```diff\nsuccess2\n```\n---\n\n",
|
||||
@@ -188,7 +213,9 @@ func TestRenderProjectResults(t *testing.T) {
|
||||
events.Plan,
|
||||
[]events.ProjectResult{
|
||||
{
|
||||
Error: errors.New("error"),
|
||||
ProjectCommandResult: events.ProjectCommandResult{
|
||||
Error: errors.New("error"),
|
||||
},
|
||||
Path: "path",
|
||||
Workspace: "workspace",
|
||||
},
|
||||
@@ -202,7 +229,9 @@ func TestRenderProjectResults(t *testing.T) {
|
||||
{
|
||||
Path: "path",
|
||||
Workspace: "workspace",
|
||||
Failure: "failure",
|
||||
ProjectCommandResult: events.ProjectCommandResult{
|
||||
Failure: "failure",
|
||||
},
|
||||
},
|
||||
},
|
||||
"Ran Plan in dir: `path` workspace: `workspace`\n**Plan Failed**: failure\n\n\n",
|
||||
@@ -214,20 +243,26 @@ func TestRenderProjectResults(t *testing.T) {
|
||||
{
|
||||
Workspace: "workspace",
|
||||
Path: "path",
|
||||
PlanSuccess: &events.PlanSuccess{
|
||||
TerraformOutput: "terraform-output",
|
||||
LockURL: "lock-url",
|
||||
ProjectCommandResult: events.ProjectCommandResult{
|
||||
PlanSuccess: &events.PlanSuccess{
|
||||
TerraformOutput: "terraform-output",
|
||||
LockURL: "lock-url",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Workspace: "workspace",
|
||||
Path: "path2",
|
||||
Failure: "failure",
|
||||
ProjectCommandResult: events.ProjectCommandResult{
|
||||
Failure: "failure",
|
||||
},
|
||||
},
|
||||
{
|
||||
Workspace: "workspace",
|
||||
Path: "path3",
|
||||
Error: errors.New("error"),
|
||||
ProjectCommandResult: events.ProjectCommandResult{
|
||||
Error: errors.New("error"),
|
||||
},
|
||||
},
|
||||
},
|
||||
"Ran Plan for 3 projects:\n1. workspace: `workspace` path: `path`\n1. workspace: `workspace` path: `path2`\n1. workspace: `workspace` path: `path3`\n\n### 1. workspace: `workspace` path: `path`\n```diff\nterraform-output\n```\n\n* To **discard** this plan click [here](lock-url).\n---\n### 2. workspace: `workspace` path: `path2`\n**Plan Failed**: failure\n\n---\n### 3. workspace: `workspace` path: `path3`\n**Plan Error**\n```\nerror\n```\n\n---\n\n",
|
||||
@@ -237,19 +272,25 @@ func TestRenderProjectResults(t *testing.T) {
|
||||
events.Apply,
|
||||
[]events.ProjectResult{
|
||||
{
|
||||
Workspace: "workspace",
|
||||
Path: "path",
|
||||
ApplySuccess: "success",
|
||||
Workspace: "workspace",
|
||||
Path: "path",
|
||||
ProjectCommandResult: events.ProjectCommandResult{
|
||||
ApplySuccess: "success",
|
||||
},
|
||||
},
|
||||
{
|
||||
Workspace: "workspace",
|
||||
Path: "path2",
|
||||
Failure: "failure",
|
||||
ProjectCommandResult: events.ProjectCommandResult{
|
||||
Failure: "failure",
|
||||
},
|
||||
},
|
||||
{
|
||||
Workspace: "workspace",
|
||||
Path: "path3",
|
||||
Error: errors.New("error"),
|
||||
ProjectCommandResult: events.ProjectCommandResult{
|
||||
Error: errors.New("error"),
|
||||
},
|
||||
},
|
||||
},
|
||||
"Ran Apply for 3 projects:\n1. workspace: `workspace` path: `path`\n1. workspace: `workspace` path: `path2`\n1. workspace: `workspace` path: `path3`\n\n### 1. workspace: `workspace` path: `path`\n```diff\nsuccess\n```\n---\n### 2. workspace: `workspace` path: `path2`\n**Apply Failed**: failure\n\n---\n### 3. workspace: `workspace` path: `path3`\n**Apply Error**\n```\nerror\n```\n\n---\n\n",
|
||||
@@ -258,18 +299,20 @@ func TestRenderProjectResults(t *testing.T) {
|
||||
|
||||
r := events.MarkdownRenderer{}
|
||||
for _, c := range cases {
|
||||
res := events.CommandResponse{
|
||||
ProjectResults: c.ProjectResults,
|
||||
}
|
||||
for _, verbose := range []bool{true, false} {
|
||||
t.Run(c.Description, func(t *testing.T) {
|
||||
s := r.Render(res, c.Command, "log", verbose, false)
|
||||
if !verbose {
|
||||
Equals(t, c.Expected, s)
|
||||
} else {
|
||||
Equals(t, c.Expected+"<details><summary>Log</summary>\n <p>\n\n```\nlog```\n</p></details>\n", s)
|
||||
}
|
||||
})
|
||||
}
|
||||
t.Run(c.Description, func(t *testing.T) {
|
||||
res := events.CommandResult{
|
||||
ProjectResults: c.ProjectResults,
|
||||
}
|
||||
for _, verbose := range []bool{true, false} {
|
||||
t.Run(c.Description, func(t *testing.T) {
|
||||
s := r.Render(res, c.Command, "log", verbose, false)
|
||||
if !verbose {
|
||||
Equals(t, c.Expected, s)
|
||||
} else {
|
||||
Equals(t, c.Expected+"<details><summary>Log</summary>\n <p>\n\n```\nlog```\n</p></details>\n", s)
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,14 +7,14 @@ import (
|
||||
events "github.com/runatlantis/atlantis/server/events"
|
||||
)
|
||||
|
||||
func AnyEventsCommandResponse() events.CommandResponse {
|
||||
pegomock.RegisterMatcher(pegomock.NewAnyMatcher(reflect.TypeOf((*(events.CommandResponse))(nil)).Elem()))
|
||||
var nullValue events.CommandResponse
|
||||
func AnyEventsCommandName() events.CommandName {
|
||||
pegomock.RegisterMatcher(pegomock.NewAnyMatcher(reflect.TypeOf((*(events.CommandName))(nil)).Elem()))
|
||||
var nullValue events.CommandName
|
||||
return nullValue
|
||||
}
|
||||
|
||||
func EqEventsCommandResponse(value events.CommandResponse) events.CommandResponse {
|
||||
func EqEventsCommandName(value events.CommandName) events.CommandName {
|
||||
pegomock.RegisterMatcher(&pegomock.EqMatcher{Value: value})
|
||||
var nullValue events.CommandResponse
|
||||
var nullValue events.CommandName
|
||||
return nullValue
|
||||
}
|
||||
20
server/events/mocks/matchers/events_commandresult.go
Normal file
20
server/events/mocks/matchers/events_commandresult.go
Normal file
@@ -0,0 +1,20 @@
|
||||
package matchers
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
|
||||
"github.com/petergtz/pegomock"
|
||||
events "github.com/runatlantis/atlantis/server/events"
|
||||
)
|
||||
|
||||
func AnyEventsCommandResult() events.CommandResult {
|
||||
pegomock.RegisterMatcher(pegomock.NewAnyMatcher(reflect.TypeOf((*(events.CommandResult))(nil)).Elem()))
|
||||
var nullValue events.CommandResult
|
||||
return nullValue
|
||||
}
|
||||
|
||||
func EqEventsCommandResult(value events.CommandResult) events.CommandResult {
|
||||
pegomock.RegisterMatcher(&pegomock.EqMatcher{Value: value})
|
||||
var nullValue events.CommandResult
|
||||
return nullValue
|
||||
}
|
||||
20
server/events/mocks/matchers/models_projectcommandcontext.go
Normal file
20
server/events/mocks/matchers/models_projectcommandcontext.go
Normal file
@@ -0,0 +1,20 @@
|
||||
package matchers
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
|
||||
"github.com/petergtz/pegomock"
|
||||
models "github.com/runatlantis/atlantis/server/events/models"
|
||||
)
|
||||
|
||||
func AnyModelsProjectCommandContext() models.ProjectCommandContext {
|
||||
pegomock.RegisterMatcher(pegomock.NewAnyMatcher(reflect.TypeOf((*(models.ProjectCommandContext))(nil)).Elem()))
|
||||
var nullValue models.ProjectCommandContext
|
||||
return nullValue
|
||||
}
|
||||
|
||||
func EqModelsProjectCommandContext(value models.ProjectCommandContext) models.ProjectCommandContext {
|
||||
pegomock.RegisterMatcher(&pegomock.EqMatcher{Value: value})
|
||||
var nullValue models.ProjectCommandContext
|
||||
return nullValue
|
||||
}
|
||||
@@ -7,14 +7,14 @@ import (
|
||||
events "github.com/runatlantis/atlantis/server/events"
|
||||
)
|
||||
|
||||
func AnyPtrToEventsCommand() *events.Command {
|
||||
pegomock.RegisterMatcher(pegomock.NewAnyMatcher(reflect.TypeOf((*(*events.Command))(nil)).Elem()))
|
||||
var nullValue *events.Command
|
||||
func AnyPtrToEventsCommand() *events.CommentCommand {
|
||||
pegomock.RegisterMatcher(pegomock.NewAnyMatcher(reflect.TypeOf((*(*events.CommentCommand))(nil)).Elem()))
|
||||
var nullValue *events.CommentCommand
|
||||
return nullValue
|
||||
}
|
||||
|
||||
func EqPtrToEventsCommand(value *events.Command) *events.Command {
|
||||
func EqPtrToEventsCommand(value *events.CommentCommand) *events.CommentCommand {
|
||||
pegomock.RegisterMatcher(&pegomock.EqMatcher{Value: value})
|
||||
var nullValue *events.Command
|
||||
var nullValue *events.CommentCommand
|
||||
return nullValue
|
||||
}
|
||||
|
||||
20
server/events/mocks/matchers/ptr_to_events_commentcommand.go
Normal file
20
server/events/mocks/matchers/ptr_to_events_commentcommand.go
Normal file
@@ -0,0 +1,20 @@
|
||||
package matchers
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
|
||||
"github.com/petergtz/pegomock"
|
||||
events "github.com/runatlantis/atlantis/server/events"
|
||||
)
|
||||
|
||||
func AnyPtrToEventsCommentCommand() *events.CommentCommand {
|
||||
pegomock.RegisterMatcher(pegomock.NewAnyMatcher(reflect.TypeOf((*(*events.CommentCommand))(nil)).Elem()))
|
||||
var nullValue *events.CommentCommand
|
||||
return nullValue
|
||||
}
|
||||
|
||||
func EqPtrToEventsCommentCommand(value *events.CommentCommand) *events.CommentCommand {
|
||||
pegomock.RegisterMatcher(&pegomock.EqMatcher{Value: value})
|
||||
var nullValue *events.CommentCommand
|
||||
return nullValue
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
package matchers
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
|
||||
github "github.com/google/go-github/github"
|
||||
"github.com/petergtz/pegomock"
|
||||
)
|
||||
|
||||
func AnyPtrToGithubPullRequestEvent() *github.PullRequestEvent {
|
||||
pegomock.RegisterMatcher(pegomock.NewAnyMatcher(reflect.TypeOf((*(*github.PullRequestEvent))(nil)).Elem()))
|
||||
var nullValue *github.PullRequestEvent
|
||||
return nullValue
|
||||
}
|
||||
|
||||
func EqPtrToGithubPullRequestEvent(value *github.PullRequestEvent) *github.PullRequestEvent {
|
||||
pegomock.RegisterMatcher(&pegomock.EqMatcher{Value: value})
|
||||
var nullValue *github.PullRequestEvent
|
||||
return nullValue
|
||||
}
|
||||
20
server/events/mocks/matchers/ptr_to_models_repo.go
Normal file
20
server/events/mocks/matchers/ptr_to_models_repo.go
Normal file
@@ -0,0 +1,20 @@
|
||||
package matchers
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
|
||||
"github.com/petergtz/pegomock"
|
||||
models "github.com/runatlantis/atlantis/server/events/models"
|
||||
)
|
||||
|
||||
func AnyPtrToModelsRepo() *models.Repo {
|
||||
pegomock.RegisterMatcher(pegomock.NewAnyMatcher(reflect.TypeOf((*(*models.Repo))(nil)).Elem()))
|
||||
var nullValue *models.Repo
|
||||
return nullValue
|
||||
}
|
||||
|
||||
func EqPtrToModelsRepo(value *models.Repo) *models.Repo {
|
||||
pegomock.RegisterMatcher(&pegomock.EqMatcher{Value: value})
|
||||
var nullValue *models.Repo
|
||||
return nullValue
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
package matchers
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
|
||||
"github.com/petergtz/pegomock"
|
||||
models "github.com/runatlantis/atlantis/server/events/models"
|
||||
)
|
||||
|
||||
func AnySliceOfModelsProjectCommandContext() []models.ProjectCommandContext {
|
||||
pegomock.RegisterMatcher(pegomock.NewAnyMatcher(reflect.TypeOf((*([]models.ProjectCommandContext))(nil)).Elem()))
|
||||
var nullValue []models.ProjectCommandContext
|
||||
return nullValue
|
||||
}
|
||||
|
||||
func EqSliceOfModelsProjectCommandContext(value []models.ProjectCommandContext) []models.ProjectCommandContext {
|
||||
pegomock.RegisterMatcher(&pegomock.EqMatcher{Value: value})
|
||||
var nullValue []models.ProjectCommandContext
|
||||
return nullValue
|
||||
}
|
||||
@@ -19,9 +19,14 @@ func NewMockCommandRunner() *MockCommandRunner {
|
||||
return &MockCommandRunner{fail: pegomock.GlobalFailHandler}
|
||||
}
|
||||
|
||||
func (mock *MockCommandRunner) ExecuteCommand(baseRepo models.Repo, headRepo models.Repo, user models.User, pullNum int, cmd *events.Command) {
|
||||
params := []pegomock.Param{baseRepo, headRepo, user, pullNum, cmd}
|
||||
pegomock.GetGenericMockFrom(mock).Invoke("ExecuteCommand", params, []reflect.Type{})
|
||||
func (mock *MockCommandRunner) RunCommentCommand(baseRepo models.Repo, maybeHeadRepo *models.Repo, user models.User, pullNum int, cmd *events.CommentCommand) {
|
||||
params := []pegomock.Param{baseRepo, maybeHeadRepo, user, pullNum, cmd}
|
||||
pegomock.GetGenericMockFrom(mock).Invoke("RunCommentCommand", params, []reflect.Type{})
|
||||
}
|
||||
|
||||
func (mock *MockCommandRunner) RunAutoplanCommand(baseRepo models.Repo, headRepo models.Repo, pull models.PullRequest, user models.User) {
|
||||
params := []pegomock.Param{baseRepo, headRepo, pull, user}
|
||||
pegomock.GetGenericMockFrom(mock).Invoke("RunAutoplanCommand", params, []reflect.Type{})
|
||||
}
|
||||
|
||||
func (mock *MockCommandRunner) VerifyWasCalledOnce() *VerifierCommandRunner {
|
||||
@@ -42,23 +47,66 @@ type VerifierCommandRunner struct {
|
||||
inOrderContext *pegomock.InOrderContext
|
||||
}
|
||||
|
||||
func (verifier *VerifierCommandRunner) ExecuteCommand(baseRepo models.Repo, headRepo models.Repo, user models.User, pullNum int, cmd *events.Command) *CommandRunner_ExecuteCommand_OngoingVerification {
|
||||
params := []pegomock.Param{baseRepo, headRepo, user, pullNum, cmd}
|
||||
methodInvocations := pegomock.GetGenericMockFrom(verifier.mock).Verify(verifier.inOrderContext, verifier.invocationCountMatcher, "ExecuteCommand", params)
|
||||
return &CommandRunner_ExecuteCommand_OngoingVerification{mock: verifier.mock, methodInvocations: methodInvocations}
|
||||
func (verifier *VerifierCommandRunner) RunCommentCommand(baseRepo models.Repo, maybeHeadRepo *models.Repo, user models.User, pullNum int, cmd *events.CommentCommand) *CommandRunner_RunCommentCommand_OngoingVerification {
|
||||
params := []pegomock.Param{baseRepo, maybeHeadRepo, user, pullNum, cmd}
|
||||
methodInvocations := pegomock.GetGenericMockFrom(verifier.mock).Verify(verifier.inOrderContext, verifier.invocationCountMatcher, "RunCommentCommand", params)
|
||||
return &CommandRunner_RunCommentCommand_OngoingVerification{mock: verifier.mock, methodInvocations: methodInvocations}
|
||||
}
|
||||
|
||||
type CommandRunner_ExecuteCommand_OngoingVerification struct {
|
||||
type CommandRunner_RunCommentCommand_OngoingVerification struct {
|
||||
mock *MockCommandRunner
|
||||
methodInvocations []pegomock.MethodInvocation
|
||||
}
|
||||
|
||||
func (c *CommandRunner_ExecuteCommand_OngoingVerification) GetCapturedArguments() (models.Repo, models.Repo, models.User, int, *events.Command) {
|
||||
baseRepo, headRepo, user, pullNum, cmd := c.GetAllCapturedArguments()
|
||||
return baseRepo[len(baseRepo)-1], headRepo[len(headRepo)-1], user[len(user)-1], pullNum[len(pullNum)-1], cmd[len(cmd)-1]
|
||||
func (c *CommandRunner_RunCommentCommand_OngoingVerification) GetCapturedArguments() (models.Repo, *models.Repo, models.User, int, *events.CommentCommand) {
|
||||
baseRepo, maybeHeadRepo, user, pullNum, cmd := c.GetAllCapturedArguments()
|
||||
return baseRepo[len(baseRepo)-1], maybeHeadRepo[len(maybeHeadRepo)-1], user[len(user)-1], pullNum[len(pullNum)-1], cmd[len(cmd)-1]
|
||||
}
|
||||
|
||||
func (c *CommandRunner_ExecuteCommand_OngoingVerification) GetAllCapturedArguments() (_param0 []models.Repo, _param1 []models.Repo, _param2 []models.User, _param3 []int, _param4 []*events.Command) {
|
||||
func (c *CommandRunner_RunCommentCommand_OngoingVerification) GetAllCapturedArguments() (_param0 []models.Repo, _param1 []*models.Repo, _param2 []models.User, _param3 []int, _param4 []*events.CommentCommand) {
|
||||
params := pegomock.GetGenericMockFrom(c.mock).GetInvocationParams(c.methodInvocations)
|
||||
if len(params) > 0 {
|
||||
_param0 = make([]models.Repo, len(params[0]))
|
||||
for u, param := range params[0] {
|
||||
_param0[u] = param.(models.Repo)
|
||||
}
|
||||
_param1 = make([]*models.Repo, len(params[1]))
|
||||
for u, param := range params[1] {
|
||||
_param1[u] = param.(*models.Repo)
|
||||
}
|
||||
_param2 = make([]models.User, len(params[2]))
|
||||
for u, param := range params[2] {
|
||||
_param2[u] = param.(models.User)
|
||||
}
|
||||
_param3 = make([]int, len(params[3]))
|
||||
for u, param := range params[3] {
|
||||
_param3[u] = param.(int)
|
||||
}
|
||||
_param4 = make([]*events.CommentCommand, len(params[4]))
|
||||
for u, param := range params[4] {
|
||||
_param4[u] = param.(*events.CommentCommand)
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (verifier *VerifierCommandRunner) RunAutoplanCommand(baseRepo models.Repo, headRepo models.Repo, pull models.PullRequest, user models.User) *CommandRunner_RunAutoplanCommand_OngoingVerification {
|
||||
params := []pegomock.Param{baseRepo, headRepo, pull, user}
|
||||
methodInvocations := pegomock.GetGenericMockFrom(verifier.mock).Verify(verifier.inOrderContext, verifier.invocationCountMatcher, "RunAutoplanCommand", params)
|
||||
return &CommandRunner_RunAutoplanCommand_OngoingVerification{mock: verifier.mock, methodInvocations: methodInvocations}
|
||||
}
|
||||
|
||||
type CommandRunner_RunAutoplanCommand_OngoingVerification struct {
|
||||
mock *MockCommandRunner
|
||||
methodInvocations []pegomock.MethodInvocation
|
||||
}
|
||||
|
||||
func (c *CommandRunner_RunAutoplanCommand_OngoingVerification) GetCapturedArguments() (models.Repo, models.Repo, models.PullRequest, models.User) {
|
||||
baseRepo, headRepo, pull, user := c.GetAllCapturedArguments()
|
||||
return baseRepo[len(baseRepo)-1], headRepo[len(headRepo)-1], pull[len(pull)-1], user[len(user)-1]
|
||||
}
|
||||
|
||||
func (c *CommandRunner_RunAutoplanCommand_OngoingVerification) GetAllCapturedArguments() (_param0 []models.Repo, _param1 []models.Repo, _param2 []models.PullRequest, _param3 []models.User) {
|
||||
params := pegomock.GetGenericMockFrom(c.mock).GetInvocationParams(c.methodInvocations)
|
||||
if len(params) > 0 {
|
||||
_param0 = make([]models.Repo, len(params[0]))
|
||||
@@ -69,17 +117,13 @@ func (c *CommandRunner_ExecuteCommand_OngoingVerification) GetAllCapturedArgumen
|
||||
for u, param := range params[1] {
|
||||
_param1[u] = param.(models.Repo)
|
||||
}
|
||||
_param2 = make([]models.User, len(params[2]))
|
||||
_param2 = make([]models.PullRequest, len(params[2]))
|
||||
for u, param := range params[2] {
|
||||
_param2[u] = param.(models.User)
|
||||
_param2[u] = param.(models.PullRequest)
|
||||
}
|
||||
_param3 = make([]int, len(params[3]))
|
||||
_param3 = make([]models.User, len(params[3]))
|
||||
for u, param := range params[3] {
|
||||
_param3[u] = param.(int)
|
||||
}
|
||||
_param4 = make([]*events.Command, len(params[4]))
|
||||
for u, param := range params[4] {
|
||||
_param4[u] = param.(*events.Command)
|
||||
_param3[u] = param.(models.User)
|
||||
}
|
||||
}
|
||||
return
|
||||
|
||||
@@ -20,8 +20,8 @@ func NewMockCommitStatusUpdater() *MockCommitStatusUpdater {
|
||||
return &MockCommitStatusUpdater{fail: pegomock.GlobalFailHandler}
|
||||
}
|
||||
|
||||
func (mock *MockCommitStatusUpdater) Update(repo models.Repo, pull models.PullRequest, status vcs.CommitStatus, cmd *events.Command) error {
|
||||
params := []pegomock.Param{repo, pull, status, cmd}
|
||||
func (mock *MockCommitStatusUpdater) Update(repo models.Repo, pull models.PullRequest, status vcs.CommitStatus, command events.CommandName) error {
|
||||
params := []pegomock.Param{repo, pull, status, command}
|
||||
result := pegomock.GetGenericMockFrom(mock).Invoke("Update", params, []reflect.Type{reflect.TypeOf((*error)(nil)).Elem()})
|
||||
var ret0 error
|
||||
if len(result) != 0 {
|
||||
@@ -32,8 +32,8 @@ func (mock *MockCommitStatusUpdater) Update(repo models.Repo, pull models.PullRe
|
||||
return ret0
|
||||
}
|
||||
|
||||
func (mock *MockCommitStatusUpdater) UpdateProjectResult(ctx *events.CommandContext, res events.CommandResponse) error {
|
||||
params := []pegomock.Param{ctx, res}
|
||||
func (mock *MockCommitStatusUpdater) UpdateProjectResult(ctx *events.CommandContext, commandName events.CommandName, res events.CommandResult) error {
|
||||
params := []pegomock.Param{ctx, commandName, res}
|
||||
result := pegomock.GetGenericMockFrom(mock).Invoke("UpdateProjectResult", params, []reflect.Type{reflect.TypeOf((*error)(nil)).Elem()})
|
||||
var ret0 error
|
||||
if len(result) != 0 {
|
||||
@@ -62,8 +62,8 @@ type VerifierCommitStatusUpdater struct {
|
||||
inOrderContext *pegomock.InOrderContext
|
||||
}
|
||||
|
||||
func (verifier *VerifierCommitStatusUpdater) Update(repo models.Repo, pull models.PullRequest, status vcs.CommitStatus, cmd *events.Command) *CommitStatusUpdater_Update_OngoingVerification {
|
||||
params := []pegomock.Param{repo, pull, status, cmd}
|
||||
func (verifier *VerifierCommitStatusUpdater) Update(repo models.Repo, pull models.PullRequest, status vcs.CommitStatus, command events.CommandName) *CommitStatusUpdater_Update_OngoingVerification {
|
||||
params := []pegomock.Param{repo, pull, status, command}
|
||||
methodInvocations := pegomock.GetGenericMockFrom(verifier.mock).Verify(verifier.inOrderContext, verifier.invocationCountMatcher, "Update", params)
|
||||
return &CommitStatusUpdater_Update_OngoingVerification{mock: verifier.mock, methodInvocations: methodInvocations}
|
||||
}
|
||||
@@ -73,12 +73,12 @@ type CommitStatusUpdater_Update_OngoingVerification struct {
|
||||
methodInvocations []pegomock.MethodInvocation
|
||||
}
|
||||
|
||||
func (c *CommitStatusUpdater_Update_OngoingVerification) GetCapturedArguments() (models.Repo, models.PullRequest, vcs.CommitStatus, *events.Command) {
|
||||
repo, pull, status, cmd := c.GetAllCapturedArguments()
|
||||
return repo[len(repo)-1], pull[len(pull)-1], status[len(status)-1], cmd[len(cmd)-1]
|
||||
func (c *CommitStatusUpdater_Update_OngoingVerification) GetCapturedArguments() (models.Repo, models.PullRequest, vcs.CommitStatus, events.CommandName) {
|
||||
repo, pull, status, command := c.GetAllCapturedArguments()
|
||||
return repo[len(repo)-1], pull[len(pull)-1], status[len(status)-1], command[len(command)-1]
|
||||
}
|
||||
|
||||
func (c *CommitStatusUpdater_Update_OngoingVerification) GetAllCapturedArguments() (_param0 []models.Repo, _param1 []models.PullRequest, _param2 []vcs.CommitStatus, _param3 []*events.Command) {
|
||||
func (c *CommitStatusUpdater_Update_OngoingVerification) GetAllCapturedArguments() (_param0 []models.Repo, _param1 []models.PullRequest, _param2 []vcs.CommitStatus, _param3 []events.CommandName) {
|
||||
params := pegomock.GetGenericMockFrom(c.mock).GetInvocationParams(c.methodInvocations)
|
||||
if len(params) > 0 {
|
||||
_param0 = make([]models.Repo, len(params[0]))
|
||||
@@ -93,16 +93,16 @@ func (c *CommitStatusUpdater_Update_OngoingVerification) GetAllCapturedArguments
|
||||
for u, param := range params[2] {
|
||||
_param2[u] = param.(vcs.CommitStatus)
|
||||
}
|
||||
_param3 = make([]*events.Command, len(params[3]))
|
||||
_param3 = make([]events.CommandName, len(params[3]))
|
||||
for u, param := range params[3] {
|
||||
_param3[u] = param.(*events.Command)
|
||||
_param3[u] = param.(events.CommandName)
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (verifier *VerifierCommitStatusUpdater) UpdateProjectResult(ctx *events.CommandContext, res events.CommandResponse) *CommitStatusUpdater_UpdateProjectResult_OngoingVerification {
|
||||
params := []pegomock.Param{ctx, res}
|
||||
func (verifier *VerifierCommitStatusUpdater) UpdateProjectResult(ctx *events.CommandContext, commandName events.CommandName, res events.CommandResult) *CommitStatusUpdater_UpdateProjectResult_OngoingVerification {
|
||||
params := []pegomock.Param{ctx, commandName, res}
|
||||
methodInvocations := pegomock.GetGenericMockFrom(verifier.mock).Verify(verifier.inOrderContext, verifier.invocationCountMatcher, "UpdateProjectResult", params)
|
||||
return &CommitStatusUpdater_UpdateProjectResult_OngoingVerification{mock: verifier.mock, methodInvocations: methodInvocations}
|
||||
}
|
||||
@@ -112,21 +112,25 @@ type CommitStatusUpdater_UpdateProjectResult_OngoingVerification struct {
|
||||
methodInvocations []pegomock.MethodInvocation
|
||||
}
|
||||
|
||||
func (c *CommitStatusUpdater_UpdateProjectResult_OngoingVerification) GetCapturedArguments() (*events.CommandContext, events.CommandResponse) {
|
||||
ctx, res := c.GetAllCapturedArguments()
|
||||
return ctx[len(ctx)-1], res[len(res)-1]
|
||||
func (c *CommitStatusUpdater_UpdateProjectResult_OngoingVerification) GetCapturedArguments() (*events.CommandContext, events.CommandName, events.CommandResult) {
|
||||
ctx, commandName, res := c.GetAllCapturedArguments()
|
||||
return ctx[len(ctx)-1], commandName[len(commandName)-1], res[len(res)-1]
|
||||
}
|
||||
|
||||
func (c *CommitStatusUpdater_UpdateProjectResult_OngoingVerification) GetAllCapturedArguments() (_param0 []*events.CommandContext, _param1 []events.CommandResponse) {
|
||||
func (c *CommitStatusUpdater_UpdateProjectResult_OngoingVerification) GetAllCapturedArguments() (_param0 []*events.CommandContext, _param1 []events.CommandName, _param2 []events.CommandResult) {
|
||||
params := pegomock.GetGenericMockFrom(c.mock).GetInvocationParams(c.methodInvocations)
|
||||
if len(params) > 0 {
|
||||
_param0 = make([]*events.CommandContext, len(params[0]))
|
||||
for u, param := range params[0] {
|
||||
_param0[u] = param.(*events.CommandContext)
|
||||
}
|
||||
_param1 = make([]events.CommandResponse, len(params[1]))
|
||||
_param1 = make([]events.CommandName, len(params[1]))
|
||||
for u, param := range params[1] {
|
||||
_param1[u] = param.(events.CommandResponse)
|
||||
_param1[u] = param.(events.CommandName)
|
||||
}
|
||||
_param2 = make([]events.CommandResult, len(params[2]))
|
||||
for u, param := range params[2] {
|
||||
_param2[u] = param.(events.CommandResult)
|
||||
}
|
||||
}
|
||||
return
|
||||
|
||||
@@ -44,45 +44,9 @@ func (mock *MockEventParsing) ParseGithubIssueCommentEvent(comment *github.Issue
|
||||
return ret0, ret1, ret2, ret3
|
||||
}
|
||||
|
||||
func (mock *MockEventParsing) ParseGithubPull(pull *github.PullRequest) (models.PullRequest, models.Repo, error) {
|
||||
func (mock *MockEventParsing) ParseGithubPull(pull *github.PullRequest) (models.PullRequest, models.Repo, models.Repo, error) {
|
||||
params := []pegomock.Param{pull}
|
||||
result := pegomock.GetGenericMockFrom(mock).Invoke("ParseGithubPull", params, []reflect.Type{reflect.TypeOf((*models.PullRequest)(nil)).Elem(), reflect.TypeOf((*models.Repo)(nil)).Elem(), reflect.TypeOf((*error)(nil)).Elem()})
|
||||
var ret0 models.PullRequest
|
||||
var ret1 models.Repo
|
||||
var ret2 error
|
||||
if len(result) != 0 {
|
||||
if result[0] != nil {
|
||||
ret0 = result[0].(models.PullRequest)
|
||||
}
|
||||
if result[1] != nil {
|
||||
ret1 = result[1].(models.Repo)
|
||||
}
|
||||
if result[2] != nil {
|
||||
ret2 = result[2].(error)
|
||||
}
|
||||
}
|
||||
return ret0, ret1, ret2
|
||||
}
|
||||
|
||||
func (mock *MockEventParsing) ParseGithubRepo(ghRepo *github.Repository) (models.Repo, error) {
|
||||
params := []pegomock.Param{ghRepo}
|
||||
result := pegomock.GetGenericMockFrom(mock).Invoke("ParseGithubRepo", params, []reflect.Type{reflect.TypeOf((*models.Repo)(nil)).Elem(), reflect.TypeOf((*error)(nil)).Elem()})
|
||||
var ret0 models.Repo
|
||||
var ret1 error
|
||||
if len(result) != 0 {
|
||||
if result[0] != nil {
|
||||
ret0 = result[0].(models.Repo)
|
||||
}
|
||||
if result[1] != nil {
|
||||
ret1 = result[1].(error)
|
||||
}
|
||||
}
|
||||
return ret0, ret1
|
||||
}
|
||||
|
||||
func (mock *MockEventParsing) ParseGitlabMergeEvent(event go_gitlab.MergeEvent) (models.PullRequest, models.Repo, models.Repo, error) {
|
||||
params := []pegomock.Param{event}
|
||||
result := pegomock.GetGenericMockFrom(mock).Invoke("ParseGitlabMergeEvent", params, []reflect.Type{reflect.TypeOf((*models.PullRequest)(nil)).Elem(), reflect.TypeOf((*models.Repo)(nil)).Elem(), reflect.TypeOf((*models.Repo)(nil)).Elem(), reflect.TypeOf((*error)(nil)).Elem()})
|
||||
result := pegomock.GetGenericMockFrom(mock).Invoke("ParseGithubPull", params, []reflect.Type{reflect.TypeOf((*models.PullRequest)(nil)).Elem(), reflect.TypeOf((*models.Repo)(nil)).Elem(), reflect.TypeOf((*models.Repo)(nil)).Elem(), reflect.TypeOf((*error)(nil)).Elem()})
|
||||
var ret0 models.PullRequest
|
||||
var ret1 models.Repo
|
||||
var ret2 models.Repo
|
||||
@@ -104,6 +68,78 @@ func (mock *MockEventParsing) ParseGitlabMergeEvent(event go_gitlab.MergeEvent)
|
||||
return ret0, ret1, ret2, ret3
|
||||
}
|
||||
|
||||
func (mock *MockEventParsing) ParseGithubPullEvent(pullEvent *github.PullRequestEvent) (models.PullRequest, models.Repo, models.Repo, models.User, error) {
|
||||
params := []pegomock.Param{pullEvent}
|
||||
result := pegomock.GetGenericMockFrom(mock).Invoke("ParseGithubPullEvent", params, []reflect.Type{reflect.TypeOf((*models.PullRequest)(nil)).Elem(), reflect.TypeOf((*models.Repo)(nil)).Elem(), reflect.TypeOf((*models.Repo)(nil)).Elem(), reflect.TypeOf((*models.User)(nil)).Elem(), reflect.TypeOf((*error)(nil)).Elem()})
|
||||
var ret0 models.PullRequest
|
||||
var ret1 models.Repo
|
||||
var ret2 models.Repo
|
||||
var ret3 models.User
|
||||
var ret4 error
|
||||
if len(result) != 0 {
|
||||
if result[0] != nil {
|
||||
ret0 = result[0].(models.PullRequest)
|
||||
}
|
||||
if result[1] != nil {
|
||||
ret1 = result[1].(models.Repo)
|
||||
}
|
||||
if result[2] != nil {
|
||||
ret2 = result[2].(models.Repo)
|
||||
}
|
||||
if result[3] != nil {
|
||||
ret3 = result[3].(models.User)
|
||||
}
|
||||
if result[4] != nil {
|
||||
ret4 = result[4].(error)
|
||||
}
|
||||
}
|
||||
return ret0, ret1, ret2, ret3, ret4
|
||||
}
|
||||
|
||||
func (mock *MockEventParsing) ParseGithubRepo(ghRepo *github.Repository) (models.Repo, error) {
|
||||
params := []pegomock.Param{ghRepo}
|
||||
result := pegomock.GetGenericMockFrom(mock).Invoke("ParseGithubRepo", params, []reflect.Type{reflect.TypeOf((*models.Repo)(nil)).Elem(), reflect.TypeOf((*error)(nil)).Elem()})
|
||||
var ret0 models.Repo
|
||||
var ret1 error
|
||||
if len(result) != 0 {
|
||||
if result[0] != nil {
|
||||
ret0 = result[0].(models.Repo)
|
||||
}
|
||||
if result[1] != nil {
|
||||
ret1 = result[1].(error)
|
||||
}
|
||||
}
|
||||
return ret0, ret1
|
||||
}
|
||||
|
||||
func (mock *MockEventParsing) ParseGitlabMergeEvent(event go_gitlab.MergeEvent) (models.PullRequest, models.Repo, models.Repo, models.User, error) {
|
||||
params := []pegomock.Param{event}
|
||||
result := pegomock.GetGenericMockFrom(mock).Invoke("ParseGitlabMergeEvent", params, []reflect.Type{reflect.TypeOf((*models.PullRequest)(nil)).Elem(), reflect.TypeOf((*models.Repo)(nil)).Elem(), reflect.TypeOf((*models.Repo)(nil)).Elem(), reflect.TypeOf((*models.User)(nil)).Elem(), reflect.TypeOf((*error)(nil)).Elem()})
|
||||
var ret0 models.PullRequest
|
||||
var ret1 models.Repo
|
||||
var ret2 models.Repo
|
||||
var ret3 models.User
|
||||
var ret4 error
|
||||
if len(result) != 0 {
|
||||
if result[0] != nil {
|
||||
ret0 = result[0].(models.PullRequest)
|
||||
}
|
||||
if result[1] != nil {
|
||||
ret1 = result[1].(models.Repo)
|
||||
}
|
||||
if result[2] != nil {
|
||||
ret2 = result[2].(models.Repo)
|
||||
}
|
||||
if result[3] != nil {
|
||||
ret3 = result[3].(models.User)
|
||||
}
|
||||
if result[4] != nil {
|
||||
ret4 = result[4].(error)
|
||||
}
|
||||
}
|
||||
return ret0, ret1, ret2, ret3, ret4
|
||||
}
|
||||
|
||||
func (mock *MockEventParsing) ParseGitlabMergeCommentEvent(event go_gitlab.MergeCommentEvent) (models.Repo, models.Repo, models.User, error) {
|
||||
params := []pegomock.Param{event}
|
||||
result := pegomock.GetGenericMockFrom(mock).Invoke("ParseGitlabMergeCommentEvent", params, []reflect.Type{reflect.TypeOf((*models.Repo)(nil)).Elem(), reflect.TypeOf((*models.Repo)(nil)).Elem(), reflect.TypeOf((*models.User)(nil)).Elem(), reflect.TypeOf((*error)(nil)).Elem()})
|
||||
@@ -212,6 +248,33 @@ func (c *EventParsing_ParseGithubPull_OngoingVerification) GetAllCapturedArgumen
|
||||
return
|
||||
}
|
||||
|
||||
func (verifier *VerifierEventParsing) ParseGithubPullEvent(pullEvent *github.PullRequestEvent) *EventParsing_ParseGithubPullEvent_OngoingVerification {
|
||||
params := []pegomock.Param{pullEvent}
|
||||
methodInvocations := pegomock.GetGenericMockFrom(verifier.mock).Verify(verifier.inOrderContext, verifier.invocationCountMatcher, "ParseGithubPullEvent", params)
|
||||
return &EventParsing_ParseGithubPullEvent_OngoingVerification{mock: verifier.mock, methodInvocations: methodInvocations}
|
||||
}
|
||||
|
||||
type EventParsing_ParseGithubPullEvent_OngoingVerification struct {
|
||||
mock *MockEventParsing
|
||||
methodInvocations []pegomock.MethodInvocation
|
||||
}
|
||||
|
||||
func (c *EventParsing_ParseGithubPullEvent_OngoingVerification) GetCapturedArguments() *github.PullRequestEvent {
|
||||
pullEvent := c.GetAllCapturedArguments()
|
||||
return pullEvent[len(pullEvent)-1]
|
||||
}
|
||||
|
||||
func (c *EventParsing_ParseGithubPullEvent_OngoingVerification) GetAllCapturedArguments() (_param0 []*github.PullRequestEvent) {
|
||||
params := pegomock.GetGenericMockFrom(c.mock).GetInvocationParams(c.methodInvocations)
|
||||
if len(params) > 0 {
|
||||
_param0 = make([]*github.PullRequestEvent, len(params[0]))
|
||||
for u, param := range params[0] {
|
||||
_param0[u] = param.(*github.PullRequestEvent)
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (verifier *VerifierEventParsing) ParseGithubRepo(ghRepo *github.Repository) *EventParsing_ParseGithubRepo_OngoingVerification {
|
||||
params := []pegomock.Param{ghRepo}
|
||||
methodInvocations := pegomock.GetGenericMockFrom(verifier.mock).Verify(verifier.inOrderContext, verifier.invocationCountMatcher, "ParseGithubRepo", params)
|
||||
|
||||
@@ -18,13 +18,13 @@ func NewMockExecutor() *MockExecutor {
|
||||
return &MockExecutor{fail: pegomock.GlobalFailHandler}
|
||||
}
|
||||
|
||||
func (mock *MockExecutor) Execute(ctx *events.CommandContext) events.CommandResponse {
|
||||
func (mock *MockExecutor) Execute(ctx *events.CommandContext) events.CommandResult {
|
||||
params := []pegomock.Param{ctx}
|
||||
result := pegomock.GetGenericMockFrom(mock).Invoke("Execute", params, []reflect.Type{reflect.TypeOf((*events.CommandResponse)(nil)).Elem()})
|
||||
var ret0 events.CommandResponse
|
||||
result := pegomock.GetGenericMockFrom(mock).Invoke("Execute", params, []reflect.Type{reflect.TypeOf((*events.CommandResult)(nil)).Elem()})
|
||||
var ret0 events.CommandResult
|
||||
if len(result) != 0 {
|
||||
if result[0] != nil {
|
||||
ret0 = result[0].(events.CommandResponse)
|
||||
ret0 = result[0].(events.CommandResult)
|
||||
}
|
||||
}
|
||||
return ret0
|
||||
|
||||
175
server/events/mocks/mock_project_command_builder.go
Normal file
175
server/events/mocks/mock_project_command_builder.go
Normal file
@@ -0,0 +1,175 @@
|
||||
// Automatically generated by pegomock. DO NOT EDIT!
|
||||
// Source: github.com/runatlantis/atlantis/server/events (interfaces: ProjectCommandBuilder)
|
||||
|
||||
package mocks
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
|
||||
pegomock "github.com/petergtz/pegomock"
|
||||
events "github.com/runatlantis/atlantis/server/events"
|
||||
models "github.com/runatlantis/atlantis/server/events/models"
|
||||
)
|
||||
|
||||
type MockProjectCommandBuilder struct {
|
||||
fail func(message string, callerSkip ...int)
|
||||
}
|
||||
|
||||
func NewMockProjectCommandBuilder() *MockProjectCommandBuilder {
|
||||
return &MockProjectCommandBuilder{fail: pegomock.GlobalFailHandler}
|
||||
}
|
||||
|
||||
func (mock *MockProjectCommandBuilder) BuildAutoplanCommands(ctx *events.CommandContext) ([]models.ProjectCommandContext, error) {
|
||||
params := []pegomock.Param{ctx}
|
||||
result := pegomock.GetGenericMockFrom(mock).Invoke("BuildAutoplanCommands", params, []reflect.Type{reflect.TypeOf((*[]models.ProjectCommandContext)(nil)).Elem(), reflect.TypeOf((*error)(nil)).Elem()})
|
||||
var ret0 []models.ProjectCommandContext
|
||||
var ret1 error
|
||||
if len(result) != 0 {
|
||||
if result[0] != nil {
|
||||
ret0 = result[0].([]models.ProjectCommandContext)
|
||||
}
|
||||
if result[1] != nil {
|
||||
ret1 = result[1].(error)
|
||||
}
|
||||
}
|
||||
return ret0, ret1
|
||||
}
|
||||
|
||||
func (mock *MockProjectCommandBuilder) BuildPlanCommand(ctx *events.CommandContext, commentCommand *events.CommentCommand) (models.ProjectCommandContext, error) {
|
||||
params := []pegomock.Param{ctx, commentCommand}
|
||||
result := pegomock.GetGenericMockFrom(mock).Invoke("BuildPlanCommand", params, []reflect.Type{reflect.TypeOf((*models.ProjectCommandContext)(nil)).Elem(), reflect.TypeOf((*error)(nil)).Elem()})
|
||||
var ret0 models.ProjectCommandContext
|
||||
var ret1 error
|
||||
if len(result) != 0 {
|
||||
if result[0] != nil {
|
||||
ret0 = result[0].(models.ProjectCommandContext)
|
||||
}
|
||||
if result[1] != nil {
|
||||
ret1 = result[1].(error)
|
||||
}
|
||||
}
|
||||
return ret0, ret1
|
||||
}
|
||||
|
||||
func (mock *MockProjectCommandBuilder) BuildApplyCommand(ctx *events.CommandContext, commentCommand *events.CommentCommand) (models.ProjectCommandContext, error) {
|
||||
params := []pegomock.Param{ctx, commentCommand}
|
||||
result := pegomock.GetGenericMockFrom(mock).Invoke("BuildApplyCommand", params, []reflect.Type{reflect.TypeOf((*models.ProjectCommandContext)(nil)).Elem(), reflect.TypeOf((*error)(nil)).Elem()})
|
||||
var ret0 models.ProjectCommandContext
|
||||
var ret1 error
|
||||
if len(result) != 0 {
|
||||
if result[0] != nil {
|
||||
ret0 = result[0].(models.ProjectCommandContext)
|
||||
}
|
||||
if result[1] != nil {
|
||||
ret1 = result[1].(error)
|
||||
}
|
||||
}
|
||||
return ret0, ret1
|
||||
}
|
||||
|
||||
func (mock *MockProjectCommandBuilder) VerifyWasCalledOnce() *VerifierProjectCommandBuilder {
|
||||
return &VerifierProjectCommandBuilder{mock, pegomock.Times(1), nil}
|
||||
}
|
||||
|
||||
func (mock *MockProjectCommandBuilder) VerifyWasCalled(invocationCountMatcher pegomock.Matcher) *VerifierProjectCommandBuilder {
|
||||
return &VerifierProjectCommandBuilder{mock, invocationCountMatcher, nil}
|
||||
}
|
||||
|
||||
func (mock *MockProjectCommandBuilder) VerifyWasCalledInOrder(invocationCountMatcher pegomock.Matcher, inOrderContext *pegomock.InOrderContext) *VerifierProjectCommandBuilder {
|
||||
return &VerifierProjectCommandBuilder{mock, invocationCountMatcher, inOrderContext}
|
||||
}
|
||||
|
||||
type VerifierProjectCommandBuilder struct {
|
||||
mock *MockProjectCommandBuilder
|
||||
invocationCountMatcher pegomock.Matcher
|
||||
inOrderContext *pegomock.InOrderContext
|
||||
}
|
||||
|
||||
func (verifier *VerifierProjectCommandBuilder) BuildAutoplanCommands(ctx *events.CommandContext) *ProjectCommandBuilder_BuildAutoplanCommands_OngoingVerification {
|
||||
params := []pegomock.Param{ctx}
|
||||
methodInvocations := pegomock.GetGenericMockFrom(verifier.mock).Verify(verifier.inOrderContext, verifier.invocationCountMatcher, "BuildAutoplanCommands", params)
|
||||
return &ProjectCommandBuilder_BuildAutoplanCommands_OngoingVerification{mock: verifier.mock, methodInvocations: methodInvocations}
|
||||
}
|
||||
|
||||
type ProjectCommandBuilder_BuildAutoplanCommands_OngoingVerification struct {
|
||||
mock *MockProjectCommandBuilder
|
||||
methodInvocations []pegomock.MethodInvocation
|
||||
}
|
||||
|
||||
func (c *ProjectCommandBuilder_BuildAutoplanCommands_OngoingVerification) GetCapturedArguments() *events.CommandContext {
|
||||
ctx := c.GetAllCapturedArguments()
|
||||
return ctx[len(ctx)-1]
|
||||
}
|
||||
|
||||
func (c *ProjectCommandBuilder_BuildAutoplanCommands_OngoingVerification) GetAllCapturedArguments() (_param0 []*events.CommandContext) {
|
||||
params := pegomock.GetGenericMockFrom(c.mock).GetInvocationParams(c.methodInvocations)
|
||||
if len(params) > 0 {
|
||||
_param0 = make([]*events.CommandContext, len(params[0]))
|
||||
for u, param := range params[0] {
|
||||
_param0[u] = param.(*events.CommandContext)
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (verifier *VerifierProjectCommandBuilder) BuildPlanCommand(ctx *events.CommandContext, commentCommand *events.CommentCommand) *ProjectCommandBuilder_BuildPlanCommand_OngoingVerification {
|
||||
params := []pegomock.Param{ctx, commentCommand}
|
||||
methodInvocations := pegomock.GetGenericMockFrom(verifier.mock).Verify(verifier.inOrderContext, verifier.invocationCountMatcher, "BuildPlanCommand", params)
|
||||
return &ProjectCommandBuilder_BuildPlanCommand_OngoingVerification{mock: verifier.mock, methodInvocations: methodInvocations}
|
||||
}
|
||||
|
||||
type ProjectCommandBuilder_BuildPlanCommand_OngoingVerification struct {
|
||||
mock *MockProjectCommandBuilder
|
||||
methodInvocations []pegomock.MethodInvocation
|
||||
}
|
||||
|
||||
func (c *ProjectCommandBuilder_BuildPlanCommand_OngoingVerification) GetCapturedArguments() (*events.CommandContext, *events.CommentCommand) {
|
||||
ctx, commentCommand := c.GetAllCapturedArguments()
|
||||
return ctx[len(ctx)-1], commentCommand[len(commentCommand)-1]
|
||||
}
|
||||
|
||||
func (c *ProjectCommandBuilder_BuildPlanCommand_OngoingVerification) GetAllCapturedArguments() (_param0 []*events.CommandContext, _param1 []*events.CommentCommand) {
|
||||
params := pegomock.GetGenericMockFrom(c.mock).GetInvocationParams(c.methodInvocations)
|
||||
if len(params) > 0 {
|
||||
_param0 = make([]*events.CommandContext, len(params[0]))
|
||||
for u, param := range params[0] {
|
||||
_param0[u] = param.(*events.CommandContext)
|
||||
}
|
||||
_param1 = make([]*events.CommentCommand, len(params[1]))
|
||||
for u, param := range params[1] {
|
||||
_param1[u] = param.(*events.CommentCommand)
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (verifier *VerifierProjectCommandBuilder) BuildApplyCommand(ctx *events.CommandContext, commentCommand *events.CommentCommand) *ProjectCommandBuilder_BuildApplyCommand_OngoingVerification {
|
||||
params := []pegomock.Param{ctx, commentCommand}
|
||||
methodInvocations := pegomock.GetGenericMockFrom(verifier.mock).Verify(verifier.inOrderContext, verifier.invocationCountMatcher, "BuildApplyCommand", params)
|
||||
return &ProjectCommandBuilder_BuildApplyCommand_OngoingVerification{mock: verifier.mock, methodInvocations: methodInvocations}
|
||||
}
|
||||
|
||||
type ProjectCommandBuilder_BuildApplyCommand_OngoingVerification struct {
|
||||
mock *MockProjectCommandBuilder
|
||||
methodInvocations []pegomock.MethodInvocation
|
||||
}
|
||||
|
||||
func (c *ProjectCommandBuilder_BuildApplyCommand_OngoingVerification) GetCapturedArguments() (*events.CommandContext, *events.CommentCommand) {
|
||||
ctx, commentCommand := c.GetAllCapturedArguments()
|
||||
return ctx[len(ctx)-1], commentCommand[len(commentCommand)-1]
|
||||
}
|
||||
|
||||
func (c *ProjectCommandBuilder_BuildApplyCommand_OngoingVerification) GetAllCapturedArguments() (_param0 []*events.CommandContext, _param1 []*events.CommentCommand) {
|
||||
params := pegomock.GetGenericMockFrom(c.mock).GetInvocationParams(c.methodInvocations)
|
||||
if len(params) > 0 {
|
||||
_param0 = make([]*events.CommandContext, len(params[0]))
|
||||
for u, param := range params[0] {
|
||||
_param0[u] = param.(*events.CommandContext)
|
||||
}
|
||||
_param1 = make([]*events.CommentCommand, len(params[1]))
|
||||
for u, param := range params[1] {
|
||||
_param1[u] = param.(*events.CommentCommand)
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
@@ -1,154 +0,0 @@
|
||||
// Automatically generated by pegomock. DO NOT EDIT!
|
||||
// Source: github.com/runatlantis/atlantis/server/events (interfaces: PullRequestOperator)
|
||||
|
||||
package mocks
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
|
||||
pegomock "github.com/petergtz/pegomock"
|
||||
events "github.com/runatlantis/atlantis/server/events"
|
||||
)
|
||||
|
||||
type MockPullRequestOperator struct {
|
||||
fail func(message string, callerSkip ...int)
|
||||
}
|
||||
|
||||
func NewMockPullRequestOperator() *MockPullRequestOperator {
|
||||
return &MockPullRequestOperator{fail: pegomock.GlobalFailHandler}
|
||||
}
|
||||
|
||||
func (mock *MockPullRequestOperator) Autoplan(ctx *events.CommandContext) events.CommandResponse {
|
||||
params := []pegomock.Param{ctx}
|
||||
result := pegomock.GetGenericMockFrom(mock).Invoke("Autoplan", params, []reflect.Type{reflect.TypeOf((*events.CommandResponse)(nil)).Elem()})
|
||||
var ret0 events.CommandResponse
|
||||
if len(result) != 0 {
|
||||
if result[0] != nil {
|
||||
ret0 = result[0].(events.CommandResponse)
|
||||
}
|
||||
}
|
||||
return ret0
|
||||
}
|
||||
|
||||
func (mock *MockPullRequestOperator) PlanViaComment(ctx *events.CommandContext) events.CommandResponse {
|
||||
params := []pegomock.Param{ctx}
|
||||
result := pegomock.GetGenericMockFrom(mock).Invoke("PlanViaComment", params, []reflect.Type{reflect.TypeOf((*events.CommandResponse)(nil)).Elem()})
|
||||
var ret0 events.CommandResponse
|
||||
if len(result) != 0 {
|
||||
if result[0] != nil {
|
||||
ret0 = result[0].(events.CommandResponse)
|
||||
}
|
||||
}
|
||||
return ret0
|
||||
}
|
||||
|
||||
func (mock *MockPullRequestOperator) ApplyViaComment(ctx *events.CommandContext) events.CommandResponse {
|
||||
params := []pegomock.Param{ctx}
|
||||
result := pegomock.GetGenericMockFrom(mock).Invoke("ApplyViaComment", params, []reflect.Type{reflect.TypeOf((*events.CommandResponse)(nil)).Elem()})
|
||||
var ret0 events.CommandResponse
|
||||
if len(result) != 0 {
|
||||
if result[0] != nil {
|
||||
ret0 = result[0].(events.CommandResponse)
|
||||
}
|
||||
}
|
||||
return ret0
|
||||
}
|
||||
|
||||
func (mock *MockPullRequestOperator) VerifyWasCalledOnce() *VerifierPullRequestOperator {
|
||||
return &VerifierPullRequestOperator{mock, pegomock.Times(1), nil}
|
||||
}
|
||||
|
||||
func (mock *MockPullRequestOperator) VerifyWasCalled(invocationCountMatcher pegomock.Matcher) *VerifierPullRequestOperator {
|
||||
return &VerifierPullRequestOperator{mock, invocationCountMatcher, nil}
|
||||
}
|
||||
|
||||
func (mock *MockPullRequestOperator) VerifyWasCalledInOrder(invocationCountMatcher pegomock.Matcher, inOrderContext *pegomock.InOrderContext) *VerifierPullRequestOperator {
|
||||
return &VerifierPullRequestOperator{mock, invocationCountMatcher, inOrderContext}
|
||||
}
|
||||
|
||||
type VerifierPullRequestOperator struct {
|
||||
mock *MockPullRequestOperator
|
||||
invocationCountMatcher pegomock.Matcher
|
||||
inOrderContext *pegomock.InOrderContext
|
||||
}
|
||||
|
||||
func (verifier *VerifierPullRequestOperator) Autoplan(ctx *events.CommandContext) *PullRequestOperator_Autoplan_OngoingVerification {
|
||||
params := []pegomock.Param{ctx}
|
||||
methodInvocations := pegomock.GetGenericMockFrom(verifier.mock).Verify(verifier.inOrderContext, verifier.invocationCountMatcher, "Autoplan", params)
|
||||
return &PullRequestOperator_Autoplan_OngoingVerification{mock: verifier.mock, methodInvocations: methodInvocations}
|
||||
}
|
||||
|
||||
type PullRequestOperator_Autoplan_OngoingVerification struct {
|
||||
mock *MockPullRequestOperator
|
||||
methodInvocations []pegomock.MethodInvocation
|
||||
}
|
||||
|
||||
func (c *PullRequestOperator_Autoplan_OngoingVerification) GetCapturedArguments() *events.CommandContext {
|
||||
ctx := c.GetAllCapturedArguments()
|
||||
return ctx[len(ctx)-1]
|
||||
}
|
||||
|
||||
func (c *PullRequestOperator_Autoplan_OngoingVerification) GetAllCapturedArguments() (_param0 []*events.CommandContext) {
|
||||
params := pegomock.GetGenericMockFrom(c.mock).GetInvocationParams(c.methodInvocations)
|
||||
if len(params) > 0 {
|
||||
_param0 = make([]*events.CommandContext, len(params[0]))
|
||||
for u, param := range params[0] {
|
||||
_param0[u] = param.(*events.CommandContext)
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (verifier *VerifierPullRequestOperator) PlanViaComment(ctx *events.CommandContext) *PullRequestOperator_PlanViaComment_OngoingVerification {
|
||||
params := []pegomock.Param{ctx}
|
||||
methodInvocations := pegomock.GetGenericMockFrom(verifier.mock).Verify(verifier.inOrderContext, verifier.invocationCountMatcher, "PlanViaComment", params)
|
||||
return &PullRequestOperator_PlanViaComment_OngoingVerification{mock: verifier.mock, methodInvocations: methodInvocations}
|
||||
}
|
||||
|
||||
type PullRequestOperator_PlanViaComment_OngoingVerification struct {
|
||||
mock *MockPullRequestOperator
|
||||
methodInvocations []pegomock.MethodInvocation
|
||||
}
|
||||
|
||||
func (c *PullRequestOperator_PlanViaComment_OngoingVerification) GetCapturedArguments() *events.CommandContext {
|
||||
ctx := c.GetAllCapturedArguments()
|
||||
return ctx[len(ctx)-1]
|
||||
}
|
||||
|
||||
func (c *PullRequestOperator_PlanViaComment_OngoingVerification) GetAllCapturedArguments() (_param0 []*events.CommandContext) {
|
||||
params := pegomock.GetGenericMockFrom(c.mock).GetInvocationParams(c.methodInvocations)
|
||||
if len(params) > 0 {
|
||||
_param0 = make([]*events.CommandContext, len(params[0]))
|
||||
for u, param := range params[0] {
|
||||
_param0[u] = param.(*events.CommandContext)
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (verifier *VerifierPullRequestOperator) ApplyViaComment(ctx *events.CommandContext) *PullRequestOperator_ApplyViaComment_OngoingVerification {
|
||||
params := []pegomock.Param{ctx}
|
||||
methodInvocations := pegomock.GetGenericMockFrom(verifier.mock).Verify(verifier.inOrderContext, verifier.invocationCountMatcher, "ApplyViaComment", params)
|
||||
return &PullRequestOperator_ApplyViaComment_OngoingVerification{mock: verifier.mock, methodInvocations: methodInvocations}
|
||||
}
|
||||
|
||||
type PullRequestOperator_ApplyViaComment_OngoingVerification struct {
|
||||
mock *MockPullRequestOperator
|
||||
methodInvocations []pegomock.MethodInvocation
|
||||
}
|
||||
|
||||
func (c *PullRequestOperator_ApplyViaComment_OngoingVerification) GetCapturedArguments() *events.CommandContext {
|
||||
ctx := c.GetAllCapturedArguments()
|
||||
return ctx[len(ctx)-1]
|
||||
}
|
||||
|
||||
func (c *PullRequestOperator_ApplyViaComment_OngoingVerification) GetAllCapturedArguments() (_param0 []*events.CommandContext) {
|
||||
params := pegomock.GetGenericMockFrom(c.mock).GetInvocationParams(c.methodInvocations)
|
||||
if len(params) > 0 {
|
||||
_param0 = make([]*events.CommandContext, len(params[0]))
|
||||
for u, param := range params[0] {
|
||||
_param0[u] = param.(*events.CommandContext)
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
@@ -92,3 +92,10 @@ func TestNewRepo_HTTPSAuth(t *testing.T) {
|
||||
Name: "repo",
|
||||
}, repo)
|
||||
}
|
||||
|
||||
func TestProject_String(t *testing.T) {
|
||||
Equals(t, "repofullname=owner/repo path=my/path", (models.Project{
|
||||
RepoFullName: "owner/repo",
|
||||
Path: "my/path",
|
||||
}).String())
|
||||
}
|
||||
|
||||
@@ -15,41 +15,45 @@ import (
|
||||
"github.com/runatlantis/atlantis/server/logging"
|
||||
)
|
||||
|
||||
//go:generate pegomock generate -m --use-experimental-model-gen --package mocks -o mocks/mock_pull_request_operator.go PullRequestOperator
|
||||
//go:generate pegomock generate -m --use-experimental-model-gen --package mocks -o mocks/mock_project_command_builder.go ProjectCommandBuilder
|
||||
|
||||
type PullRequestOperator interface {
|
||||
Autoplan(ctx *CommandContext) CommandResponse
|
||||
PlanViaComment(ctx *CommandContext) CommandResponse
|
||||
ApplyViaComment(ctx *CommandContext) CommandResponse
|
||||
type ProjectCommandBuilder interface {
|
||||
BuildAutoplanCommands(ctx *CommandContext) ([]models.ProjectCommandContext, error)
|
||||
BuildPlanCommand(ctx *CommandContext, commentCommand *CommentCommand) (models.ProjectCommandContext, error)
|
||||
BuildApplyCommand(ctx *CommandContext, commentCommand *CommentCommand) (models.ProjectCommandContext, error)
|
||||
}
|
||||
|
||||
type DefaultPullRequestOperator struct {
|
||||
TerraformExecutor TerraformExec
|
||||
DefaultTFVersion *version.Version
|
||||
ParserValidator *yaml.ParserValidator
|
||||
ProjectFinder ProjectFinder
|
||||
VCSClient vcs.ClientProxy
|
||||
Workspace AtlantisWorkspace
|
||||
ProjectOperator ProjectOperator
|
||||
type DefaultProjectCommandBuilder struct {
|
||||
ParserValidator *yaml.ParserValidator
|
||||
ProjectFinder ProjectFinder
|
||||
VCSClient vcs.ClientProxy
|
||||
Workspace AtlantisWorkspace
|
||||
AtlantisWorkspaceLocker AtlantisWorkspaceLocker
|
||||
}
|
||||
|
||||
type TerraformExec interface {
|
||||
RunCommandWithVersion(log *logging.SimpleLogger, path string, args []string, v *version.Version, workspace string) (string, error)
|
||||
}
|
||||
|
||||
func (p *DefaultPullRequestOperator) Autoplan(ctx *CommandContext) CommandResponse {
|
||||
// check out repo to parse atlantis.yaml
|
||||
// this will check out the repo to a * dir
|
||||
repoDir, err := p.Workspace.Clone(ctx.Log, ctx.BaseRepo, ctx.HeadRepo, ctx.Pull, ctx.Command.Workspace)
|
||||
func (p *DefaultProjectCommandBuilder) BuildAutoplanCommands(ctx *CommandContext) ([]models.ProjectCommandContext, error) {
|
||||
// Need to lock the workspace we're about to clone to.
|
||||
workspace := DefaultWorkspace
|
||||
unlockFn, err := p.AtlantisWorkspaceLocker.TryLock2(ctx.BaseRepo.FullName, workspace, ctx.Pull.Num)
|
||||
if err != nil {
|
||||
return CommandResponse{Error: err}
|
||||
return nil, err
|
||||
}
|
||||
defer unlockFn()
|
||||
|
||||
repoDir, err := p.Workspace.Clone(ctx.Log, ctx.BaseRepo, ctx.HeadRepo, ctx.Pull, workspace)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Parse config file if it exists.
|
||||
ctx.Log.Debug("parsing config file")
|
||||
config, err := p.ParserValidator.ReadConfig(repoDir)
|
||||
if err != nil && !os.IsNotExist(err) {
|
||||
return CommandResponse{Error: err}
|
||||
return nil, err
|
||||
}
|
||||
noAtlantisYAML := os.IsNotExist(err)
|
||||
if noAtlantisYAML {
|
||||
@@ -61,11 +65,11 @@ func (p *DefaultPullRequestOperator) Autoplan(ctx *CommandContext) CommandRespon
|
||||
// We'll need the list of modified files.
|
||||
modifiedFiles, err := p.VCSClient.GetModifiedFiles(ctx.BaseRepo, ctx.Pull)
|
||||
if err != nil {
|
||||
return CommandResponse{Error: err}
|
||||
return nil, err
|
||||
}
|
||||
ctx.Log.Debug("%d files were modified in this pull request", len(modifiedFiles))
|
||||
|
||||
// Prepare the project contexts so the ProjectOperator can execute.
|
||||
// Prepare the project contexts so the ProjectCommandRunner can execute.
|
||||
var projCtxs []models.ProjectCommandContext
|
||||
|
||||
// If there is no config file, then we try to plan for each project that
|
||||
@@ -93,7 +97,7 @@ func (p *DefaultPullRequestOperator) Autoplan(ctx *CommandContext) CommandRespon
|
||||
// in the config file.
|
||||
matchingProjects, err := p.matchingProjects(ctx.Log, modifiedFiles, config)
|
||||
if err != nil {
|
||||
return CommandResponse{Error: err}
|
||||
return nil, err
|
||||
}
|
||||
ctx.Log.Info("%d projects are to be autoplanned based on their when_modified config", len(matchingProjects))
|
||||
|
||||
@@ -120,22 +124,21 @@ func (p *DefaultPullRequestOperator) Autoplan(ctx *CommandContext) CommandRespon
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// Execute the operations.
|
||||
var results []ProjectResult
|
||||
for _, pCtx := range projCtxs {
|
||||
res := p.ProjectOperator.Plan(pCtx, nil)
|
||||
res.Path = pCtx.RepoRelPath
|
||||
res.Workspace = pCtx.Workspace
|
||||
results = append(results, res)
|
||||
}
|
||||
return CommandResponse{ProjectResults: results}
|
||||
return projCtxs, nil
|
||||
}
|
||||
|
||||
func (p *DefaultPullRequestOperator) PlanViaComment(ctx *CommandContext) CommandResponse {
|
||||
repoDir, err := p.Workspace.Clone(ctx.Log, ctx.BaseRepo, ctx.HeadRepo, ctx.Pull, ctx.Command.Workspace)
|
||||
func (p *DefaultProjectCommandBuilder) BuildPlanCommand(ctx *CommandContext, cmd *CommentCommand) (models.ProjectCommandContext, error) {
|
||||
var projCtx models.ProjectCommandContext
|
||||
|
||||
unlockFn, err := p.AtlantisWorkspaceLocker.TryLock2(ctx.BaseRepo.FullName, cmd.Workspace, ctx.Pull.Num)
|
||||
if err != nil {
|
||||
return CommandResponse{Error: err}
|
||||
return projCtx, err
|
||||
}
|
||||
defer unlockFn()
|
||||
|
||||
repoDir, err := p.Workspace.Clone(ctx.Log, ctx.BaseRepo, ctx.HeadRepo, ctx.Pull, cmd.Workspace)
|
||||
if err != nil {
|
||||
return projCtx, err
|
||||
}
|
||||
|
||||
var projCfg *valid.Project
|
||||
@@ -144,55 +147,49 @@ func (p *DefaultPullRequestOperator) PlanViaComment(ctx *CommandContext) Command
|
||||
// Parse config file if it exists.
|
||||
config, err := p.ParserValidator.ReadConfig(repoDir)
|
||||
if err != nil && !os.IsNotExist(err) {
|
||||
return CommandResponse{Error: err}
|
||||
return projCtx, err
|
||||
}
|
||||
hasAtlantisYAML := !os.IsNotExist(err)
|
||||
if hasAtlantisYAML {
|
||||
// If they've specified a project by name we look it up. Otherwise we
|
||||
// use the dir and workspace.
|
||||
if ctx.Command.ProjectName != "" {
|
||||
projCfg = config.FindProjectByName(ctx.Command.ProjectName)
|
||||
if cmd.ProjectName != "" {
|
||||
projCfg = config.FindProjectByName(cmd.ProjectName)
|
||||
if projCfg == nil {
|
||||
return CommandResponse{Error: fmt.Errorf("no project with name %q configured", ctx.Command.ProjectName)}
|
||||
return projCtx, fmt.Errorf("no project with name %q configured", cmd.ProjectName)
|
||||
}
|
||||
} else {
|
||||
projCfg = config.FindProject(ctx.Command.Dir, ctx.Command.Workspace)
|
||||
projCfg = config.FindProject(cmd.Dir, cmd.Workspace)
|
||||
}
|
||||
globalCfg = &config
|
||||
}
|
||||
|
||||
if ctx.Command.ProjectName != "" && !hasAtlantisYAML {
|
||||
return CommandResponse{Error: fmt.Errorf("cannot specify a project name unless an %s file exists to configure projects", yaml.AtlantisYAMLFilename)}
|
||||
if cmd.ProjectName != "" && !hasAtlantisYAML {
|
||||
return projCtx, fmt.Errorf("cannot specify a project name unless an %s file exists to configure projects", yaml.AtlantisYAMLFilename)
|
||||
}
|
||||
|
||||
projCtx := models.ProjectCommandContext{
|
||||
projCtx = models.ProjectCommandContext{
|
||||
BaseRepo: ctx.BaseRepo,
|
||||
HeadRepo: ctx.HeadRepo,
|
||||
Pull: ctx.Pull,
|
||||
User: ctx.User,
|
||||
Log: ctx.Log,
|
||||
CommentArgs: ctx.Command.Flags,
|
||||
Workspace: ctx.Command.Workspace,
|
||||
RepoRelPath: ctx.Command.Dir,
|
||||
ProjectName: ctx.Command.ProjectName,
|
||||
CommentArgs: cmd.Flags,
|
||||
Workspace: cmd.Workspace,
|
||||
RepoRelPath: cmd.Dir,
|
||||
ProjectName: cmd.ProjectName,
|
||||
ProjectConfig: projCfg,
|
||||
GlobalConfig: globalCfg,
|
||||
}
|
||||
projAbsPath := filepath.Join(repoDir, ctx.Command.Dir)
|
||||
res := p.ProjectOperator.Plan(projCtx, &projAbsPath)
|
||||
res.Workspace = projCtx.Workspace
|
||||
res.Path = projCtx.RepoRelPath
|
||||
return CommandResponse{
|
||||
ProjectResults: []ProjectResult{
|
||||
res,
|
||||
},
|
||||
}
|
||||
return projCtx, nil
|
||||
}
|
||||
|
||||
func (p *DefaultPullRequestOperator) ApplyViaComment(ctx *CommandContext) CommandResponse {
|
||||
repoDir, err := p.Workspace.GetWorkspace(ctx.BaseRepo, ctx.Pull, ctx.Command.Workspace)
|
||||
func (p *DefaultProjectCommandBuilder) BuildApplyCommand(ctx *CommandContext, cmd *CommentCommand) (models.ProjectCommandContext, error) {
|
||||
var projCtx models.ProjectCommandContext
|
||||
|
||||
repoDir, err := p.Workspace.GetWorkspace(ctx.BaseRepo, ctx.Pull, cmd.Workspace)
|
||||
if err != nil {
|
||||
return CommandResponse{Failure: "No workspace found. Did you run plan?"}
|
||||
return projCtx, err
|
||||
}
|
||||
|
||||
// todo: can deduplicate this between PlanViaComment
|
||||
@@ -202,53 +199,46 @@ func (p *DefaultPullRequestOperator) ApplyViaComment(ctx *CommandContext) Comman
|
||||
// Parse config file if it exists.
|
||||
config, err := p.ParserValidator.ReadConfig(repoDir)
|
||||
if err != nil && !os.IsNotExist(err) {
|
||||
return CommandResponse{Error: err}
|
||||
return projCtx, err
|
||||
}
|
||||
hasAtlantisYAML := !os.IsNotExist(err)
|
||||
if hasAtlantisYAML {
|
||||
// If they've specified a project by name we look it up. Otherwise we
|
||||
// use the dir and workspace.
|
||||
if ctx.Command.ProjectName != "" {
|
||||
projCfg = config.FindProjectByName(ctx.Command.ProjectName)
|
||||
if cmd.ProjectName != "" {
|
||||
projCfg = config.FindProjectByName(cmd.ProjectName)
|
||||
if projCfg == nil {
|
||||
return CommandResponse{Error: fmt.Errorf("no project with name %q configured", ctx.Command.ProjectName)}
|
||||
return projCtx, fmt.Errorf("no project with name %q configured", cmd.ProjectName)
|
||||
}
|
||||
} else {
|
||||
projCfg = config.FindProject(ctx.Command.Dir, ctx.Command.Workspace)
|
||||
projCfg = config.FindProject(cmd.Dir, cmd.Workspace)
|
||||
}
|
||||
globalCfg = &config
|
||||
}
|
||||
|
||||
if ctx.Command.ProjectName != "" && !hasAtlantisYAML {
|
||||
return CommandResponse{Error: fmt.Errorf("cannot specify a project name unless an %s file exists to configure projects", yaml.AtlantisYAMLFilename)}
|
||||
if cmd.ProjectName != "" && !hasAtlantisYAML {
|
||||
return projCtx, fmt.Errorf("cannot specify a project name unless an %s file exists to configure projects", yaml.AtlantisYAMLFilename)
|
||||
}
|
||||
|
||||
projCtx := models.ProjectCommandContext{
|
||||
projCtx = models.ProjectCommandContext{
|
||||
BaseRepo: ctx.BaseRepo,
|
||||
HeadRepo: ctx.HeadRepo,
|
||||
Pull: ctx.Pull,
|
||||
User: ctx.User,
|
||||
Log: ctx.Log,
|
||||
CommentArgs: ctx.Command.Flags,
|
||||
Workspace: ctx.Command.Workspace,
|
||||
RepoRelPath: ctx.Command.Dir,
|
||||
ProjectName: ctx.Command.ProjectName,
|
||||
CommentArgs: cmd.Flags,
|
||||
Workspace: cmd.Workspace,
|
||||
RepoRelPath: cmd.Dir,
|
||||
ProjectName: cmd.ProjectName,
|
||||
ProjectConfig: projCfg,
|
||||
GlobalConfig: globalCfg,
|
||||
}
|
||||
res := p.ProjectOperator.Apply(projCtx, filepath.Join(repoDir, ctx.Command.Dir))
|
||||
res.Workspace = projCtx.Workspace
|
||||
res.Path = projCtx.RepoRelPath
|
||||
return CommandResponse{
|
||||
ProjectResults: []ProjectResult{
|
||||
res,
|
||||
},
|
||||
}
|
||||
return projCtx, nil
|
||||
}
|
||||
|
||||
// matchingProjects returns the list of projects whose WhenModified fields match
|
||||
// any of the modifiedFiles.
|
||||
func (p *DefaultPullRequestOperator) matchingProjects(log *logging.SimpleLogger, modifiedFiles []string, config valid.Spec) ([]valid.Project, error) {
|
||||
func (p *DefaultProjectCommandBuilder) matchingProjects(log *logging.SimpleLogger, modifiedFiles []string, config valid.Spec) ([]valid.Project, error) {
|
||||
var projects []valid.Project
|
||||
for _, project := range config.Projects {
|
||||
log.Debug("checking if project at dir %q workspace %q was modified", project.Dir, project.Workspace)
|
||||
@@ -14,6 +14,7 @@
|
||||
package events
|
||||
|
||||
import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
@@ -41,46 +42,48 @@ type PlanSuccess struct {
|
||||
LockURL string
|
||||
}
|
||||
|
||||
type ProjectOperator struct {
|
||||
Locker ProjectLocker
|
||||
LockURLGenerator LockURLGenerator
|
||||
InitStepOperator runtime.InitStepOperator
|
||||
PlanStepOperator runtime.PlanStepOperator
|
||||
ApplyStepOperator runtime.ApplyStepOperator
|
||||
RunStepOperator runtime.RunStepOperator
|
||||
ApprovalOperator runtime.ApprovalOperator
|
||||
Workspace AtlantisWorkspace
|
||||
Webhooks WebhooksSender
|
||||
type ProjectCommandRunner struct {
|
||||
Locker ProjectLocker
|
||||
LockURLGenerator LockURLGenerator
|
||||
InitStepRunner runtime.InitStepRunner
|
||||
PlanStepRunner runtime.PlanStepRunner
|
||||
ApplyStepRunner runtime.ApplyStepRunner
|
||||
RunStepRunner runtime.RunStepRunner
|
||||
PullApprovedChecker runtime.PullApprovedChecker
|
||||
Workspace AtlantisWorkspace
|
||||
Webhooks WebhooksSender
|
||||
AtlantisWorkspaceLocker AtlantisWorkspaceLocker
|
||||
}
|
||||
|
||||
func (p *ProjectOperator) Plan(ctx models.ProjectCommandContext, projAbsPathPtr *string) ProjectResult {
|
||||
func (p *ProjectCommandRunner) Plan(ctx models.ProjectCommandContext) ProjectCommandResult {
|
||||
// Acquire Atlantis lock for this repo/dir/workspace.
|
||||
lockAttempt, err := p.Locker.TryLock(ctx.Log, ctx.Pull, ctx.User, ctx.Workspace, models.NewProject(ctx.BaseRepo.FullName, ctx.RepoRelPath))
|
||||
if err != nil {
|
||||
return ProjectResult{Error: errors.Wrap(err, "acquiring lock")}
|
||||
return ProjectCommandResult{
|
||||
Error: errors.Wrap(err, "acquiring lock"),
|
||||
}
|
||||
}
|
||||
if !lockAttempt.LockAcquired {
|
||||
return ProjectResult{Failure: lockAttempt.LockFailureReason}
|
||||
return ProjectCommandResult{Failure: lockAttempt.LockFailureReason}
|
||||
}
|
||||
ctx.Log.Debug("acquired lock for project")
|
||||
|
||||
// Ensure project has been cloned.
|
||||
var projAbsPath string
|
||||
if projAbsPathPtr == nil {
|
||||
ctx.Log.Debug("project has not yet been cloned")
|
||||
repoDir, cloneErr := p.Workspace.Clone(ctx.Log, ctx.BaseRepo, ctx.HeadRepo, ctx.Pull, ctx.Workspace)
|
||||
if cloneErr != nil {
|
||||
if unlockErr := lockAttempt.UnlockFn(); unlockErr != nil {
|
||||
ctx.Log.Err("error unlocking state after plan error: %v", unlockErr)
|
||||
}
|
||||
return ProjectResult{Error: cloneErr}
|
||||
}
|
||||
projAbsPath = filepath.Join(repoDir, ctx.RepoRelPath)
|
||||
ctx.Log.Debug("project successfully cloned to %q", projAbsPath)
|
||||
} else {
|
||||
projAbsPath = *projAbsPathPtr
|
||||
ctx.Log.Debug("project was already cloned to %q", projAbsPath)
|
||||
// Acquire internal lock for the directory we're going to operate in.
|
||||
unlockFn, err := p.AtlantisWorkspaceLocker.TryLock2(ctx.BaseRepo.FullName, ctx.Workspace, ctx.Pull.Num)
|
||||
if err != nil {
|
||||
return ProjectCommandResult{Error: err}
|
||||
}
|
||||
defer unlockFn()
|
||||
|
||||
// Clone is idempotent so okay to run even if the repo was already cloned.
|
||||
repoDir, cloneErr := p.Workspace.Clone(ctx.Log, ctx.BaseRepo, ctx.HeadRepo, ctx.Pull, ctx.Workspace)
|
||||
if cloneErr != nil {
|
||||
if unlockErr := lockAttempt.UnlockFn(); unlockErr != nil {
|
||||
ctx.Log.Err("error unlocking state after plan error: %v", unlockErr)
|
||||
}
|
||||
return ProjectCommandResult{Error: cloneErr}
|
||||
}
|
||||
projAbsPath := filepath.Join(repoDir, ctx.RepoRelPath)
|
||||
|
||||
// Use default stage unless another workflow is defined in config
|
||||
stage := p.defaultPlanStage()
|
||||
@@ -98,10 +101,10 @@ func (p *ProjectOperator) Plan(ctx models.ProjectCommandContext, projAbsPathPtr
|
||||
ctx.Log.Err("error unlocking state after plan error: %v", unlockErr)
|
||||
}
|
||||
// todo: include output from other steps.
|
||||
return ProjectResult{Error: err}
|
||||
return ProjectCommandResult{Error: err}
|
||||
}
|
||||
|
||||
return ProjectResult{
|
||||
return ProjectCommandResult{
|
||||
PlanSuccess: &PlanSuccess{
|
||||
LockURL: p.LockURLGenerator.GenerateLockURL(lockAttempt.LockKey),
|
||||
TerraformOutput: strings.Join(outputs, "\n"),
|
||||
@@ -109,20 +112,20 @@ func (p *ProjectOperator) Plan(ctx models.ProjectCommandContext, projAbsPathPtr
|
||||
}
|
||||
}
|
||||
|
||||
func (p *ProjectOperator) runSteps(steps []valid.Step, ctx models.ProjectCommandContext, absPath string) ([]string, error) {
|
||||
func (p *ProjectCommandRunner) runSteps(steps []valid.Step, ctx models.ProjectCommandContext, absPath string) ([]string, error) {
|
||||
var outputs []string
|
||||
for _, step := range steps {
|
||||
var out string
|
||||
var err error
|
||||
switch step.StepName {
|
||||
case "init":
|
||||
out, err = p.InitStepOperator.Run(ctx, step.ExtraArgs, absPath)
|
||||
out, err = p.InitStepRunner.Run(ctx, step.ExtraArgs, absPath)
|
||||
case "plan":
|
||||
out, err = p.PlanStepOperator.Run(ctx, step.ExtraArgs, absPath)
|
||||
out, err = p.PlanStepRunner.Run(ctx, step.ExtraArgs, absPath)
|
||||
case "apply":
|
||||
out, err = p.ApplyStepOperator.Run(ctx, step.ExtraArgs, absPath)
|
||||
out, err = p.ApplyStepRunner.Run(ctx, step.ExtraArgs, absPath)
|
||||
case "run":
|
||||
out, err = p.RunStepOperator.Run(ctx, step.RunCommand, absPath)
|
||||
out, err = p.RunStepRunner.Run(ctx, step.RunCommand, absPath)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
@@ -136,21 +139,36 @@ func (p *ProjectOperator) runSteps(steps []valid.Step, ctx models.ProjectCommand
|
||||
return outputs, nil
|
||||
}
|
||||
|
||||
func (p *ProjectOperator) Apply(ctx models.ProjectCommandContext, absPath string) ProjectResult {
|
||||
func (p *ProjectCommandRunner) Apply(ctx models.ProjectCommandContext) ProjectCommandResult {
|
||||
repoDir, err := p.Workspace.GetWorkspace(ctx.BaseRepo, ctx.Pull, ctx.Workspace)
|
||||
if err != nil {
|
||||
if os.IsNotExist(err) {
|
||||
return ProjectCommandResult{Error: errors.New("project has not been cloned–did you run plan?")}
|
||||
}
|
||||
return ProjectCommandResult{Error: err}
|
||||
}
|
||||
absPath := filepath.Join(repoDir, ctx.RepoRelPath)
|
||||
|
||||
if ctx.ProjectConfig != nil {
|
||||
for _, req := range ctx.ProjectConfig.ApplyRequirements {
|
||||
switch req {
|
||||
case "approved":
|
||||
approved, err := p.ApprovalOperator.IsApproved(ctx.BaseRepo, ctx.Pull)
|
||||
approved, err := p.PullApprovedChecker.IsApproved(ctx.BaseRepo, ctx.Pull)
|
||||
if err != nil {
|
||||
return ProjectResult{Error: errors.Wrap(err, "checking if pull request was approved")}
|
||||
return ProjectCommandResult{Error: errors.Wrap(err, "checking if pull request was approved")}
|
||||
}
|
||||
if !approved {
|
||||
return ProjectResult{Failure: "Pull request must be approved before running apply."}
|
||||
return ProjectCommandResult{Failure: "Pull request must be approved before running apply."}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// Acquire internal lock for the directory we're going to operate in.
|
||||
unlockFn, err := p.AtlantisWorkspaceLocker.TryLock2(ctx.BaseRepo.FullName, ctx.Workspace, ctx.Pull.Num)
|
||||
if err != nil {
|
||||
return ProjectCommandResult{Error: err}
|
||||
}
|
||||
defer unlockFn()
|
||||
|
||||
// Use default stage unless another workflow is defined in config
|
||||
stage := p.defaultApplyStage()
|
||||
@@ -170,14 +188,14 @@ func (p *ProjectOperator) Apply(ctx models.ProjectCommandContext, absPath string
|
||||
})
|
||||
if err != nil {
|
||||
// todo: include output from other steps.
|
||||
return ProjectResult{Error: err}
|
||||
return ProjectCommandResult{Error: err}
|
||||
}
|
||||
return ProjectResult{
|
||||
return ProjectCommandResult{
|
||||
ApplySuccess: strings.Join(outputs, "\n"),
|
||||
}
|
||||
}
|
||||
|
||||
func (p ProjectOperator) defaultPlanStage() valid.Stage {
|
||||
func (p ProjectCommandRunner) defaultPlanStage() valid.Stage {
|
||||
return valid.Stage{
|
||||
Steps: []valid.Step{
|
||||
{
|
||||
@@ -190,7 +208,7 @@ func (p ProjectOperator) defaultPlanStage() valid.Stage {
|
||||
}
|
||||
}
|
||||
|
||||
func (p ProjectOperator) defaultApplyStage() valid.Stage {
|
||||
func (p ProjectCommandRunner) defaultApplyStage() valid.Stage {
|
||||
return valid.Stage{
|
||||
Steps: []valid.Step{
|
||||
{
|
||||
@@ -17,8 +17,12 @@ import "github.com/runatlantis/atlantis/server/events/vcs"
|
||||
|
||||
// ProjectResult is the result of executing a plan/apply for a project.
|
||||
type ProjectResult struct {
|
||||
Path string
|
||||
Workspace string
|
||||
ProjectCommandResult
|
||||
Path string
|
||||
Workspace string
|
||||
}
|
||||
|
||||
type ProjectCommandResult struct {
|
||||
Error error
|
||||
Failure string
|
||||
PlanSuccess *PlanSuccess
|
||||
|
||||
@@ -9,12 +9,12 @@ import (
|
||||
"github.com/runatlantis/atlantis/server/events/models"
|
||||
)
|
||||
|
||||
// ApplyStepOperator runs `terraform apply`.
|
||||
type ApplyStepOperator struct {
|
||||
// ApplyStepRunner runs `terraform apply`.
|
||||
type ApplyStepRunner struct {
|
||||
TerraformExecutor TerraformExec
|
||||
}
|
||||
|
||||
func (a *ApplyStepOperator) Run(ctx models.ProjectCommandContext, extraArgs []string, path string) (string, error) {
|
||||
func (a *ApplyStepRunner) Run(ctx models.ProjectCommandContext, extraArgs []string, path string) (string, error) {
|
||||
// todo: move this to a common library
|
||||
planFileName := fmt.Sprintf("%s.tfplan", ctx.Workspace)
|
||||
if ctx.ProjectName != "" {
|
||||
@@ -17,7 +17,7 @@ import (
|
||||
)
|
||||
|
||||
func TestRun_NoDir(t *testing.T) {
|
||||
o := runtime.ApplyStepOperator{
|
||||
o := runtime.ApplyStepRunner{
|
||||
TerraformExecutor: nil,
|
||||
}
|
||||
_, err := o.Run(models.ProjectCommandContext{
|
||||
@@ -30,7 +30,7 @@ func TestRun_NoDir(t *testing.T) {
|
||||
func TestRun_NoPlanFile(t *testing.T) {
|
||||
tmpDir, cleanup := TempDir(t)
|
||||
defer cleanup()
|
||||
o := runtime.ApplyStepOperator{
|
||||
o := runtime.ApplyStepRunner{
|
||||
TerraformExecutor: nil,
|
||||
}
|
||||
_, err := o.Run(models.ProjectCommandContext{
|
||||
@@ -49,7 +49,7 @@ func TestRun_Success(t *testing.T) {
|
||||
|
||||
RegisterMockTestingT(t)
|
||||
terraform := mocks.NewMockClient()
|
||||
o := runtime.ApplyStepOperator{
|
||||
o := runtime.ApplyStepRunner{
|
||||
TerraformExecutor: terraform,
|
||||
}
|
||||
|
||||
@@ -74,7 +74,7 @@ func TestRun_UsesConfiguredTFVersion(t *testing.T) {
|
||||
|
||||
RegisterMockTestingT(t)
|
||||
terraform := mocks.NewMockClient()
|
||||
o := runtime.ApplyStepOperator{
|
||||
o := runtime.ApplyStepRunner{
|
||||
TerraformExecutor: terraform,
|
||||
}
|
||||
tfVersion, _ := version.NewVersion("0.11.0")
|
||||
@@ -6,13 +6,13 @@ import (
|
||||
)
|
||||
|
||||
// InitStep runs `terraform init`.
|
||||
type InitStepOperator struct {
|
||||
type InitStepRunner struct {
|
||||
TerraformExecutor TerraformExec
|
||||
DefaultTFVersion *version.Version
|
||||
}
|
||||
|
||||
// nolint: unparam
|
||||
func (i *InitStepOperator) Run(ctx models.ProjectCommandContext, extraArgs []string, path string) (string, error) {
|
||||
func (i *InitStepRunner) Run(ctx models.ProjectCommandContext, extraArgs []string, path string) (string, error) {
|
||||
tfVersion := i.DefaultTFVersion
|
||||
if ctx.ProjectConfig != nil && ctx.ProjectConfig.TerraformVersion != nil {
|
||||
tfVersion = ctx.ProjectConfig.TerraformVersion
|
||||
@@ -44,7 +44,7 @@ func TestRun_UsesGetOrInitForRightVersion(t *testing.T) {
|
||||
|
||||
tfVersion, _ := version.NewVersion(c.version)
|
||||
logger := logging.NewNoopLogger()
|
||||
iso := runtime.InitStepOperator{
|
||||
iso := runtime.InitStepRunner{
|
||||
TerraformExecutor: terraform,
|
||||
DefaultTFVersion: tfVersion,
|
||||
}
|
||||
@@ -15,12 +15,12 @@ import (
|
||||
const atlantisUserTFVar = "atlantis_user"
|
||||
const defaultWorkspace = "default"
|
||||
|
||||
type PlanStepOperator struct {
|
||||
type PlanStepRunner struct {
|
||||
TerraformExecutor TerraformExec
|
||||
DefaultTFVersion *version.Version
|
||||
}
|
||||
|
||||
func (p *PlanStepOperator) Run(ctx models.ProjectCommandContext, extraArgs []string, path string) (string, error) {
|
||||
func (p *PlanStepRunner) Run(ctx models.ProjectCommandContext, extraArgs []string, path string) (string, error) {
|
||||
tfVersion := p.DefaultTFVersion
|
||||
if ctx.ProjectConfig != nil && ctx.ProjectConfig.TerraformVersion != nil {
|
||||
tfVersion = ctx.ProjectConfig.TerraformVersion
|
||||
@@ -55,7 +55,7 @@ func (p *PlanStepOperator) Run(ctx models.ProjectCommandContext, extraArgs []str
|
||||
|
||||
// switchWorkspace changes the terraform workspace if necessary and will create
|
||||
// it if it doesn't exist. It handles differences between versions.
|
||||
func (p *PlanStepOperator) switchWorkspace(ctx models.ProjectCommandContext, path string, tfVersion *version.Version) error {
|
||||
func (p *PlanStepRunner) switchWorkspace(ctx models.ProjectCommandContext, path string, tfVersion *version.Version) error {
|
||||
// In versions less than 0.9 there is no support for workspaces.
|
||||
noWorkspaceSupport := MustConstraint("<0.9").Check(tfVersion)
|
||||
// If the user tried to set a specific workspace in the comment but their
|
||||
@@ -26,7 +26,7 @@ func TestRun_NoWorkspaceIn08(t *testing.T) {
|
||||
tfVersion, _ := version.NewVersion("0.8")
|
||||
logger := logging.NewNoopLogger()
|
||||
workspace := "default"
|
||||
s := runtime.PlanStepOperator{
|
||||
s := runtime.PlanStepRunner{
|
||||
DefaultTFVersion: tfVersion,
|
||||
TerraformExecutor: terraform,
|
||||
}
|
||||
@@ -59,7 +59,7 @@ func TestRun_ErrWorkspaceIn08(t *testing.T) {
|
||||
tfVersion, _ := version.NewVersion("0.8")
|
||||
logger := logging.NewNoopLogger()
|
||||
workspace := "notdefault"
|
||||
s := runtime.PlanStepOperator{
|
||||
s := runtime.PlanStepRunner{
|
||||
TerraformExecutor: terraform,
|
||||
DefaultTFVersion: tfVersion,
|
||||
}
|
||||
@@ -107,7 +107,7 @@ func TestRun_SwitchesWorkspace(t *testing.T) {
|
||||
tfVersion, _ := version.NewVersion(c.tfVersion)
|
||||
logger := logging.NewNoopLogger()
|
||||
|
||||
s := runtime.PlanStepOperator{
|
||||
s := runtime.PlanStepRunner{
|
||||
TerraformExecutor: terraform,
|
||||
DefaultTFVersion: tfVersion,
|
||||
}
|
||||
@@ -162,7 +162,7 @@ func TestRun_CreatesWorkspace(t *testing.T) {
|
||||
terraform := mocks.NewMockClient()
|
||||
tfVersion, _ := version.NewVersion(c.tfVersion)
|
||||
logger := logging.NewNoopLogger()
|
||||
s := runtime.PlanStepOperator{
|
||||
s := runtime.PlanStepRunner{
|
||||
TerraformExecutor: terraform,
|
||||
DefaultTFVersion: tfVersion,
|
||||
}
|
||||
@@ -201,7 +201,7 @@ func TestRun_NoWorkspaceSwitchIfNotNecessary(t *testing.T) {
|
||||
terraform := mocks.NewMockClient()
|
||||
tfVersion, _ := version.NewVersion("0.10.0")
|
||||
logger := logging.NewNoopLogger()
|
||||
s := runtime.PlanStepOperator{
|
||||
s := runtime.PlanStepRunner{
|
||||
TerraformExecutor: terraform,
|
||||
DefaultTFVersion: tfVersion,
|
||||
}
|
||||
@@ -243,7 +243,7 @@ func TestRun_AddsEnvVarFile(t *testing.T) {
|
||||
// Using version >= 0.10 here so we don't expect any env commands.
|
||||
tfVersion, _ := version.NewVersion("0.10.0")
|
||||
logger := logging.NewNoopLogger()
|
||||
s := runtime.PlanStepOperator{
|
||||
s := runtime.PlanStepRunner{
|
||||
TerraformExecutor: terraform,
|
||||
DefaultTFVersion: tfVersion,
|
||||
}
|
||||
@@ -265,3 +265,31 @@ func TestRun_AddsEnvVarFile(t *testing.T) {
|
||||
terraform.VerifyWasCalledOnce().RunCommandWithVersion(logger, tmpDir, expPlanArgs, tfVersion, "workspace")
|
||||
Equals(t, "output", output)
|
||||
}
|
||||
|
||||
func TestRun_UsesDiffPathForProject(t *testing.T) {
|
||||
// Test that if running for a project, uses a different path for the plan
|
||||
// file.
|
||||
RegisterMockTestingT(t)
|
||||
terraform := mocks.NewMockClient()
|
||||
tfVersion, _ := version.NewVersion("0.10.0")
|
||||
logger := logging.NewNoopLogger()
|
||||
s := runtime.PlanStepRunner{
|
||||
TerraformExecutor: terraform,
|
||||
DefaultTFVersion: tfVersion,
|
||||
}
|
||||
When(terraform.RunCommandWithVersion(logger, "/path", []string{"workspace", "show"}, tfVersion, "workspace")).ThenReturn("workspace\n", nil)
|
||||
|
||||
expPlanArgs := []string{"plan", "-refresh", "-no-color", "-out", "/path/projectname-default.tfplan", "-var", "atlantis_user=username", "extra", "args", "comment", "args"}
|
||||
When(terraform.RunCommandWithVersion(logger, "/path", expPlanArgs, tfVersion, "default")).ThenReturn("output", nil)
|
||||
|
||||
output, err := s.Run(models.ProjectCommandContext{
|
||||
Log: logger,
|
||||
Workspace: "default",
|
||||
RepoRelPath: ".",
|
||||
ProjectName: "projectname",
|
||||
User: models.User{Username: "username"},
|
||||
CommentArgs: []string{"comment", "args"},
|
||||
}, []string{"extra", "args"}, "/path")
|
||||
Ok(t, err)
|
||||
Equals(t, "output", output)
|
||||
}
|
||||
@@ -5,11 +5,11 @@ import (
|
||||
"github.com/runatlantis/atlantis/server/events/vcs"
|
||||
)
|
||||
|
||||
type ApprovalOperator struct {
|
||||
type PullApprovedChecker struct {
|
||||
VCSClient vcs.ClientProxy
|
||||
}
|
||||
|
||||
func (a *ApprovalOperator) IsApproved(baseRepo models.Repo, pull models.PullRequest) (bool, error) {
|
||||
func (a *PullApprovedChecker) IsApproved(baseRepo models.Repo, pull models.PullRequest) (bool, error) {
|
||||
approved, err := a.VCSClient.PullIsApproved(baseRepo, pull)
|
||||
if err != nil {
|
||||
return false, err
|
||||
@@ -1,3 +0,0 @@
|
||||
package runtime_test
|
||||
|
||||
// todo
|
||||
@@ -9,11 +9,11 @@ import (
|
||||
"github.com/runatlantis/atlantis/server/events/models"
|
||||
)
|
||||
|
||||
// RunStepOperator runs custom commands.
|
||||
type RunStepOperator struct {
|
||||
// RunStepRunner runs custom commands.
|
||||
type RunStepRunner struct {
|
||||
}
|
||||
|
||||
func (r *RunStepOperator) Run(ctx models.ProjectCommandContext, command []string, path string) (string, error) {
|
||||
func (r *RunStepRunner) Run(ctx models.ProjectCommandContext, command []string, path string) (string, error) {
|
||||
if len(command) < 1 {
|
||||
return "", errors.New("no commands for run step")
|
||||
}
|
||||
50
server/events/runtime/run_step_runner_test.go
Normal file
50
server/events/runtime/run_step_runner_test.go
Normal file
@@ -0,0 +1,50 @@
|
||||
package runtime_test
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/runatlantis/atlantis/server/events/models"
|
||||
"github.com/runatlantis/atlantis/server/events/runtime"
|
||||
"github.com/runatlantis/atlantis/server/logging"
|
||||
. "github.com/runatlantis/atlantis/testing"
|
||||
)
|
||||
|
||||
func TestRunStepRunner_Run(t *testing.T) {
|
||||
cases := []struct {
|
||||
Command string
|
||||
ExpOut string
|
||||
ExpErr string
|
||||
}{
|
||||
{
|
||||
Command: "echo hi",
|
||||
ExpOut: "hi\n",
|
||||
},
|
||||
{
|
||||
Command: "echo hi >> file && cat file",
|
||||
ExpOut: "hi\n",
|
||||
},
|
||||
{
|
||||
Command: "lkjlkj",
|
||||
ExpErr: "exit status 127: running \"lkjlkj\" in",
|
||||
},
|
||||
}
|
||||
|
||||
r := runtime.RunStepRunner{}
|
||||
ctx := models.ProjectCommandContext{
|
||||
Log: logging.NewNoopLogger(),
|
||||
}
|
||||
for _, c := range cases {
|
||||
t.Run(c.Command, func(t *testing.T) {
|
||||
tmpDir, cleanup := TempDir(t)
|
||||
defer cleanup()
|
||||
out, err := r.Run(ctx, strings.Split(c.Command, " "), tmpDir)
|
||||
if c.ExpErr != "" {
|
||||
ErrContains(t, c.ExpErr, err)
|
||||
return
|
||||
}
|
||||
Ok(t, err)
|
||||
Equals(t, c.ExpOut, out)
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -15,6 +15,14 @@ package fixtures
|
||||
|
||||
import "github.com/google/go-github/github"
|
||||
|
||||
var PullEvent = github.PullRequestEvent{
|
||||
Sender: &github.User{
|
||||
Login: github.String("user"),
|
||||
},
|
||||
Repo: &Repo,
|
||||
PullRequest: &Pull,
|
||||
}
|
||||
|
||||
var Pull = github.PullRequest{
|
||||
Head: &github.PullRequestBranch{
|
||||
SHA: github.String("sha256"),
|
||||
|
||||
19
server/events/vcs/vcs_test.go
Normal file
19
server/events/vcs/vcs_test.go
Normal file
@@ -0,0 +1,19 @@
|
||||
package vcs_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/runatlantis/atlantis/server/events/vcs"
|
||||
. "github.com/runatlantis/atlantis/testing"
|
||||
)
|
||||
|
||||
func TestStatus_String(t *testing.T) {
|
||||
cases := map[vcs.CommitStatus]string{
|
||||
vcs.Pending: "pending",
|
||||
vcs.Success: "success",
|
||||
vcs.Failed: "failed",
|
||||
}
|
||||
for k, v := range cases {
|
||||
Equals(t, v, k.String())
|
||||
}
|
||||
}
|
||||
@@ -29,7 +29,7 @@ const githubHeader = "X-Github-Event"
|
||||
const gitlabHeader = "X-Gitlab-Event"
|
||||
|
||||
// EventsController handles all webhook requests which signify 'events' in the
|
||||
// VCS host, ex. GitHub. It's split out from Server to make testing easier.
|
||||
// VCS host, ex. GitHub.
|
||||
type EventsController struct {
|
||||
CommandRunner events.CommandRunner
|
||||
PullCleaner events.PullCleaner
|
||||
@@ -51,11 +51,7 @@ type EventsController struct {
|
||||
// startup to support.
|
||||
SupportedVCSHosts []models.VCSHostType
|
||||
VCSClient vcs.ClientProxy
|
||||
// AtlantisGithubUser is the user that atlantis is running as for Github.
|
||||
AtlantisGithubUser models.User
|
||||
// AtlantisGitlabUser is the user that atlantis is running as for Gitlab.
|
||||
AtlantisGitlabUser models.User
|
||||
TestingMode bool
|
||||
TestingMode bool
|
||||
}
|
||||
|
||||
// Post handles POST webhook requests.
|
||||
@@ -117,27 +113,20 @@ func (e *EventsController) HandleGithubCommentEvent(w http.ResponseWriter, event
|
||||
return
|
||||
}
|
||||
|
||||
// We pass in an empty models.Repo for headRepo because we need to do additional
|
||||
// calls to get that information but we need this code path to be generic.
|
||||
// Later on in CommandHandler we detect that this is a GitHub event and
|
||||
// make the necessary calls to get the headRepo.
|
||||
e.handleCommentEvent(w, baseRepo, models.Repo{}, user, pullNum, event.Comment.GetBody(), models.Github)
|
||||
// We pass in nil for maybeHeadRepo because the head repo data isn't
|
||||
// available in the GithubIssueComment event.
|
||||
e.handleCommentEvent(w, baseRepo, nil, user, pullNum, event.Comment.GetBody(), models.Github)
|
||||
}
|
||||
|
||||
// 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 *EventsController) HandleGithubPullRequestEvent(w http.ResponseWriter, pullEvent *github.PullRequestEvent, githubReqID string) {
|
||||
pull, headRepo, err := e.Parser.ParseGithubPull(pullEvent.PullRequest)
|
||||
pull, 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
|
||||
}
|
||||
baseRepo, err := e.Parser.ParseGithubRepo(pullEvent.Repo)
|
||||
if err != nil {
|
||||
e.respond(w, logging.Error, http.StatusBadRequest, "Error parsing repo data: %s %s", err, githubReqID)
|
||||
return
|
||||
}
|
||||
var eventType string
|
||||
switch pullEvent.GetAction() {
|
||||
case "opened":
|
||||
@@ -150,7 +139,7 @@ func (e *EventsController) HandleGithubPullRequestEvent(w http.ResponseWriter, p
|
||||
eventType = OtherPullEvent
|
||||
}
|
||||
e.Logger.Info("identified event as type %q", eventType)
|
||||
e.handlePullRequestEvent(w, baseRepo, headRepo, pull, e.AtlantisGithubUser, eventType)
|
||||
e.handlePullRequestEvent(w, baseRepo, headRepo, pull, user, eventType)
|
||||
}
|
||||
|
||||
const OpenPullEvent = "opened"
|
||||
@@ -179,16 +168,13 @@ func (e *EventsController) handlePullRequestEvent(w http.ResponseWriter, baseRep
|
||||
// We use a goroutine so that this function returns and the connection is
|
||||
// closed.
|
||||
fmt.Fprintln(w, "Processing...")
|
||||
// We use a Command to represent autoplanning but we set dir and
|
||||
// workspace to '*' to indicate that all applicable dirs and workspaces
|
||||
// should be planned.
|
||||
autoplanCmd := events.NewCommand("*", nil, events.Plan, false, "*", "", true)
|
||||
e.Logger.Info("executing command %s", autoplanCmd)
|
||||
|
||||
e.Logger.Info("executing autoplan")
|
||||
if !e.TestingMode {
|
||||
go e.CommandRunner.ExecuteCommand(baseRepo, headRepo, user, pull.Num, autoplanCmd)
|
||||
go e.CommandRunner.RunAutoplanCommand(baseRepo, headRepo, pull, user)
|
||||
} else {
|
||||
// When testing we want to wait for everything to complete.
|
||||
e.CommandRunner.ExecuteCommand(baseRepo, headRepo, user, pull.Num, autoplanCmd)
|
||||
e.CommandRunner.RunAutoplanCommand(baseRepo, headRepo, pull, user)
|
||||
}
|
||||
return
|
||||
case ClosedPullEvent:
|
||||
@@ -202,7 +188,7 @@ func (e *EventsController) handlePullRequestEvent(w http.ResponseWriter, baseRep
|
||||
return
|
||||
case OtherPullEvent:
|
||||
// Else we ignore the event.
|
||||
e.respond(w, logging.Debug, http.StatusOK, "Ignoring opened pull request event")
|
||||
e.respond(w, logging.Debug, http.StatusOK, "Ignoring non-actionable pull request event")
|
||||
return
|
||||
}
|
||||
}
|
||||
@@ -236,10 +222,10 @@ func (e *EventsController) HandleGitlabCommentEvent(w http.ResponseWriter, event
|
||||
e.respond(w, logging.Error, http.StatusBadRequest, "Error parsing webhook: %s", err)
|
||||
return
|
||||
}
|
||||
e.handleCommentEvent(w, baseRepo, headRepo, user, event.MergeRequest.IID, event.ObjectAttributes.Note, models.Gitlab)
|
||||
e.handleCommentEvent(w, baseRepo, &headRepo, user, event.MergeRequest.IID, event.ObjectAttributes.Note, models.Gitlab)
|
||||
}
|
||||
|
||||
func (e *EventsController) handleCommentEvent(w http.ResponseWriter, baseRepo models.Repo, headRepo models.Repo, user models.User, pullNum int, comment string, vcsHost models.VCSHostType) {
|
||||
func (e *EventsController) handleCommentEvent(w http.ResponseWriter, baseRepo models.Repo, maybeHeadRepo *models.Repo, user models.User, pullNum int, comment string, vcsHost models.VCSHostType) {
|
||||
parseResult := e.CommentParser.Parse(comment, vcsHost)
|
||||
if parseResult.Ignore {
|
||||
truncated := comment
|
||||
@@ -278,10 +264,10 @@ func (e *EventsController) handleCommentEvent(w http.ResponseWriter, baseRepo mo
|
||||
// Respond with success and then actually execute the command asynchronously.
|
||||
// We use a goroutine so that this function returns and the connection is
|
||||
// closed.
|
||||
go e.CommandRunner.ExecuteCommand(baseRepo, headRepo, user, pullNum, parseResult.Command)
|
||||
go e.CommandRunner.RunCommentCommand(baseRepo, maybeHeadRepo, user, pullNum, parseResult.Command)
|
||||
} else {
|
||||
// When testing we want to wait for everything to complete.
|
||||
e.CommandRunner.ExecuteCommand(baseRepo, headRepo, user, pullNum, parseResult.Command)
|
||||
e.CommandRunner.RunCommentCommand(baseRepo, maybeHeadRepo, user, pullNum, parseResult.Command)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -289,7 +275,7 @@ func (e *EventsController) handleCommentEvent(w http.ResponseWriter, baseRepo mo
|
||||
// request if the event is a merge request closed event. It's exported to make
|
||||
// testing easier.
|
||||
func (e *EventsController) HandleGitlabMergeRequestEvent(w http.ResponseWriter, event gitlab.MergeEvent) {
|
||||
pull, baseRepo, headRepo, err := e.Parser.ParseGitlabMergeEvent(event)
|
||||
pull, baseRepo, headRepo, user, err := e.Parser.ParseGitlabMergeEvent(event)
|
||||
if err != nil {
|
||||
e.respond(w, logging.Error, http.StatusBadRequest, "Error parsing webhook: %s", err)
|
||||
return
|
||||
@@ -306,7 +292,7 @@ func (e *EventsController) HandleGitlabMergeRequestEvent(w http.ResponseWriter,
|
||||
eventType = OtherPullEvent
|
||||
}
|
||||
e.Logger.Info("identified event as type %q", eventType)
|
||||
e.handlePullRequestEvent(w, baseRepo, headRepo, pull, e.AtlantisGitlabUser, eventType)
|
||||
e.handlePullRequestEvent(w, baseRepo, headRepo, pull, user, eventType)
|
||||
}
|
||||
|
||||
// supportsHost returns true if h is in e.SupportedVCSHosts and false otherwise.
|
||||
|
||||
@@ -164,7 +164,7 @@ func TestGitHubWorkflow(t *testing.T) {
|
||||
When(vcsClient.GetModifiedFiles(AnyRepo(), matchers.AnyModelsPullRequest())).ThenReturn(c.ModifiedFiles, nil)
|
||||
|
||||
// First, send the open pull request event and trigger an autoplan.
|
||||
pullOpenedReq := GitHubPullRequestOpenedEvent(t)
|
||||
pullOpenedReq := GitHubPullRequestOpenedEvent(t, headSHA)
|
||||
ctrl.Post(w, pullOpenedReq)
|
||||
responseContains(t, w, 200, "Processing...")
|
||||
if c.ExpAutoplanCommentFile != "" {
|
||||
@@ -236,51 +236,51 @@ func setupE2E(t *testing.T) (server.EventsController, *vcsmocks.MockClientProxy,
|
||||
}
|
||||
|
||||
defaultTFVersion := terraformClient.Version()
|
||||
commandHandler := &events.CommandHandler{
|
||||
locker := events.NewDefaultAtlantisWorkspaceLocker()
|
||||
commandRunner := &events.DefaultCommandRunner{
|
||||
ProjectCommandRunner: &events.ProjectCommandRunner{
|
||||
Locker: projectLocker,
|
||||
LockURLGenerator: &mockLockURLGenerator{},
|
||||
InitStepRunner: runtime.InitStepRunner{
|
||||
TerraformExecutor: terraformClient,
|
||||
DefaultTFVersion: defaultTFVersion,
|
||||
},
|
||||
PlanStepRunner: runtime.PlanStepRunner{
|
||||
TerraformExecutor: terraformClient,
|
||||
DefaultTFVersion: defaultTFVersion,
|
||||
},
|
||||
ApplyStepRunner: runtime.ApplyStepRunner{
|
||||
TerraformExecutor: terraformClient,
|
||||
},
|
||||
RunStepRunner: runtime.RunStepRunner{},
|
||||
PullApprovedChecker: runtime.PullApprovedChecker{
|
||||
VCSClient: e2eVCSClient,
|
||||
},
|
||||
Workspace: atlantisWorkspace,
|
||||
Webhooks: &mockWebhookSender{},
|
||||
AtlantisWorkspaceLocker: locker,
|
||||
},
|
||||
EventParser: eventParser,
|
||||
VCSClient: e2eVCSClient,
|
||||
GithubPullGetter: e2eGithubGetter,
|
||||
GitlabMergeRequestGetter: e2eGitlabGetter,
|
||||
CommitStatusUpdater: e2eStatusUpdater,
|
||||
AtlantisWorkspaceLocker: events.NewDefaultAtlantisWorkspaceLocker(),
|
||||
MarkdownRenderer: &events.MarkdownRenderer{},
|
||||
Logger: logger,
|
||||
AllowForkPRs: allowForkPRs,
|
||||
AllowForkPRsFlag: "allow-fork-prs",
|
||||
PullRequestOperator: &events.DefaultPullRequestOperator{
|
||||
TerraformExecutor: terraformClient,
|
||||
DefaultTFVersion: defaultTFVersion,
|
||||
ParserValidator: &yaml.ParserValidator{},
|
||||
ProjectFinder: &events.DefaultProjectFinder{},
|
||||
VCSClient: e2eVCSClient,
|
||||
Workspace: atlantisWorkspace,
|
||||
ProjectOperator: events.ProjectOperator{
|
||||
Locker: projectLocker,
|
||||
LockURLGenerator: &mockLockURLGenerator{},
|
||||
InitStepOperator: runtime.InitStepOperator{
|
||||
TerraformExecutor: terraformClient,
|
||||
DefaultTFVersion: defaultTFVersion,
|
||||
},
|
||||
PlanStepOperator: runtime.PlanStepOperator{
|
||||
TerraformExecutor: terraformClient,
|
||||
DefaultTFVersion: defaultTFVersion,
|
||||
},
|
||||
ApplyStepOperator: runtime.ApplyStepOperator{
|
||||
TerraformExecutor: terraformClient,
|
||||
},
|
||||
RunStepOperator: runtime.RunStepOperator{},
|
||||
ApprovalOperator: runtime.ApprovalOperator{
|
||||
VCSClient: e2eVCSClient,
|
||||
},
|
||||
Workspace: atlantisWorkspace,
|
||||
Webhooks: &mockWebhookSender{},
|
||||
},
|
||||
ProjectCommandBuilder: &events.DefaultProjectCommandBuilder{
|
||||
ParserValidator: &yaml.ParserValidator{},
|
||||
ProjectFinder: &events.DefaultProjectFinder{},
|
||||
VCSClient: e2eVCSClient,
|
||||
Workspace: atlantisWorkspace,
|
||||
AtlantisWorkspaceLocker: locker,
|
||||
},
|
||||
}
|
||||
|
||||
ctrl := server.EventsController{
|
||||
TestingMode: true,
|
||||
CommandRunner: commandHandler,
|
||||
CommandRunner: commandRunner,
|
||||
PullCleaner: &events.PullClosedExecutor{
|
||||
Locker: lockingClient,
|
||||
VCSClient: e2eVCSClient,
|
||||
@@ -298,12 +298,6 @@ func setupE2E(t *testing.T) (server.EventsController, *vcsmocks.MockClientProxy,
|
||||
},
|
||||
SupportedVCSHosts: []models.VCSHostType{models.Gitlab, models.Github},
|
||||
VCSClient: e2eVCSClient,
|
||||
AtlantisGithubUser: models.User{
|
||||
Username: "atlantisbot",
|
||||
},
|
||||
AtlantisGitlabUser: models.User{
|
||||
Username: "atlantisbot",
|
||||
},
|
||||
}
|
||||
return ctrl, e2eVCSClient, e2eGithubGetter, atlantisWorkspace
|
||||
}
|
||||
@@ -331,10 +325,12 @@ func GitHubCommentEvent(t *testing.T, comment string) *http.Request {
|
||||
return req
|
||||
}
|
||||
|
||||
func GitHubPullRequestOpenedEvent(t *testing.T) *http.Request {
|
||||
func GitHubPullRequestOpenedEvent(t *testing.T, headSHA string) *http.Request {
|
||||
requestJSON, err := ioutil.ReadFile(filepath.Join("testfixtures", "githubPullRequestOpenedEvent.json"))
|
||||
Ok(t, err)
|
||||
req, err := http.NewRequest("POST", "/events", bytes.NewBuffer(requestJSON))
|
||||
// Replace sha with expected sha.
|
||||
requestJSONStr := strings.Replace(string(requestJSON), "c31fd9ea6f557ad2ea659944c3844a059b83bc5d", headSHA, -1)
|
||||
req, err := http.NewRequest("POST", "/events", bytes.NewBuffer([]byte(requestJSONStr)))
|
||||
Ok(t, err)
|
||||
req.Header.Set("Content-Type", "application/json")
|
||||
req.Header.Set(githubHeader, "pull_request")
|
||||
@@ -357,7 +353,7 @@ func GitHubPullRequestParsed(headSHA string) *github.PullRequest {
|
||||
headSHA = "13940d121be73f656e2132c6d7b4c8e87878ac8d"
|
||||
}
|
||||
return &github.PullRequest{
|
||||
Number: github.Int(1),
|
||||
Number: github.Int(2),
|
||||
State: github.String("open"),
|
||||
HTMLURL: github.String("htmlurl"),
|
||||
Head: &github.PullRequestBranch{
|
||||
|
||||
@@ -16,6 +16,7 @@ package server_test
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
@@ -267,9 +268,7 @@ func TestPost_GitlabCommentSuccess(t *testing.T) {
|
||||
e.Post(w, req)
|
||||
responseContains(t, w, http.StatusOK, "Processing...")
|
||||
|
||||
// wait for 200ms so goroutine is called
|
||||
time.Sleep(200 * time.Millisecond)
|
||||
cr.VerifyWasCalledOnce().ExecuteCommand(models.Repo{}, models.Repo{}, models.User{}, 0, nil)
|
||||
cr.VerifyWasCalledOnce().RunCommentCommand(models.Repo{}, &models.Repo{}, models.User{}, 0, nil)
|
||||
}
|
||||
|
||||
func TestPost_GithubCommentSuccess(t *testing.T) {
|
||||
@@ -281,16 +280,14 @@ func TestPost_GithubCommentSuccess(t *testing.T) {
|
||||
When(v.Validate(req, secret)).ThenReturn([]byte(event), nil)
|
||||
baseRepo := models.Repo{}
|
||||
user := models.User{}
|
||||
cmd := events.Command{}
|
||||
cmd := events.CommentCommand{}
|
||||
When(p.ParseGithubIssueCommentEvent(matchers.AnyPtrToGithubIssueCommentEvent())).ThenReturn(baseRepo, user, 1, nil)
|
||||
When(cp.Parse("", models.Github)).ThenReturn(events.CommentParseResult{Command: &cmd})
|
||||
w := httptest.NewRecorder()
|
||||
e.Post(w, req)
|
||||
responseContains(t, w, http.StatusOK, "Processing...")
|
||||
|
||||
// wait for 200ms so goroutine is called
|
||||
time.Sleep(200 * time.Millisecond)
|
||||
cr.VerifyWasCalledOnce().ExecuteCommand(baseRepo, baseRepo, user, 1, &cmd)
|
||||
cr.VerifyWasCalledOnce().RunCommentCommand(baseRepo, nil, user, 1, &cmd)
|
||||
}
|
||||
|
||||
func TestPost_GithubPullRequestInvalid(t *testing.T) {
|
||||
@@ -301,45 +298,87 @@ func TestPost_GithubPullRequestInvalid(t *testing.T) {
|
||||
|
||||
event := `{"action": "closed"}`
|
||||
When(v.Validate(req, secret)).ThenReturn([]byte(event), nil)
|
||||
When(p.ParseGithubPull(matchers.AnyPtrToGithubPullRequest())).ThenReturn(models.PullRequest{}, models.Repo{}, errors.New("err"))
|
||||
When(p.ParseGithubPullEvent(matchers.AnyPtrToGithubPullRequestEvent())).ThenReturn(models.PullRequest{}, models.Repo{}, models.Repo{}, models.User{}, errors.New("err"))
|
||||
w := httptest.NewRecorder()
|
||||
e.Post(w, req)
|
||||
responseContains(t, w, http.StatusBadRequest, "Error parsing pull data: err")
|
||||
}
|
||||
|
||||
func TestPost_GithubPullRequestInvalidRepo(t *testing.T) {
|
||||
t.Log("when the event is a github pull request with invalid repo data we return a 400")
|
||||
e, v, _, p, _, _, _, _ := setup(t)
|
||||
func TestPost_GitlabMergeRequestInvalid(t *testing.T) {
|
||||
t.Log("when the event is a gitlab merge request with invalid data we return a 400")
|
||||
e, _, gl, p, _, _, _, _ := setup(t)
|
||||
req, _ := http.NewRequest("GET", "", bytes.NewBuffer(nil))
|
||||
req.Header.Set(githubHeader, "pull_request")
|
||||
|
||||
event := `{"action": "closed"}`
|
||||
When(v.Validate(req, secret)).ThenReturn([]byte(event), nil)
|
||||
When(p.ParseGithubPull(matchers.AnyPtrToGithubPullRequest())).ThenReturn(models.PullRequest{}, models.Repo{}, nil)
|
||||
When(p.ParseGithubRepo(matchers.AnyPtrToGithubRepository())).ThenReturn(models.Repo{}, errors.New("err"))
|
||||
req.Header.Set(gitlabHeader, "value")
|
||||
When(gl.ParseAndValidate(req, secret)).ThenReturn(gitlabMergeEvent, nil)
|
||||
repo := models.Repo{}
|
||||
pullRequest := models.PullRequest{State: models.Closed}
|
||||
When(p.ParseGitlabMergeEvent(gitlabMergeEvent)).ThenReturn(pullRequest, repo, repo, models.User{}, errors.New("err"))
|
||||
w := httptest.NewRecorder()
|
||||
e.Post(w, req)
|
||||
responseContains(t, w, http.StatusBadRequest, "Error parsing repo data: err")
|
||||
responseContains(t, w, http.StatusBadRequest, "Error parsing webhook: err")
|
||||
}
|
||||
|
||||
func TestPost_GithubPullRequestNotWhitelisted(t *testing.T) {
|
||||
t.Log("when the event is a github pull request to a non-whitelisted repo we return a 400")
|
||||
e, v, _, p, _, _, _, _ := setup(t)
|
||||
e, v, _, _, _, _, _, _ := setup(t)
|
||||
e.RepoWhitelistChecker = &events.RepoWhitelistChecker{Whitelist: "github.com/nevermatch"}
|
||||
req, _ := http.NewRequest("GET", "", bytes.NewBuffer(nil))
|
||||
req.Header.Set(githubHeader, "pull_request")
|
||||
|
||||
event := `{"action": "closed"}`
|
||||
When(v.Validate(req, secret)).ThenReturn([]byte(event), nil)
|
||||
When(p.ParseGithubPull(matchers.AnyPtrToGithubPullRequest())).ThenReturn(models.PullRequest{}, models.Repo{}, nil)
|
||||
When(p.ParseGithubRepo(matchers.AnyPtrToGithubRepository())).ThenReturn(models.Repo{}, nil)
|
||||
w := httptest.NewRecorder()
|
||||
e.Post(w, req)
|
||||
responseContains(t, w, http.StatusForbidden, "Ignoring pull request event from non-whitelisted repo")
|
||||
}
|
||||
|
||||
func TestPost_GithubPullRequestErrCleaningPull(t *testing.T) {
|
||||
t.Log("when the event is a pull request and we have an error calling CleanUpPull we return a 503")
|
||||
func TestPost_GitlabMergeRequestNotWhitelisted(t *testing.T) {
|
||||
t.Log("when the event is a gitlab merge request to a non-whitelisted repo we return a 400")
|
||||
e, _, gl, p, _, _, _, _ := setup(t)
|
||||
req, _ := http.NewRequest("GET", "", bytes.NewBuffer(nil))
|
||||
req.Header.Set(gitlabHeader, "value")
|
||||
|
||||
e.RepoWhitelistChecker = &events.RepoWhitelistChecker{Whitelist: "github.com/nevermatch"}
|
||||
When(gl.ParseAndValidate(req, secret)).ThenReturn(gitlabMergeEvent, nil)
|
||||
repo := models.Repo{}
|
||||
pullRequest := models.PullRequest{State: models.Closed}
|
||||
When(p.ParseGitlabMergeEvent(gitlabMergeEvent)).ThenReturn(pullRequest, repo, repo, models.User{}, nil)
|
||||
|
||||
w := httptest.NewRecorder()
|
||||
e.Post(w, req)
|
||||
responseContains(t, w, http.StatusForbidden, "Ignoring pull request event from non-whitelisted repo")
|
||||
}
|
||||
|
||||
func TestPost_GithubPullRequestUnsupportedAction(t *testing.T) {
|
||||
e, v, _, _, _, _, _, _ := setup(t)
|
||||
req, _ := http.NewRequest("GET", "", bytes.NewBuffer(nil))
|
||||
req.Header.Set(githubHeader, "pull_request")
|
||||
|
||||
event := `{"action": "unsupported"}`
|
||||
When(v.Validate(req, secret)).ThenReturn([]byte(event), nil)
|
||||
w := httptest.NewRecorder()
|
||||
e.Post(w, req)
|
||||
responseContains(t, w, http.StatusOK, "Ignoring non-actionable pull request event")
|
||||
}
|
||||
|
||||
func TestPost_GitlabMergeRequestUnsupportedAction(t *testing.T) {
|
||||
t.Log("when the event is a gitlab merge request to a non-whitelisted repo we return a 400")
|
||||
e, _, gl, p, _, _, _, _ := setup(t)
|
||||
req, _ := http.NewRequest("GET", "", bytes.NewBuffer(nil))
|
||||
req.Header.Set(gitlabHeader, "value")
|
||||
gitlabMergeEvent.ObjectAttributes.Action = "unsupported"
|
||||
When(gl.ParseAndValidate(req, secret)).ThenReturn(gitlabMergeEvent, nil)
|
||||
repo := models.Repo{}
|
||||
pullRequest := models.PullRequest{State: models.Closed}
|
||||
When(p.ParseGitlabMergeEvent(gitlabMergeEvent)).ThenReturn(pullRequest, repo, repo, models.User{}, nil)
|
||||
|
||||
w := httptest.NewRecorder()
|
||||
e.Post(w, req)
|
||||
responseContains(t, w, http.StatusOK, "Ignoring non-actionable pull request event")
|
||||
}
|
||||
|
||||
func TestPost_GithubPullRequestClosedErrCleaningPull(t *testing.T) {
|
||||
t.Log("when the event is a closed pull request and we have an error calling CleanUpPull we return a 503")
|
||||
RegisterMockTestingT(t)
|
||||
e, v, _, p, _, c, _, _ := setup(t)
|
||||
req, _ := http.NewRequest("GET", "", bytes.NewBuffer(nil))
|
||||
@@ -349,30 +388,30 @@ func TestPost_GithubPullRequestErrCleaningPull(t *testing.T) {
|
||||
When(v.Validate(req, secret)).ThenReturn([]byte(event), nil)
|
||||
repo := models.Repo{}
|
||||
pull := models.PullRequest{State: models.Closed}
|
||||
When(p.ParseGithubPull(matchers.AnyPtrToGithubPullRequest())).ThenReturn(pull, repo, nil)
|
||||
When(p.ParseGithubRepo(matchers.AnyPtrToGithubRepository())).ThenReturn(repo, nil)
|
||||
When(p.ParseGithubPullEvent(matchers.AnyPtrToGithubPullRequestEvent())).ThenReturn(pull, repo, repo, models.User{}, nil)
|
||||
When(c.CleanUpPull(repo, pull)).ThenReturn(errors.New("cleanup err"))
|
||||
w := httptest.NewRecorder()
|
||||
e.Post(w, req)
|
||||
responseContains(t, w, http.StatusInternalServerError, "Error cleaning pull request: cleanup err")
|
||||
}
|
||||
|
||||
func TestPost_GitlabMergeRequestErrCleaningPull(t *testing.T) {
|
||||
t.Log("when the event is a gitlab merge request and an error occurs calling CleanUpPull we return a 500")
|
||||
func TestPost_GitlabMergeRequestClosedErrCleaningPull(t *testing.T) {
|
||||
t.Log("when the event is a closed gitlab merge request and an error occurs calling CleanUpPull we return a 500")
|
||||
e, _, gl, p, _, c, _, _ := setup(t)
|
||||
req, _ := http.NewRequest("GET", "", bytes.NewBuffer(nil))
|
||||
req.Header.Set(gitlabHeader, "value")
|
||||
gitlabMergeEvent.ObjectAttributes.Action = "close"
|
||||
When(gl.ParseAndValidate(req, secret)).ThenReturn(gitlabMergeEvent, nil)
|
||||
repo := models.Repo{}
|
||||
pullRequest := models.PullRequest{State: models.Closed}
|
||||
When(p.ParseGitlabMergeEvent(gitlabMergeEvent)).ThenReturn(pullRequest, repo, repo, nil)
|
||||
When(p.ParseGitlabMergeEvent(gitlabMergeEvent)).ThenReturn(pullRequest, repo, repo, models.User{}, nil)
|
||||
When(c.CleanUpPull(repo, pullRequest)).ThenReturn(errors.New("err"))
|
||||
w := httptest.NewRecorder()
|
||||
e.Post(w, req)
|
||||
responseContains(t, w, http.StatusInternalServerError, "Error cleaning pull request: err")
|
||||
}
|
||||
|
||||
func TestPost_GithubPullRequestSuccess(t *testing.T) {
|
||||
func TestPost_GithubClosedPullRequestSuccess(t *testing.T) {
|
||||
t.Log("when the event is a pull request and everything works we return a 200")
|
||||
e, v, _, p, _, c, _, _ := setup(t)
|
||||
req, _ := http.NewRequest("GET", "", bytes.NewBuffer(nil))
|
||||
@@ -382,8 +421,7 @@ func TestPost_GithubPullRequestSuccess(t *testing.T) {
|
||||
When(v.Validate(req, secret)).ThenReturn([]byte(event), nil)
|
||||
repo := models.Repo{}
|
||||
pull := models.PullRequest{State: models.Closed}
|
||||
When(p.ParseGithubPull(matchers.AnyPtrToGithubPullRequest())).ThenReturn(pull, repo, nil)
|
||||
When(p.ParseGithubRepo(matchers.AnyPtrToGithubRepository())).ThenReturn(repo, nil)
|
||||
When(p.ParseGithubPullEvent(matchers.AnyPtrToGithubPullRequestEvent())).ThenReturn(pull, repo, repo, models.User{}, nil)
|
||||
When(c.CleanUpPull(repo, pull)).ThenReturn(nil)
|
||||
w := httptest.NewRecorder()
|
||||
e.Post(w, req)
|
||||
@@ -398,12 +436,68 @@ func TestPost_GitlabMergeRequestSuccess(t *testing.T) {
|
||||
When(gl.ParseAndValidate(req, secret)).ThenReturn(gitlabMergeEvent, nil)
|
||||
repo := models.Repo{}
|
||||
pullRequest := models.PullRequest{State: models.Closed}
|
||||
When(p.ParseGitlabMergeEvent(gitlabMergeEvent)).ThenReturn(pullRequest, repo, repo, nil)
|
||||
When(p.ParseGitlabMergeEvent(gitlabMergeEvent)).ThenReturn(pullRequest, repo, repo, models.User{}, nil)
|
||||
w := httptest.NewRecorder()
|
||||
e.Post(w, req)
|
||||
responseContains(t, w, http.StatusOK, "Pull request cleaned successfully")
|
||||
}
|
||||
|
||||
func TestPost_PullOpenedOrUpdated(t *testing.T) {
|
||||
cases := []struct {
|
||||
Description string
|
||||
HostType models.VCSHostType
|
||||
Action string
|
||||
}{
|
||||
{
|
||||
"github opened",
|
||||
models.Github,
|
||||
"opened",
|
||||
},
|
||||
{
|
||||
"gitlab opened",
|
||||
models.Gitlab,
|
||||
"open",
|
||||
},
|
||||
{
|
||||
"github synchronized",
|
||||
models.Github,
|
||||
"synchronize",
|
||||
},
|
||||
{
|
||||
"gitlab update",
|
||||
models.Gitlab,
|
||||
"update",
|
||||
},
|
||||
}
|
||||
|
||||
for _, c := range cases {
|
||||
t.Run(c.Description, func(t *testing.T) {
|
||||
e, v, gl, p, cr, _, _, _ := setup(t)
|
||||
req, _ := http.NewRequest("GET", "", bytes.NewBuffer(nil))
|
||||
switch c.HostType {
|
||||
case models.Gitlab:
|
||||
req.Header.Set(gitlabHeader, "value")
|
||||
gitlabMergeEvent.ObjectAttributes.Action = c.Action
|
||||
When(gl.ParseAndValidate(req, secret)).ThenReturn(gitlabMergeEvent, nil)
|
||||
repo := models.Repo{}
|
||||
pullRequest := models.PullRequest{State: models.Closed}
|
||||
When(p.ParseGitlabMergeEvent(gitlabMergeEvent)).ThenReturn(pullRequest, repo, repo, models.User{}, nil)
|
||||
case models.Github:
|
||||
req.Header.Set(githubHeader, "pull_request")
|
||||
event := fmt.Sprintf(`{"action": "%s"}`, c.Action)
|
||||
When(v.Validate(req, secret)).ThenReturn([]byte(event), nil)
|
||||
repo := models.Repo{}
|
||||
pull := models.PullRequest{State: models.Closed}
|
||||
When(p.ParseGithubPullEvent(matchers.AnyPtrToGithubPullRequestEvent())).ThenReturn(pull, repo, repo, models.User{}, nil)
|
||||
}
|
||||
w := httptest.NewRecorder()
|
||||
e.Post(w, req)
|
||||
responseContains(t, w, http.StatusOK, "Processing...")
|
||||
cr.VerifyWasCalledOnce().RunAutoplanCommand(models.Repo{}, models.Repo{}, models.PullRequest{State: models.Closed}, models.User{})
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func setup(t *testing.T) (server.EventsController, *mocks.MockGithubRequestValidator, *mocks.MockGitlabRequestParserValidator, *emocks.MockEventParsing, *emocks.MockCommandRunner, *emocks.MockPullCleaner, *vcsmocks.MockClientProxy, *emocks.MockCommentParsing) {
|
||||
RegisterMockTestingT(t)
|
||||
v := mocks.NewMockGithubRequestValidator()
|
||||
@@ -414,6 +508,7 @@ func setup(t *testing.T) (server.EventsController, *mocks.MockGithubRequestValid
|
||||
c := emocks.NewMockPullCleaner()
|
||||
vcsmock := vcsmocks.NewMockClientProxy()
|
||||
e := server.EventsController{
|
||||
TestingMode: true,
|
||||
Logger: logging.NewNoopLogger(),
|
||||
GithubRequestValidator: v,
|
||||
Parser: p,
|
||||
|
||||
27
server/router_test.go
Normal file
27
server/router_test.go
Normal file
@@ -0,0 +1,27 @@
|
||||
package server_test
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"testing"
|
||||
|
||||
"github.com/gorilla/mux"
|
||||
"github.com/runatlantis/atlantis/server"
|
||||
. "github.com/runatlantis/atlantis/testing"
|
||||
)
|
||||
|
||||
func TestRouter_GenerateLockURL(t *testing.T) {
|
||||
queryParam := "queryparam"
|
||||
routeName := "routename"
|
||||
atlantisURL := "https://example.com"
|
||||
|
||||
underlyingRouter := mux.NewRouter()
|
||||
underlyingRouter.HandleFunc("/lock", func(_ http.ResponseWriter, _ *http.Request) {}).Methods("GET").Queries(queryParam, "{queryparam}").Name(routeName)
|
||||
|
||||
router := &server.Router{
|
||||
AtlantisURL: atlantisURL,
|
||||
LockViewRouteIDQueryParam: queryParam,
|
||||
LockViewRouteName: routeName,
|
||||
Underlying: underlyingRouter,
|
||||
}
|
||||
Equals(t, "https://example.com/lock?queryparam=myid", router.GenerateLockURL("myid"))
|
||||
}
|
||||
@@ -63,7 +63,7 @@ type Server struct {
|
||||
AtlantisVersion string
|
||||
Router *mux.Router
|
||||
Port int
|
||||
CommandHandler *events.CommandHandler
|
||||
CommandRunner *events.DefaultCommandRunner
|
||||
Logger *logging.SimpleLogger
|
||||
Locker locking.Locker
|
||||
AtlantisURL string
|
||||
@@ -221,45 +221,43 @@ func NewServer(userConfig UserConfig, config Config) (*Server, error) {
|
||||
GitlabToken: userConfig.GitlabToken,
|
||||
}
|
||||
defaultTfVersion := terraformClient.Version()
|
||||
commandHandler := &events.CommandHandler{
|
||||
EventParser: eventParser,
|
||||
commandRunner := &events.DefaultCommandRunner{
|
||||
VCSClient: vcsClient,
|
||||
GithubPullGetter: githubClient,
|
||||
GitlabMergeRequestGetter: gitlabClient,
|
||||
CommitStatusUpdater: commitStatusUpdater,
|
||||
AtlantisWorkspaceLocker: workspaceLocker,
|
||||
EventParser: eventParser,
|
||||
MarkdownRenderer: markdownRenderer,
|
||||
Logger: logger,
|
||||
AllowForkPRs: userConfig.AllowForkPRs,
|
||||
AllowForkPRsFlag: config.AllowForkPRsFlag,
|
||||
PullRequestOperator: &events.DefaultPullRequestOperator{
|
||||
TerraformExecutor: terraformClient,
|
||||
DefaultTFVersion: defaultTfVersion,
|
||||
ParserValidator: &yaml.ParserValidator{},
|
||||
ProjectFinder: &events.DefaultProjectFinder{},
|
||||
VCSClient: vcsClient,
|
||||
Workspace: workspace,
|
||||
ProjectOperator: events.ProjectOperator{
|
||||
Locker: projectLocker,
|
||||
LockURLGenerator: router,
|
||||
InitStepOperator: runtime.InitStepOperator{
|
||||
TerraformExecutor: terraformClient,
|
||||
DefaultTFVersion: defaultTfVersion,
|
||||
},
|
||||
PlanStepOperator: runtime.PlanStepOperator{
|
||||
TerraformExecutor: terraformClient,
|
||||
DefaultTFVersion: defaultTfVersion,
|
||||
},
|
||||
ApplyStepOperator: runtime.ApplyStepOperator{
|
||||
TerraformExecutor: terraformClient,
|
||||
},
|
||||
RunStepOperator: runtime.RunStepOperator{},
|
||||
ApprovalOperator: runtime.ApprovalOperator{
|
||||
VCSClient: vcsClient,
|
||||
},
|
||||
Workspace: workspace,
|
||||
Webhooks: webhooksManager,
|
||||
ProjectCommandBuilder: &events.DefaultProjectCommandBuilder{
|
||||
ParserValidator: &yaml.ParserValidator{},
|
||||
ProjectFinder: &events.DefaultProjectFinder{},
|
||||
VCSClient: vcsClient,
|
||||
Workspace: workspace,
|
||||
},
|
||||
ProjectCommandRunner: &events.ProjectCommandRunner{
|
||||
Locker: projectLocker,
|
||||
LockURLGenerator: router,
|
||||
InitStepRunner: runtime.InitStepRunner{
|
||||
TerraformExecutor: terraformClient,
|
||||
DefaultTFVersion: defaultTfVersion,
|
||||
},
|
||||
PlanStepRunner: runtime.PlanStepRunner{
|
||||
TerraformExecutor: terraformClient,
|
||||
DefaultTFVersion: defaultTfVersion,
|
||||
},
|
||||
ApplyStepRunner: runtime.ApplyStepRunner{
|
||||
TerraformExecutor: terraformClient,
|
||||
},
|
||||
RunStepRunner: runtime.RunStepRunner{},
|
||||
PullApprovedChecker: runtime.PullApprovedChecker{
|
||||
VCSClient: vcsClient,
|
||||
},
|
||||
Workspace: workspace,
|
||||
Webhooks: webhooksManager,
|
||||
AtlantisWorkspaceLocker: workspaceLocker,
|
||||
},
|
||||
}
|
||||
repoWhitelist := &events.RepoWhitelistChecker{
|
||||
@@ -273,7 +271,7 @@ func NewServer(userConfig UserConfig, config Config) (*Server, error) {
|
||||
LockDetailTemplate: lockTemplate,
|
||||
}
|
||||
eventsController := &EventsController{
|
||||
CommandRunner: commandHandler,
|
||||
CommandRunner: commandRunner,
|
||||
PullCleaner: pullClosedExecutor,
|
||||
Parser: eventParser,
|
||||
CommentParser: commentParser,
|
||||
@@ -285,14 +283,12 @@ func NewServer(userConfig UserConfig, config Config) (*Server, error) {
|
||||
RepoWhitelistChecker: repoWhitelist,
|
||||
SupportedVCSHosts: supportedVCSHosts,
|
||||
VCSClient: vcsClient,
|
||||
AtlantisGithubUser: models.User{Username: userConfig.GithubUser},
|
||||
AtlantisGitlabUser: models.User{Username: userConfig.GitlabUser},
|
||||
}
|
||||
return &Server{
|
||||
AtlantisVersion: config.AtlantisVersion,
|
||||
Router: underlyingRouter,
|
||||
Port: userConfig.Port,
|
||||
CommandHandler: commandHandler,
|
||||
CommandRunner: commandRunner,
|
||||
Logger: logger,
|
||||
Locker: lockingClient,
|
||||
AtlantisURL: userConfig.AtlantisURL,
|
||||
@@ -313,7 +309,8 @@ func (s *Server) Start() error {
|
||||
s.Router.PathPrefix("/static/").Handler(http.FileServer(&assetfs.AssetFS{Asset: static.Asset, AssetDir: static.AssetDir, AssetInfo: static.AssetInfo}))
|
||||
s.Router.HandleFunc("/events", s.EventsController.Post).Methods("POST")
|
||||
s.Router.HandleFunc("/locks", s.LocksController.DeleteLock).Methods("DELETE").Queries("id", "{id:.*}")
|
||||
s.Router.HandleFunc("/lock", s.LocksController.GetLock).Methods("GET").Queries(LockViewRouteIDQueryParam, "{id}").Name(LockViewRouteName)
|
||||
s.Router.HandleFunc("/lock", s.LocksController.GetLock).Methods("GET").
|
||||
Queries(LockViewRouteIDQueryParam, fmt.Sprintf("{%s}", LockViewRouteIDQueryParam)).Name(LockViewRouteName)
|
||||
n := negroni.New(&negroni.Recovery{
|
||||
Logger: log.New(os.Stdout, "", log.LstdFlags),
|
||||
PrintStack: false,
|
||||
|
||||
@@ -103,6 +103,7 @@ func TestIndex_Success(t *testing.T) {
|
||||
}
|
||||
|
||||
func responseContains(t *testing.T, r *httptest.ResponseRecorder, status int, bodySubstr string) {
|
||||
t.Helper()
|
||||
Equals(t, status, r.Result().StatusCode)
|
||||
body, _ := ioutil.ReadAll(r.Result().Body)
|
||||
Assert(t, strings.Contains(string(body), bodySubstr), "exp %q to be contained in %q", bodySubstr, string(body))
|
||||
|
||||
@@ -1,15 +1,15 @@
|
||||
{
|
||||
"action": "closed",
|
||||
"number": 1,
|
||||
"number": 2,
|
||||
"pull_request": {
|
||||
"url": "https://api.github.com/repos/runatlantis/atlantis-tests/pulls/1",
|
||||
"url": "https://api.github.com/repos/runatlantis/atlantis-tests/pulls/2",
|
||||
"id": 193308707,
|
||||
"node_id": "MDExOlB1bGxSZXF1ZXN0MTkzMzA4NzA3",
|
||||
"html_url": "https://github.com/runatlantis/atlantis-tests/pull/1",
|
||||
"diff_url": "https://github.com/runatlantis/atlantis-tests/pull/1.diff",
|
||||
"patch_url": "https://github.com/runatlantis/atlantis-tests/pull/1.patch",
|
||||
"issue_url": "https://api.github.com/repos/runatlantis/atlantis-tests/issues/1",
|
||||
"number": 1,
|
||||
"html_url": "https://github.com/runatlantis/atlantis-tests/pull/2",
|
||||
"diff_url": "https://github.com/runatlantis/atlantis-tests/pull/2.diff",
|
||||
"patch_url": "https://github.com/runatlantis/atlantis-tests/pull/2.patch",
|
||||
"issue_url": "https://api.github.com/repos/runatlantis/atlantis-tests/issues/2",
|
||||
"number": 2,
|
||||
"state": "closed",
|
||||
"locked": false,
|
||||
"title": "Add new project layouts",
|
||||
@@ -53,10 +53,10 @@
|
||||
|
||||
],
|
||||
"milestone": null,
|
||||
"commits_url": "https://api.github.com/repos/runatlantis/atlantis-tests/pulls/1/commits",
|
||||
"review_comments_url": "https://api.github.com/repos/runatlantis/atlantis-tests/pulls/1/comments",
|
||||
"commits_url": "https://api.github.com/repos/runatlantis/atlantis-tests/pulls/2/commits",
|
||||
"review_comments_url": "https://api.github.com/repos/runatlantis/atlantis-tests/pulls/2/comments",
|
||||
"review_comment_url": "https://api.github.com/repos/runatlantis/atlantis-tests/pulls/comments{/number}",
|
||||
"comments_url": "https://api.github.com/repos/runatlantis/atlantis-tests/issues/1/comments",
|
||||
"comments_url": "https://api.github.com/repos/runatlantis/atlantis-tests/issues/2/comments",
|
||||
"statuses_url": "https://api.github.com/repos/runatlantis/atlantis-tests/statuses/5e2d140b2d74bf61675677f01dc947ae8512e18e",
|
||||
"head": {
|
||||
"label": "runatlantis:atlantisyaml",
|
||||
@@ -308,25 +308,25 @@
|
||||
},
|
||||
"_links": {
|
||||
"self": {
|
||||
"href": "https://api.github.com/repos/runatlantis/atlantis-tests/pulls/1"
|
||||
"href": "https://api.github.com/repos/runatlantis/atlantis-tests/pulls/2"
|
||||
},
|
||||
"html": {
|
||||
"href": "https://github.com/runatlantis/atlantis-tests/pull/1"
|
||||
"href": "https://github.com/runatlantis/atlantis-tests/pull/2"
|
||||
},
|
||||
"issue": {
|
||||
"href": "https://api.github.com/repos/runatlantis/atlantis-tests/issues/1"
|
||||
"href": "https://api.github.com/repos/runatlantis/atlantis-tests/issues/2"
|
||||
},
|
||||
"comments": {
|
||||
"href": "https://api.github.com/repos/runatlantis/atlantis-tests/issues/1/comments"
|
||||
"href": "https://api.github.com/repos/runatlantis/atlantis-tests/issues/2/comments"
|
||||
},
|
||||
"review_comments": {
|
||||
"href": "https://api.github.com/repos/runatlantis/atlantis-tests/pulls/1/comments"
|
||||
"href": "https://api.github.com/repos/runatlantis/atlantis-tests/pulls/2/comments"
|
||||
},
|
||||
"review_comment": {
|
||||
"href": "https://api.github.com/repos/runatlantis/atlantis-tests/pulls/comments{/number}"
|
||||
},
|
||||
"commits": {
|
||||
"href": "https://api.github.com/repos/runatlantis/atlantis-tests/pulls/1/commits"
|
||||
"href": "https://api.github.com/repos/runatlantis/atlantis-tests/pulls/2/commits"
|
||||
},
|
||||
"statuses": {
|
||||
"href": "https://api.github.com/repos/runatlantis/atlantis-tests/statuses/5e2d140b2d74bf61675677f01dc947ae8512e18e"
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
"number": 2,
|
||||
"state": "open",
|
||||
"locked": false,
|
||||
"title": "Noyaml",
|
||||
"title": "branch",
|
||||
"user": {
|
||||
"login": "runatlantis",
|
||||
"id": 1034429,
|
||||
@@ -59,8 +59,8 @@
|
||||
"comments_url": "https://api.github.com/repos/runatlantis/atlantis-tests/issues/2/comments",
|
||||
"statuses_url": "https://api.github.com/repos/runatlantis/atlantis-tests/statuses/c31fd9ea6f557ad2ea659944c3844a059b83bc5d",
|
||||
"head": {
|
||||
"label": "runatlantis:noyaml",
|
||||
"ref": "noyaml",
|
||||
"label": "runatlantis:branch",
|
||||
"ref": "branch",
|
||||
"sha": "c31fd9ea6f557ad2ea659944c3844a059b83bc5d",
|
||||
"user": {
|
||||
"login": "runatlantis",
|
||||
|
||||
@@ -1,7 +0,0 @@
|
||||
Ran Apply in dir: `.` workspace: `default`
|
||||
**Apply Error**
|
||||
```
|
||||
no plan found at path "." and workspace "default"–did you run plan?
|
||||
```
|
||||
|
||||
|
||||
@@ -1,61 +0,0 @@
|
||||
Ran Plan for 2 projects:
|
||||
1. workspace: `default` path: `.`
|
||||
1. workspace: `default` path: `.`
|
||||
|
||||
### 1. workspace: `default` path: `.`
|
||||
**Plan Error**
|
||||
```
|
||||
exit status 1: running "sh -c terraform init -no-color -backend-config=default.backend.tfvars" in "/var/folders/z1/4z__7jv12y7f9yz__nwy55z40000gp/T/201347935/repos/runatlantis/atlantis-tests/1/default":
|
||||
|
||||
Initializing the backend...
|
||||
|
||||
Successfully configured the backend "local"! Terraform will automatically
|
||||
use this backend unless the backend configuration changes.
|
||||
|
||||
Initializing provider plugins...
|
||||
- Checking for available provider plugins on https://releases.hashicorp.com...
|
||||
|
||||
Error installing provider "null": Get https://releases.hashicorp.com/terraform-provider-null/: net/http: TLS handshake timeout.
|
||||
|
||||
Terraform analyses the configuration and state and automatically downloads
|
||||
plugins for the providers used. However, when attempting to download this
|
||||
plugin an unexpected error occured.
|
||||
|
||||
This may be caused if for some reason Terraform is unable to reach the
|
||||
plugin repository. The repository may be unreachable if access is blocked
|
||||
by a firewall.
|
||||
|
||||
If automatic installation is not possible or desirable in your environment,
|
||||
you may alternatively manually install plugins by downloading a suitable
|
||||
distribution package and placing the plugin's executable file in the
|
||||
following directory:
|
||||
terraform.d/plugins/darwin_amd64
|
||||
|
||||
|
||||
```
|
||||
|
||||
---
|
||||
### 2. workspace: `default` path: `.`
|
||||
```diff
|
||||
Refreshing Terraform state in-memory prior to plan...
|
||||
The refreshed state will be used to calculate this plan, but will not be
|
||||
persisted to local or remote state storage.
|
||||
|
||||
|
||||
------------------------------------------------------------------------
|
||||
|
||||
An execution plan has been generated and is shown below.
|
||||
Resource actions are indicated with the following symbols:
|
||||
+ create
|
||||
|
||||
Terraform will perform the following actions:
|
||||
|
||||
+ null_resource.simple
|
||||
id: <computed>
|
||||
Plan: 1 to add, 0 to change, 0 to destroy.
|
||||
|
||||
```
|
||||
|
||||
* To **discard** this plan click [here](lock-url).
|
||||
---
|
||||
|
||||
Reference in New Issue
Block a user