feat: cli: consolidate schedule-related commands#2402
Conversation
This commit makes the following changes: - renames autostart -> schedule starat - renames ttl -> schedule stop - renames bump -> schedule override - adds schedule show command - moves some cli-related stuff to util.go
| -covermode=atomic -coverprofile="gotests.coverage" -timeout=5m \ | ||
| -covermode=atomic -coverprofile="gotests.coverage" -timeout=10m \ | ||
| -coverpkg=./...,github.com/coder/coder/codersdk \ | ||
| -count=1 -parallel=1 -race -failfast | ||
| -count=1 -parallel=2 -race -failfast |
There was a problem hiding this comment.
👀 bumping these here because we are absolutely hitting the five-minute mark
There was a problem hiding this comment.
Spike's PR (#2413) actually addresses this better so I'm going to revert these changes.
|
|
||
| const ( | ||
| timeFormat = "3:04:05 PM MST" | ||
| timeFormat = "3:04PM MST" |
There was a problem hiding this comment.
I don't think seconds are useful to users here.
| header := table.Row{"workspace", "template", "status", "last built", "outdated", "autostart", "ttl"} | ||
| header := table.Row{"workspace", "template", "status", "last built", "outdated", "starts at", "stops after"} |
| if !ptr.NilOrEmpty(workspace.AutostartSchedule) { | ||
| if sched, err := schedule.Weekly(*workspace.AutostartSchedule); err == nil { | ||
| autostartDisplay = sched.Cron() | ||
| autostartDisplay = fmt.Sprintf("%s %s (%s)", sched.Time(), sched.DaysOfWeek(), sched.Location()) |
There was a problem hiding this comment.
Consistent schedule format
| @@ -0,0 +1,207 @@ | |||
| package cli | |||
There was a problem hiding this comment.
There were some helper methods about the place that I decided to pull into here.
| // Time returns a humanized form of the minute and hour fields. | ||
| func (s Schedule) Time() string { | ||
| minute := strings.Fields(s.cronStr)[0] | ||
| hour := strings.Fields(s.cronStr)[1] | ||
| maybeTime := fmt.Sprintf("%s:%s", hour, minute) | ||
| t, err := time.ParseInLocation("3:4", maybeTime, s.sched.Location) | ||
| if err != nil { | ||
| // return the original cronspec for minute and hour, who knows what's in there! | ||
| return fmt.Sprintf("cron(%s %s)", minute, hour) | ||
| } | ||
| return t.Format(time.Kitchen) | ||
| } |
There was a problem hiding this comment.
This is a best-effort of showing the scheduled time of a schedule. If we can't display it as a simple time, we just return the cron for it.
| if has, ext := hasExtension(workspace); has { | ||
| autostopDisplay += fmt.Sprintf(" (+%s)", durationDisplay(ext.Round(time.Minute))) | ||
| if !workspace.LatestBuild.Deadline.IsZero() && workspace.LatestBuild.Deadline.After(now) && status == "Running" { | ||
| remaining := time.Until(workspace.LatestBuild.Deadline) | ||
| autostopDisplay = fmt.Sprintf("%s (%s)", autostopDisplay, relative(remaining)) |
There was a problem hiding this comment.
Felt it's more useful to show the time remaining here
| func durationDisplay(d time.Duration) string { | ||
| duration := d | ||
| sign := "" | ||
| if duration == 0 { | ||
| return "0s" | ||
| } | ||
| if duration < 0 { | ||
| duration *= -1 | ||
| } | ||
| // duration > 0 now | ||
| if duration < time.Minute { | ||
| return sign + "<1m" | ||
| } | ||
| if duration > 24*time.Hour { | ||
| duration = duration.Truncate(time.Hour) | ||
| } | ||
| if duration > time.Minute { | ||
| duration = duration.Truncate(time.Minute) | ||
| } | ||
| days := 0 | ||
| for duration.Hours() >= 24 { | ||
| days++ | ||
| duration -= 24 * time.Hour | ||
| } | ||
| durationDisplay := duration.String() | ||
| if days > 0 { | ||
| durationDisplay = fmt.Sprintf("%dd%s", days, durationDisplay) | ||
| } | ||
| for _, suffix := range []string{"m0s", "h0m", "d0s"} { | ||
| if strings.HasSuffix(durationDisplay, suffix) { | ||
| durationDisplay = durationDisplay[:len(durationDisplay)-2] | ||
| } | ||
| } | ||
| return sign + durationDisplay | ||
| } |
There was a problem hiding this comment.
review: smaller durations are always <1m now, it can also handle negative durations
| * The new stop time must be at least 30 minutes in the future. | ||
| * The workspace template may restrict the maximum workspace runtime. | ||
| ` | ||
| ) |
| Use: "stop <workspace_name> { <duration> | manual }", | ||
| Example: `stop my-workspace 2h30m`, | ||
| Short: "Edit workspace stop schedule", | ||
| Long: scheduleStopDescriptionLong, |
| * The new stop time is calculated from *now*. | ||
| * The new stop time must be at least 30 minutes in the future. | ||
| * The workspace template may restrict the maximum workspace runtime. | ||
| ` |
|
|
||
| loc, err := tz.TimezoneIANA() | ||
| if err != nil { | ||
| loc = time.UTC // best effort |

This commit makes the following changes:
Sample output: