fix: preserve Atlantis apply lock after plan (#5570)

Signed-off-by: yasinlachiny <your-email@example.com>
Signed-off-by: yasinlachiny <yasin.lachiny@gmail.com>
Co-authored-by: yasinlachiny <your_email@example.com>
Co-authored-by: PePe Amengual <2208324+jamengual@users.noreply.github.com>
This commit is contained in:
doodle
2025-06-19 01:23:05 +02:00
committed by GitHub
parent a7c712b921
commit 59f16ef868
2 changed files with 44 additions and 9 deletions

View File

@@ -17,6 +17,7 @@ import (
"errors"
"fmt"
"regexp"
"strconv"
"strings"
"testing"
@@ -882,7 +883,6 @@ func TestRunCommentCommand_FailedPreWorkflowHook_FailOnPreWorkflowHookError_Fals
ch.FailOnPreWorkflowHookError = false
ch.RunCommentCommand(testdata.GithubRepo, nil, nil, testdata.User, testdata.Pull.Num, &events.CommentCommand{Name: command.Plan})
pendingPlanFinder.VerifyWasCalledOnce().DeletePlans(tmp)
lockingLocker.VerifyWasCalledOnce().UnlockByPull(testdata.Pull.BaseRepo.FullName, testdata.Pull.Num)
}
func TestRunCommentCommand_FailedPreWorkflowHook_FailOnPreWorkflowHookError_True(t *testing.T) {
@@ -919,7 +919,22 @@ func TestRunGenericPlanCommand_DeletePlans(t *testing.T) {
autoMerger.GlobalAutomerge = true
defer func() { autoMerger.GlobalAutomerge = false }()
When(projectCommandRunner.Plan(Any[command.ProjectContext]())).ThenReturn(command.ProjectResult{PlanSuccess: &models.PlanSuccess{}})
projectCtx := command.ProjectContext{
CommandName: command.Plan,
ExecutionOrderGroup: 0,
ProjectName: "TestProject",
Workspace: "default",
BaseRepo: testdata.GithubRepo,
Pull: testdata.Pull,
}
When(projectCommandBuilder.BuildPlanCommands(Any[*command.Context](), Any[*events.CommentCommand]())).
ThenReturn([]command.ProjectContext{projectCtx}, nil)
When(projectCommandRunner.Plan(projectCtx)).ThenReturn(command.ProjectResult{
Command: command.Plan,
PlanSuccess: &models.PlanSuccess{
TerraformOutput: "true",
},
})
When(workingDir.GetPullDir(Any[models.Repo](), Any[models.PullRequest]())).ThenReturn(tmp, nil)
pull := &github.PullRequest{State: github.Ptr("open")}
modelPull := models.PullRequest{BaseRepo: testdata.GithubRepo, State: models.OpenPullState, Num: testdata.Pull.Num}
@@ -927,8 +942,13 @@ func TestRunGenericPlanCommand_DeletePlans(t *testing.T) {
When(eventParsing.ParseGithubPull(Any[logging.SimpleLogging](), Eq(pull))).ThenReturn(modelPull, modelPull.BaseRepo, testdata.GithubRepo, nil)
testdata.Pull.BaseRepo = testdata.GithubRepo
ch.RunCommentCommand(testdata.GithubRepo, nil, nil, testdata.User, testdata.Pull.Num, &events.CommentCommand{Name: command.Plan})
pendingPlanFinder.VerifyWasCalledOnce().DeletePlans(tmp)
lockingLocker.VerifyWasCalledOnce().UnlockByPull(testdata.Pull.BaseRepo.FullName, testdata.Pull.Num)
lockingLocker.VerifyWasCalledOnce().Unlock(fmt.Sprintf("%s/%d/%s/%s", testdata.Pull.BaseRepo.FullName, testdata.Pull.Num, projectCtx.ProjectName, projectCtx.Workspace))
lockingLocker.VerifyWasCalledOnce().
Unlock(testdata.Pull.BaseRepo.FullName + "/" +
strconv.Itoa(testdata.Pull.Num) + "/" +
projectCtx.ProjectName + "/" +
projectCtx.Workspace)
}
func TestRunSpecificPlanCommandDoesnt_DeletePlans(t *testing.T) {
@@ -1031,7 +1051,6 @@ func TestRunGenericPlanCommand_DiscardApprovals(t *testing.T) {
testdata.Pull.BaseRepo = testdata.GithubRepo
ch.RunCommentCommand(testdata.GithubRepo, nil, nil, testdata.User, testdata.Pull.Num, &events.CommentCommand{Name: command.Plan})
pendingPlanFinder.VerifyWasCalledOnce().DeletePlans(tmp)
lockingLocker.VerifyWasCalledOnce().UnlockByPull(testdata.Pull.BaseRepo.FullName, testdata.Pull.Num)
vcsClient.VerifyWasCalledOnce().DiscardReviews(Any[logging.SimpleLogging](), Any[models.Repo](), Any[models.PullRequest]())
}

View File

@@ -1,6 +1,8 @@
package events
import (
"strconv"
"github.com/runatlantis/atlantis/server/core/locking"
"github.com/runatlantis/atlantis/server/events/command"
"github.com/runatlantis/atlantis/server/events/models"
@@ -239,10 +241,6 @@ func (p *PlanCommandRunner) run(ctx *command.Context, cmd *CommentCommand) {
if !cmd.IsForSpecificProject() {
ctx.Log.Debug("deleting previous plans and locks")
p.deletePlans(ctx)
_, err = p.lockingLocker.UnlockByPull(baseRepo.FullName, pull.Num)
if err != nil {
ctx.Log.Err("deleting locks: %s", err)
}
}
// Only run commands in parallel if enabled
@@ -255,6 +253,24 @@ func (p *PlanCommandRunner) run(ctx *command.Context, cmd *CommentCommand) {
}
ctx.CommandHasErrors = result.HasErrors()
for i, projResult := range result.ProjectResults {
projCtx := projectCmds[i]
if projResult.PlanStatus() == models.PlannedNoChangesPlanStatus || projResult.PlanStatus() == models.ErroredPlanStatus {
ctx.Log.Info("Keeping lock for project '%s' (no changes or error)", projCtx.ProjectName)
continue
}
// delete lock only if there are changes
ctx.Log.Info("Deleting lock for project '%s' (changes detected)", projCtx.ProjectName)
lockID := projCtx.BaseRepo.FullName + "/" + strconv.Itoa(projCtx.Pull.Num) + "/" + projCtx.ProjectName + "/" + projCtx.Workspace
_, err := p.lockingLocker.Unlock(lockID)
if err != nil {
ctx.Log.Err("failed unlocking project '%s': %s", projCtx.ProjectName, err)
}
}
if p.autoMerger.automergeEnabled(projectCmds) && result.HasErrors() {
ctx.Log.Info("deleting plans because there were errors and automerge requires all plans succeed")
p.deletePlans(ctx)