From d31039c1571e26a8e62f990a375af8106ce355b7 Mon Sep 17 00:00:00 2001
From: Tiffany Chiang <14845269+tlin4194@users.noreply.github.com>
Date: Tue, 3 Dec 2024 10:00:53 -0500
Subject: [PATCH] Restore unlock action on queued checkruns (#774)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
When we update checkruns for queued commits (after the blocking commit
gets confirmed or rejected), we need to check if the queue was
previously locked by something else and restore the unlock button for
each commit.
### Sequence of events without this change:
1. Queue is locked due to diverged commits on latest deployment vs
current plan (i.e. if someone previously force applied). All queued
commits should have a unlock button.
2. New commit comes in with a pending confirm/reject action. My other PR
makes it so that all queued checkruns are updated to reflect that the
queue is waiting on this confirm reject action.
4. Confirm/reject commit gets resolved. All queued commit checkruns get
updated to the default status, without the unlock button. Currently
users would have to click on the commit they want to unlock (first
screenshot below), then click on an older checkrun of that commit
(linked in blue) to access the unlock button (2nd screenshot below).
### After this change:
4. (Previous step 4) All queued commit checkruns get updated back to its
original status from before the confirm/reject commit was applied,
**including** unlock actions if the queue was previously locked.
### Additional notes:
* LockState is migrated out of the queue package into its own `lock`
package to avoid cyclic imports with terraform module
* bumping max interfacebloat golangci error to allow adding
GetLockState() function to queue interface
### Testing
1. Open PR 1. Force apply PR 1, and verify that the force apply is
successful.
2. Open PR 2. Merge PR 2 to master, and verify that PR 2 deployment
failed and is now locked on PR 1's last commit (there should be an
unlock button and link to the locking commit).
3. Open PR 3. Force apply PR 3, and verify that there is a
Confirm/Reject action.
4. Verify that PR 2's deployment checkrun on master has an updated
summary with link to the pending Confirm/Reject run on PR 3.
5. Reject forced apply on PR 3.
6. Verify that PR 2's deployment checkrun updates again, this time back
to the original checkrun summary from step 2. If the queue was
previously locked (which it was), there should be an unlock button.
---
.golangci.yml | 2 +-
.../workflows/internal/deploy/lock/lock.go | 15 ++++++++
.../deploy/revision/notifier/slack.go | 3 +-
.../deploy/revision/notifier/slack_test.go | 11 +++---
.../internal/deploy/revision/queue/queue.go | 29 +++++----------
.../deploy/revision/queue/queue_test.go | 17 ++++-----
.../internal/deploy/revision/queue/updater.go | 7 ++--
.../deploy/revision/queue/updater_test.go | 7 ++--
.../internal/deploy/revision/queue/worker.go | 12 ++++---
.../revision/queue/worker_state_test.go | 11 +++---
.../deploy/revision/queue/worker_test.go | 31 +++++++++-------
.../internal/deploy/revision/revision.go | 15 ++++----
.../internal/deploy/revision/revision_test.go | 35 ++++++++++---------
.../internal/deploy/terraform/runner.go | 2 ++
.../internal/deploy/terraform/state.go | 17 +++++++--
.../internal/deploy/terraform/state_test.go | 6 ++++
16 files changed, 129 insertions(+), 91 deletions(-)
create mode 100644 server/neptune/workflows/internal/deploy/lock/lock.go
diff --git a/.golangci.yml b/.golangci.yml
index 1fb9e8879..5986dec2e 100644
--- a/.golangci.yml
+++ b/.golangci.yml
@@ -35,4 +35,4 @@ issues:
- dogsled
linters-settings:
interfacebloat:
- max: 6
+ max: 7
diff --git a/server/neptune/workflows/internal/deploy/lock/lock.go b/server/neptune/workflows/internal/deploy/lock/lock.go
new file mode 100644
index 000000000..ec40cb0ff
--- /dev/null
+++ b/server/neptune/workflows/internal/deploy/lock/lock.go
@@ -0,0 +1,15 @@
+package lock
+
+type LockStatus int
+
+type LockState struct {
+ Revision string
+ Status LockStatus
+}
+
+const (
+ UnlockedStatus LockStatus = iota
+ LockedStatus
+
+ QueueDepthStat = "queue.depth"
+)
diff --git a/server/neptune/workflows/internal/deploy/revision/notifier/slack.go b/server/neptune/workflows/internal/deploy/revision/notifier/slack.go
index b4cfc57a6..6c199295b 100644
--- a/server/neptune/workflows/internal/deploy/revision/notifier/slack.go
+++ b/server/neptune/workflows/internal/deploy/revision/notifier/slack.go
@@ -7,6 +7,7 @@ import (
"github.com/pkg/errors"
"github.com/runatlantis/atlantis/server/neptune/workflows/activities"
"github.com/runatlantis/atlantis/server/neptune/workflows/activities/github"
+ "github.com/runatlantis/atlantis/server/neptune/workflows/internal/deploy/lock"
"github.com/runatlantis/atlantis/server/neptune/workflows/internal/deploy/revision/queue"
"github.com/slack-go/slack"
"go.temporal.io/sdk/workflow"
@@ -24,7 +25,7 @@ type Slack struct {
func (s *Slack) Notify(ctx workflow.Context) error {
state := s.DeployQueue.GetLockState()
- if state.Status == queue.UnlockedStatus {
+ if state.Status == lock.UnlockedStatus {
return nil
}
diff --git a/server/neptune/workflows/internal/deploy/revision/notifier/slack_test.go b/server/neptune/workflows/internal/deploy/revision/notifier/slack_test.go
index 14da6d1a6..b95266e92 100644
--- a/server/neptune/workflows/internal/deploy/revision/notifier/slack_test.go
+++ b/server/neptune/workflows/internal/deploy/revision/notifier/slack_test.go
@@ -8,6 +8,7 @@ import (
"github.com/runatlantis/atlantis/server/neptune/workflows/activities"
"github.com/runatlantis/atlantis/server/neptune/workflows/activities/github"
terraformActivities "github.com/runatlantis/atlantis/server/neptune/workflows/activities/terraform"
+ "github.com/runatlantis/atlantis/server/neptune/workflows/internal/deploy/lock"
"github.com/runatlantis/atlantis/server/neptune/workflows/internal/deploy/revision/notifier"
"github.com/runatlantis/atlantis/server/neptune/workflows/internal/deploy/revision/queue"
"github.com/runatlantis/atlantis/server/neptune/workflows/internal/deploy/terraform"
@@ -20,7 +21,7 @@ import (
)
type request struct {
- LockState queue.LockState
+ LockState lock.LockState
InitialItems []terraform.DeploymentInfo
}
@@ -51,7 +52,7 @@ func testWorkflow(ctx workflow.Context, request request) error {
func TestNotifier(t *testing.T) {
t.Run("empty queue", func(t *testing.T) {
- state := queue.LockState{Status: queue.UnlockedStatus}
+ state := lock.LockState{Status: lock.UnlockedStatus}
ts := testsuite.WorkflowTestSuite{}
env := ts.NewTestWorkflowEnvironment()
@@ -68,7 +69,7 @@ func TestNotifier(t *testing.T) {
})
t.Run("locked state", func(t *testing.T) {
- state := queue.LockState{Status: queue.LockedStatus}
+ state := lock.LockState{Status: lock.LockedStatus}
ts := testsuite.WorkflowTestSuite{}
env := ts.NewTestWorkflowEnvironment()
@@ -85,7 +86,7 @@ func TestNotifier(t *testing.T) {
})
t.Run("no slack config", func(t *testing.T) {
- state := queue.LockState{Status: queue.LockedStatus}
+ state := lock.LockState{Status: lock.LockedStatus}
ts := testsuite.WorkflowTestSuite{}
env := ts.NewTestWorkflowEnvironment()
@@ -121,7 +122,7 @@ func TestNotifier(t *testing.T) {
})
t.Run("activity called", func(t *testing.T) {
- state := queue.LockState{Status: queue.LockedStatus}
+ state := lock.LockState{Status: lock.LockedStatus}
ts := testsuite.WorkflowTestSuite{}
env := ts.NewTestWorkflowEnvironment()
diff --git a/server/neptune/workflows/internal/deploy/revision/queue/queue.go b/server/neptune/workflows/internal/deploy/revision/queue/queue.go
index 7a77a0199..051cecce2 100644
--- a/server/neptune/workflows/internal/deploy/revision/queue/queue.go
+++ b/server/neptune/workflows/internal/deploy/revision/queue/queue.go
@@ -7,32 +7,19 @@ import (
"github.com/runatlantis/atlantis/server/neptune/workflows/activities/github"
activity "github.com/runatlantis/atlantis/server/neptune/workflows/activities/terraform"
+ "github.com/runatlantis/atlantis/server/neptune/workflows/internal/deploy/lock"
"github.com/runatlantis/atlantis/server/neptune/workflows/internal/deploy/terraform"
"github.com/runatlantis/atlantis/server/neptune/workflows/internal/metrics"
"go.temporal.io/sdk/workflow"
)
-type LockStatus int
-
-type LockState struct {
- Revision string
- Status LockStatus
-}
-
-const (
- UnlockedStatus LockStatus = iota
- LockedStatus
-
- QueueDepthStat = "queue.depth"
-)
-
type Deploy struct {
queue *priority
lockStatusCallback func(workflow.Context, *Deploy)
scope metrics.Scope
// mutable: default is unlocked
- lock LockState
+ lock lock.LockState
}
func NewQueue(callback func(workflow.Context, *Deploy), scope metrics.Scope) *Deploy {
@@ -43,12 +30,12 @@ func NewQueue(callback func(workflow.Context, *Deploy), scope metrics.Scope) *De
}
}
-func (q *Deploy) GetLockState() LockState {
+func (q *Deploy) GetLockState() lock.LockState {
return q.lock
}
-func (q *Deploy) SetLockForMergedItems(ctx workflow.Context, state LockState) {
- if state.Status == LockedStatus {
+func (q *Deploy) SetLockForMergedItems(ctx workflow.Context, state lock.LockState) {
+ if state.Status == lock.LockedStatus {
q.scope.Counter("locked").Inc(1)
} else {
q.scope.Counter("unlocked").Inc(1)
@@ -58,11 +45,11 @@ func (q *Deploy) SetLockForMergedItems(ctx workflow.Context, state LockState) {
}
func (q *Deploy) CanPop() bool {
- return q.queue.HasItemsOfPriority(High) || (q.lock.Status == UnlockedStatus && !q.queue.IsEmpty())
+ return q.queue.HasItemsOfPriority(High) || (q.lock.Status == lock.UnlockedStatus && !q.queue.IsEmpty())
}
func (q *Deploy) Pop() (terraform.DeploymentInfo, error) {
- defer q.scope.Gauge(QueueDepthStat).Update(float64(q.queue.Size()))
+ defer q.scope.Gauge(lock.QueueDepthStat).Update(float64(q.queue.Size()))
return q.queue.Pop()
}
@@ -79,7 +66,7 @@ func (q *Deploy) IsEmpty() bool {
}
func (q *Deploy) Push(msg terraform.DeploymentInfo) {
- defer q.scope.Gauge(QueueDepthStat).Update(float64(q.queue.Size()))
+ defer q.scope.Gauge(lock.QueueDepthStat).Update(float64(q.queue.Size()))
if msg.Root.TriggerInfo.Type == activity.ManualTrigger {
q.queue.Push(msg, High)
return
diff --git a/server/neptune/workflows/internal/deploy/revision/queue/queue_test.go b/server/neptune/workflows/internal/deploy/revision/queue/queue_test.go
index d54178921..9cbac7bf9 100644
--- a/server/neptune/workflows/internal/deploy/revision/queue/queue_test.go
+++ b/server/neptune/workflows/internal/deploy/revision/queue/queue_test.go
@@ -5,6 +5,7 @@ import (
"github.com/runatlantis/atlantis/server/neptune/workflows/activities/github"
activity "github.com/runatlantis/atlantis/server/neptune/workflows/activities/terraform"
+ "github.com/runatlantis/atlantis/server/neptune/workflows/internal/deploy/lock"
"github.com/runatlantis/atlantis/server/neptune/workflows/internal/deploy/revision/queue"
"github.com/runatlantis/atlantis/server/neptune/workflows/internal/deploy/terraform"
"github.com/runatlantis/atlantis/server/neptune/workflows/internal/metrics"
@@ -38,8 +39,8 @@ func TestQueue(t *testing.T) {
q := queue.NewQueue(func(ctx workflow.Context, d *queue.Deploy) {
called = true
}, metrics.NewNullableScope())
- q.SetLockForMergedItems(test.Background(), queue.LockState{
- Status: queue.LockedStatus,
+ q.SetLockForMergedItems(test.Background(), lock.LockState{
+ Status: lock.LockedStatus,
})
assert.True(t, called)
@@ -52,8 +53,8 @@ func TestQueue(t *testing.T) {
t.Run("can pop empty queue locked", func(t *testing.T) {
q := queue.NewQueue(noopCallback, metrics.NewNullableScope())
- q.SetLockForMergedItems(test.Background(), queue.LockState{
- Status: queue.LockedStatus,
+ q.SetLockForMergedItems(test.Background(), lock.LockState{
+ Status: lock.LockedStatus,
})
assert.Equal(t, false, q.CanPop())
})
@@ -61,8 +62,8 @@ func TestQueue(t *testing.T) {
q := queue.NewQueue(noopCallback, metrics.NewNullableScope())
msg1 := wrap("1", activity.ManualTrigger)
q.Push(msg1)
- q.SetLockForMergedItems(test.Background(), queue.LockState{
- Status: queue.LockedStatus,
+ q.SetLockForMergedItems(test.Background(), lock.LockState{
+ Status: lock.LockedStatus,
})
assert.Equal(t, true, q.CanPop())
})
@@ -76,8 +77,8 @@ func TestQueue(t *testing.T) {
q := queue.NewQueue(noopCallback, metrics.NewNullableScope())
msg1 := wrap("1", activity.MergeTrigger)
q.Push(msg1)
- q.SetLockForMergedItems(test.Background(), queue.LockState{
- Status: queue.LockedStatus,
+ q.SetLockForMergedItems(test.Background(), lock.LockState{
+ Status: lock.LockedStatus,
})
assert.Equal(t, false, q.CanPop())
})
diff --git a/server/neptune/workflows/internal/deploy/revision/queue/updater.go b/server/neptune/workflows/internal/deploy/revision/queue/updater.go
index e9fa950c4..dfd42bd30 100644
--- a/server/neptune/workflows/internal/deploy/revision/queue/updater.go
+++ b/server/neptune/workflows/internal/deploy/revision/queue/updater.go
@@ -7,6 +7,7 @@ import (
"github.com/runatlantis/atlantis/server/neptune/workflows/internal/notifier"
"github.com/runatlantis/atlantis/server/neptune/workflows/activities/github"
+ "github.com/runatlantis/atlantis/server/neptune/workflows/internal/deploy/lock"
"go.temporal.io/sdk/workflow"
)
@@ -20,17 +21,17 @@ type LockStateUpdater struct {
}
func (u *LockStateUpdater) UpdateQueuedRevisions(ctx workflow.Context, queue *Deploy, repoFullName string) {
- lock := queue.GetLockState()
+ queueLock := queue.GetLockState()
infos := queue.GetOrderedMergedItems()
var actions []github.CheckRunAction
var summary string
var revisionsSummary string = queue.GetQueuedRevisionsSummary()
state := github.CheckRunQueued
- if lock.Status == LockedStatus {
+ if queueLock.Status == lock.LockedStatus {
actions = append(actions, github.CreateUnlockAction())
state = github.CheckRunActionRequired
- revisionLink := github.BuildRevisionURLMarkdown(repoFullName, lock.Revision)
+ revisionLink := github.BuildRevisionURLMarkdown(repoFullName, queueLock.Revision)
summary = fmt.Sprintf("This deploy is locked from a manual deployment for revision %s. Unlock to proceed.\n%s", revisionLink, revisionsSummary)
}
diff --git a/server/neptune/workflows/internal/deploy/revision/queue/updater_test.go b/server/neptune/workflows/internal/deploy/revision/queue/updater_test.go
index c99cafab7..9fd6f85df 100644
--- a/server/neptune/workflows/internal/deploy/revision/queue/updater_test.go
+++ b/server/neptune/workflows/internal/deploy/revision/queue/updater_test.go
@@ -10,6 +10,7 @@ import (
"github.com/runatlantis/atlantis/server/neptune/workflows/activities"
"github.com/runatlantis/atlantis/server/neptune/workflows/activities/github"
tfActivity "github.com/runatlantis/atlantis/server/neptune/workflows/activities/terraform"
+ "github.com/runatlantis/atlantis/server/neptune/workflows/internal/deploy/lock"
"github.com/runatlantis/atlantis/server/neptune/workflows/internal/deploy/revision/queue"
"github.com/runatlantis/atlantis/server/neptune/workflows/internal/deploy/terraform"
"github.com/runatlantis/atlantis/server/neptune/workflows/internal/metrics"
@@ -126,8 +127,8 @@ func TestLockStateUpdater_locked_new_version(t *testing.T) {
env.ExecuteWorkflow(testUpdaterWorkflow, updaterReq{
Queue: []terraform.DeploymentInfo{info},
- Lock: queue.LockState{
- Status: queue.LockedStatus,
+ Lock: lock.LockState{
+ Status: lock.LockedStatus,
Revision: "1234",
},
ExpectedRequest: notifier.GithubCheckRunRequest{
@@ -152,7 +153,7 @@ func TestLockStateUpdater_locked_new_version(t *testing.T) {
type updaterReq struct {
Queue []terraform.DeploymentInfo
- Lock queue.LockState
+ Lock lock.LockState
ExpectedRequest notifier.GithubCheckRunRequest
ExpectedDeploymentID string
ExpectedT *testing.T
diff --git a/server/neptune/workflows/internal/deploy/revision/queue/worker.go b/server/neptune/workflows/internal/deploy/revision/queue/worker.go
index a4839c332..f2acd0287 100644
--- a/server/neptune/workflows/internal/deploy/revision/queue/worker.go
+++ b/server/neptune/workflows/internal/deploy/revision/queue/worker.go
@@ -13,6 +13,7 @@ import (
internalContext "github.com/runatlantis/atlantis/server/neptune/context"
"github.com/runatlantis/atlantis/server/neptune/workflows/activities/deployment"
tfModel "github.com/runatlantis/atlantis/server/neptune/workflows/activities/terraform"
+ "github.com/runatlantis/atlantis/server/neptune/workflows/internal/deploy/lock"
"github.com/runatlantis/atlantis/server/neptune/workflows/internal/deploy/terraform"
"github.com/runatlantis/atlantis/server/neptune/workflows/internal/metrics"
"github.com/runatlantis/atlantis/server/neptune/workflows/plugins"
@@ -24,9 +25,10 @@ type queue interface {
IsEmpty() bool
CanPop() bool
Pop() (terraform.DeploymentInfo, error)
- SetLockForMergedItems(ctx workflow.Context, state LockState)
+ SetLockForMergedItems(ctx workflow.Context, state lock.LockState)
GetOrderedMergedItems() []terraform.DeploymentInfo
GetQueuedRevisionsSummary() string
+ GetLockState() lock.LockState
}
type deployer interface {
@@ -114,8 +116,8 @@ func NewWorker(
// we don't persist lock state anywhere so in the case of workflow completion we need to rebuild
// the lock state
if latestDeployment != nil && latestDeployment.Root.Trigger == string(tfModel.ManualTrigger) {
- q.SetLockForMergedItems(ctx, LockState{
- Status: LockedStatus,
+ q.SetLockForMergedItems(ctx, lock.LockState{
+ Status: lock.LockedStatus,
Revision: latestDeployment.Revision,
})
}
@@ -181,8 +183,8 @@ func (w *Worker) Work(ctx workflow.Context) {
workflow.GetMetricsHandler(ctx).WithTags(map[string]string{metricNames.SignalNameTag: UnlockSignalName}).
Counter(metricNames.SignalReceive).
Inc(1)
- w.Queue.SetLockForMergedItems(ctx, LockState{
- Status: UnlockedStatus,
+ w.Queue.SetLockForMergedItems(ctx, lock.LockState{
+ Status: lock.UnlockedStatus,
})
continue
default:
diff --git a/server/neptune/workflows/internal/deploy/revision/queue/worker_state_test.go b/server/neptune/workflows/internal/deploy/revision/queue/worker_state_test.go
index 26de4050a..55ec9f36c 100644
--- a/server/neptune/workflows/internal/deploy/revision/queue/worker_state_test.go
+++ b/server/neptune/workflows/internal/deploy/revision/queue/worker_state_test.go
@@ -13,6 +13,7 @@ import (
"go.temporal.io/sdk/testsuite"
"go.temporal.io/sdk/workflow"
+ "github.com/runatlantis/atlantis/server/neptune/workflows/internal/deploy/lock"
"github.com/runatlantis/atlantis/server/neptune/workflows/internal/deploy/revision/queue"
internalTerraform "github.com/runatlantis/atlantis/server/neptune/workflows/internal/deploy/terraform"
"github.com/runatlantis/atlantis/server/neptune/workflows/internal/metrics"
@@ -47,7 +48,7 @@ func testStateWorkflow(ctx workflow.Context, r workerRequest) (workerResponse, e
Queue: list.New(),
}
- q.SetLockForMergedItems(ctx, queue.LockState{Status: r.InitialLockStatus})
+ q.SetLockForMergedItems(ctx, lock.LockState{Status: r.InitialLockStatus})
workflow.Go(ctx, func(ctx workflow.Context) {
ch := workflow.GetSignalChannel(ctx, "add-to-queue")
@@ -109,8 +110,8 @@ func TestWorker_StartsWithEmptyQueue(t *testing.T) {
assert.NoError(t, err)
assert.True(t, q.QueueIsEmpty)
- assert.Equal(t, queue.LockState{
- Status: queue.UnlockedStatus,
+ assert.Equal(t, lock.LockState{
+ Status: lock.UnlockedStatus,
}, q.Lock)
assert.Equal(t, queue.WaitingWorkerState, q.State)
}, 2*time.Second)
@@ -145,8 +146,8 @@ func TestWorker_StartsWithEmptyQueue(t *testing.T) {
assert.NoError(t, err)
assert.True(t, q.QueueIsEmpty)
- assert.Equal(t, queue.LockState{
- Status: queue.UnlockedStatus,
+ assert.Equal(t, lock.LockState{
+ Status: lock.UnlockedStatus,
}, q.Lock)
assert.Equal(t, queue.WorkingWorkerState, q.State)
}, 6*time.Second)
diff --git a/server/neptune/workflows/internal/deploy/revision/queue/worker_test.go b/server/neptune/workflows/internal/deploy/revision/queue/worker_test.go
index 4363a907b..8f0362e85 100644
--- a/server/neptune/workflows/internal/deploy/revision/queue/worker_test.go
+++ b/server/neptune/workflows/internal/deploy/revision/queue/worker_test.go
@@ -12,6 +12,7 @@ import (
"github.com/runatlantis/atlantis/server/neptune/workflows/activities/deployment"
"github.com/runatlantis/atlantis/server/neptune/workflows/activities/github"
"github.com/runatlantis/atlantis/server/neptune/workflows/activities/terraform"
+ "github.com/runatlantis/atlantis/server/neptune/workflows/internal/deploy/lock"
"github.com/runatlantis/atlantis/server/neptune/workflows/internal/deploy/revision/queue"
internalTerraform "github.com/runatlantis/atlantis/server/neptune/workflows/internal/deploy/terraform"
"github.com/runatlantis/atlantis/server/neptune/workflows/internal/metrics"
@@ -25,7 +26,7 @@ import (
type testQueue struct {
Queue *list.List
- Lock queue.LockState
+ Lock lock.LockState
}
func (q *testQueue) IsEmpty() bool {
@@ -55,10 +56,14 @@ func (q *testQueue) GetOrderedMergedItems() []internalTerraform.DeploymentInfo {
return result
}
-func (q *testQueue) SetLockForMergedItems(ctx workflow.Context, state queue.LockState) {
+func (q *testQueue) SetLockForMergedItems(ctx workflow.Context, state lock.LockState) {
q.Lock = state
}
+func (q *testQueue) GetLockState() lock.LockState {
+ return q.Lock
+}
+
func (q *testQueue) GetQueuedRevisionsSummary() string {
return "Revisions in queue"
}
@@ -68,7 +73,7 @@ type workerRequest struct {
ExpectedValidationErrors []*queue.ValidationError
ExpectedPlanRejectionErrros []*internalTerraform.PlanRejectionError
ExpectedTerraformClientErrors []*activities.TerraformClientError
- InitialLockStatus queue.LockStatus
+ InitialLockStatus lock.LockStatus
}
type workerResponse struct {
@@ -80,7 +85,7 @@ type workerResponse struct {
type queueAndState struct {
QueueIsEmpty bool
State queue.WorkerState
- Lock queue.LockState
+ Lock lock.LockState
CurrentDeployment queue.CurrentDeployment
LatestDeployment *deployment.Info
}
@@ -125,7 +130,7 @@ func testWorkerWorkflow(ctx workflow.Context, r workerRequest) (workerResponse,
Queue: list.New(),
}
- q.SetLockForMergedItems(ctx, queue.LockState{Status: r.InitialLockStatus})
+ q.SetLockForMergedItems(ctx, lock.LockState{Status: r.InitialLockStatus})
var infos []*deployment.Info
for _, s := range r.Queue {
@@ -191,8 +196,8 @@ func TestWorker_ReceivesUnlockSignal(t *testing.T) {
assert.NoError(t, err)
assert.True(t, q.QueueIsEmpty)
- assert.Equal(t, queue.LockState{
- Status: queue.UnlockedStatus,
+ assert.Equal(t, lock.LockState{
+ Status: lock.UnlockedStatus,
}, q.Lock)
assert.Equal(t, queue.WaitingWorkerState, q.State)
@@ -201,7 +206,7 @@ func TestWorker_ReceivesUnlockSignal(t *testing.T) {
env.ExecuteWorkflow(testWorkerWorkflow, workerRequest{
// start locked and ensure we can unlock it.
- InitialLockStatus: queue.LockedStatus,
+ InitialLockStatus: lock.LockedStatus,
})
env.AssertExpectations(t)
@@ -400,7 +405,7 @@ func TestNewWorker(t *testing.T) {
}
type res struct {
- Lock queue.LockState
+ Lock lock.LockState
}
testWorkflow := func(ctx workflow.Context) (res, error) {
@@ -444,9 +449,9 @@ func TestNewWorker(t *testing.T) {
err := env.GetWorkflowResult(&r)
assert.NoError(t, err)
- assert.Equal(t, queue.LockState{
+ assert.Equal(t, lock.LockState{
Revision: "1234",
- Status: queue.LockedStatus,
+ Status: lock.LockedStatus,
}, r.Lock)
})
@@ -475,7 +480,7 @@ func TestNewWorker(t *testing.T) {
err := env.GetWorkflowResult(&r)
assert.NoError(t, err)
- assert.Equal(t, queue.LockState{}, r.Lock)
+ assert.Equal(t, lock.LockState{}, r.Lock)
})
t.Run("first deploy", func(t *testing.T) {
@@ -497,7 +502,7 @@ func TestNewWorker(t *testing.T) {
err := env.GetWorkflowResult(&r)
assert.NoError(t, err)
- assert.Equal(t, queue.LockState{}, r.Lock)
+ assert.Equal(t, lock.LockState{}, r.Lock)
})
}
diff --git a/server/neptune/workflows/internal/deploy/revision/revision.go b/server/neptune/workflows/internal/deploy/revision/revision.go
index 48de52081..9d2b6a924 100644
--- a/server/neptune/workflows/internal/deploy/revision/revision.go
+++ b/server/neptune/workflows/internal/deploy/revision/revision.go
@@ -13,6 +13,7 @@ import (
"github.com/runatlantis/atlantis/server/neptune/workflows/activities"
"github.com/runatlantis/atlantis/server/neptune/workflows/activities/github"
activity "github.com/runatlantis/atlantis/server/neptune/workflows/activities/terraform"
+ "github.com/runatlantis/atlantis/server/neptune/workflows/internal/deploy/lock"
"github.com/runatlantis/atlantis/server/neptune/workflows/internal/deploy/request"
"github.com/runatlantis/atlantis/server/neptune/workflows/internal/deploy/request/converter"
"github.com/runatlantis/atlantis/server/neptune/workflows/internal/deploy/revision/queue"
@@ -43,8 +44,8 @@ type NewRevisionRequest struct {
type Queue interface {
Push(terraform.DeploymentInfo)
- GetLockState() queue.LockState
- SetLockForMergedItems(ctx workflow.Context, state queue.LockState)
+ GetLockState() lock.LockState
+ SetLockForMergedItems(ctx workflow.Context, state lock.LockState)
Scan() []terraform.DeploymentInfo
GetQueuedRevisionsSummary() string
}
@@ -115,8 +116,8 @@ func (n *Receiver) Receive(c workflow.ReceiveChannel, more bool) {
// lock the queue on a manual deployment
if root.TriggerInfo.Type == activity.ManualTrigger {
// Lock the queue on a manual deployment
- n.queue.SetLockForMergedItems(ctx, queue.LockState{
- Status: queue.LockedStatus,
+ n.queue.SetLockForMergedItems(ctx, lock.LockState{
+ Status: lock.LockedStatus,
Revision: request.Revision,
})
}
@@ -135,16 +136,16 @@ func (n *Receiver) Receive(c workflow.ReceiveChannel, more bool) {
}
func (n *Receiver) createCheckRun(ctx workflow.Context, id, revision string, root activity.Root, repo github.Repo) int64 {
- lock := n.queue.GetLockState()
+ queueLock := n.queue.GetLockState()
var actions []github.CheckRunAction
var revisionsSummary string = n.queue.GetQueuedRevisionsSummary()
summary := "This deploy is queued and will be processed as soon as possible.\n" + revisionsSummary
state := github.CheckRunQueued
- if lock.Status == queue.LockedStatus && (root.TriggerInfo.Type == activity.MergeTrigger) {
+ if queueLock.Status == lock.LockedStatus && (root.TriggerInfo.Type == activity.MergeTrigger) {
actions = append(actions, github.CreateUnlockAction())
state = github.CheckRunActionRequired
- revisionLink := github.BuildRevisionURLMarkdown(repo.GetFullName(), lock.Revision)
+ revisionLink := github.BuildRevisionURLMarkdown(repo.GetFullName(), queueLock.Revision)
summary = fmt.Sprintf("This deploy is locked from a manual deployment for revision %s. Unlock to proceed.\n%s", revisionLink, revisionsSummary)
}
diff --git a/server/neptune/workflows/internal/deploy/revision/revision_test.go b/server/neptune/workflows/internal/deploy/revision/revision_test.go
index d5a43156e..e43215176 100644
--- a/server/neptune/workflows/internal/deploy/revision/revision_test.go
+++ b/server/neptune/workflows/internal/deploy/revision/revision_test.go
@@ -11,6 +11,7 @@ import (
"github.com/google/uuid"
"github.com/runatlantis/atlantis/server/neptune/workflows/activities/github"
"github.com/runatlantis/atlantis/server/neptune/workflows/activities/terraform"
+ "github.com/runatlantis/atlantis/server/neptune/workflows/internal/deploy/lock"
"github.com/runatlantis/atlantis/server/neptune/workflows/internal/deploy/request"
"github.com/runatlantis/atlantis/server/neptune/workflows/internal/deploy/revision"
"github.com/runatlantis/atlantis/server/neptune/workflows/internal/deploy/revision/queue"
@@ -35,7 +36,7 @@ func (t *testCheckRunClient) CreateOrUpdate(ctx workflow.Context, deploymentID s
type testQueue struct {
Queue []terraformWorkflow.DeploymentInfo
- Lock queue.LockState
+ Lock lock.LockState
}
func (q *testQueue) Scan() []terraformWorkflow.DeploymentInfo {
@@ -46,11 +47,11 @@ func (q *testQueue) Push(msg terraformWorkflow.DeploymentInfo) {
q.Queue = append(q.Queue, msg)
}
-func (q *testQueue) GetLockState() queue.LockState {
+func (q *testQueue) GetLockState() lock.LockState {
return q.Lock
}
-func (q *testQueue) SetLockForMergedItems(ctx workflow.Context, state queue.LockState) {
+func (q *testQueue) SetLockForMergedItems(ctx workflow.Context, state lock.LockState) {
q.Lock = state
}
@@ -79,7 +80,7 @@ func (t testWorker) GetCurrentDeploymentState() queue.CurrentDeployment {
type req struct {
ID uuid.UUID
- Lock queue.LockState
+ Lock lock.LockState
Current queue.CurrentDeployment
InitialElements []terraformWorkflow.DeploymentInfo
ExpectedRequest notifier.GithubCheckRunRequest
@@ -88,7 +89,7 @@ type req struct {
type response struct {
Queue []terraformWorkflow.DeploymentInfo
- Lock queue.LockState
+ Lock lock.LockState
Timeout bool
}
@@ -186,8 +187,8 @@ func TestEnqueue(t *testing.T) {
Repo: github.Repo{Name: "nish"},
},
}, resp.Queue)
- assert.Equal(t, queue.LockState{
- Status: queue.UnlockedStatus,
+ assert.Equal(t, lock.LockState{
+ Status: lock.UnlockedStatus,
}, resp.Lock)
assert.False(t, resp.Timeout)
}
@@ -247,8 +248,8 @@ func TestEnqueue_ManualTrigger(t *testing.T) {
Repo: github.Repo{Name: "nish"},
},
}, resp.Queue)
- assert.Equal(t, queue.LockState{
- Status: queue.LockedStatus,
+ assert.Equal(t, lock.LockState{
+ Status: lock.LockedStatus,
Revision: "1234",
}, resp.Lock)
assert.False(t, resp.Timeout)
@@ -279,9 +280,9 @@ func TestEnqueue_ManualTrigger_QueueAlreadyLocked(t *testing.T) {
env.ExecuteWorkflow(testWorkflow, req{
ID: id,
- Lock: queue.LockState{
+ Lock: lock.LockState{
// ensure that the lock gets updated
- Status: queue.LockedStatus,
+ Status: lock.LockedStatus,
Revision: "123334444555",
},
ExpectedRequest: notifier.GithubCheckRunRequest{
@@ -314,8 +315,8 @@ func TestEnqueue_ManualTrigger_QueueAlreadyLocked(t *testing.T) {
Repo: github.Repo{Name: "nish"},
},
}, resp.Queue)
- assert.Equal(t, queue.LockState{
- Status: queue.LockedStatus,
+ assert.Equal(t, lock.LockState{
+ Status: lock.LockedStatus,
Revision: "1234",
}, resp.Lock)
assert.False(t, resp.Timeout)
@@ -360,9 +361,9 @@ func TestEnqueue_MergeTrigger_QueueAlreadyLocked(t *testing.T) {
env.ExecuteWorkflow(testWorkflow, req{
ID: id,
InitialElements: []terraformWorkflow.DeploymentInfo{deploymentInfo},
- Lock: queue.LockState{
+ Lock: lock.LockState{
// ensure that the lock gets updated
- Status: queue.LockedStatus,
+ Status: lock.LockedStatus,
Revision: "123334444555",
},
ExpectedRequest: notifier.GithubCheckRunRequest{
@@ -397,8 +398,8 @@ func TestEnqueue_MergeTrigger_QueueAlreadyLocked(t *testing.T) {
Repo: github.Repo{Name: "nish"},
},
}, resp.Queue)
- assert.Equal(t, queue.LockState{
- Status: queue.LockedStatus,
+ assert.Equal(t, lock.LockState{
+ Status: lock.LockedStatus,
Revision: "123334444555",
}, resp.Lock)
assert.False(t, resp.Timeout)
diff --git a/server/neptune/workflows/internal/deploy/terraform/runner.go b/server/neptune/workflows/internal/deploy/terraform/runner.go
index 87d5baa75..e07619f93 100644
--- a/server/neptune/workflows/internal/deploy/terraform/runner.go
+++ b/server/neptune/workflows/internal/deploy/terraform/runner.go
@@ -3,6 +3,7 @@ package terraform
import (
"github.com/pkg/errors"
terraformActivities "github.com/runatlantis/atlantis/server/neptune/workflows/activities/terraform"
+ "github.com/runatlantis/atlantis/server/neptune/workflows/internal/deploy/lock"
"github.com/runatlantis/atlantis/server/neptune/workflows/internal/metrics"
"github.com/runatlantis/atlantis/server/neptune/workflows/internal/terraform"
"github.com/runatlantis/atlantis/server/neptune/workflows/internal/terraform/state"
@@ -36,6 +37,7 @@ type stateReceiver interface {
type deployQueue interface {
GetOrderedMergedItems() []DeploymentInfo
GetQueuedRevisionsSummary() string
+ GetLockState() lock.LockState
}
func NewWorkflowRunner(queue deployQueue, w Workflow, githubCheckRunCache CheckRunClient, internalNotifiers []WorkflowNotifier, additionalNotifiers ...plugins.TerraformWorkflowNotifier) *WorkflowRunner {
diff --git a/server/neptune/workflows/internal/deploy/terraform/state.go b/server/neptune/workflows/internal/deploy/terraform/state.go
index 2b621395b..8d806199a 100644
--- a/server/neptune/workflows/internal/deploy/terraform/state.go
+++ b/server/neptune/workflows/internal/deploy/terraform/state.go
@@ -6,6 +6,7 @@ import (
"github.com/pkg/errors"
"github.com/runatlantis/atlantis/server/metrics"
"github.com/runatlantis/atlantis/server/neptune/workflows/activities/github"
+ "github.com/runatlantis/atlantis/server/neptune/workflows/internal/deploy/lock"
"github.com/runatlantis/atlantis/server/neptune/workflows/internal/notifier"
"github.com/runatlantis/atlantis/server/neptune/workflows/internal/terraform/state"
"github.com/runatlantis/atlantis/server/neptune/workflows/plugins"
@@ -68,6 +69,8 @@ func (n *StateReceiver) Receive(ctx workflow.Context, c workflow.ReceiveChannel,
len(workflowState.Apply.OnWaitingActions.Actions) > 0 {
queuedDeployments := n.Queue.GetOrderedMergedItems()
revisionsSummary := n.Queue.GetQueuedRevisionsSummary()
+ runState := github.CheckRunQueued
+ var actions []github.CheckRunAction
var summary string
if workflowState.Apply.Status == state.WaitingJobStatus {
@@ -75,15 +78,25 @@ func (n *StateReceiver) Receive(ctx workflow.Context, c workflow.ReceiveChannel,
summary = fmt.Sprintf("This deploy is queued pending action on run for revision %s.\n%s", runLink, revisionsSummary)
} else if workflowState.Apply.Status == state.RejectedJobStatus || workflowState.Apply.Status == state.InProgressJobStatus {
// If the current deployment is Rejected or In Progress status, we need to restore the queued check runs to reflect that the queued deployments are not blocked.
- summary = "This deploy is queued and will be processed as soon as possible.\n" + revisionsSummary
+ // If the queue is currently locked we need to provide the unlock action.
+ queueLock := n.Queue.GetLockState()
+ if queueLock.Status == lock.LockedStatus {
+ actions = append(actions, github.CreateUnlockAction())
+ runState = github.CheckRunActionRequired
+ revisionLink := github.BuildRevisionURLMarkdown(deploymentInfo.Repo.GetFullName(), queueLock.Revision)
+ summary = fmt.Sprintf("This deploy is locked from a manual deployment for revision %s. Unlock to proceed.\n%s", revisionLink, revisionsSummary)
+ } else {
+ summary = "This deploy is queued and will be processed as soon as possible.\n" + revisionsSummary
+ }
}
for _, i := range queuedDeployments {
request := notifier.GithubCheckRunRequest{
Title: notifier.BuildDeployCheckRunTitle(i.Root.Name),
Sha: i.Commit.Revision,
- State: github.CheckRunQueued,
+ State: runState,
Repo: i.Repo,
Summary: summary,
+ Actions: actions,
}
workflow.GetLogger(ctx).Debug(fmt.Sprintf("Updating action pending summary for deployment id: %s", i.ID.String()))
diff --git a/server/neptune/workflows/internal/deploy/terraform/state_test.go b/server/neptune/workflows/internal/deploy/terraform/state_test.go
index 27dac5e0a..4f4041966 100644
--- a/server/neptune/workflows/internal/deploy/terraform/state_test.go
+++ b/server/neptune/workflows/internal/deploy/terraform/state_test.go
@@ -12,6 +12,7 @@ import (
"github.com/google/uuid"
"github.com/runatlantis/atlantis/server/neptune/workflows/activities/github"
"github.com/runatlantis/atlantis/server/neptune/workflows/activities/terraform"
+ "github.com/runatlantis/atlantis/server/neptune/workflows/internal/deploy/lock"
internalTerraform "github.com/runatlantis/atlantis/server/neptune/workflows/internal/deploy/terraform"
"github.com/runatlantis/atlantis/server/neptune/workflows/internal/terraform/state"
"github.com/runatlantis/atlantis/server/neptune/workflows/plugins"
@@ -63,6 +64,11 @@ func (t *testCheckRunClient) CreateOrUpdate(ctx workflow.Context, deploymentID s
type testQueue struct {
Queue []internalTerraform.DeploymentInfo
+ Lock lock.LockState
+}
+
+func (q *testQueue) GetLockState() lock.LockState {
+ return q.Lock
}
func (q *testQueue) GetQueuedRevisionsSummary() string {