feat(cli): prevent coder schedule command on prebuilt workspaces (#19259)

## Description

This PR adds CLI-side validation to prevent the use of the `coder
schedule` command (including both `start` and `stop` subcommands) on
prebuilt workspaces.

Prebuilt workspaces are scheduled independently by the reconciliation
loop, based on template and preset-level configuration. They do not
participate in the regular user workspace lifecycle, and cannot be
configured via the `coder schedule` CLI command. This change ensures
that attempting to configure scheduling on a prebuilt workspace results
in a clear CLI error.

## Changes

- `coder schedule start` — now returns an error if the target workspace
is a prebuild
- `coder schedule stop` — now returns an error if the target workspace
is a prebuild

Related with: 
* Issue: https://github.com/coder/coder/issues/18898
* **Depends on PR**: https://github.com/coder/coder/pull/19252
This commit is contained in:
Susana Ferreira
2025-08-13 18:15:53 +01:00
committed by GitHub
parent e10f29c481
commit 92d505c52b
4 changed files with 174 additions and 3 deletions
+22 -1
View File
@@ -46,7 +46,7 @@ When enabling scheduled stop, enter a duration in one of the following formats:
* 2m (2 minutes)
* 2 (2 minutes)
`
scheduleExtendDescriptionLong = `
scheduleExtendDescriptionLong = `Extends the workspace deadline.
* 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.
@@ -157,6 +157,13 @@ func (r *RootCmd) scheduleStart() *serpent.Command {
return err
}
// Autostart configuration is not supported for prebuilt workspaces.
// Prebuild lifecycle is managed by the reconciliation loop, with scheduling behavior
// defined per preset at the template level, not per workspace.
if workspace.IsPrebuild {
return xerrors.Errorf("autostart configuration is not supported for prebuilt workspaces")
}
var schedStr *string
if inv.Args[1] != "manual" {
sched, err := parseCLISchedule(inv.Args[1:]...)
@@ -205,6 +212,13 @@ func (r *RootCmd) scheduleStop() *serpent.Command {
return err
}
// Autostop configuration is not supported for prebuilt workspaces.
// Prebuild lifecycle is managed by the reconciliation loop, with scheduling behavior
// defined per preset at the template level, not per workspace.
if workspace.IsPrebuild {
return xerrors.Errorf("autostop configuration is not supported for prebuilt workspaces")
}
var durMillis *int64
if inv.Args[1] != "manual" {
dur, err := parseDuration(inv.Args[1])
@@ -255,6 +269,13 @@ func (r *RootCmd) scheduleExtend() *serpent.Command {
return xerrors.Errorf("get workspace: %w", err)
}
// Deadline extensions are not supported for prebuilt workspaces.
// Prebuild lifecycle is managed by the reconciliation loop, with TTL behavior
// defined per preset at the template level, not per workspace.
if workspace.IsPrebuild {
return xerrors.Errorf("extend configuration is not supported for prebuilt workspaces")
}
loc, err := tz.TimezoneIANA()
if err != nil {
loc = time.UTC // best effort
+2 -1
View File
@@ -7,7 +7,8 @@ USAGE:
Aliases: override-stop
* The new stop time is calculated from *now*.
Extends the workspace deadline.
* 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.