Skip to content

Commit 89a83aa

Browse files
giuli007giuli007
authored andcommitted
fix: Delete locks and workdirs with potentially stale previous plans which fixes 1624 (runatlantis#1704)
* Delete previous plans on autoplan or atlantis plan When using non-default workspaces, plans are stored in pr-and-workspace-specific directories. If a PR is subsequently updated it might happen that some of the plans are no longer relevant with regards to the latest changes. This change ensures that plans are always deleted when a generic plan is triggered either by autoplan or by a "atlantis plan" command. NB Plans are not cleaned up when specific projects are planned explicitly with "atlantis plan -p/-d/-w". * Use DeleteLockCommand to delete locks and workdirs containing previous plans Co-authored-by: giuli007 <[email protected]>
1 parent 9e6427e commit 89a83aa

File tree

5 files changed

+100
-1
lines changed

5 files changed

+100
-1
lines changed

Diff for: runatlantis.io/docs/using-atlantis.md

+5
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,10 @@ atlantis plan -w staging
4545
* `-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.
4646
* `--verbose` Append Atlantis log to comment.
4747

48+
::: warning NOTE
49+
A `atlantis plan` (without flags), like autoplans, discards all plans previously created with `atlantis plan` `-p`/`-d`/`-w`
50+
:::
51+
4852
### Additional Terraform flags
4953

5054
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
6569

6670
::: tip
6771
If no directory/project/workspace is specified, ex. `atlantis apply`, this command will apply **all unapplied plans from this pull request**.
72+
This includes all projects that have been planned manually with `atlantis plan` `-p`/`-d`/`-w` since the last autoplan or `atlantis plan` command.
6873
:::
6974

7075
### Examples

Diff for: server/controllers/events/events_controller_e2e_test.go

+3
Original file line numberDiff line numberDiff line change
@@ -872,6 +872,8 @@ func setupE2E(t *testing.T, repoDir string) (events_controllers.VCSEventsControl
872872
locker := events.NewDefaultWorkingDirLocker()
873873
parser := &config.ParserValidator{}
874874

875+
deleteLockCommand := mocks.NewMockDeleteLockCommand()
876+
875877
globalCfgArgs := valid.GlobalCfgArgs{
876878
AllowRepoCfg: true,
877879
MergeableReq: false,
@@ -1024,6 +1026,7 @@ func setupE2E(t *testing.T, repoDir string) (events_controllers.VCSEventsControl
10241026
parallelPoolSize,
10251027
silenceNoProjects,
10261028
boltdb,
1029+
deleteLockCommand,
10271030
)
10281031

10291032
e2ePullReqStatusFetcher := vcs.NewPullReqStatusFetcher(e2eVCSClient)

Diff for: server/events/command_runner_test.go

+71-1
Original file line numberDiff line numberDiff line change
@@ -132,6 +132,7 @@ func setup(t *testing.T) *vcsmocks.MockClient {
132132
parallelPoolSize,
133133
SilenceNoProjects,
134134
defaultBoltDB,
135+
deleteLockCommand,
135136
)
136137

137138
pullReqStatusFetcher := vcs.NewPullReqStatusFetcher(vcsClient)
@@ -527,9 +528,77 @@ func TestRunUnlockCommandFail_VCSComment(t *testing.T) {
527528
vcsClient.VerifyWasCalledOnce().CreateComment(fixtures.GithubRepo, fixtures.Pull.Num, "Failed to delete PR locks", "unlock")
528529
}
529530

531+
func TestRunAutoplanCommand_DeleteLocksByPull(t *testing.T) {
532+
setup(t)
533+
tmp, cleanup := TempDir(t)
534+
defer cleanup()
535+
boltDB, err := db.New(tmp)
536+
Ok(t, err)
537+
dbUpdater.DB = boltDB
538+
applyCommandRunner.DB = boltDB
539+
autoMerger.GlobalAutomerge = true
540+
defer func() { autoMerger.GlobalAutomerge = false }()
541+
542+
When(projectCommandBuilder.BuildAutoplanCommands(matchers.AnyPtrToEventsCommandContext())).
543+
ThenReturn([]models.ProjectCommandContext{
544+
{
545+
CommandName: models.PlanCommand,
546+
},
547+
{
548+
CommandName: models.PlanCommand,
549+
},
550+
}, nil)
551+
When(projectCommandRunner.Plan(matchers.AnyModelsProjectCommandContext())).ThenReturn(models.ProjectResult{PlanSuccess: &models.PlanSuccess{}})
552+
When(workingDir.GetPullDir(matchers.AnyModelsRepo(), matchers.AnyModelsPullRequest())).ThenReturn(tmp, nil)
553+
fixtures.Pull.BaseRepo = fixtures.GithubRepo
554+
ch.RunAutoplanCommand(fixtures.GithubRepo, fixtures.GithubRepo, fixtures.Pull, fixtures.User)
555+
deleteLockCommand.VerifyWasCalledOnce().DeleteLocksByPull(fixtures.Pull.BaseRepo.FullName, fixtures.Pull.Num)
556+
}
557+
558+
func TestRunGenericPlanCommand_DeleteLocksByPull(t *testing.T) {
559+
setup(t)
560+
tmp, cleanup := TempDir(t)
561+
defer cleanup()
562+
boltDB, err := db.New(tmp)
563+
Ok(t, err)
564+
dbUpdater.DB = boltDB
565+
applyCommandRunner.DB = boltDB
566+
autoMerger.GlobalAutomerge = true
567+
defer func() { autoMerger.GlobalAutomerge = false }()
568+
569+
When(projectCommandRunner.Plan(matchers.AnyModelsProjectCommandContext())).ThenReturn(models.ProjectResult{PlanSuccess: &models.PlanSuccess{}})
570+
When(workingDir.GetPullDir(matchers.AnyModelsRepo(), matchers.AnyModelsPullRequest())).ThenReturn(tmp, nil)
571+
pull := &github.PullRequest{State: github.String("open")}
572+
modelPull := models.PullRequest{BaseRepo: fixtures.GithubRepo, State: models.OpenPullState, Num: fixtures.Pull.Num}
573+
When(githubGetter.GetPullRequest(fixtures.GithubRepo, fixtures.Pull.Num)).ThenReturn(pull, nil)
574+
When(eventParsing.ParseGithubPull(pull)).ThenReturn(modelPull, modelPull.BaseRepo, fixtures.GithubRepo, nil)
575+
576+
fixtures.Pull.BaseRepo = fixtures.GithubRepo
577+
ch.RunCommentCommand(fixtures.GithubRepo, nil, nil, fixtures.User, fixtures.Pull.Num, &events.CommentCommand{Name: models.PlanCommand})
578+
deleteLockCommand.VerifyWasCalledOnce().DeleteLocksByPull(fixtures.Pull.BaseRepo.FullName, fixtures.Pull.Num)
579+
}
580+
581+
func TestRunSpecificPlanCommandDoesnt_DeleteLocksByPull(t *testing.T) {
582+
setup(t)
583+
tmp, cleanup := TempDir(t)
584+
defer cleanup()
585+
boltDB, err := db.New(tmp)
586+
Ok(t, err)
587+
dbUpdater.DB = boltDB
588+
applyCommandRunner.DB = boltDB
589+
autoMerger.GlobalAutomerge = true
590+
defer func() { autoMerger.GlobalAutomerge = false }()
591+
592+
When(projectCommandRunner.Plan(matchers.AnyModelsProjectCommandContext())).ThenReturn(models.ProjectResult{PlanSuccess: &models.PlanSuccess{}})
593+
When(workingDir.GetPullDir(matchers.AnyModelsRepo(), matchers.AnyModelsPullRequest())).ThenReturn(tmp, nil)
594+
fixtures.Pull.BaseRepo = fixtures.GithubRepo
595+
ch.RunCommentCommand(fixtures.GithubRepo, nil, nil, fixtures.User, fixtures.Pull.Num, &events.CommentCommand{Name: models.PlanCommand, ProjectName: "default"})
596+
deleteLockCommand.VerifyWasCalled(Never()).DeleteLocksByPull(fixtures.Pull.BaseRepo.FullName, fixtures.Pull.Num)
597+
}
598+
530599
// Test that if one plan fails and we are using automerge, that
531600
// we delete the plans.
532-
func TestRunAutoplanCommand_DeletePlans(t *testing.T) {
601+
func TestRunAutoplanCommandWithError_DeletePlans(t *testing.T) {
533602
setup(t)
534603
tmp, cleanup := TempDir(t)
535604
defer cleanup()
@@ -572,6 +641,7 @@ func TestRunAutoplanCommand_DeletePlans(t *testing.T) {
572641
ThenReturn(tmp, nil)
573642
fixtures.Pull.BaseRepo = fixtures.GithubRepo
574643
ch.RunAutoplanCommand(fixtures.GithubRepo, fixtures.GithubRepo, fixtures.Pull, fixtures.User)
644+
// gets called twice: the first time before the plan starts, the second time after the plan errors
575645
pendingPlanFinder.VerifyWasCalledOnce().DeletePlans(tmp)
576646
}
577647

Diff for: server/events/plan_command_runner.go

+20
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ func NewPlanCommandRunner(
2222
parallelPoolSize int,
2323
SilenceNoProjects bool,
2424
pullStatusFetcher PullStatusFetcher,
25+
deleteLockCommand DeleteLockCommand,
2526
) *PlanCommandRunner {
2627
return &PlanCommandRunner{
2728
silenceVCSStatusNoPlans: silenceVCSStatusNoPlans,
@@ -39,6 +40,7 @@ func NewPlanCommandRunner(
3940
parallelPoolSize: parallelPoolSize,
4041
SilenceNoProjects: SilenceNoProjects,
4142
pullStatusFetcher: pullStatusFetcher,
43+
deleteLockCommand: deleteLockCommand,
4244
}
4345
}
4446

@@ -64,6 +66,7 @@ type PlanCommandRunner struct {
6466
autoMerger *AutoMerger
6567
parallelPoolSize int
6668
pullStatusFetcher PullStatusFetcher
69+
deleteLockCommand DeleteLockCommand
6770
}
6871

6972
func (p *PlanCommandRunner) runAutoplan(ctx *command.Context) {
@@ -106,6 +109,13 @@ func (p *PlanCommandRunner) runAutoplan(ctx *command.Context) {
106109
ctx.Log.Warn("unable to update plan commit status: %s", err)
107110
}
108111

112+
// discard previous plans that might not be relevant anymore
113+
ctx.Log.Debug("deleting locks and workdir(s) with previous plans")
114+
_, err = p.deleteLockCommand.DeleteLocksByPull(ctx.Pull.BaseRepo.FullName, ctx.Pull.Num)
115+
if err != nil {
116+
ctx.Log.Err("deleting locks: %s", err)
117+
}
118+
109119
// Only run commands in parallel if enabled
110120
var result command.Result
111121
if p.isParallelEnabled(projectCmds) {
@@ -180,6 +190,16 @@ func (p *PlanCommandRunner) run(ctx *command.Context, cmd *CommentCommand) {
180190

181191
projectCmds, policyCheckCmds := p.partitionProjectCmds(ctx, projectCmds)
182192

193+
// if the plan is generic, new plans will be generated based on changes
194+
// discard previous plans that might not be relevant anymore
195+
if !cmd.IsForSpecificProject() {
196+
ctx.Log.Debug("deleting locks and workdir(s) with previous plans")
197+
_, err = p.deleteLockCommand.DeleteLocksByPull(ctx.Pull.BaseRepo.FullName, ctx.Pull.Num)
198+
if err != nil {
199+
ctx.Log.Err("deleting locks: %s", err)
200+
}
201+
}
202+
183203
// Only run commands in parallel if enabled
184204
var result command.Result
185205
if p.isParallelEnabled(projectCmds) {

Diff for: server/server.go

+1
Original file line numberDiff line numberDiff line change
@@ -613,6 +613,7 @@ func NewServer(userConfig UserConfig, config Config) (*Server, error) {
613613
userConfig.ParallelPoolSize,
614614
userConfig.SilenceNoProjects,
615615
boltdb,
616+
deleteLockCommand,
616617
)
617618

618619
pullReqStatusFetcher := vcs.NewPullReqStatusFetcher(vcsClient)

0 commit comments

Comments
 (0)