From f991d2c5e5975a5fe1cac1736279e1ae7e086d5e Mon Sep 17 00:00:00 2001 From: McKayla Washburn Date: Mon, 8 Apr 2024 23:08:46 +0000 Subject: [PATCH 1/3] fix: show template autostop setting in settings when user customization is disabled --- .../WorkspaceSchedulePage.tsx | 16 ++++++++++++---- .../WorkspaceSettingsPage.tsx | 3 ++- 2 files changed, 14 insertions(+), 5 deletions(-) diff --git a/site/src/pages/WorkspaceSettingsPage/WorkspaceSchedulePage/WorkspaceSchedulePage.tsx b/site/src/pages/WorkspaceSettingsPage/WorkspaceSchedulePage/WorkspaceSchedulePage.tsx index 7830a161c879e..79b6798d1d8e7 100644 --- a/site/src/pages/WorkspaceSettingsPage/WorkspaceSchedulePage/WorkspaceSchedulePage.tsx +++ b/site/src/pages/WorkspaceSettingsPage/WorkspaceSchedulePage/WorkspaceSchedulePage.tsx @@ -101,7 +101,7 @@ export const WorkspaceSchedulePage: FC = () => { error={submitScheduleMutation.error} initialValues={{ ...getAutostart(workspace), - ...getAutostop(workspace), + ...getAutostop(workspace, template), }} isLoading={submitScheduleMutation.isLoading} defaultTTL={dayjs.duration(template.default_ttl_ms, "ms").asHours()} @@ -117,7 +117,10 @@ export const WorkspaceSchedulePage: FC = () => { getAutostart(workspace), values, ), - autostopChanged: scheduleChanged(getAutostop(workspace), values), + autostopChanged: scheduleChanged( + getAutostop(workspace, template), + values, + ), }; await submitScheduleMutation.mutateAsync(data); @@ -151,8 +154,13 @@ export const WorkspaceSchedulePage: FC = () => { const getAutostart = (workspace: TypesGen.Workspace) => scheduleToAutostart(workspace.autostart_schedule); -const getAutostop = (workspace: TypesGen.Workspace) => - ttlMsToAutostop(workspace.ttl_ms); +const getAutostop = ( + workspace: TypesGen.Workspace, + template: TypesGen.Template, +) => + template.allow_user_autostop + ? ttlMsToAutostop(workspace.ttl_ms) + : ttlMsToAutostop(template.default_ttl_ms); type SubmitScheduleData = { workspace: TypesGen.Workspace; diff --git a/site/src/pages/WorkspaceSettingsPage/WorkspaceSettingsPage.tsx b/site/src/pages/WorkspaceSettingsPage/WorkspaceSettingsPage.tsx index af05809a9aca7..e289a58c5ce59 100644 --- a/site/src/pages/WorkspaceSettingsPage/WorkspaceSettingsPage.tsx +++ b/site/src/pages/WorkspaceSettingsPage/WorkspaceSettingsPage.tsx @@ -1,3 +1,4 @@ +import type { FC } from "react"; import { Helmet } from "react-helmet-async"; import { useMutation } from "react-query"; import { useNavigate, useParams } from "react-router-dom"; @@ -8,7 +9,7 @@ import type { WorkspaceSettingsFormValues } from "./WorkspaceSettingsForm"; import { useWorkspaceSettings } from "./WorkspaceSettingsLayout"; import { WorkspaceSettingsPageView } from "./WorkspaceSettingsPageView"; -const WorkspaceSettingsPage = () => { +const WorkspaceSettingsPage: FC = () => { const params = useParams() as { workspace: string; username: string; From 66cc750df389fbc69d236475e1a700de3eecad90 Mon Sep 17 00:00:00 2001 From: McKayla Washburn Date: Wed, 10 Apr 2024 23:00:13 +0000 Subject: [PATCH 2/3] fix it on the backend instead --- coderd/workspaces.go | 5 +++++ .../WorkspaceSchedulePage.tsx | 16 ++++------------ 2 files changed, 9 insertions(+), 12 deletions(-) diff --git a/coderd/workspaces.go b/coderd/workspaces.go index d3456fab00992..87aea6919a351 100644 --- a/coderd/workspaces.go +++ b/coderd/workspaces.go @@ -1649,6 +1649,11 @@ func convertWorkspace( } ttlMillis := convertWorkspaceTTLMillis(workspace.Ttl) + // If the template doesn't allow a workspace-configured value, then report the + // template value instead. + if !template.AllowUserAutostop { + ttlMillis = convertWorkspaceTTLMillis(sql.NullInt64{Valid: true, Int64: template.DefaultTTL}) + } // Only show favorite status if you own the workspace. requesterFavorite := workspace.OwnerID == requesterID && workspace.Favorite diff --git a/site/src/pages/WorkspaceSettingsPage/WorkspaceSchedulePage/WorkspaceSchedulePage.tsx b/site/src/pages/WorkspaceSettingsPage/WorkspaceSchedulePage/WorkspaceSchedulePage.tsx index 79b6798d1d8e7..7830a161c879e 100644 --- a/site/src/pages/WorkspaceSettingsPage/WorkspaceSchedulePage/WorkspaceSchedulePage.tsx +++ b/site/src/pages/WorkspaceSettingsPage/WorkspaceSchedulePage/WorkspaceSchedulePage.tsx @@ -101,7 +101,7 @@ export const WorkspaceSchedulePage: FC = () => { error={submitScheduleMutation.error} initialValues={{ ...getAutostart(workspace), - ...getAutostop(workspace, template), + ...getAutostop(workspace), }} isLoading={submitScheduleMutation.isLoading} defaultTTL={dayjs.duration(template.default_ttl_ms, "ms").asHours()} @@ -117,10 +117,7 @@ export const WorkspaceSchedulePage: FC = () => { getAutostart(workspace), values, ), - autostopChanged: scheduleChanged( - getAutostop(workspace, template), - values, - ), + autostopChanged: scheduleChanged(getAutostop(workspace), values), }; await submitScheduleMutation.mutateAsync(data); @@ -154,13 +151,8 @@ export const WorkspaceSchedulePage: FC = () => { const getAutostart = (workspace: TypesGen.Workspace) => scheduleToAutostart(workspace.autostart_schedule); -const getAutostop = ( - workspace: TypesGen.Workspace, - template: TypesGen.Template, -) => - template.allow_user_autostop - ? ttlMsToAutostop(workspace.ttl_ms) - : ttlMsToAutostop(template.default_ttl_ms); +const getAutostop = (workspace: TypesGen.Workspace) => + ttlMsToAutostop(workspace.ttl_ms); type SubmitScheduleData = { workspace: TypesGen.Workspace; From 4d69e0a6d20c3b57a894440e626c5b418ad09bf2 Mon Sep 17 00:00:00 2001 From: McKayla Washburn Date: Thu, 11 Apr 2024 18:14:01 +0000 Subject: [PATCH 3/3] =?UTF-8?q?=E2=9A=97=EF=B8=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- coderd/workspaces_test.go | 6 ++--- enterprise/coderd/workspaces_test.go | 38 ++++++++++++++++++++++++++++ 2 files changed, 41 insertions(+), 3 deletions(-) diff --git a/coderd/workspaces_test.go b/coderd/workspaces_test.go index f16d40f07267f..c01f9689d6ace 100644 --- a/coderd/workspaces_test.go +++ b/coderd/workspaces_test.go @@ -761,8 +761,8 @@ func TestPostWorkspacesByOrganization(t *testing.T) { coderdtest.AwaitWorkspaceBuildJobCompleted(t, client, workspace.LatestBuild.ID) // TTL should be set by the template - require.Equal(t, template.DefaultTTLMillis, templateTTL) - require.Equal(t, template.DefaultTTLMillis, *workspace.TTLMillis) + require.Equal(t, templateTTL, template.DefaultTTLMillis) + require.Equal(t, templateTTL, *workspace.TTLMillis) }) t.Run("InvalidTTL", func(t *testing.T) { @@ -789,7 +789,7 @@ func TestPostWorkspacesByOrganization(t *testing.T) { require.ErrorAs(t, err, &apiErr) require.Equal(t, http.StatusBadRequest, apiErr.StatusCode()) require.Len(t, apiErr.Validations, 1) - require.Equal(t, apiErr.Validations[0].Field, "ttl_ms") + require.Equal(t, "ttl_ms", apiErr.Validations[0].Field) require.Equal(t, "time until shutdown must be at least one minute", apiErr.Validations[0].Detail) }) }) diff --git a/enterprise/coderd/workspaces_test.go b/enterprise/coderd/workspaces_test.go index 6d40d77bc218b..b44357c5b5dde 100644 --- a/enterprise/coderd/workspaces_test.go +++ b/enterprise/coderd/workspaces_test.go @@ -913,6 +913,44 @@ func TestWorkspaceAutobuild(t *testing.T) { ws = coderdtest.MustWorkspace(t, client, ws.ID) require.Equal(t, version2.ID, ws.LatestBuild.TemplateVersionID) }) + + t.Run("TemplateDoesNotAllowUserAutostop", func(t *testing.T) { + t.Parallel() + client := coderdtest.New(t, &coderdtest.Options{ + IncludeProvisionerDaemon: true, + TemplateScheduleStore: schedule.NewEnterpriseTemplateScheduleStore(agplUserQuietHoursScheduleStore()), + }) + user := coderdtest.CreateFirstUser(t, client) + version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, nil) + templateTTL := 24 * time.Hour.Milliseconds() + template := coderdtest.CreateTemplate(t, client, user.OrganizationID, version.ID, func(ctr *codersdk.CreateTemplateRequest) { + ctr.DefaultTTLMillis = ptr.Ref(templateTTL) + ctr.AllowUserAutostop = ptr.Ref(false) + }) + coderdtest.AwaitTemplateVersionJobCompleted(t, client, version.ID) + workspace := coderdtest.CreateWorkspace(t, client, user.OrganizationID, template.ID, func(cwr *codersdk.CreateWorkspaceRequest) { + cwr.TTLMillis = nil // ensure that no default TTL is set + }) + coderdtest.AwaitWorkspaceBuildJobCompleted(t, client, workspace.LatestBuild.ID) + + // TTL should be set by the template + require.Equal(t, false, template.AllowUserAutostop) + require.Equal(t, templateTTL, template.DefaultTTLMillis) + require.Equal(t, templateTTL, *workspace.TTLMillis) + + // Change the template's default TTL and refetch the workspace + templateTTL = 72 * time.Hour.Milliseconds() + ctx := testutil.Context(t, testutil.WaitShort) + template = coderdtest.UpdateTemplateMeta(t, client, template.ID, codersdk.UpdateTemplateMeta{ + DefaultTTLMillis: templateTTL, + }) + workspace, err := client.Workspace(ctx, workspace.ID) + require.NoError(t, err) + + // Ensure that the new value is reflected in the template and workspace + require.Equal(t, templateTTL, template.DefaultTTLMillis) + require.Equal(t, templateTTL, *workspace.TTLMillis) + }) } // Blocked by autostart requirements