Skip to content

Commit ab4b266

Browse files
committed
feat(cli): allow showing schedules for multiple workspaces
1 parent 5a61631 commit ab4b266

File tree

1 file changed

+62
-53
lines changed

1 file changed

+62
-53
lines changed

cli/schedule.go

+62-53
Original file line numberDiff line numberDiff line change
@@ -5,19 +5,17 @@ import (
55
"io"
66
"time"
77

8-
"github.com/jedib0t/go-pretty/v6/table"
98
"golang.org/x/xerrors"
109

1110
"github.com/coder/coder/v2/cli/clibase"
1211
"github.com/coder/coder/v2/cli/cliui"
13-
"github.com/coder/coder/v2/coderd/schedule/cron"
1412
"github.com/coder/coder/v2/coderd/util/ptr"
1513
"github.com/coder/coder/v2/coderd/util/tz"
1614
"github.com/coder/coder/v2/codersdk"
1715
)
1816

1917
const (
20-
scheduleShowDescriptionLong = `Shows the following information for the given workspace:
18+
scheduleShowDescriptionLong = `Shows the following information for the given workspace(s):
2119
* The automatic start schedule
2220
* The next scheduled start time
2321
* The duration after which it will stop
@@ -72,25 +70,72 @@ func (r *RootCmd) schedules() *clibase.Cmd {
7270
return scheduleCmd
7371
}
7472

73+
// scheduleShow() is just a wrapper for list() with some different defaults.
7574
func (r *RootCmd) scheduleShow() *clibase.Cmd {
75+
var (
76+
all bool
77+
defaultQuery = "owner:me"
78+
searchQuery string
79+
formatter = cliui.NewOutputFormatter(
80+
cliui.TableFormat(
81+
[]workspaceListRow{},
82+
[]string{
83+
"workspace",
84+
"starts at",
85+
"starts next",
86+
"stops after",
87+
"stops next",
88+
},
89+
),
90+
cliui.JSONFormat(),
91+
)
92+
)
7693
client := new(codersdk.Client)
7794
showCmd := &clibase.Cmd{
78-
Use: "show <workspace-name>",
79-
Short: "Show workspace schedule",
95+
Use: "show <workspace 1> ... <workspace N>",
96+
Short: "Show workspace schedules",
8097
Long: scheduleShowDescriptionLong,
8198
Middleware: clibase.Chain(
82-
clibase.RequireNArgs(1),
99+
clibase.RequireRangeArgs(0, 1),
83100
r.InitClient(client),
84101
),
85102
Handler: func(inv *clibase.Invocation) error {
86-
workspace, err := namedWorkspace(inv.Context(), client, inv.Args[0])
87-
if err != nil {
88-
return err
103+
// To preserve existing behavior, if an argument is passed we will
104+
// only show the schedule for that workspace.
105+
// This will clobber the search query if one is passed.
106+
if len(inv.Args) == 1 {
107+
searchQuery = fmt.Sprintf("%s name:%s", defaultQuery, inv.Args[0])
89108
}
109+
return handleList(
110+
inv.Context(),
111+
handleListArgs{
112+
formatter: formatter,
113+
client: client,
114+
searchQuery: searchQuery,
115+
all: all,
116+
stdout: inv.Stdout,
117+
stderr: inv.Stderr,
118+
},
119+
)
120+
},
121+
}
122+
showCmd.Options = clibase.OptionSet{
123+
{
124+
Flag: "all",
125+
FlagShorthand: "a",
126+
Description: "Specifies whether all workspaces will be listed or not.",
90127

91-
return displaySchedule(workspace, inv.Stdout)
128+
Value: clibase.BoolOf(&all),
129+
},
130+
{
131+
Flag: "search",
132+
Description: "Search for a workspace with a query.",
133+
Default: defaultQuery,
134+
Value: clibase.StringOf(&searchQuery),
92135
},
93136
}
137+
138+
formatter.AttachOptions(&showCmd.Options)
94139
return showCmd
95140
}
96141

@@ -242,50 +287,14 @@ func (r *RootCmd) scheduleOverride() *clibase.Cmd {
242287
return overrideCmd
243288
}
244289

245-
func displaySchedule(workspace codersdk.Workspace, out io.Writer) error {
246-
loc, err := tz.TimezoneIANA()
290+
func displaySchedule(ws codersdk.Workspace, out io.Writer) error {
291+
rows := []workspaceListRow{workspaceListRowFromWorkspace(time.Now(), ws)}
292+
rendered, err := cliui.DisplayTable(rows, "workspace", []string{
293+
"workspace", "starts at", "starts next", "stops after", "stops next",
294+
})
247295
if err != nil {
248-
loc = time.UTC // best effort
296+
panic(err)
249297
}
250-
251-
var (
252-
schedStart = "manual"
253-
schedStop = "manual"
254-
schedNextStart = "-"
255-
schedNextStop = "-"
256-
)
257-
if !ptr.NilOrEmpty(workspace.AutostartSchedule) {
258-
sched, err := cron.Weekly(ptr.NilToEmpty(workspace.AutostartSchedule))
259-
if err != nil {
260-
// This should never happen.
261-
_, _ = fmt.Fprintf(out, "Invalid autostart schedule %q for workspace %s: %s\n", *workspace.AutostartSchedule, workspace.Name, err.Error())
262-
return nil
263-
}
264-
schedNext := sched.Next(time.Now()).In(sched.Location())
265-
schedStart = fmt.Sprintf("%s %s (%s)", sched.Time(), sched.DaysOfWeek(), sched.Location())
266-
schedNextStart = schedNext.Format(timeFormat + " on " + dateFormat)
267-
}
268-
269-
if !ptr.NilOrZero(workspace.TTLMillis) {
270-
d := time.Duration(*workspace.TTLMillis) * time.Millisecond
271-
schedStop = durationDisplay(d) + " after start"
272-
}
273-
274-
if !workspace.LatestBuild.Deadline.IsZero() {
275-
if workspace.LatestBuild.Transition != "start" {
276-
schedNextStop = "-"
277-
} else {
278-
schedNextStop = workspace.LatestBuild.Deadline.Time.In(loc).Format(timeFormat + " on " + dateFormat)
279-
schedNextStop = fmt.Sprintf("%s (in %s)", schedNextStop, durationDisplay(time.Until(workspace.LatestBuild.Deadline.Time)))
280-
}
281-
}
282-
283-
tw := cliui.Table()
284-
tw.AppendRow(table.Row{"Starts at", schedStart})
285-
tw.AppendRow(table.Row{"Starts next", schedNextStart})
286-
tw.AppendRow(table.Row{"Stops at", schedStop})
287-
tw.AppendRow(table.Row{"Stops next", schedNextStop})
288-
289-
_, _ = fmt.Fprintln(out, tw.Render())
298+
_, _ = fmt.Fprintln(out, rendered)
290299
return nil
291300
}

0 commit comments

Comments
 (0)