diff --git a/coderd/database/migrations/000038_template_max_ttl_cap_7_days.up.sql b/coderd/database/migrations/000038_template_max_ttl_cap_7_days.up.sql index 083f9501aafaf..70b04fc1295ee 100644 --- a/coderd/database/migrations/000038_template_max_ttl_cap_7_days.up.sql +++ b/coderd/database/migrations/000038_template_max_ttl_cap_7_days.up.sql @@ -1,2 +1,5 @@ -- Set a cap of 7 days on template max_ttl +-- +-- NOTE: this migration was added in August 2022, but the cap has been changed +-- to 30 days in July 2023. UPDATE templates SET max_ttl = 604800000000000 WHERE max_ttl > 604800000000000; diff --git a/coderd/workspaces.go b/coderd/workspaces.go index fe26804c3c78e..ff7cef0f6dfa9 100644 --- a/coderd/workspaces.go +++ b/coderd/workspaces.go @@ -32,10 +32,10 @@ import ( var ( ttlMin = time.Minute //nolint:revive // min here means 'minimum' not 'minutes' - ttlMax = 7 * 24 * time.Hour + ttlMax = 30 * 24 * time.Hour errTTLMin = xerrors.New("time until shutdown must be at least one minute") - errTTLMax = xerrors.New("time until shutdown must be less than 7 days") + errTTLMax = xerrors.New("time until shutdown must be less than 30 days") errDeadlineTooSoon = xerrors.New("new deadline must be at least 30 minutes in the future") errDeadlineBeforeStart = xerrors.New("new deadline must be before workspace start time") ) diff --git a/coderd/workspaces_test.go b/coderd/workspaces_test.go index 839bef3c60fa9..3efc735c69764 100644 --- a/coderd/workspaces_test.go +++ b/coderd/workspaces_test.go @@ -1837,13 +1837,13 @@ func TestWorkspaceUpdateTTL(t *testing.T) { }, { name: "maximum ttl", - ttlMillis: ptr.Ref((24 * 7 * time.Hour).Milliseconds()), + ttlMillis: ptr.Ref((24 * 30 * time.Hour).Milliseconds()), expectedError: "", }, { name: "above maximum ttl", - ttlMillis: ptr.Ref((24*7*time.Hour + time.Minute).Milliseconds()), - expectedError: "time until shutdown must be less than 7 days", + ttlMillis: ptr.Ref((24*30*time.Hour + time.Minute).Milliseconds()), + expectedError: "time until shutdown must be less than 30 days", }, } diff --git a/site/src/components/WorkspaceScheduleForm/WorkspaceScheduleForm.test.ts b/site/src/components/WorkspaceScheduleForm/WorkspaceScheduleForm.test.ts index d0af10f3d73b0..ee74b3ba95722 100644 --- a/site/src/components/WorkspaceScheduleForm/WorkspaceScheduleForm.test.ts +++ b/site/src/components/WorkspaceScheduleForm/WorkspaceScheduleForm.test.ts @@ -158,10 +158,19 @@ describe("validationSchema", () => { expect(validate).not.toThrowError() }) - it("disallows a ttl of 7 days + 1 hour", () => { + it("allows a ttl of 30 days", () => { const values: WorkspaceScheduleFormValues = { ...valid, - ttl: 24 * 7 + 1, + ttl: 24 * 30, + } + const validate = () => validationSchema.validateSync(values) + expect(validate).not.toThrowError() + }) + + it("disallows a ttl of 30 days + 1 hour", () => { + const values: WorkspaceScheduleFormValues = { + ...valid, + ttl: 24 * 30 + 1, } const validate = () => validationSchema.validateSync(values) expect(validate).toThrowError(Language.errorTtlMax) diff --git a/site/src/components/WorkspaceScheduleForm/WorkspaceScheduleForm.tsx b/site/src/components/WorkspaceScheduleForm/WorkspaceScheduleForm.tsx index bb1cc8c6f6a46..6190750d16dac 100644 --- a/site/src/components/WorkspaceScheduleForm/WorkspaceScheduleForm.tsx +++ b/site/src/components/WorkspaceScheduleForm/WorkspaceScheduleForm.tsx @@ -48,7 +48,7 @@ export const Language = { errorNoStop: "Time until shutdown must be greater than zero when autostop is enabled.", errorTtlMax: - "Please enter a limit that is less than or equal to 168 hours (7 days).", + "Please enter a limit that is less than or equal to 720 hours (30 days).", daysOfWeekLabel: "Days of Week", daySundayLabel: "Sun", dayMondayLabel: "Mon", @@ -172,7 +172,7 @@ export const validationSchema = Yup.object({ ttl: Yup.number() .integer() .min(0) - .max(24 * 7 /* 7 days */, Language.errorTtlMax) + .max(24 * 30 /* 30 days */, Language.errorTtlMax) .test("positive-if-autostop", Language.errorNoStop, function (value) { const parent = this.parent as WorkspaceScheduleFormValues if (parent.autostopEnabled) { diff --git a/site/src/i18n/en/createTemplatePage.json b/site/src/i18n/en/createTemplatePage.json index d87e285c9265e..dff838b7e2445 100644 --- a/site/src/i18n/en/createTemplatePage.json +++ b/site/src/i18n/en/createTemplatePage.json @@ -48,9 +48,9 @@ }, "error": { "descriptionMax": "Please enter a description that is less than or equal to 128 characters.", - "defaultTTLMax": "Please enter a limit that is less than or equal to 168 hours (7 days).", + "defaultTTLMax": "Please enter a limit that is less than or equal to 720 hours (30 days).", "defaultTTLMin": "Default time until autostop must not be less than 0.", - "maxTTLMax": "Please enter a limit that is less than or equal to 168 hours (7 days).", + "maxTTLMax": "Please enter a limit that is less than or equal to 720 hours (30 days).", "maxTTLMin": "Maximum time until autostop must not be less than 0." } } diff --git a/site/src/i18n/en/templateSettingsPage.json b/site/src/i18n/en/templateSettingsPage.json index a5f01fc08e9ea..11f314e0a7472 100644 --- a/site/src/i18n/en/templateSettingsPage.json +++ b/site/src/i18n/en/templateSettingsPage.json @@ -9,12 +9,12 @@ "iconLabel": "Icon", "formAriaLabel": "Template settings form", "selectEmoji": "Select emoji", - "defaultTTLMaxError": "Please enter a limit that is less than or equal to 168 hours (7 days).", + "defaultTTLMaxError": "Please enter a limit that is less than or equal to 720 hours (30 days).", "defaultTTLMinError": "Default time until autostop must not be less than 0.", "defaultTTLHelperText_zero": "Workspaces will run until stopped manually.", "defaultTTLHelperText_one": "Workspaces will default to stopping after {{count}} hour. If Coder detects workspace connection activity, the autostop timer is bumped up one hour.", "defaultTTLHelperText_other": "Workspaces will default to stopping after {{count}} hours. If Coder detects workspace connection activity, the autostop timer is bumped up one hour.", - "maxTTLMaxError": "Please enter a limit that is less than or equal to 168 hours (7 days).", + "maxTTLMaxError": "Please enter a limit that is less than or equal to 720 hours (30 days).", "maxTTLMinError": "Maximum time until autostop must not be less than 0.", "maxTTLHelperText_zero": "Workspaces may run indefinitely.", "maxTTLHelperText_one": "Workspaces must stop within 1 hour of starting, regardless of any active connections.", diff --git a/site/src/pages/CreateTemplatePage/CreateTemplateForm.tsx b/site/src/pages/CreateTemplatePage/CreateTemplateForm.tsx index 901b18c8331f4..2b0ac583a1c0f 100644 --- a/site/src/pages/CreateTemplatePage/CreateTemplateForm.tsx +++ b/site/src/pages/CreateTemplatePage/CreateTemplateForm.tsx @@ -41,7 +41,7 @@ import capitalize from "lodash/capitalize" import { VariableInput } from "./VariableInput" const MAX_DESCRIPTION_CHAR_LIMIT = 128 -const MAX_TTL_DAYS = 7 +const MAX_TTL_DAYS = 30 const TTLHelperText = ({ ttl, @@ -78,15 +78,15 @@ const validationSchema = Yup.object({ .integer() .min(0, "Default time until autostop must not be less than 0.") .max( - 24 * MAX_TTL_DAYS /* 7 days in hours */, - "Please enter a limit that is less than or equal to 168 hours (7 days).", + 24 * MAX_TTL_DAYS /* 30 days in hours */, + "Please enter a limit that is less than or equal to 720 hours (30 days).", ), max_ttl_hours: Yup.number() .integer() .min(0, "Maximum time until autostop must not be less than 0.") .max( - 24 * MAX_TTL_DAYS /* 7 days in hours */, - "Please enter a limit that is less than or equal to 168 hours(7 days).", + 24 * MAX_TTL_DAYS /* 30 days in hours */, + "Please enter a limit that is less than or equal to 720 hours (30 days).", ), }) @@ -98,6 +98,9 @@ const defaultInitialValues: CreateTemplateData = { default_ttl_hours: 24, // max_ttl is an enterprise-only feature, and the server ignores the value if // you are not licensed. We hide the form value based on entitlements. + // + // The maximum value is 30 days but we default to 7 days as it's a much more + // sensible value for most teams. max_ttl_hours: 24 * 7, allow_user_cancel_workspace_jobs: false, allow_user_autostart: false, diff --git a/site/src/pages/TemplateSettingsPage/TemplateSchedulePage/TemplateScheduleForm/formHelpers.tsx b/site/src/pages/TemplateSettingsPage/TemplateSchedulePage/TemplateScheduleForm/formHelpers.tsx index 5325998b251be..ee1fe7b13d302 100644 --- a/site/src/pages/TemplateSettingsPage/TemplateSchedulePage/TemplateScheduleForm/formHelpers.tsx +++ b/site/src/pages/TemplateSettingsPage/TemplateSchedulePage/TemplateScheduleForm/formHelpers.tsx @@ -8,7 +8,7 @@ export interface TemplateScheduleFormValues extends UpdateTemplateMeta { locked_cleanup_enabled: boolean } -const MAX_TTL_DAYS = 7 +const MAX_TTL_DAYS = 30 export const getValidationSchema = (): Yup.AnyObjectSchema => Yup.object({ @@ -21,7 +21,7 @@ export const getValidationSchema = (): Yup.AnyObjectSchema => .toString(), ) .max( - 24 * MAX_TTL_DAYS /* 7 days in hours */, + 24 * MAX_TTL_DAYS /* 30 days in hours */, i18next .t("defaultTTLMaxError", { ns: "templateSettingsPage" }) .toString(), @@ -33,7 +33,7 @@ export const getValidationSchema = (): Yup.AnyObjectSchema => i18next.t("maxTTLMinError", { ns: "templateSettingsPage" }).toString(), ) .max( - 24 * MAX_TTL_DAYS /* 7 days in hours */, + 24 * MAX_TTL_DAYS /* 30 days in hours */, i18next.t("maxTTLMaxError", { ns: "templateSettingsPage" }).toString(), ), failure_ttl_ms: Yup.number() diff --git a/site/src/pages/TemplateSettingsPage/TemplateSchedulePage/TemplateSchedulePage.test.tsx b/site/src/pages/TemplateSettingsPage/TemplateSchedulePage/TemplateSchedulePage.test.tsx index a633a3e7a318a..5b37f01f5b8ce 100644 --- a/site/src/pages/TemplateSettingsPage/TemplateSchedulePage/TemplateSchedulePage.test.tsx +++ b/site/src/pages/TemplateSettingsPage/TemplateSchedulePage/TemplateSchedulePage.test.tsx @@ -159,10 +159,19 @@ describe("TemplateSchedulePage", () => { expect(validate).not.toThrowError() }) - it("disallows a default ttl of 7 days + 1 hour", () => { + it("allows a default ttl of 30 days", () => { const values: UpdateTemplateMeta = { ...validFormValues, - default_ttl_ms: 24 * 7 + 1, + default_ttl_ms: 24 * 30, + } + const validate = () => getValidationSchema().validateSync(values) + expect(validate).not.toThrowError() + }) + + it("disallows a default ttl of 30 days + 1 hour", () => { + const values: UpdateTemplateMeta = { + ...validFormValues, + default_ttl_ms: 24 * 30 + 1, } const validate = () => getValidationSchema().validateSync(values) expect(validate).toThrowError(