diff --git a/runatlantis.io/docs/using-atlantis.md b/runatlantis.io/docs/using-atlantis.md index 24a0a29352..0311fa9e81 100644 --- a/runatlantis.io/docs/using-atlantis.md +++ b/runatlantis.io/docs/using-atlantis.md @@ -45,6 +45,10 @@ atlantis plan -w staging * `-w workspace` Switch to this [Terraform workspace](https://www.terraform.io/docs/state/workspaces.html) before planning. Defaults to `default`. If not using Terraform workspaces you can ignore this. * `--verbose` Append Atlantis log to comment. +::: warning NOTE +An `atlantis plan` (without flags), like autoplans, discards all plans previously created with `atlantis plan` `-p`/`-d`/`-w` +::: + ### Additional Terraform flags If you need to run `terraform plan` with additional arguments, like `-target=resource` or `-var 'foo-bar'` or `-var-file myfile.tfvars` @@ -65,6 +69,7 @@ Runs `terraform apply` for the plan that matches the directory/project/workspace ::: tip If no directory/project/workspace is specified, ex. `atlantis apply`, this command will apply **all unapplied plans from this pull request**. +This includes all projects that have been planned manually with `atlantis plan` `-p`/`-d`/`-w` since the last autoplan or `atlantis plan` command. ::: ### Examples diff --git a/server/events/command_runner_test.go b/server/events/command_runner_test.go index 1254e96d03..a25603d874 100644 --- a/server/events/command_runner_test.go +++ b/server/events/command_runner_test.go @@ -431,9 +431,72 @@ func TestRunUnlockCommandFail_VCSComment(t *testing.T) { vcsClient.VerifyWasCalledOnce().CreateComment(fixtures.GithubRepo, fixtures.Pull.Num, "Failed to delete PR locks", "unlock") } +func TestRunAutoplanCommand_DeletePlans(t *testing.T) { + setup(t) + tmp, cleanup := TempDir(t) + defer cleanup() + boltDB, err := db.New(tmp) + Ok(t, err) + dbUpdater.DB = boltDB + applyCommandRunner.DB = boltDB + autoMerger.GlobalAutomerge = true + defer func() { autoMerger.GlobalAutomerge = false }() + + When(projectCommandBuilder.BuildAutoplanCommands(matchers.AnyPtrToEventsCommandContext())). + ThenReturn([]models.ProjectCommandContext{ + { + CommandName: models.PlanCommand, + }, + { + CommandName: models.PlanCommand, + }, + }, nil) + When(projectCommandRunner.Plan(matchers.AnyModelsProjectCommandContext())).ThenReturn(models.ProjectResult{PlanSuccess: &models.PlanSuccess{}}) + When(workingDir.GetPullDir(matchers.AnyModelsRepo(), matchers.AnyModelsPullRequest())).ThenReturn(tmp, nil) + fixtures.Pull.BaseRepo = fixtures.GithubRepo + ch.RunAutoplanCommand(fixtures.GithubRepo, fixtures.GithubRepo, fixtures.Pull, fixtures.User) + pendingPlanFinder.VerifyWasCalledOnce().DeletePlans(tmp) +} + +func TestRunGenericPlanCommand_DeletePlans(t *testing.T) { + setup(t) + tmp, cleanup := TempDir(t) + defer cleanup() + boltDB, err := db.New(tmp) + Ok(t, err) + dbUpdater.DB = boltDB + applyCommandRunner.DB = boltDB + autoMerger.GlobalAutomerge = true + defer func() { autoMerger.GlobalAutomerge = false }() + + When(projectCommandRunner.Plan(matchers.AnyModelsProjectCommandContext())).ThenReturn(models.ProjectResult{PlanSuccess: &models.PlanSuccess{}}) + When(workingDir.GetPullDir(matchers.AnyModelsRepo(), matchers.AnyModelsPullRequest())).ThenReturn(tmp, nil) + fixtures.Pull.BaseRepo = fixtures.GithubRepo + ch.RunCommentCommand(fixtures.GithubRepo, nil, nil, fixtures.User, fixtures.Pull.Num, &events.CommentCommand{Name: models.PlanCommand}) + pendingPlanFinder.VerifyWasCalledOnce().DeletePlans(tmp) +} + +func TestRunSpecificPlanCommandDoesnt_DeletePlans(t *testing.T) { + setup(t) + tmp, cleanup := TempDir(t) + defer cleanup() + boltDB, err := db.New(tmp) + Ok(t, err) + dbUpdater.DB = boltDB + applyCommandRunner.DB = boltDB + autoMerger.GlobalAutomerge = true + defer func() { autoMerger.GlobalAutomerge = false }() + + When(projectCommandRunner.Plan(matchers.AnyModelsProjectCommandContext())).ThenReturn(models.ProjectResult{PlanSuccess: &models.PlanSuccess{}}) + When(workingDir.GetPullDir(matchers.AnyModelsRepo(), matchers.AnyModelsPullRequest())).ThenReturn(tmp, nil) + fixtures.Pull.BaseRepo = fixtures.GithubRepo + ch.RunCommentCommand(fixtures.GithubRepo, nil, nil, fixtures.User, fixtures.Pull.Num, &events.CommentCommand{Name: models.PlanCommand, ProjectName: "default"}) + pendingPlanFinder.VerifyWasCalled(Never()).DeletePlans(tmp) +} + // Test that if one plan fails and we are using automerge, that // we delete the plans. -func TestRunAutoplanCommand_DeletePlans(t *testing.T) { +func TestRunAutoplanCommandWithError_DeletePlans(t *testing.T) { setup(t) tmp, cleanup := TempDir(t) defer cleanup() @@ -476,7 +539,8 @@ func TestRunAutoplanCommand_DeletePlans(t *testing.T) { ThenReturn(tmp, nil) fixtures.Pull.BaseRepo = fixtures.GithubRepo ch.RunAutoplanCommand(fixtures.GithubRepo, fixtures.GithubRepo, fixtures.Pull, fixtures.User) - pendingPlanFinder.VerifyWasCalledOnce().DeletePlans(tmp) + // gets called twice: the first time before the plan starts, the second time after the plan errors + pendingPlanFinder.VerifyWasCalled(Times(2)).DeletePlans(tmp) } func TestFailedApprovalCreatesFailedStatusUpdate(t *testing.T) { diff --git a/server/events/plan_command_runner.go b/server/events/plan_command_runner.go index 36fcb2253d..f3e6f59f37 100644 --- a/server/events/plan_command_runner.go +++ b/server/events/plan_command_runner.go @@ -105,6 +105,10 @@ func (p *PlanCommandRunner) runAutoplan(ctx *CommandContext) { ctx.Log.Warn("unable to update commit status: %s", err) } + // discard previous plans that might not be relevant anymore + ctx.Log.Debug("deleting previous plans") + p.deletePlans(ctx) + // Only run commands in parallel if enabled var result CommandResult if p.isParallelEnabled(projectCmds) { @@ -179,6 +183,13 @@ func (p *PlanCommandRunner) run(ctx *CommandContext, cmd *CommentCommand) { projectCmds, policyCheckCmds := p.partitionProjectCmds(ctx, projectCmds) + // if the plan is generic, new plans will be generated based on changes + // discard previous plans that might not be relevant anymore + if !cmd.IsForSpecificProject() { + ctx.Log.Debug("deleting previous plans") + p.deletePlans(ctx) + } + // Only run commands in parallel if enabled var result CommandResult if p.isParallelEnabled(projectCmds) {