mirror of https://github.com/coder/coder.git
152 lines
4.4 KiB
Go
152 lines
4.4 KiB
Go
package cli
|
|
|
|
import (
|
|
"fmt"
|
|
"time"
|
|
|
|
"github.com/google/uuid"
|
|
|
|
"github.com/coder/coder/cli/clibase"
|
|
"github.com/coder/coder/cli/cliui"
|
|
"github.com/coder/coder/coderd/schedule"
|
|
"github.com/coder/coder/coderd/util/ptr"
|
|
"github.com/coder/coder/codersdk"
|
|
)
|
|
|
|
// workspaceListRow is the type provided to the OutputFormatter. This is a bit
|
|
// dodgy but it's the only way to do complex display code for one format vs. the
|
|
// other.
|
|
type workspaceListRow struct {
|
|
// For JSON format:
|
|
codersdk.Workspace `table:"-"`
|
|
|
|
// For table format:
|
|
WorkspaceName string `json:"-" table:"workspace,default_sort"`
|
|
Template string `json:"-" table:"template"`
|
|
Status string `json:"-" table:"status"`
|
|
LastBuilt string `json:"-" table:"last built"`
|
|
Outdated bool `json:"-" table:"outdated"`
|
|
StartsAt string `json:"-" table:"starts at"`
|
|
StopsAfter string `json:"-" table:"stops after"`
|
|
}
|
|
|
|
func workspaceListRowFromWorkspace(now time.Time, usersByID map[uuid.UUID]codersdk.User, workspace codersdk.Workspace) workspaceListRow {
|
|
status := codersdk.WorkspaceDisplayStatus(workspace.LatestBuild.Job.Status, workspace.LatestBuild.Transition)
|
|
|
|
lastBuilt := now.UTC().Sub(workspace.LatestBuild.Job.CreatedAt).Truncate(time.Second)
|
|
autostartDisplay := "-"
|
|
if !ptr.NilOrEmpty(workspace.AutostartSchedule) {
|
|
if sched, err := schedule.Weekly(*workspace.AutostartSchedule); err == nil {
|
|
autostartDisplay = fmt.Sprintf("%s %s (%s)", sched.Time(), sched.DaysOfWeek(), sched.Location())
|
|
}
|
|
}
|
|
|
|
autostopDisplay := "-"
|
|
if !ptr.NilOrZero(workspace.TTLMillis) {
|
|
dur := time.Duration(*workspace.TTLMillis) * time.Millisecond
|
|
autostopDisplay = durationDisplay(dur)
|
|
if !workspace.LatestBuild.Deadline.IsZero() && workspace.LatestBuild.Deadline.Time.After(now) && status == "Running" {
|
|
remaining := time.Until(workspace.LatestBuild.Deadline.Time)
|
|
autostopDisplay = fmt.Sprintf("%s (%s)", autostopDisplay, relative(remaining))
|
|
}
|
|
}
|
|
|
|
user := usersByID[workspace.OwnerID]
|
|
return workspaceListRow{
|
|
Workspace: workspace,
|
|
WorkspaceName: user.Username + "/" + workspace.Name,
|
|
Template: workspace.TemplateName,
|
|
Status: status,
|
|
LastBuilt: durationDisplay(lastBuilt),
|
|
Outdated: workspace.Outdated,
|
|
StartsAt: autostartDisplay,
|
|
StopsAfter: autostopDisplay,
|
|
}
|
|
}
|
|
|
|
func (r *RootCmd) list() *clibase.Cmd {
|
|
var (
|
|
all bool
|
|
defaultQuery = "owner:me"
|
|
searchQuery string
|
|
displayWorkspaces []workspaceListRow
|
|
formatter = cliui.NewOutputFormatter(
|
|
cliui.TableFormat([]workspaceListRow{}, nil),
|
|
cliui.JSONFormat(),
|
|
)
|
|
)
|
|
client := new(codersdk.Client)
|
|
cmd := &clibase.Cmd{
|
|
Annotations: workspaceCommand,
|
|
Use: "list",
|
|
Short: "List workspaces",
|
|
Aliases: []string{"ls"},
|
|
Middleware: clibase.Chain(
|
|
clibase.RequireNArgs(0),
|
|
r.InitClient(client),
|
|
),
|
|
Handler: func(inv *clibase.Invocation) error {
|
|
filter := codersdk.WorkspaceFilter{
|
|
FilterQuery: searchQuery,
|
|
}
|
|
if all && searchQuery == defaultQuery {
|
|
filter.FilterQuery = ""
|
|
}
|
|
|
|
res, err := client.Workspaces(inv.Context(), filter)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if len(res.Workspaces) == 0 {
|
|
_, _ = fmt.Fprintln(inv.Stderr, cliui.Styles.Prompt.String()+"No workspaces found! Create one:")
|
|
_, _ = fmt.Fprintln(inv.Stderr)
|
|
_, _ = fmt.Fprintln(inv.Stderr, " "+cliui.Styles.Code.Render("coder create <name>"))
|
|
_, _ = fmt.Fprintln(inv.Stderr)
|
|
return nil
|
|
}
|
|
|
|
userRes, err := client.Users(inv.Context(), codersdk.UsersRequest{})
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
usersByID := map[uuid.UUID]codersdk.User{}
|
|
for _, user := range userRes.Users {
|
|
usersByID[user.ID] = user
|
|
}
|
|
|
|
now := time.Now()
|
|
displayWorkspaces = make([]workspaceListRow, len(res.Workspaces))
|
|
for i, workspace := range res.Workspaces {
|
|
displayWorkspaces[i] = workspaceListRowFromWorkspace(now, usersByID, workspace)
|
|
}
|
|
|
|
out, err := formatter.Format(inv.Context(), displayWorkspaces)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
_, err = fmt.Fprintln(inv.Stdout, out)
|
|
return err
|
|
},
|
|
}
|
|
cmd.Options = clibase.OptionSet{
|
|
{
|
|
Flag: "all",
|
|
FlagShorthand: "a",
|
|
Description: "Specifies whether all workspaces will be listed or not.",
|
|
|
|
Value: clibase.BoolOf(&all),
|
|
},
|
|
{
|
|
Flag: "search",
|
|
Description: "Search for a workspace with a query.",
|
|
Default: defaultQuery,
|
|
Value: clibase.StringOf(&searchQuery),
|
|
},
|
|
}
|
|
|
|
formatter.AttachOptions(&cmd.Options)
|
|
return cmd
|
|
}
|