Skip to content

Commit

Permalink
feat: add stale state to runs
Browse files Browse the repository at this point in the history
  • Loading branch information
leg100 committed May 1, 2024
1 parent 05e4932 commit 246315c
Show file tree
Hide file tree
Showing 6 changed files with 89 additions and 5 deletions.
6 changes: 5 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -180,7 +180,11 @@ If you add/remove workspaces outside of Pug, you can instruct Pug to reload work

A run represents a terraform plan and the optional apply of that plan. Under the hood, it invokes `terraform plan -out <plan-file>`. Should you then apply the run, it invokes `terraform apply <plan-file>`.

A run starts in the `pending` state. If its workspace doesn't have a current run, then Pug transitions it into the `scheduled` state and sets it as the workspace's current run. Otherwise the run remains in the `pending` state until the current run has finished.
A run starts in the `pending` state. It remains in that state until it can be scheduled. It can only be scheduled once all runs created prior to it on the same workspace have finished.

An exception to this rule is if the previous run is in the `planned` state. In which case the previous run is placed into the `stale` termination state, i.e. its plan file is deemed stale and cannot be applied.

Once a run is scheduled, it's placed into `scheduled` state and it's designated as the *current run* for the workspace.

If there are no blocked tasks running on its workspace and module (see tasks below) then the run transitions into the `plan queued` state. Once there is sufficient task capacity, the run enters the `planning` state, and `terraform plan` is invoked.

Expand Down
2 changes: 1 addition & 1 deletion internal/app/helpers_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,7 @@ func cleanupArtefacts(workdir string, opts setupOptions) {

}

// setupProviderMirror configures a dedicated provider filesystem mirror for for
// setupProviderMirror configures a dedicated provider filesystem mirror for
// a test.
func setupProviderMirror(t *testing.T) {
t.Helper()
Expand Down
65 changes: 65 additions & 0 deletions internal/app/run_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,71 @@ func TestRun(t *testing.T) {
initAndApplyModuleA(t, tm)
}

// TestRun_Stale tests that a planned run is placed into the 'stale' state when
// a succeeding run is created.
func TestRun_Stale(t *testing.T) {
tm := setup(t, "./testdata/module_list")

// Wait for module to be loaded
waitFor(t, tm, func(s string) bool {
return strings.Contains(s, "modules/a")
})

// Initialize module
tm.Type("i")
waitFor(t, tm, func(s string) bool {
return strings.Contains(s, "Terraform has been successfully initialized!")
})

// Go to workspaces
tm.Type("W")

// Wait for workspace to be loaded
waitFor(t, tm, func(s string) bool {
return strings.Contains(s, "default")
})

// Create plan for first workspace
tm.Type("p")

// User should now be taken to the run page...

// Expect to see summary of changes
waitFor(t, tm, func(s string) bool {
// Remove bold formatting
s = internal.StripAnsi(s)
return strings.Contains(s, "Plan: 10 to add, 0 to change, 0 to destroy.")
})

// Go to its workspace page
tm.Type("w")

// Expect one run in the planned state
waitFor(t, tm, func(s string) bool {
return strings.Contains(s, "runs (1)") && matchPattern(t, `planned.*\+10~0\-0`, s)
})

// Start another run
tm.Type("p")

// Expect to see summary of changes, again.
waitFor(t, tm, func(s string) bool {
// Remove bold formatting
s = internal.StripAnsi(s)
return strings.Contains(s, "Plan: 10 to add, 0 to change, 0 to destroy.")
})

// Go to its workspace page
tm.Type("w")

// Expect two runs, one in the planned state, one in the stale state
waitFor(t, tm, func(s string) bool {
return strings.Contains(s, "runs (2)") &&
matchPattern(t, `planned.*\+10~0\-0`, s) &&
matchPattern(t, `stale.*\+10~0\-0`, s)
})
}

func TestRun_WithVars(t *testing.T) {
tm := setup(t, "./testdata/run_with_vars")

Expand Down
3 changes: 2 additions & 1 deletion internal/run/run.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ const (
ApplyQueued Status = "apply queued"
Applying Status = "applying"
Applied Status = "applied"
Stale Status = "stale"
Errored Status = "errored"
Canceled Status = "canceled"
Discarded Status = "discarded"
Expand Down Expand Up @@ -126,7 +127,7 @@ func (r *Run) PlanArgs() []string {

func (r *Run) IsFinished() bool {
switch r.Status {
case NoChanges, Applied, Errored, Canceled:
case NoChanges, Applied, Errored, Canceled, Stale:
return true
default:
return false
Expand Down
14 changes: 13 additions & 1 deletion internal/run/scheduler.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,20 @@ func StartScheduler(runs *Service, workspaces *workspace.Service) {
for _, run := range s.schedule() {
// Update status from pending to scheduled
run.updateStatus(Scheduled)

// If the current current run is in planned state, mark it as stale
//
// TODO: handle errors
ws, _ := workspaces.Get(run.WorkspaceID())
if runID := ws.CurrentRunID; runID != nil {
current, _ := runs.Get(*runID)
if current.Status == Planned {
current.updateStatus(Stale)
}
}

// Set run as workspace's current run
workspaces.SetCurrentRun(run.WorkspaceID(), run.ID)
_ = workspaces.SetCurrentRun(run.WorkspaceID(), run.ID)
// Trigger a plan task
_, _ = runs.plan(run)
}
Expand Down
4 changes: 3 additions & 1 deletion internal/tui/helpers.go
Original file line number Diff line number Diff line change
Expand Up @@ -162,13 +162,15 @@ func (h *Helpers) RunStatus(r *run.Run) string {
color = Green
case run.Errored:
color = Red
case run.Stale:
color = Orange
}
return Regular.Copy().Foreground(color).Render(string(r.Status))
}

func (h *Helpers) LatestRunReport(r *run.Run) string {
switch r.Status {
case run.Planned, run.NoChanges:
case run.Planned, run.NoChanges, run.Stale:
return h.RunReport(r.PlanReport)
case run.Applied:
return h.RunReport(r.ApplyReport)
Expand Down

0 comments on commit 246315c

Please sign in to comment.