Skip to content

Commit 91875c2

Browse files
fix: allow users to extend their running workspace's deadline (#15895)
Fixes #15515 This change effectively reverts the changes introduced by #13182 (for #13078). We also rename the `override-stop` command name to `extend` to match the API endpoint's name (keeping an alias to allow `override-stop` to be used).
1 parent 4c5b737 commit 91875c2

File tree

11 files changed

+85
-126
lines changed

11 files changed

+85
-126
lines changed

cli/schedule.go

Lines changed: 14 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ When enabling scheduled stop, enter a duration in one of the following formats:
4646
* 2m (2 minutes)
4747
* 2 (2 minutes)
4848
`
49-
scheduleOverrideDescriptionLong = `
49+
scheduleExtendDescriptionLong = `
5050
* The new stop time is calculated from *now*.
5151
* The new stop time must be at least 30 minutes in the future.
5252
* The workspace template may restrict the maximum workspace runtime.
@@ -56,7 +56,7 @@ When enabling scheduled stop, enter a duration in one of the following formats:
5656
func (r *RootCmd) schedules() *serpent.Command {
5757
scheduleCmd := &serpent.Command{
5858
Annotations: workspaceCommand,
59-
Use: "schedule { show | start | stop | override } <workspace>",
59+
Use: "schedule { show | start | stop | extend } <workspace>",
6060
Short: "Schedule automated start and stop times for workspaces",
6161
Handler: func(inv *serpent.Invocation) error {
6262
return inv.Command.HelpHandler(inv)
@@ -65,7 +65,7 @@ func (r *RootCmd) schedules() *serpent.Command {
6565
r.scheduleShow(),
6666
r.scheduleStart(),
6767
r.scheduleStop(),
68-
r.scheduleOverride(),
68+
r.scheduleExtend(),
6969
},
7070
}
7171

@@ -229,22 +229,23 @@ func (r *RootCmd) scheduleStop() *serpent.Command {
229229
}
230230
}
231231

232-
func (r *RootCmd) scheduleOverride() *serpent.Command {
232+
func (r *RootCmd) scheduleExtend() *serpent.Command {
233233
client := new(codersdk.Client)
234-
overrideCmd := &serpent.Command{
235-
Use: "override-stop <workspace-name> <duration from now>",
236-
Short: "Override the stop time of a currently running workspace instance.",
237-
Long: scheduleOverrideDescriptionLong + "\n" + FormatExamples(
234+
extendCmd := &serpent.Command{
235+
Use: "extend <workspace-name> <duration from now>",
236+
Aliases: []string{"override-stop"},
237+
Short: "Extend the stop time of a currently running workspace instance.",
238+
Long: scheduleExtendDescriptionLong + "\n" + FormatExamples(
238239
Example{
239-
Command: "coder schedule override-stop my-workspace 90m",
240+
Command: "coder schedule extend my-workspace 90m",
240241
},
241242
),
242243
Middleware: serpent.Chain(
243244
serpent.RequireNArgs(2),
244245
r.InitClient(client),
245246
),
246247
Handler: func(inv *serpent.Invocation) error {
247-
overrideDuration, err := parseDuration(inv.Args[1])
248+
extendDuration, err := parseDuration(inv.Args[1])
248249
if err != nil {
249250
return err
250251
}
@@ -259,15 +260,15 @@ func (r *RootCmd) scheduleOverride() *serpent.Command {
259260
loc = time.UTC // best effort
260261
}
261262

262-
if overrideDuration < 29*time.Minute {
263+
if extendDuration < 29*time.Minute {
263264
_, _ = fmt.Fprintf(
264265
inv.Stdout,
265266
"Please specify a duration of at least 30 minutes.\n",
266267
)
267268
return nil
268269
}
269270

270-
newDeadline := time.Now().In(loc).Add(overrideDuration)
271+
newDeadline := time.Now().In(loc).Add(extendDuration)
271272
if err := client.PutExtendWorkspace(inv.Context(), workspace.ID, codersdk.PutExtendWorkspaceRequest{
272273
Deadline: newDeadline,
273274
}); err != nil {
@@ -281,7 +282,7 @@ func (r *RootCmd) scheduleOverride() *serpent.Command {
281282
return displaySchedule(updated, inv.Stdout)
282283
},
283284
}
284-
return overrideCmd
285+
return extendCmd
285286
}
286287

287288
func displaySchedule(ws codersdk.Workspace, out io.Writer) error {

cli/schedule_test.go

Lines changed: 42 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -332,32 +332,46 @@ func TestScheduleModify(t *testing.T) {
332332

333333
//nolint:paralleltest // t.Setenv
334334
func TestScheduleOverride(t *testing.T) {
335-
// Given
336-
// Set timezone to Asia/Kolkata to surface any timezone-related bugs.
337-
t.Setenv("TZ", "Asia/Kolkata")
338-
loc, err := tz.TimezoneIANA()
339-
require.NoError(t, err)
340-
require.Equal(t, "Asia/Kolkata", loc.String())
341-
sched, err := cron.Weekly("CRON_TZ=Europe/Dublin 30 7 * * Mon-Fri")
342-
require.NoError(t, err, "invalid schedule")
343-
ownerClient, _, _, ws := setupTestSchedule(t, sched)
344-
now := time.Now()
345-
// To avoid the likelihood of time-related flakes, only matching up to the hour.
346-
expectedDeadline := time.Now().In(loc).Add(10 * time.Hour).Format("2006-01-02T15:")
347-
348-
// When: we override the stop schedule
349-
inv, root := clitest.New(t,
350-
"schedule", "override-stop", ws[0].OwnerName+"/"+ws[0].Name, "10h",
351-
)
352-
353-
clitest.SetupConfig(t, ownerClient, root)
354-
pty := ptytest.New(t).Attach(inv)
355-
require.NoError(t, inv.Run())
356-
357-
// Then: the updated schedule should be shown
358-
pty.ExpectMatch(ws[0].OwnerName + "/" + ws[0].Name)
359-
pty.ExpectMatch(sched.Humanize())
360-
pty.ExpectMatch(sched.Next(now).In(loc).Format(time.RFC3339))
361-
pty.ExpectMatch("8h")
362-
pty.ExpectMatch(expectedDeadline)
335+
tests := []struct {
336+
command string
337+
}{
338+
{command: "extend"},
339+
// test for backwards compatibility
340+
{command: "override-stop"},
341+
}
342+
343+
for _, tt := range tests {
344+
tt := tt
345+
346+
t.Run(tt.command, func(t *testing.T) {
347+
// Given
348+
// Set timezone to Asia/Kolkata to surface any timezone-related bugs.
349+
t.Setenv("TZ", "Asia/Kolkata")
350+
loc, err := tz.TimezoneIANA()
351+
require.NoError(t, err)
352+
require.Equal(t, "Asia/Kolkata", loc.String())
353+
sched, err := cron.Weekly("CRON_TZ=Europe/Dublin 30 7 * * Mon-Fri")
354+
require.NoError(t, err, "invalid schedule")
355+
ownerClient, _, _, ws := setupTestSchedule(t, sched)
356+
now := time.Now()
357+
// To avoid the likelihood of time-related flakes, only matching up to the hour.
358+
expectedDeadline := time.Now().In(loc).Add(10 * time.Hour).Format("2006-01-02T15:")
359+
360+
// When: we override the stop schedule
361+
inv, root := clitest.New(t,
362+
"schedule", tt.command, ws[0].OwnerName+"/"+ws[0].Name, "10h",
363+
)
364+
365+
clitest.SetupConfig(t, ownerClient, root)
366+
pty := ptytest.New(t).Attach(inv)
367+
require.NoError(t, inv.Run())
368+
369+
// Then: the updated schedule should be shown
370+
pty.ExpectMatch(ws[0].OwnerName + "/" + ws[0].Name)
371+
pty.ExpectMatch(sched.Humanize())
372+
pty.ExpectMatch(sched.Next(now).In(loc).Format(time.RFC3339))
373+
pty.ExpectMatch("8h")
374+
pty.ExpectMatch(expectedDeadline)
375+
})
376+
}
363377
}
Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,15 @@
11
coder v0.0.0-devel
22

33
USAGE:
4-
coder schedule { show | start | stop | override } <workspace>
4+
coder schedule { show | start | stop | extend } <workspace>
55

66
Schedule automated start and stop times for workspaces
77

88
SUBCOMMANDS:
9-
override-stop Override the stop time of a currently running workspace
10-
instance.
11-
show Show workspace schedules
12-
start Edit workspace start schedule
13-
stop Edit workspace stop schedule
9+
extend Extend the stop time of a currently running workspace instance.
10+
show Show workspace schedules
11+
start Edit workspace start schedule
12+
stop Edit workspace stop schedule
1413

1514
———
1615
Run `coder --help` for a list of global options.
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,17 @@
11
coder v0.0.0-devel
22

33
USAGE:
4-
coder schedule override-stop <workspace-name> <duration from now>
4+
coder schedule extend <workspace-name> <duration from now>
55

6-
Override the stop time of a currently running workspace instance.
6+
Extend the stop time of a currently running workspace instance.
7+
8+
Aliases: override-stop
79

810
* The new stop time is calculated from *now*.
911
* The new stop time must be at least 30 minutes in the future.
1012
* The workspace template may restrict the maximum workspace runtime.
1113

12-
$ coder schedule override-stop my-workspace 90m
14+
$ coder schedule extend my-workspace 90m
1315

1416
———
1517
Run `coder --help` for a list of global options.

coderd/workspaces.go

Lines changed: 0 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1200,18 +1200,6 @@ func (api *API) putExtendWorkspace(rw http.ResponseWriter, r *http.Request) {
12001200
return xerrors.Errorf("workspace shutdown is manual")
12011201
}
12021202

1203-
tmpl, err := s.GetTemplateByID(ctx, workspace.TemplateID)
1204-
if err != nil {
1205-
code = http.StatusInternalServerError
1206-
resp.Message = "Error fetching template."
1207-
return xerrors.Errorf("get template: %w", err)
1208-
}
1209-
if !tmpl.AllowUserAutostop {
1210-
code = http.StatusBadRequest
1211-
resp.Message = "Cannot extend workspace: template does not allow user autostop."
1212-
return xerrors.New("cannot extend workspace: template does not allow user autostop")
1213-
}
1214-
12151203
newDeadline := req.Deadline.UTC()
12161204
if err := validWorkspaceDeadline(job.CompletedAt.Time, newDeadline); err != nil {
12171205
// NOTE(Cian): Putting the error in the Message field on request from the FE folks.

docs/manifest.json

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1194,9 +1194,9 @@
11941194
"path": "reference/cli/schedule.md"
11951195
},
11961196
{
1197-
"title": "schedule override-stop",
1198-
"description": "Override the stop time of a currently running workspace instance.",
1199-
"path": "reference/cli/schedule_override-stop.md"
1197+
"title": "schedule extend",
1198+
"description": "Extend the stop time of a currently running workspace instance.",
1199+
"path": "reference/cli/schedule_extend.md"
12001200
},
12011201
{
12021202
"title": "schedule show",

docs/reference/cli/schedule.md

Lines changed: 7 additions & 7 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

docs/reference/cli/schedule_override-stop.md renamed to docs/reference/cli/schedule_extend.md

Lines changed: 8 additions & 4 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

enterprise/coderd/workspaces_test.go

Lines changed: 0 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -1391,35 +1391,6 @@ func TestTemplateDoesNotAllowUserAutostop(t *testing.T) {
13911391
require.Equal(t, templateTTL, template.DefaultTTLMillis)
13921392
require.Equal(t, templateTTL, *workspace.TTLMillis)
13931393
})
1394-
1395-
t.Run("ExtendIsNotEnabledByTemplate", func(t *testing.T) {
1396-
t.Parallel()
1397-
logger := slogtest.Make(t, &slogtest.Options{IgnoreErrors: true}).Leveled(slog.LevelDebug)
1398-
client := coderdtest.New(t, &coderdtest.Options{
1399-
IncludeProvisionerDaemon: true,
1400-
TemplateScheduleStore: schedule.NewEnterpriseTemplateScheduleStore(agplUserQuietHoursScheduleStore(), notifications.NewNoopEnqueuer(), logger, nil),
1401-
})
1402-
user := coderdtest.CreateFirstUser(t, client)
1403-
version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, nil)
1404-
template := coderdtest.CreateTemplate(t, client, user.OrganizationID, version.ID, func(ctr *codersdk.CreateTemplateRequest) {
1405-
ctr.AllowUserAutostop = ptr.Ref(false)
1406-
})
1407-
coderdtest.AwaitTemplateVersionJobCompleted(t, client, version.ID)
1408-
workspace := coderdtest.CreateWorkspace(t, client, template.ID)
1409-
coderdtest.AwaitWorkspaceBuildJobCompleted(t, client, workspace.LatestBuild.ID)
1410-
1411-
require.Equal(t, false, template.AllowUserAutostop, "template should have AllowUserAutostop as false")
1412-
1413-
ctx := testutil.Context(t, testutil.WaitShort)
1414-
ttl := 8 * time.Hour
1415-
newDeadline := time.Now().Add(ttl + time.Hour).UTC()
1416-
1417-
err := client.PutExtendWorkspace(ctx, workspace.ID, codersdk.PutExtendWorkspaceRequest{
1418-
Deadline: newDeadline,
1419-
})
1420-
1421-
require.ErrorContains(t, err, "template does not allow user autostop")
1422-
})
14231394
}
14241395

14251396
// TestWorkspaceTagsTerraform tests that a workspace can be created with tags.

site/src/pages/WorkspacePage/WorkspaceTopbar.stories.tsx

Lines changed: 0 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -303,24 +303,6 @@ export const WithQuotaWithOrgs: Story = {
303303
},
304304
};
305305

306-
export const TemplateDoesNotAllowAutostop: Story = {
307-
args: {
308-
workspace: {
309-
...MockWorkspace,
310-
latest_build: {
311-
...MockWorkspace.latest_build,
312-
get deadline() {
313-
return addHours(new Date(), 8).toISOString();
314-
},
315-
},
316-
},
317-
template: {
318-
...MockTemplate,
319-
allow_user_autostop: false,
320-
},
321-
},
322-
};
323-
324306
export const TemplateInfoPopover: Story = {
325307
play: async ({ canvasElement, step }) => {
326308
const canvas = within(canvasElement);

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy