-
Notifications
You must be signed in to change notification settings - Fork 1.1k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Signed-off-by: X-Guardian <[email protected]>
- Loading branch information
1 parent
4d68422
commit aae2fd7
Showing
5 changed files
with
2,053 additions
and
7 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,197 @@ | ||
package events | ||
|
||
import ( | ||
"github.com/runatlantis/atlantis/server/core/locking" | ||
"github.com/runatlantis/atlantis/server/events/command" | ||
"github.com/runatlantis/atlantis/server/events/models" | ||
"github.com/runatlantis/atlantis/server/events/vcs" | ||
) | ||
|
||
func NewApplyCommandRunner( | ||
vcsClient vcs.Client, | ||
disableApplyAll bool, | ||
applyCommandLocker locking.ApplyLockChecker, | ||
commitStatusUpdater CommitStatusUpdater, | ||
prjCommandBuilder ProjectApplyCommandBuilder, | ||
prjCmdRunner ProjectApplyCommandRunner, | ||
autoMerger *AutoMerger, | ||
pullUpdater *PullUpdater, | ||
dbUpdater *DBUpdater, | ||
backend locking.Backend, | ||
parallelPoolSize int, | ||
SilenceNoProjects bool, | ||
silenceVCSStatusNoProjects bool, | ||
pullReqStatusFetcher vcs.PullReqStatusFetcher, | ||
) *ApplyCommandRunner { | ||
return &ApplyCommandRunner{ | ||
vcsClient: vcsClient, | ||
DisableApplyAll: disableApplyAll, | ||
locker: applyCommandLocker, | ||
commitStatusUpdater: commitStatusUpdater, | ||
prjCmdBuilder: prjCommandBuilder, | ||
prjCmdRunner: prjCmdRunner, | ||
autoMerger: autoMerger, | ||
pullUpdater: pullUpdater, | ||
dbUpdater: dbUpdater, | ||
Backend: backend, | ||
parallelPoolSize: parallelPoolSize, | ||
SilenceNoProjects: SilenceNoProjects, | ||
silenceVCSStatusNoProjects: silenceVCSStatusNoProjects, | ||
pullReqStatusFetcher: pullReqStatusFetcher, | ||
} | ||
} | ||
|
||
type ApplyCommandRunner struct { | ||
DisableApplyAll bool | ||
Backend locking.Backend | ||
locker locking.ApplyLockChecker | ||
vcsClient vcs.Client | ||
commitStatusUpdater CommitStatusUpdater | ||
prjCmdBuilder ProjectApplyCommandBuilder | ||
prjCmdRunner ProjectApplyCommandRunner | ||
autoMerger *AutoMerger | ||
pullUpdater *PullUpdater | ||
dbUpdater *DBUpdater | ||
parallelPoolSize int | ||
pullReqStatusFetcher vcs.PullReqStatusFetcher | ||
// SilenceNoProjects is whether Atlantis should respond to PRs if no projects | ||
// are found | ||
SilenceNoProjects bool | ||
// SilenceVCSStatusNoPlans is whether any plan should set commit status if no projects | ||
// are found | ||
silenceVCSStatusNoProjects bool | ||
SilencePRComments []string | ||
} | ||
|
||
func (a *ApplyCommandRunner) Run(ctx *command.Context, cmd *CommentCommand) { | ||
var err error | ||
baseRepo := ctx.Pull.BaseRepo | ||
pull := ctx.Pull | ||
|
||
// Get the mergeable status before we set any build statuses of our own. | ||
// We do this here because when we set a "Pending" status, if users have | ||
// required the Atlantis status checks to pass, then we've now changed | ||
// the mergeability status of the pull request. | ||
// This sets the approved, mergeable, and sqlocked status in the context. | ||
ctx.PullRequestStatus, err = a.pullReqStatusFetcher.FetchPullStatus(ctx.Log, pull) | ||
if err != nil { | ||
// On error we continue the request with mergeable assumed false. | ||
// We want to continue because not all apply's will need this status, | ||
// only if they rely on the mergeability requirement. | ||
// All PullRequestStatus fields are set to false by default when error. | ||
ctx.Log.Warn("unable to get pull request status: %s. Continuing with mergeable and approved assumed false", err) | ||
} | ||
|
||
var projectCmds []command.ProjectContext | ||
projectCmds, err = a.prjCmdBuilder.BuildApplyCommands(ctx, cmd) | ||
|
||
if err != nil { | ||
if statusErr := a.commitStatusUpdater.UpdateCombined(ctx.Log, ctx.Pull.BaseRepo, ctx.Pull, models.FailedCommitStatus, cmd.CommandName()); statusErr != nil { | ||
ctx.Log.Warn("unable to update commit status: %s", statusErr) | ||
} | ||
a.pullUpdater.updatePull(ctx, cmd, command.Result{Error: err}) | ||
return | ||
} | ||
|
||
// If there are no projects to apply, don't respond to the PR and ignore | ||
if len(projectCmds) == 0 && a.SilenceNoProjects { | ||
ctx.Log.Info("determined there was no project to run plan in") | ||
if !a.silenceVCSStatusNoProjects { | ||
if cmd.IsForSpecificProject() { | ||
// With a specific apply, just reset the status so it's not stuck in pending state | ||
pullStatus, err := a.Backend.GetPullStatus(pull) | ||
if err != nil { | ||
ctx.Log.Warn("unable to fetch pull status: %s", err) | ||
return | ||
} | ||
if pullStatus == nil { | ||
// default to 0/0 | ||
ctx.Log.Debug("setting VCS status to 0/0 success as no previous state was found") | ||
if err := a.commitStatusUpdater.UpdateCombinedCount(ctx.Log, baseRepo, pull, models.SuccessCommitStatus, command.Apply, 0, 0); err != nil { | ||
ctx.Log.Warn("unable to update commit status: %s", err) | ||
} | ||
return | ||
} | ||
ctx.Log.Debug("resetting VCS status") | ||
a.updateCommitStatus(ctx, *pullStatus) | ||
} else { | ||
// With a generic apply, we set successful commit statuses | ||
// with 0/0 projects planned successfully because some users require | ||
// the Atlantis status to be passing for all pull requests. | ||
// Does not apply to skipped runs for specific projects | ||
ctx.Log.Debug("setting VCS status to success with no projects found") | ||
if err := a.commitStatusUpdater.UpdateCombinedCount(ctx.Log, baseRepo, pull, models.SuccessCommitStatus, command.Apply, 0, 0); err != nil { | ||
ctx.Log.Warn("unable to update commit status: %s", err) | ||
} | ||
} | ||
} | ||
return | ||
} | ||
|
||
// Only run commands in parallel if enabled | ||
var result command.Result | ||
if a.isParallelEnabled(projectCmds) { | ||
ctx.Log.Info("Running applies in parallel") | ||
result = runProjectCmdsParallelGroups(ctx, projectCmds, a.prjCmdRunner.Apply, a.parallelPoolSize) | ||
} else { | ||
result = runProjectCmds(projectCmds, a.prjCmdRunner.Apply) | ||
} | ||
|
||
a.pullUpdater.updatePull( | ||
ctx, | ||
cmd, | ||
result) | ||
|
||
pullStatus, err := a.dbUpdater.updateDB(ctx, pull, result.ProjectResults) | ||
if err != nil { | ||
ctx.Log.Err("writing results: %s", err) | ||
return | ||
} | ||
|
||
a.updateCommitStatus(ctx, pullStatus) | ||
|
||
if a.autoMerger.automergeEnabled(projectCmds) && !cmd.AutoMergeDisabled { | ||
a.autoMerger.automerge(ctx, pullStatus, a.autoMerger.deleteSourceBranchOnMergeEnabled(projectCmds), cmd.AutoMergeMethod) | ||
} | ||
} | ||
|
||
func (a *ApplyCommandRunner) isParallelEnabled(projectCmds []command.ProjectContext) bool { | ||
return len(projectCmds) > 0 && projectCmds[0].ParallelApplyEnabled | ||
} | ||
|
||
func (a *ApplyCommandRunner) updateCommitStatus(ctx *command.Context, pullStatus models.PullStatus) { | ||
var numSuccess int | ||
var numErrored int | ||
status := models.SuccessCommitStatus | ||
|
||
numSuccess = pullStatus.StatusCount(models.AppliedPlanStatus) + pullStatus.StatusCount(models.PlannedNoChangesPlanStatus) | ||
numErrored = pullStatus.StatusCount(models.ErroredApplyStatus) | ||
|
||
if numErrored > 0 { | ||
status = models.FailedCommitStatus | ||
} else if numSuccess < len(pullStatus.Projects) { | ||
// If there are plans that haven't been applied yet, we'll use a pending | ||
// status. | ||
status = models.PendingCommitStatus | ||
} | ||
|
||
if err := a.commitStatusUpdater.UpdateCombinedCount( | ||
ctx.Log, | ||
ctx.Pull.BaseRepo, | ||
ctx.Pull, | ||
status, | ||
command.Apply, | ||
numSuccess, | ||
len(pullStatus.Projects), | ||
); err != nil { | ||
ctx.Log.Warn("unable to update commit status: %s", err) | ||
} | ||
} | ||
|
||
// applyAllDisabledComment is posted when apply all commands (i.e. "atlantis apply") | ||
// are disabled and an apply all command is issued. | ||
var applyAllDisabledComment = "**Error:** Running `atlantis apply` without flags is disabled." + | ||
" You must specify which project to apply via the `-d <dir>`, `-w <workspace>` or `-p <project name>` flags." | ||
|
||
// applyDisabledComment is posted when apply commands are disabled globally and an apply command is issued. | ||
var applyDisabledComment = "**Error:** Running `atlantis apply` is disabled." |
Oops, something went wrong.