diff --git a/cli/schedule.go b/cli/schedule.go index 80fdc873fb205..9ade82b9c4a36 100644 --- a/cli/schedule.go +++ b/cli/schedule.go @@ -46,7 +46,7 @@ When enabling scheduled stop, enter a duration in one of the following formats: * 2m (2 minutes) * 2 (2 minutes) ` - scheduleOverrideDescriptionLong = ` + scheduleExtendDescriptionLong = ` * 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. @@ -56,7 +56,7 @@ When enabling scheduled stop, enter a duration in one of the following formats: func (r *RootCmd) schedules() *serpent.Command { scheduleCmd := &serpent.Command{ Annotations: workspaceCommand, - Use: "schedule { show | start | stop | override } ", + Use: "schedule { show | start | stop | extend } ", Short: "Schedule automated start and stop times for workspaces", Handler: func(inv *serpent.Invocation) error { return inv.Command.HelpHandler(inv) @@ -65,7 +65,7 @@ func (r *RootCmd) schedules() *serpent.Command { r.scheduleShow(), r.scheduleStart(), r.scheduleStop(), - r.scheduleOverride(), + r.scheduleExtend(), }, } @@ -229,14 +229,15 @@ func (r *RootCmd) scheduleStop() *serpent.Command { } } -func (r *RootCmd) scheduleOverride() *serpent.Command { +func (r *RootCmd) scheduleExtend() *serpent.Command { client := new(codersdk.Client) - overrideCmd := &serpent.Command{ - Use: "override-stop ", - Short: "Override the stop time of a currently running workspace instance.", - Long: scheduleOverrideDescriptionLong + "\n" + FormatExamples( + extendCmd := &serpent.Command{ + Use: "extend ", + Aliases: []string{"override-stop"}, + Short: "Extend the stop time of a currently running workspace instance.", + Long: scheduleExtendDescriptionLong + "\n" + FormatExamples( Example{ - Command: "coder schedule override-stop my-workspace 90m", + Command: "coder schedule extend my-workspace 90m", }, ), Middleware: serpent.Chain( @@ -244,7 +245,7 @@ func (r *RootCmd) scheduleOverride() *serpent.Command { r.InitClient(client), ), Handler: func(inv *serpent.Invocation) error { - overrideDuration, err := parseDuration(inv.Args[1]) + extendDuration, err := parseDuration(inv.Args[1]) if err != nil { return err } @@ -259,7 +260,7 @@ func (r *RootCmd) scheduleOverride() *serpent.Command { loc = time.UTC // best effort } - if overrideDuration < 29*time.Minute { + if extendDuration < 29*time.Minute { _, _ = fmt.Fprintf( inv.Stdout, "Please specify a duration of at least 30 minutes.\n", @@ -267,7 +268,7 @@ func (r *RootCmd) scheduleOverride() *serpent.Command { return nil } - newDeadline := time.Now().In(loc).Add(overrideDuration) + newDeadline := time.Now().In(loc).Add(extendDuration) if err := client.PutExtendWorkspace(inv.Context(), workspace.ID, codersdk.PutExtendWorkspaceRequest{ Deadline: newDeadline, }); err != nil { @@ -281,7 +282,7 @@ func (r *RootCmd) scheduleOverride() *serpent.Command { return displaySchedule(updated, inv.Stdout) }, } - return overrideCmd + return extendCmd } func displaySchedule(ws codersdk.Workspace, out io.Writer) error { diff --git a/cli/schedule_test.go b/cli/schedule_test.go index bf18155be293a..60fbf19f4db08 100644 --- a/cli/schedule_test.go +++ b/cli/schedule_test.go @@ -332,32 +332,46 @@ func TestScheduleModify(t *testing.T) { //nolint:paralleltest // t.Setenv func TestScheduleOverride(t *testing.T) { - // Given - // Set timezone to Asia/Kolkata to surface any timezone-related bugs. - t.Setenv("TZ", "Asia/Kolkata") - loc, err := tz.TimezoneIANA() - require.NoError(t, err) - require.Equal(t, "Asia/Kolkata", loc.String()) - sched, err := cron.Weekly("CRON_TZ=Europe/Dublin 30 7 * * Mon-Fri") - require.NoError(t, err, "invalid schedule") - ownerClient, _, _, ws := setupTestSchedule(t, sched) - now := time.Now() - // To avoid the likelihood of time-related flakes, only matching up to the hour. - expectedDeadline := time.Now().In(loc).Add(10 * time.Hour).Format("2006-01-02T15:") - - // When: we override the stop schedule - inv, root := clitest.New(t, - "schedule", "override-stop", ws[0].OwnerName+"/"+ws[0].Name, "10h", - ) - - clitest.SetupConfig(t, ownerClient, root) - pty := ptytest.New(t).Attach(inv) - require.NoError(t, inv.Run()) - - // Then: the updated schedule should be shown - pty.ExpectMatch(ws[0].OwnerName + "/" + ws[0].Name) - pty.ExpectMatch(sched.Humanize()) - pty.ExpectMatch(sched.Next(now).In(loc).Format(time.RFC3339)) - pty.ExpectMatch("8h") - pty.ExpectMatch(expectedDeadline) + tests := []struct { + command string + }{ + {command: "extend"}, + // test for backwards compatibility + {command: "override-stop"}, + } + + for _, tt := range tests { + tt := tt + + t.Run(tt.command, func(t *testing.T) { + // Given + // Set timezone to Asia/Kolkata to surface any timezone-related bugs. + t.Setenv("TZ", "Asia/Kolkata") + loc, err := tz.TimezoneIANA() + require.NoError(t, err) + require.Equal(t, "Asia/Kolkata", loc.String()) + sched, err := cron.Weekly("CRON_TZ=Europe/Dublin 30 7 * * Mon-Fri") + require.NoError(t, err, "invalid schedule") + ownerClient, _, _, ws := setupTestSchedule(t, sched) + now := time.Now() + // To avoid the likelihood of time-related flakes, only matching up to the hour. + expectedDeadline := time.Now().In(loc).Add(10 * time.Hour).Format("2006-01-02T15:") + + // When: we override the stop schedule + inv, root := clitest.New(t, + "schedule", tt.command, ws[0].OwnerName+"/"+ws[0].Name, "10h", + ) + + clitest.SetupConfig(t, ownerClient, root) + pty := ptytest.New(t).Attach(inv) + require.NoError(t, inv.Run()) + + // Then: the updated schedule should be shown + pty.ExpectMatch(ws[0].OwnerName + "/" + ws[0].Name) + pty.ExpectMatch(sched.Humanize()) + pty.ExpectMatch(sched.Next(now).In(loc).Format(time.RFC3339)) + pty.ExpectMatch("8h") + pty.ExpectMatch(expectedDeadline) + }) + } } diff --git a/cli/testdata/coder_schedule_--help.golden b/cli/testdata/coder_schedule_--help.golden index 7c6e06a31b656..61a32d7fea490 100644 --- a/cli/testdata/coder_schedule_--help.golden +++ b/cli/testdata/coder_schedule_--help.golden @@ -1,16 +1,15 @@ coder v0.0.0-devel USAGE: - coder schedule { show | start | stop | override } + coder schedule { show | start | stop | extend } Schedule automated start and stop times for workspaces SUBCOMMANDS: - override-stop Override the stop time of a currently running workspace - instance. - show Show workspace schedules - start Edit workspace start schedule - stop Edit workspace stop schedule + extend Extend the stop time of a currently running workspace instance. + show Show workspace schedules + start Edit workspace start schedule + stop Edit workspace stop schedule ——— Run `coder --help` for a list of global options. diff --git a/cli/testdata/coder_schedule_override-stop_--help.golden b/cli/testdata/coder_schedule_extend_--help.golden similarity index 59% rename from cli/testdata/coder_schedule_override-stop_--help.golden rename to cli/testdata/coder_schedule_extend_--help.golden index 77fd2d5c4f57d..2135b09dc7cc3 100644 --- a/cli/testdata/coder_schedule_override-stop_--help.golden +++ b/cli/testdata/coder_schedule_extend_--help.golden @@ -1,15 +1,17 @@ coder v0.0.0-devel USAGE: - coder schedule override-stop + coder schedule extend - Override the stop time of a currently running workspace instance. + Extend the stop time of a currently running workspace instance. + + Aliases: override-stop * 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. - $ coder schedule override-stop my-workspace 90m + $ coder schedule extend my-workspace 90m ——— Run `coder --help` for a list of global options. diff --git a/coderd/workspaces.go b/coderd/workspaces.go index 8828ad6509d52..0234c31888d41 100644 --- a/coderd/workspaces.go +++ b/coderd/workspaces.go @@ -1200,18 +1200,6 @@ func (api *API) putExtendWorkspace(rw http.ResponseWriter, r *http.Request) { return xerrors.Errorf("workspace shutdown is manual") } - tmpl, err := s.GetTemplateByID(ctx, workspace.TemplateID) - if err != nil { - code = http.StatusInternalServerError - resp.Message = "Error fetching template." - return xerrors.Errorf("get template: %w", err) - } - if !tmpl.AllowUserAutostop { - code = http.StatusBadRequest - resp.Message = "Cannot extend workspace: template does not allow user autostop." - return xerrors.New("cannot extend workspace: template does not allow user autostop") - } - newDeadline := req.Deadline.UTC() if err := validWorkspaceDeadline(job.CompletedAt.Time, newDeadline); err != nil { // NOTE(Cian): Putting the error in the Message field on request from the FE folks. diff --git a/docs/manifest.json b/docs/manifest.json index 0aa4d8cb86cf8..47e8968805a12 100644 --- a/docs/manifest.json +++ b/docs/manifest.json @@ -1189,9 +1189,9 @@ "path": "reference/cli/schedule.md" }, { - "title": "schedule override-stop", - "description": "Override the stop time of a currently running workspace instance.", - "path": "reference/cli/schedule_override-stop.md" + "title": "schedule extend", + "description": "Extend the stop time of a currently running workspace instance.", + "path": "reference/cli/schedule_extend.md" }, { "title": "schedule show", diff --git a/docs/reference/cli/schedule.md b/docs/reference/cli/schedule.md index cfaf5911bf51a..4319f80438af3 100644 --- a/docs/reference/cli/schedule.md +++ b/docs/reference/cli/schedule.md @@ -7,14 +7,14 @@ Schedule automated start and stop times for workspaces ## Usage ```console -coder schedule { show | start | stop | override } +coder schedule { show | start | stop | extend } ``` ## Subcommands -| Name | Purpose | -| --------------------------------------------------------- | ----------------------------------------------------------------- | -| [show](./schedule_show.md) | Show workspace schedules | -| [start](./schedule_start.md) | Edit workspace start schedule | -| [stop](./schedule_stop.md) | Edit workspace stop schedule | -| [override-stop](./schedule_override-stop.md) | Override the stop time of a currently running workspace instance. | +| Name | Purpose | +| ------------------------------------------- | --------------------------------------------------------------- | +| [show](./schedule_show.md) | Show workspace schedules | +| [start](./schedule_start.md) | Edit workspace start schedule | +| [stop](./schedule_stop.md) | Edit workspace stop schedule | +| [extend](./schedule_extend.md) | Extend the stop time of a currently running workspace instance. | diff --git a/docs/reference/cli/schedule_override-stop.md b/docs/reference/cli/schedule_extend.md similarity index 57% rename from docs/reference/cli/schedule_override-stop.md rename to docs/reference/cli/schedule_extend.md index 8c565d734a585..06ba46d358da7 100644 --- a/docs/reference/cli/schedule_override-stop.md +++ b/docs/reference/cli/schedule_extend.md @@ -1,13 +1,17 @@ -# schedule override-stop +# schedule extend -Override the stop time of a currently running workspace instance. +Extend the stop time of a currently running workspace instance. + +Aliases: + +- override-stop ## Usage ```console -coder schedule override-stop +coder schedule extend ``` ## Description @@ -18,5 +22,5 @@ coder schedule override-stop * The new stop time must be at least 30 minutes in the future. * The workspace template may restrict the maximum workspace runtime. - $ coder schedule override-stop my-workspace 90m + $ coder schedule extend my-workspace 90m ``` diff --git a/enterprise/coderd/workspaces_test.go b/enterprise/coderd/workspaces_test.go index e5142c1a83ee8..896f02e4ff27f 100644 --- a/enterprise/coderd/workspaces_test.go +++ b/enterprise/coderd/workspaces_test.go @@ -1389,35 +1389,6 @@ func TestTemplateDoesNotAllowUserAutostop(t *testing.T) { require.Equal(t, templateTTL, template.DefaultTTLMillis) require.Equal(t, templateTTL, *workspace.TTLMillis) }) - - t.Run("ExtendIsNotEnabledByTemplate", func(t *testing.T) { - t.Parallel() - logger := slogtest.Make(t, &slogtest.Options{IgnoreErrors: true}).Leveled(slog.LevelDebug) - client := coderdtest.New(t, &coderdtest.Options{ - IncludeProvisionerDaemon: true, - TemplateScheduleStore: schedule.NewEnterpriseTemplateScheduleStore(agplUserQuietHoursScheduleStore(), notifications.NewNoopEnqueuer(), logger, nil), - }) - user := coderdtest.CreateFirstUser(t, client) - version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, nil) - template := coderdtest.CreateTemplate(t, client, user.OrganizationID, version.ID, func(ctr *codersdk.CreateTemplateRequest) { - ctr.AllowUserAutostop = ptr.Ref(false) - }) - coderdtest.AwaitTemplateVersionJobCompleted(t, client, version.ID) - workspace := coderdtest.CreateWorkspace(t, client, template.ID) - coderdtest.AwaitWorkspaceBuildJobCompleted(t, client, workspace.LatestBuild.ID) - - require.Equal(t, false, template.AllowUserAutostop, "template should have AllowUserAutostop as false") - - ctx := testutil.Context(t, testutil.WaitShort) - ttl := 8 * time.Hour - newDeadline := time.Now().Add(ttl + time.Hour).UTC() - - err := client.PutExtendWorkspace(ctx, workspace.ID, codersdk.PutExtendWorkspaceRequest{ - Deadline: newDeadline, - }) - - require.ErrorContains(t, err, "template does not allow user autostop") - }) } // Blocked by autostart requirements diff --git a/site/src/pages/WorkspacePage/WorkspaceTopbar.stories.tsx b/site/src/pages/WorkspacePage/WorkspaceTopbar.stories.tsx index d95cfc3d60daf..f5706a4facc3b 100644 --- a/site/src/pages/WorkspacePage/WorkspaceTopbar.stories.tsx +++ b/site/src/pages/WorkspacePage/WorkspaceTopbar.stories.tsx @@ -303,24 +303,6 @@ export const WithQuotaWithOrgs: Story = { }, }; -export const TemplateDoesNotAllowAutostop: Story = { - args: { - workspace: { - ...MockWorkspace, - latest_build: { - ...MockWorkspace.latest_build, - get deadline() { - return addHours(new Date(), 8).toISOString(); - }, - }, - }, - template: { - ...MockTemplate, - allow_user_autostop: false, - }, - }, -}; - export const TemplateInfoPopover: Story = { play: async ({ canvasElement, step }) => { const canvas = within(canvasElement); diff --git a/site/src/pages/WorkspacePage/WorkspaceTopbar.tsx b/site/src/pages/WorkspacePage/WorkspaceTopbar.tsx index 7ca112befb4e5..2c0c74d29703f 100644 --- a/site/src/pages/WorkspacePage/WorkspaceTopbar.tsx +++ b/site/src/pages/WorkspacePage/WorkspaceTopbar.tsx @@ -234,9 +234,7 @@ export const WorkspaceTopbar: FC = ({