diff --git a/cli/list.go b/cli/list.go index 2a07508b01..c69308a2f6 100644 --- a/cli/list.go +++ b/cli/list.go @@ -58,33 +58,7 @@ func list() *cobra.Command { now := time.Now() for _, workspace := range workspaces { - status := "" - inProgress := false - if workspace.LatestBuild.Job.Status == codersdk.ProvisionerJobRunning || - workspace.LatestBuild.Job.Status == codersdk.ProvisionerJobCanceling { - inProgress = true - } - - switch workspace.LatestBuild.Transition { - case codersdk.WorkspaceTransitionStart: - status = "Running" - if inProgress { - status = "Starting" - } - case codersdk.WorkspaceTransitionStop: - status = "Stopped" - if inProgress { - status = "Stopping" - } - case codersdk.WorkspaceTransitionDelete: - status = "Deleted" - if inProgress { - status = "Deleting" - } - } - if workspace.LatestBuild.Job.Status == codersdk.ProvisionerJobFailed { - status = "Failed" - } + status := codersdk.WorkspaceDisplayStatus(workspace.LatestBuild.Job.Status, workspace.LatestBuild.Transition) lastBuilt := time.Now().UTC().Sub(workspace.LatestBuild.Job.CreatedAt).Truncate(time.Second) autostartDisplay := "-" diff --git a/cli/list_test.go b/cli/list_test.go index 98a3ae7fdf..597e4451a9 100644 --- a/cli/list_test.go +++ b/cli/list_test.go @@ -37,7 +37,7 @@ func TestList(t *testing.T) { close(done) }() pty.ExpectMatch(workspace.Name) - pty.ExpectMatch("Running") + pty.ExpectMatch("Started") cancelFunc() <-done }) diff --git a/codersdk/workspacedisplaystatus.go b/codersdk/workspacedisplaystatus.go new file mode 100644 index 0000000000..48e62e420d --- /dev/null +++ b/codersdk/workspacedisplaystatus.go @@ -0,0 +1,48 @@ +package codersdk + +// Maps workspace transition to display status for Running job status +var runningStatusFromTransition = map[WorkspaceTransition]string{ + WorkspaceTransitionStart: "Starting", + WorkspaceTransitionStop: "Stopping", + WorkspaceTransitionDelete: "Deleting", +} + +// Maps workspace transition to display status for Succeeded job status +var succeededStatusFromTransition = map[WorkspaceTransition]string{ + WorkspaceTransitionStart: "Started", + WorkspaceTransitionStop: "Stopped", + WorkspaceTransitionDelete: "Deleted", +} + +const unknownStatus = "Unknown" + +// WorkspaceDisplayStatus computes a status to display on CLI/UI based on +// the workspace transition and the status of the provisioner job. +// This code is in sync with how we compute the status on frontend. +// Ref: site/src/util/workspace.ts (getWorkspaceStatus) +func WorkspaceDisplayStatus(jobStatus ProvisionerJobStatus, transition WorkspaceTransition) string { + switch jobStatus { + case ProvisionerJobSucceeded: + status, ok := succeededStatusFromTransition[transition] + if !ok { + return unknownStatus + } + return status + case ProvisionerJobRunning: + status, ok := runningStatusFromTransition[transition] + if !ok { + return unknownStatus + } + return status + case ProvisionerJobPending: + return "Queued" + case ProvisionerJobCanceling: + return "Canceling action" + case ProvisionerJobCanceled: + return "Canceled action" + case ProvisionerJobFailed: + return "Failed" + default: + return unknownStatus + } +} diff --git a/codersdk/workspacedisplaystatus_internal_test.go b/codersdk/workspacedisplaystatus_internal_test.go new file mode 100644 index 0000000000..1379de5724 --- /dev/null +++ b/codersdk/workspacedisplaystatus_internal_test.go @@ -0,0 +1,101 @@ +package codersdk + +import "testing" + +func TestWorkspaceDisplayStatus(t *testing.T) { + t.Parallel() + tests := []struct { + name string + jobStatus ProvisionerJobStatus + transition WorkspaceTransition + want string + }{ + { + name: "SucceededStatusWithStartTransition", + jobStatus: ProvisionerJobSucceeded, + transition: WorkspaceTransitionStart, + want: "Started", + }, + { + name: "SucceededStatusWithStopTransition", + jobStatus: ProvisionerJobSucceeded, + transition: WorkspaceTransitionStop, + want: "Stopped", + }, + { + name: "SucceededStatusWithDeleteTransition", + jobStatus: ProvisionerJobSucceeded, + transition: WorkspaceTransitionDelete, + want: "Deleted", + }, + { + name: "RunningStatusWithStartTransition", + jobStatus: ProvisionerJobRunning, + transition: WorkspaceTransitionStart, + want: "Starting", + }, + { + name: "RunningStatusWithStopTransition", + jobStatus: ProvisionerJobRunning, + transition: WorkspaceTransitionStop, + want: "Stopping", + }, + { + name: "RunningStatusWithDeleteTransition", + jobStatus: ProvisionerJobRunning, + transition: WorkspaceTransitionDelete, + want: "Deleting", + }, + { + name: "PendingStatusWithStartTransition", + jobStatus: ProvisionerJobPending, + transition: WorkspaceTransitionStart, + want: "Queued", + }, + { + name: "CancelingStatusWithStartTransition", + jobStatus: ProvisionerJobCanceling, + transition: WorkspaceTransitionStart, + want: "Canceling action", + }, + { + name: "CanceledStatusWithStartTransition", + jobStatus: ProvisionerJobCanceled, + transition: WorkspaceTransitionStart, + want: "Canceled action", + }, + { + name: "FailedStatusWithDeleteTransition", + jobStatus: ProvisionerJobFailed, + transition: WorkspaceTransitionDelete, + want: "Failed", + }, + { + name: "EmptyStatusWithDeleteTransition", + jobStatus: "", + transition: WorkspaceTransitionDelete, + want: unknownStatus, + }, + { + name: "RunningStatusWithEmptyTransition", + jobStatus: ProvisionerJobRunning, + transition: "", + want: unknownStatus, + }, + { + name: "SucceededStatusWithEmptyTransition", + jobStatus: ProvisionerJobSucceeded, + transition: "", + want: unknownStatus, + }, + } + for _, tt := range tests { + tt := tt + t.Run(tt.name, func(t *testing.T) { + t.Parallel() + if got := WorkspaceDisplayStatus(tt.jobStatus, tt.transition); got != tt.want { + t.Errorf("workspaceStatus() = %v, want %v", got, tt.want) + } + }) + } +}