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 {