From d789a60d4728173ef6706606eb52826a031f1675 Mon Sep 17 00:00:00 2001 From: Steven Masley Date: Wed, 20 Mar 2024 10:37:57 -0500 Subject: [PATCH] chore: remove max_ttl from templates (#12644) * chore: remove max_ttl from templates Completely removing max_ttl as a feature on template scheduling. Must use other template scheduling features to achieve autostop. --- cli/templatecreate.go | 12 +- cli/templateedit.go | 20 +- cli/templateedit_test.go | 213 --------------- cli/testdata/coder_server_--help.golden | 2 +- .../coder_templates_create_--help.golden | 5 - .../coder_templates_edit_--help.golden | 16 +- cli/testdata/server-config.yaml.golden | 2 +- coderd/activitybump_test.go | 14 +- coderd/apidoc/docs.go | 16 +- coderd/apidoc/swagger.json | 16 +- coderd/database/dbmem/dbmem.go | 2 - coderd/database/dump.sql | 4 - .../migrations/000202_remove_max_ttl.down.sql | 21 ++ .../migrations/000202_remove_max_ttl.up.sql | 20 ++ coderd/database/modelqueries.go | 2 - coderd/database/models.go | 6 +- coderd/database/queries.sql.go | 36 +-- coderd/database/queries/templates.sql | 14 +- coderd/database/sqlc.yaml | 2 - .../provisionerdserver_test.go | 243 ------------------ coderd/schedule/autostop.go | 8 +- coderd/schedule/autostop_test.go | 47 +--- coderd/schedule/template.go | 10 - coderd/telemetry/telemetry.go | 4 - coderd/templates.go | 37 +-- coderd/templates_test.go | 199 -------------- coderd/workspaces.go | 33 +-- codersdk/deployment.go | 2 +- codersdk/organizations.go | 4 - codersdk/templates.go | 9 - codersdk/users.go | 2 +- docs/admin/audit-logs.md | 32 +-- docs/api/schemas.md | 16 +- docs/api/templates.md | 23 +- docs/cli/templates_create.md | 8 - docs/cli/templates_edit.md | 24 +- enterprise/audit/table.go | 2 - enterprise/cli/templateedit_test.go | 4 - .../cli/testdata/coder_server_--help.golden | 2 +- enterprise/coderd/schedule/template.go | 6 - enterprise/coderd/schedule/template_test.go | 4 - enterprise/coderd/templates_test.go | 117 --------- site/src/api/typesGenerated.ts | 4 - .../CreateTemplatePage/CreateTemplateForm.tsx | 10 - site/src/pages/CreateTemplatePage/utils.ts | 16 +- .../TemplateSettingsPage.test.tsx | 2 +- .../TemplateSchedulePage/TTLHelperText.tsx | 20 -- .../TemplateScheduleForm.tsx | 116 +-------- .../TemplateSchedulePage.test.tsx | 17 +- .../TemplateSchedulePage/formHelpers.tsx | 9 - site/src/testHelpers/entities.ts | 2 - 51 files changed, 151 insertions(+), 1304 deletions(-) create mode 100644 coderd/database/migrations/000202_remove_max_ttl.down.sql create mode 100644 coderd/database/migrations/000202_remove_max_ttl.up.sql diff --git a/cli/templatecreate.go b/cli/templatecreate.go index 16c0f684cd..c570a0d606 100644 --- a/cli/templatecreate.go +++ b/cli/templatecreate.go @@ -29,7 +29,6 @@ func (r *RootCmd) templateCreate() *serpent.Command { failureTTL time.Duration dormancyThreshold time.Duration dormancyAutoDeletion time.Duration - maxTTL time.Duration uploadFlags templateUploadFlags ) @@ -46,7 +45,7 @@ func (r *RootCmd) templateCreate() *serpent.Command { r.InitClient(client), ), Handler: func(inv *serpent.Invocation) error { - isTemplateSchedulingOptionsSet := failureTTL != 0 || dormancyThreshold != 0 || dormancyAutoDeletion != 0 || maxTTL != 0 + isTemplateSchedulingOptionsSet := failureTTL != 0 || dormancyThreshold != 0 || dormancyAutoDeletion != 0 if isTemplateSchedulingOptionsSet || requireActiveVersion { entitlements, err := client.Entitlements(inv.Context()) @@ -58,7 +57,7 @@ func (r *RootCmd) templateCreate() *serpent.Command { if isTemplateSchedulingOptionsSet { if !entitlements.Features[codersdk.FeatureAdvancedTemplateScheduling].Enabled { - return xerrors.Errorf("your license is not entitled to use advanced template scheduling, so you cannot set --failure-ttl, --inactivity-ttl, or --max-ttl") + return xerrors.Errorf("your license is not entitled to use advanced template scheduling, so you cannot set --failure-ttl, or --inactivity-ttl") } } @@ -154,7 +153,6 @@ func (r *RootCmd) templateCreate() *serpent.Command { VersionID: job.ID, DefaultTTLMillis: ptr.Ref(defaultTTL.Milliseconds()), FailureTTLMillis: ptr.Ref(failureTTL.Milliseconds()), - MaxTTLMillis: ptr.Ref(maxTTL.Milliseconds()), TimeTilDormantMillis: ptr.Ref(dormancyThreshold.Milliseconds()), TimeTilDormantAutoDeleteMillis: ptr.Ref(dormancyAutoDeletion.Milliseconds()), DisableEveryoneGroupAccess: disableEveryone, @@ -229,12 +227,6 @@ func (r *RootCmd) templateCreate() *serpent.Command { Default: "0h", Value: serpent.DurationOf(&dormancyAutoDeletion), }, - - { - Flag: "max-ttl", - Description: "Edit the template maximum time before shutdown - workspaces created from this template must shutdown within the given duration after starting. This is an enterprise-only feature.", - Value: serpent.DurationOf(&maxTTL), - }, { Flag: "test.provisioner", Description: "Customize the provisioner backend.", diff --git a/cli/templateedit.go b/cli/templateedit.go index c714948335..fbf740097b 100644 --- a/cli/templateedit.go +++ b/cli/templateedit.go @@ -24,7 +24,6 @@ func (r *RootCmd) templateEdit() *serpent.Command { icon string defaultTTL time.Duration activityBump time.Duration - maxTTL time.Duration autostopRequirementDaysOfWeek []string autostopRequirementWeeks int64 autostartRequirementDaysOfWeek []string @@ -53,7 +52,6 @@ func (r *RootCmd) templateEdit() *serpent.Command { autostopRequirementWeeks > 0 || !allowUserAutostart || !allowUserAutostop || - maxTTL != 0 || failureTTL != 0 || dormancyThreshold != 0 || dormancyAutoDeletion != 0 || @@ -69,7 +67,7 @@ func (r *RootCmd) templateEdit() *serpent.Command { } if requiresScheduling && !entitlements.Features[codersdk.FeatureAdvancedTemplateScheduling].Enabled { - return xerrors.Errorf("your license is not entitled to use advanced template scheduling, so you cannot set --max-ttl, --failure-ttl, --inactivityTTL, --allow-user-autostart=false or --allow-user-autostop=false") + return xerrors.Errorf("your license is not entitled to use advanced template scheduling, so you cannot set --failure-ttl, --inactivityTTL, --allow-user-autostart=false or --allow-user-autostop=false") } if requireActiveVersion { @@ -101,10 +99,6 @@ func (r *RootCmd) templateEdit() *serpent.Command { displayName = template.DisplayName } - if !userSetOption(inv, "max-ttl") { - maxTTL = time.Duration(template.MaxTTLMillis) * time.Millisecond - } - if !userSetOption(inv, "default-ttl") { defaultTTL = time.Duration(template.DefaultTTLMillis) * time.Millisecond } @@ -179,7 +173,6 @@ func (r *RootCmd) templateEdit() *serpent.Command { Icon: icon, DefaultTTLMillis: defaultTTL.Milliseconds(), ActivityBumpMillis: activityBump.Milliseconds(), - MaxTTLMillis: maxTTL.Milliseconds(), AutostopRequirement: &codersdk.TemplateAutostopRequirement{ DaysOfWeek: autostopRequirementDaysOfWeek, Weeks: autostopRequirementWeeks, @@ -244,11 +237,6 @@ func (r *RootCmd) templateEdit() *serpent.Command { Description: "Edit the template activity bump - workspaces created from this template will have their shutdown time bumped by this value when activity is detected. Maps to \"Activity bump\" in the UI.", Value: serpent.DurationOf(&activityBump), }, - { - Flag: "max-ttl", - Description: "Edit the template maximum time before shutdown - workspaces created from this template must shutdown within the given duration after starting, regardless of user activity. This is an enterprise-only feature. Maps to \"Max lifetime\" in the UI.", - Value: serpent.DurationOf(&maxTTL), - }, { Flag: "autostart-requirement-weekdays", // workspaces created from this template must be restarted on the given weekdays. To unset this value for the template (and disable the autostop requirement for the template), pass 'none'. @@ -268,8 +256,6 @@ func (r *RootCmd) templateEdit() *serpent.Command { { Flag: "autostop-requirement-weekdays", Description: "Edit the template autostop requirement weekdays - workspaces created from this template must be restarted on the given weekdays. To unset this value for the template (and disable the autostop requirement for the template), pass 'none'.", - // TODO(@dean): unhide when we delete max_ttl - Hidden: true, Value: serpent.Validate(serpent.StringArrayOf(&autostopRequirementDaysOfWeek), func(value *serpent.StringArray) error { v := value.GetSlice() if len(v) == 1 && v[0] == "none" { @@ -285,9 +271,7 @@ func (r *RootCmd) templateEdit() *serpent.Command { { Flag: "autostop-requirement-weeks", Description: "Edit the template autostop requirement weeks - workspaces created from this template must be restarted on an n-weekly basis.", - // TODO(@dean): unhide when we delete max_ttl - Hidden: true, - Value: serpent.Int64Of(&autostopRequirementWeeks), + Value: serpent.Int64Of(&autostopRequirementWeeks), }, { Flag: "failure-ttl", diff --git a/cli/templateedit_test.go b/cli/templateedit_test.go index d7cfb01e9b..d5fe730a14 100644 --- a/cli/templateedit_test.go +++ b/cli/templateedit_test.go @@ -552,220 +552,7 @@ func TestTemplateEdit(t *testing.T) { assert.Equal(t, template.AutostartRequirement.DaysOfWeek, updated.AutostartRequirement.DaysOfWeek) }) }) - // TODO(@dean): remove this test when we remove max_ttl - t.Run("MaxTTL", func(t *testing.T) { - t.Parallel() - t.Run("BlockedAGPL", func(t *testing.T) { - t.Parallel() - client := coderdtest.New(t, &coderdtest.Options{IncludeProvisionerDaemon: true}) - owner := coderdtest.CreateFirstUser(t, client) - templateAdmin, _ := coderdtest.CreateAnotherUser(t, client, owner.OrganizationID, rbac.RoleTemplateAdmin()) - version := coderdtest.CreateTemplateVersion(t, client, owner.OrganizationID, nil) - _ = coderdtest.AwaitTemplateVersionJobCompleted(t, client, version.ID) - template := coderdtest.CreateTemplate(t, client, owner.OrganizationID, version.ID, func(ctr *codersdk.CreateTemplateRequest) { - ctr.DefaultTTLMillis = nil - ctr.MaxTTLMillis = nil - }) - // Test the cli command. - cmdArgs := []string{ - "templates", - "edit", - template.Name, - "--max-ttl", "1h", - } - inv, root := clitest.New(t, cmdArgs...) - clitest.SetupConfig(t, templateAdmin, root) - - ctx := testutil.Context(t, testutil.WaitLong) - err := inv.WithContext(ctx).Run() - require.Error(t, err) - require.ErrorContains(t, err, "appears to be an AGPL deployment") - - // Assert that the template metadata did not change. - updated, err := client.Template(context.Background(), template.ID) - require.NoError(t, err) - assert.Equal(t, template.Name, updated.Name) - assert.Equal(t, template.Description, updated.Description) - assert.Equal(t, template.Icon, updated.Icon) - assert.Equal(t, template.DisplayName, updated.DisplayName) - assert.Equal(t, template.DefaultTTLMillis, updated.DefaultTTLMillis) - assert.Equal(t, template.MaxTTLMillis, updated.MaxTTLMillis) - }) - - t.Run("BlockedNotEntitled", func(t *testing.T) { - t.Parallel() - client := coderdtest.New(t, &coderdtest.Options{IncludeProvisionerDaemon: true}) - owner := coderdtest.CreateFirstUser(t, client) - templateAdmin, _ := coderdtest.CreateAnotherUser(t, client, owner.OrganizationID, rbac.RoleTemplateAdmin()) - version := coderdtest.CreateTemplateVersion(t, client, owner.OrganizationID, nil) - _ = coderdtest.AwaitTemplateVersionJobCompleted(t, client, version.ID) - template := coderdtest.CreateTemplate(t, client, owner.OrganizationID, version.ID, func(ctr *codersdk.CreateTemplateRequest) { - ctr.DefaultTTLMillis = nil - ctr.MaxTTLMillis = nil - }) - - // Make a proxy server that will return a valid entitlements - // response, but without advanced scheduling entitlement. - proxy := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - if r.URL.Path == "/api/v2/entitlements" { - res := codersdk.Entitlements{ - Features: map[codersdk.FeatureName]codersdk.Feature{}, - Warnings: []string{}, - Errors: []string{}, - HasLicense: true, - Trial: true, - RequireTelemetry: false, - } - for _, feature := range codersdk.FeatureNames { - res.Features[feature] = codersdk.Feature{ - Entitlement: codersdk.EntitlementNotEntitled, - Enabled: false, - Limit: nil, - Actual: nil, - } - } - httpapi.Write(r.Context(), w, http.StatusOK, res) - return - } - - // Otherwise, proxy the request to the real API server. - rp := httputil.NewSingleHostReverseProxy(client.URL) - tp := &http.Transport{} - defer tp.CloseIdleConnections() - rp.Transport = tp - rp.ServeHTTP(w, r) - })) - defer proxy.Close() - - // Create a new client that uses the proxy server. - proxyURL, err := url.Parse(proxy.URL) - require.NoError(t, err) - proxyClient := codersdk.New(proxyURL) - proxyClient.SetSessionToken(templateAdmin.SessionToken()) - t.Cleanup(proxyClient.HTTPClient.CloseIdleConnections) - - // Test the cli command. - cmdArgs := []string{ - "templates", - "edit", - template.Name, - "--max-ttl", "1h", - } - inv, root := clitest.New(t, cmdArgs...) - clitest.SetupConfig(t, proxyClient, root) - - ctx := testutil.Context(t, testutil.WaitLong) - err = inv.WithContext(ctx).Run() - require.Error(t, err) - require.ErrorContains(t, err, "license is not entitled") - - // Assert that the template metadata did not change. - updated, err := client.Template(context.Background(), template.ID) - require.NoError(t, err) - assert.Equal(t, template.Name, updated.Name) - assert.Equal(t, template.Description, updated.Description) - assert.Equal(t, template.Icon, updated.Icon) - assert.Equal(t, template.DisplayName, updated.DisplayName) - assert.Equal(t, template.DefaultTTLMillis, updated.DefaultTTLMillis) - assert.Equal(t, template.MaxTTLMillis, updated.MaxTTLMillis) - }) - t.Run("Entitled", func(t *testing.T) { - t.Parallel() - client := coderdtest.New(t, &coderdtest.Options{IncludeProvisionerDaemon: true}) - owner := coderdtest.CreateFirstUser(t, client) - templateAdmin, _ := coderdtest.CreateAnotherUser(t, client, owner.OrganizationID, rbac.RoleTemplateAdmin()) - version := coderdtest.CreateTemplateVersion(t, client, owner.OrganizationID, nil) - _ = coderdtest.AwaitTemplateVersionJobCompleted(t, client, version.ID) - template := coderdtest.CreateTemplate(t, client, owner.OrganizationID, version.ID, func(ctr *codersdk.CreateTemplateRequest) { - ctr.DefaultTTLMillis = nil - ctr.MaxTTLMillis = nil - }) - - // Make a proxy server that will return a valid entitlements - // response, including a valid advanced scheduling entitlement. - var updateTemplateCalled int64 - proxy := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - if r.URL.Path == "/api/v2/entitlements" { - res := codersdk.Entitlements{ - Features: map[codersdk.FeatureName]codersdk.Feature{}, - Warnings: []string{}, - Errors: []string{}, - HasLicense: true, - Trial: true, - RequireTelemetry: false, - } - for _, feature := range codersdk.FeatureNames { - var one int64 = 1 - res.Features[feature] = codersdk.Feature{ - Entitlement: codersdk.EntitlementNotEntitled, - Enabled: true, - Limit: &one, - Actual: &one, - } - } - httpapi.Write(r.Context(), w, http.StatusOK, res) - return - } - if strings.HasPrefix(r.URL.Path, "/api/v2/templates/") { - body, err := io.ReadAll(r.Body) - require.NoError(t, err) - _ = r.Body.Close() - - var req codersdk.UpdateTemplateMeta - err = json.Unmarshal(body, &req) - require.NoError(t, err) - assert.Equal(t, time.Hour.Milliseconds(), req.MaxTTLMillis) - - r.Body = io.NopCloser(bytes.NewReader(body)) - atomic.AddInt64(&updateTemplateCalled, 1) - // We still want to call the real route. - } - - // Otherwise, proxy the request to the real API server. - rp := httputil.NewSingleHostReverseProxy(client.URL) - tp := &http.Transport{} - defer tp.CloseIdleConnections() - rp.Transport = tp - rp.ServeHTTP(w, r) - })) - defer proxy.Close() - - // Create a new client that uses the proxy server. - proxyURL, err := url.Parse(proxy.URL) - require.NoError(t, err) - proxyClient := codersdk.New(proxyURL) - proxyClient.SetSessionToken(templateAdmin.SessionToken()) - t.Cleanup(proxyClient.HTTPClient.CloseIdleConnections) - - // Test the cli command. - cmdArgs := []string{ - "templates", - "edit", - template.Name, - "--max-ttl", "1h", - } - inv, root := clitest.New(t, cmdArgs...) - clitest.SetupConfig(t, proxyClient, root) - - ctx := testutil.Context(t, testutil.WaitLong) - err = inv.WithContext(ctx).Run() - require.NoError(t, err) - - require.EqualValues(t, 1, atomic.LoadInt64(&updateTemplateCalled)) - - // Assert that the template metadata did not change. We verify the - // correct request gets sent to the server already. - updated, err := client.Template(context.Background(), template.ID) - require.NoError(t, err) - assert.Equal(t, template.Name, updated.Name) - assert.Equal(t, template.Description, updated.Description) - assert.Equal(t, template.Icon, updated.Icon) - assert.Equal(t, template.DisplayName, updated.DisplayName) - assert.Equal(t, template.DefaultTTLMillis, updated.DefaultTTLMillis) - assert.Equal(t, template.MaxTTLMillis, updated.MaxTTLMillis) - }) - }) t.Run("AllowUserScheduling", func(t *testing.T) { t.Parallel() t.Run("BlockedAGPL", func(t *testing.T) { diff --git a/cli/testdata/coder_server_--help.golden b/cli/testdata/coder_server_--help.golden index 3c3c0f4031..a19d9e91b2 100644 --- a/cli/testdata/coder_server_--help.golden +++ b/cli/testdata/coder_server_--help.golden @@ -467,7 +467,7 @@ telemetrywhen required by your organization's security policy. USER QUIET HOURS SCHEDULE OPTIONS: Allow users to set quiet hours schedules each day for workspaces to avoid -workspaces stopping during the day due to template max TTL. +workspaces stopping during the day due to template scheduling. --allow-custom-quiet-hours bool, $CODER_ALLOW_CUSTOM_QUIET_HOURS (default: true) Allow users to set their own quiet hours schedule for workspaces to diff --git a/cli/testdata/coder_templates_create_--help.golden b/cli/testdata/coder_templates_create_--help.golden index 4fb6512cba..be37480655 100644 --- a/cli/testdata/coder_templates_create_--help.golden +++ b/cli/testdata/coder_templates_create_--help.golden @@ -35,11 +35,6 @@ OPTIONS: Ignore warnings about not having a .terraform.lock.hcl file present in the template. - --max-ttl duration - Edit the template maximum time before shutdown - workspaces created - from this template must shutdown within the given duration after - starting. This is an enterprise-only feature. - -m, --message string Specify a message describing the changes in this version of the template. Messages longer than 72 characters will be displayed as diff --git a/cli/testdata/coder_templates_edit_--help.golden b/cli/testdata/coder_templates_edit_--help.golden index 1f6ce3c4d4..29184b969b 100644 --- a/cli/testdata/coder_templates_edit_--help.golden +++ b/cli/testdata/coder_templates_edit_--help.golden @@ -28,6 +28,16 @@ OPTIONS: this value for the template (and allow autostart on all days), pass 'all'. + --autostop-requirement-weekdays string-array + Edit the template autostop requirement weekdays - workspaces created + from this template must be restarted on the given weekdays. To unset + this value for the template (and disable the autostop requirement for + the template), pass 'none'. + + --autostop-requirement-weeks int + Edit the template autostop requirement weeks - workspaces created from + this template must be restarted on an n-weekly basis. + --default-ttl duration Edit the template default time before shutdown - workspaces created from this template default to this value. Maps to "Default autostop" @@ -62,12 +72,6 @@ OPTIONS: --icon string Edit the template icon path. - --max-ttl duration - Edit the template maximum time before shutdown - workspaces created - from this template must shutdown within the given duration after - starting, regardless of user activity. This is an enterprise-only - feature. Maps to "Max lifetime" in the UI. - --name string Edit the template name. diff --git a/cli/testdata/server-config.yaml.golden b/cli/testdata/server-config.yaml.golden index 3b8d439cd1..83b2a770c4 100644 --- a/cli/testdata/server-config.yaml.golden +++ b/cli/testdata/server-config.yaml.golden @@ -462,7 +462,7 @@ externalAuthProviders: [] # (default: , type: string) wgtunnelHost: "" # Allow users to set quiet hours schedules each day for workspaces to avoid -# workspaces stopping during the day due to template max TTL. +# workspaces stopping during the day due to template scheduling. userQuietHoursSchedule: # The default daily cron schedule applied to users that haven't set a custom quiet # hours schedule themselves. The quiet hours schedule determines when workspaces diff --git a/coderd/activitybump_test.go b/coderd/activitybump_test.go index 0b55b40508..2f54a598e6 100644 --- a/coderd/activitybump_test.go +++ b/coderd/activitybump_test.go @@ -31,10 +31,6 @@ func TestWorkspaceActivityBump(t *testing.T) { setupActivityTest := func(t *testing.T, deadline ...time.Duration) (client *codersdk.Client, workspace codersdk.Workspace, assertBumped func(want bool)) { t.Helper() const ttl = time.Hour - maxTTL := time.Duration(0) - if len(deadline) > 0 { - maxTTL = deadline[0] - } db, pubsub := dbtestutil.NewDB(t) client = coderdtest.New(t, &coderdtest.Options{ @@ -73,8 +69,8 @@ func TestWorkspaceActivityBump(t *testing.T) { var maxDeadline time.Time // Update the max deadline. - if maxTTL != 0 { - maxDeadline = dbtime.Now().Add(maxTTL) + if len(deadline) > 0 { + maxDeadline = dbtime.Now().Add(deadline[0]) } err := db.UpdateWorkspaceBuildDeadlineByID(ctx, database.UpdateWorkspaceBuildDeadlineByIDParams{ @@ -99,9 +95,9 @@ func TestWorkspaceActivityBump(t *testing.T) { ) firstDeadline := workspace.LatestBuild.Deadline.Time - if maxTTL != 0 { + if !maxDeadline.IsZero() { require.WithinDuration(t, - time.Now().Add(maxTTL), + maxDeadline, workspace.LatestBuild.MaxDeadline.Time, testutil.WaitMedium, ) @@ -218,6 +214,6 @@ func TestWorkspaceActivityBump(t *testing.T) { require.NoError(t, err) _ = sshConn.Close() - assertBumped(true) // also asserts max ttl not exceeded + assertBumped(true) }) } diff --git a/coderd/apidoc/docs.go b/coderd/apidoc/docs.go index d947ec67da..0b59ae2e46 100644 --- a/coderd/apidoc/docs.go +++ b/coderd/apidoc/docs.go @@ -8739,7 +8739,7 @@ const docTemplate = `{ ] }, "autostop_requirement": { - "description": "AutostopRequirement allows optionally specifying the autostop requirement\nfor workspaces created from this template. This is an enterprise feature.\nOnly one of MaxTTLMillis or AutostopRequirement can be specified.", + "description": "AutostopRequirement allows optionally specifying the autostop requirement\nfor workspaces created from this template. This is an enterprise feature.", "allOf": [ { "$ref": "#/definitions/codersdk.TemplateAutostopRequirement" @@ -8778,10 +8778,6 @@ const docTemplate = `{ "description": "Icon is a relative path or external URL that specifies\nan icon to be displayed in the dashboard.", "type": "string" }, - "max_ttl_ms": { - "description": "TODO(@dean): remove max_ttl once autostop_requirement is matured\nOnly one of MaxTTLMillis or AutostopRequirement can be specified.", - "type": "integer" - }, "name": { "description": "Name is the name of the template.", "type": "string" @@ -11601,10 +11597,6 @@ const docTemplate = `{ "max_port_share_level": { "$ref": "#/definitions/codersdk.WorkspaceAgentPortShareLevel" }, - "max_ttl_ms": { - "description": "TODO(@dean): remove max_ttl once autostop_requirement is matured", - "type": "integer" - }, "name": { "type": "string" }, @@ -11631,10 +11623,6 @@ const docTemplate = `{ "updated_at": { "type": "string", "format": "date-time" - }, - "use_max_ttl": { - "description": "UseMaxTTL picks whether to use the deprecated max TTL for the template or\nthe new autostop requirement.", - "type": "boolean" } } }, @@ -12367,7 +12355,7 @@ const docTemplate = `{ ], "properties": { "schedule": { - "description": "Schedule is a cron expression that defines when the user's quiet hours\nwindow is. Schedule must not be empty. For new users, the schedule is set\nto 2am in their browser or computer's timezone. The schedule denotes the\nbeginning of a 4 hour window where the workspace is allowed to\nautomatically stop or restart due to maintenance or template max TTL.\n\nThe schedule must be daily with a single time, and should have a timezone\nspecified via a CRON_TZ prefix (otherwise UTC will be used).\n\nIf the schedule is empty, the user will be updated to use the default\nschedule.", + "description": "Schedule is a cron expression that defines when the user's quiet hours\nwindow is. Schedule must not be empty. For new users, the schedule is set\nto 2am in their browser or computer's timezone. The schedule denotes the\nbeginning of a 4 hour window where the workspace is allowed to\nautomatically stop or restart due to maintenance or template schedule.\n\nThe schedule must be daily with a single time, and should have a timezone\nspecified via a CRON_TZ prefix (otherwise UTC will be used).\n\nIf the schedule is empty, the user will be updated to use the default\nschedule.", "type": "string" } } diff --git a/coderd/apidoc/swagger.json b/coderd/apidoc/swagger.json index 149d63578b..3255e8711c 100644 --- a/coderd/apidoc/swagger.json +++ b/coderd/apidoc/swagger.json @@ -7779,7 +7779,7 @@ ] }, "autostop_requirement": { - "description": "AutostopRequirement allows optionally specifying the autostop requirement\nfor workspaces created from this template. This is an enterprise feature.\nOnly one of MaxTTLMillis or AutostopRequirement can be specified.", + "description": "AutostopRequirement allows optionally specifying the autostop requirement\nfor workspaces created from this template. This is an enterprise feature.", "allOf": [ { "$ref": "#/definitions/codersdk.TemplateAutostopRequirement" @@ -7818,10 +7818,6 @@ "description": "Icon is a relative path or external URL that specifies\nan icon to be displayed in the dashboard.", "type": "string" }, - "max_ttl_ms": { - "description": "TODO(@dean): remove max_ttl once autostop_requirement is matured\nOnly one of MaxTTLMillis or AutostopRequirement can be specified.", - "type": "integer" - }, "name": { "description": "Name is the name of the template.", "type": "string" @@ -10481,10 +10477,6 @@ "max_port_share_level": { "$ref": "#/definitions/codersdk.WorkspaceAgentPortShareLevel" }, - "max_ttl_ms": { - "description": "TODO(@dean): remove max_ttl once autostop_requirement is matured", - "type": "integer" - }, "name": { "type": "string" }, @@ -10509,10 +10501,6 @@ "updated_at": { "type": "string", "format": "date-time" - }, - "use_max_ttl": { - "description": "UseMaxTTL picks whether to use the deprecated max TTL for the template or\nthe new autostop requirement.", - "type": "boolean" } } }, @@ -11196,7 +11184,7 @@ "required": ["schedule"], "properties": { "schedule": { - "description": "Schedule is a cron expression that defines when the user's quiet hours\nwindow is. Schedule must not be empty. For new users, the schedule is set\nto 2am in their browser or computer's timezone. The schedule denotes the\nbeginning of a 4 hour window where the workspace is allowed to\nautomatically stop or restart due to maintenance or template max TTL.\n\nThe schedule must be daily with a single time, and should have a timezone\nspecified via a CRON_TZ prefix (otherwise UTC will be used).\n\nIf the schedule is empty, the user will be updated to use the default\nschedule.", + "description": "Schedule is a cron expression that defines when the user's quiet hours\nwindow is. Schedule must not be empty. For new users, the schedule is set\nto 2am in their browser or computer's timezone. The schedule denotes the\nbeginning of a 4 hour window where the workspace is allowed to\nautomatically stop or restart due to maintenance or template schedule.\n\nThe schedule must be daily with a single time, and should have a timezone\nspecified via a CRON_TZ prefix (otherwise UTC will be used).\n\nIf the schedule is empty, the user will be updated to use the default\nschedule.", "type": "string" } } diff --git a/coderd/database/dbmem/dbmem.go b/coderd/database/dbmem/dbmem.go index 131020e53f..7ed8d8397c 100644 --- a/coderd/database/dbmem/dbmem.go +++ b/coderd/database/dbmem/dbmem.go @@ -6961,8 +6961,6 @@ func (q *FakeQuerier) UpdateTemplateScheduleByID(_ context.Context, arg database tpl.UpdatedAt = dbtime.Now() tpl.DefaultTTL = arg.DefaultTTL tpl.ActivityBump = arg.ActivityBump - tpl.UseMaxTtl = arg.UseMaxTtl - tpl.MaxTTL = arg.MaxTTL tpl.AutostopRequirementDaysOfWeek = arg.AutostopRequirementDaysOfWeek tpl.AutostopRequirementWeeks = arg.AutostopRequirementWeeks tpl.AutostartBlockDaysOfWeek = arg.AutostartBlockDaysOfWeek diff --git a/coderd/database/dump.sql b/coderd/database/dump.sql index 2cde1c6462..93e6ef23b2 100644 --- a/coderd/database/dump.sql +++ b/coderd/database/dump.sql @@ -907,7 +907,6 @@ CREATE TABLE templates ( group_acl jsonb DEFAULT '{}'::jsonb NOT NULL, display_name character varying(64) DEFAULT ''::character varying NOT NULL, allow_user_cancel_workspace_jobs boolean DEFAULT true NOT NULL, - max_ttl bigint DEFAULT '0'::bigint NOT NULL, allow_user_autostart boolean DEFAULT true NOT NULL, allow_user_autostop boolean DEFAULT true NOT NULL, failure_ttl bigint DEFAULT 0 NOT NULL, @@ -918,7 +917,6 @@ CREATE TABLE templates ( autostart_block_days_of_week smallint DEFAULT 0 NOT NULL, require_active_version boolean DEFAULT false NOT NULL, deprecated text DEFAULT ''::text NOT NULL, - use_max_ttl boolean DEFAULT false NOT NULL, activity_bump bigint DEFAULT '3600000000000'::bigint NOT NULL, max_port_sharing_level app_sharing_level DEFAULT 'owner'::app_sharing_level NOT NULL ); @@ -958,7 +956,6 @@ CREATE VIEW template_with_users AS templates.group_acl, templates.display_name, templates.allow_user_cancel_workspace_jobs, - templates.max_ttl, templates.allow_user_autostart, templates.allow_user_autostop, templates.failure_ttl, @@ -969,7 +966,6 @@ CREATE VIEW template_with_users AS templates.autostart_block_days_of_week, templates.require_active_version, templates.deprecated, - templates.use_max_ttl, templates.activity_bump, templates.max_port_sharing_level, COALESCE(visible_users.avatar_url, ''::text) AS created_by_avatar_url, diff --git a/coderd/database/migrations/000202_remove_max_ttl.down.sql b/coderd/database/migrations/000202_remove_max_ttl.down.sql new file mode 100644 index 0000000000..ab1c1c5c51 --- /dev/null +++ b/coderd/database/migrations/000202_remove_max_ttl.down.sql @@ -0,0 +1,21 @@ +-- Update the template_with_users view by recreating it. +DROP VIEW template_with_users; + +ALTER TABLE "templates" ADD COLUMN "max_ttl" bigint DEFAULT '0'::bigint NOT NULL; +-- Most templates should have this set to false by now. +ALTER TABLE templates ADD COLUMN use_max_ttl boolean NOT NULL DEFAULT false; + +CREATE VIEW + template_with_users +AS +SELECT + templates.*, + coalesce(visible_users.avatar_url, '') AS created_by_avatar_url, + coalesce(visible_users.username, '') AS created_by_username +FROM + templates + LEFT JOIN + visible_users + ON + templates.created_by = visible_users.id; +COMMENT ON VIEW template_with_users IS 'Joins in the username + avatar url of the created by user.'; diff --git a/coderd/database/migrations/000202_remove_max_ttl.up.sql b/coderd/database/migrations/000202_remove_max_ttl.up.sql new file mode 100644 index 0000000000..36bcc15867 --- /dev/null +++ b/coderd/database/migrations/000202_remove_max_ttl.up.sql @@ -0,0 +1,20 @@ +-- Update the template_with_users view by recreating it. +DROP VIEW template_with_users; + +ALTER TABLE templates DROP COLUMN "max_ttl"; +ALTER TABLE templates DROP COLUMN "use_max_ttl"; + +CREATE VIEW + template_with_users +AS +SELECT + templates.*, + coalesce(visible_users.avatar_url, '') AS created_by_avatar_url, + coalesce(visible_users.username, '') AS created_by_username +FROM + templates + LEFT JOIN + visible_users + ON + templates.created_by = visible_users.id; +COMMENT ON VIEW template_with_users IS 'Joins in the username + avatar url of the created by user.'; diff --git a/coderd/database/modelqueries.go b/coderd/database/modelqueries.go index 142fd343cb..0879aca441 100644 --- a/coderd/database/modelqueries.go +++ b/coderd/database/modelqueries.go @@ -78,7 +78,6 @@ func (q *sqlQuerier) GetAuthorizedTemplates(ctx context.Context, arg GetTemplate &i.GroupACL, &i.DisplayName, &i.AllowUserCancelWorkspaceJobs, - &i.MaxTTL, &i.AllowUserAutostart, &i.AllowUserAutostop, &i.FailureTTL, @@ -89,7 +88,6 @@ func (q *sqlQuerier) GetAuthorizedTemplates(ctx context.Context, arg GetTemplate &i.AutostartBlockDaysOfWeek, &i.RequireActiveVersion, &i.Deprecated, - &i.UseMaxTtl, &i.ActivityBump, &i.MaxPortSharingLevel, &i.CreatedByAvatarURL, diff --git a/coderd/database/models.go b/coderd/database/models.go index d66ee21c31..49eb5bf811 100644 --- a/coderd/database/models.go +++ b/coderd/database/models.go @@ -2083,7 +2083,6 @@ type Template struct { GroupACL TemplateACL `db:"group_acl" json:"group_acl"` DisplayName string `db:"display_name" json:"display_name"` AllowUserCancelWorkspaceJobs bool `db:"allow_user_cancel_workspace_jobs" json:"allow_user_cancel_workspace_jobs"` - MaxTTL int64 `db:"max_ttl" json:"max_ttl"` AllowUserAutostart bool `db:"allow_user_autostart" json:"allow_user_autostart"` AllowUserAutostop bool `db:"allow_user_autostop" json:"allow_user_autostop"` FailureTTL int64 `db:"failure_ttl" json:"failure_ttl"` @@ -2094,7 +2093,6 @@ type Template struct { AutostartBlockDaysOfWeek int16 `db:"autostart_block_days_of_week" json:"autostart_block_days_of_week"` RequireActiveVersion bool `db:"require_active_version" json:"require_active_version"` Deprecated string `db:"deprecated" json:"deprecated"` - UseMaxTtl bool `db:"use_max_ttl" json:"use_max_ttl"` ActivityBump int64 `db:"activity_bump" json:"activity_bump"` MaxPortSharingLevel AppSharingLevel `db:"max_port_sharing_level" json:"max_port_sharing_level"` CreatedByAvatarURL string `db:"created_by_avatar_url" json:"created_by_avatar_url"` @@ -2120,8 +2118,7 @@ type TemplateTable struct { // Display name is a custom, human-friendly template name that user can set. DisplayName string `db:"display_name" json:"display_name"` // Allow users to cancel in-progress workspace jobs. - AllowUserCancelWorkspaceJobs bool `db:"allow_user_cancel_workspace_jobs" json:"allow_user_cancel_workspace_jobs"` - MaxTTL int64 `db:"max_ttl" json:"max_ttl"` + AllowUserCancelWorkspaceJobs bool `db:"allow_user_cancel_workspace_jobs" json:"allow_user_cancel_workspace_jobs"` // Allow users to specify an autostart schedule for workspaces (enterprise). AllowUserAutostart bool `db:"allow_user_autostart" json:"allow_user_autostart"` // Allow users to specify custom autostop values for workspaces (enterprise). @@ -2138,7 +2135,6 @@ type TemplateTable struct { RequireActiveVersion bool `db:"require_active_version" json:"require_active_version"` // If set to a non empty string, the template will no longer be able to be used. The message will be displayed to the user. Deprecated string `db:"deprecated" json:"deprecated"` - UseMaxTtl bool `db:"use_max_ttl" json:"use_max_ttl"` ActivityBump int64 `db:"activity_bump" json:"activity_bump"` MaxPortSharingLevel AppSharingLevel `db:"max_port_sharing_level" json:"max_port_sharing_level"` } diff --git a/coderd/database/queries.sql.go b/coderd/database/queries.sql.go index 85ff4a91cd..e31546da3a 100644 --- a/coderd/database/queries.sql.go +++ b/coderd/database/queries.sql.go @@ -6056,7 +6056,7 @@ func (q *sqlQuerier) GetTemplateAverageBuildTime(ctx context.Context, arg GetTem const getTemplateByID = `-- name: GetTemplateByID :one SELECT - id, created_at, updated_at, organization_id, deleted, name, provisioner, active_version_id, description, default_ttl, created_by, icon, user_acl, group_acl, display_name, allow_user_cancel_workspace_jobs, max_ttl, allow_user_autostart, allow_user_autostop, failure_ttl, time_til_dormant, time_til_dormant_autodelete, autostop_requirement_days_of_week, autostop_requirement_weeks, autostart_block_days_of_week, require_active_version, deprecated, use_max_ttl, activity_bump, max_port_sharing_level, created_by_avatar_url, created_by_username + id, created_at, updated_at, organization_id, deleted, name, provisioner, active_version_id, description, default_ttl, created_by, icon, user_acl, group_acl, display_name, allow_user_cancel_workspace_jobs, allow_user_autostart, allow_user_autostop, failure_ttl, time_til_dormant, time_til_dormant_autodelete, autostop_requirement_days_of_week, autostop_requirement_weeks, autostart_block_days_of_week, require_active_version, deprecated, activity_bump, max_port_sharing_level, created_by_avatar_url, created_by_username FROM template_with_users WHERE @@ -6085,7 +6085,6 @@ func (q *sqlQuerier) GetTemplateByID(ctx context.Context, id uuid.UUID) (Templat &i.GroupACL, &i.DisplayName, &i.AllowUserCancelWorkspaceJobs, - &i.MaxTTL, &i.AllowUserAutostart, &i.AllowUserAutostop, &i.FailureTTL, @@ -6096,7 +6095,6 @@ func (q *sqlQuerier) GetTemplateByID(ctx context.Context, id uuid.UUID) (Templat &i.AutostartBlockDaysOfWeek, &i.RequireActiveVersion, &i.Deprecated, - &i.UseMaxTtl, &i.ActivityBump, &i.MaxPortSharingLevel, &i.CreatedByAvatarURL, @@ -6107,7 +6105,7 @@ func (q *sqlQuerier) GetTemplateByID(ctx context.Context, id uuid.UUID) (Templat const getTemplateByOrganizationAndName = `-- name: GetTemplateByOrganizationAndName :one SELECT - id, created_at, updated_at, organization_id, deleted, name, provisioner, active_version_id, description, default_ttl, created_by, icon, user_acl, group_acl, display_name, allow_user_cancel_workspace_jobs, max_ttl, allow_user_autostart, allow_user_autostop, failure_ttl, time_til_dormant, time_til_dormant_autodelete, autostop_requirement_days_of_week, autostop_requirement_weeks, autostart_block_days_of_week, require_active_version, deprecated, use_max_ttl, activity_bump, max_port_sharing_level, created_by_avatar_url, created_by_username + id, created_at, updated_at, organization_id, deleted, name, provisioner, active_version_id, description, default_ttl, created_by, icon, user_acl, group_acl, display_name, allow_user_cancel_workspace_jobs, allow_user_autostart, allow_user_autostop, failure_ttl, time_til_dormant, time_til_dormant_autodelete, autostop_requirement_days_of_week, autostop_requirement_weeks, autostart_block_days_of_week, require_active_version, deprecated, activity_bump, max_port_sharing_level, created_by_avatar_url, created_by_username FROM template_with_users AS templates WHERE @@ -6144,7 +6142,6 @@ func (q *sqlQuerier) GetTemplateByOrganizationAndName(ctx context.Context, arg G &i.GroupACL, &i.DisplayName, &i.AllowUserCancelWorkspaceJobs, - &i.MaxTTL, &i.AllowUserAutostart, &i.AllowUserAutostop, &i.FailureTTL, @@ -6155,7 +6152,6 @@ func (q *sqlQuerier) GetTemplateByOrganizationAndName(ctx context.Context, arg G &i.AutostartBlockDaysOfWeek, &i.RequireActiveVersion, &i.Deprecated, - &i.UseMaxTtl, &i.ActivityBump, &i.MaxPortSharingLevel, &i.CreatedByAvatarURL, @@ -6165,7 +6161,7 @@ func (q *sqlQuerier) GetTemplateByOrganizationAndName(ctx context.Context, arg G } const getTemplates = `-- name: GetTemplates :many -SELECT id, created_at, updated_at, organization_id, deleted, name, provisioner, active_version_id, description, default_ttl, created_by, icon, user_acl, group_acl, display_name, allow_user_cancel_workspace_jobs, max_ttl, allow_user_autostart, allow_user_autostop, failure_ttl, time_til_dormant, time_til_dormant_autodelete, autostop_requirement_days_of_week, autostop_requirement_weeks, autostart_block_days_of_week, require_active_version, deprecated, use_max_ttl, activity_bump, max_port_sharing_level, created_by_avatar_url, created_by_username FROM template_with_users AS templates +SELECT id, created_at, updated_at, organization_id, deleted, name, provisioner, active_version_id, description, default_ttl, created_by, icon, user_acl, group_acl, display_name, allow_user_cancel_workspace_jobs, allow_user_autostart, allow_user_autostop, failure_ttl, time_til_dormant, time_til_dormant_autodelete, autostop_requirement_days_of_week, autostop_requirement_weeks, autostart_block_days_of_week, require_active_version, deprecated, activity_bump, max_port_sharing_level, created_by_avatar_url, created_by_username FROM template_with_users AS templates ORDER BY (name, id) ASC ` @@ -6195,7 +6191,6 @@ func (q *sqlQuerier) GetTemplates(ctx context.Context) ([]Template, error) { &i.GroupACL, &i.DisplayName, &i.AllowUserCancelWorkspaceJobs, - &i.MaxTTL, &i.AllowUserAutostart, &i.AllowUserAutostop, &i.FailureTTL, @@ -6206,7 +6201,6 @@ func (q *sqlQuerier) GetTemplates(ctx context.Context) ([]Template, error) { &i.AutostartBlockDaysOfWeek, &i.RequireActiveVersion, &i.Deprecated, - &i.UseMaxTtl, &i.ActivityBump, &i.MaxPortSharingLevel, &i.CreatedByAvatarURL, @@ -6227,7 +6221,7 @@ func (q *sqlQuerier) GetTemplates(ctx context.Context) ([]Template, error) { const getTemplatesWithFilter = `-- name: GetTemplatesWithFilter :many SELECT - id, created_at, updated_at, organization_id, deleted, name, provisioner, active_version_id, description, default_ttl, created_by, icon, user_acl, group_acl, display_name, allow_user_cancel_workspace_jobs, max_ttl, allow_user_autostart, allow_user_autostop, failure_ttl, time_til_dormant, time_til_dormant_autodelete, autostop_requirement_days_of_week, autostop_requirement_weeks, autostart_block_days_of_week, require_active_version, deprecated, use_max_ttl, activity_bump, max_port_sharing_level, created_by_avatar_url, created_by_username + id, created_at, updated_at, organization_id, deleted, name, provisioner, active_version_id, description, default_ttl, created_by, icon, user_acl, group_acl, display_name, allow_user_cancel_workspace_jobs, allow_user_autostart, allow_user_autostop, failure_ttl, time_til_dormant, time_til_dormant_autodelete, autostop_requirement_days_of_week, autostop_requirement_weeks, autostart_block_days_of_week, require_active_version, deprecated, activity_bump, max_port_sharing_level, created_by_avatar_url, created_by_username FROM template_with_users AS templates WHERE @@ -6307,7 +6301,6 @@ func (q *sqlQuerier) GetTemplatesWithFilter(ctx context.Context, arg GetTemplate &i.GroupACL, &i.DisplayName, &i.AllowUserCancelWorkspaceJobs, - &i.MaxTTL, &i.AllowUserAutostart, &i.AllowUserAutostop, &i.FailureTTL, @@ -6318,7 +6311,6 @@ func (q *sqlQuerier) GetTemplatesWithFilter(ctx context.Context, arg GetTemplate &i.AutostartBlockDaysOfWeek, &i.RequireActiveVersion, &i.Deprecated, - &i.UseMaxTtl, &i.ActivityBump, &i.MaxPortSharingLevel, &i.CreatedByAvatarURL, @@ -6535,14 +6527,12 @@ SET allow_user_autostop = $4, default_ttl = $5, activity_bump = $6, - use_max_ttl = $7, - max_ttl = $8, - autostop_requirement_days_of_week = $9, - autostop_requirement_weeks = $10, - autostart_block_days_of_week = $11, - failure_ttl = $12, - time_til_dormant = $13, - time_til_dormant_autodelete = $14 + autostop_requirement_days_of_week = $7, + autostop_requirement_weeks = $8, + autostart_block_days_of_week = $9, + failure_ttl = $10, + time_til_dormant = $11, + time_til_dormant_autodelete = $12 WHERE id = $1 ` @@ -6554,8 +6544,6 @@ type UpdateTemplateScheduleByIDParams struct { AllowUserAutostop bool `db:"allow_user_autostop" json:"allow_user_autostop"` DefaultTTL int64 `db:"default_ttl" json:"default_ttl"` ActivityBump int64 `db:"activity_bump" json:"activity_bump"` - UseMaxTtl bool `db:"use_max_ttl" json:"use_max_ttl"` - MaxTTL int64 `db:"max_ttl" json:"max_ttl"` AutostopRequirementDaysOfWeek int16 `db:"autostop_requirement_days_of_week" json:"autostop_requirement_days_of_week"` AutostopRequirementWeeks int64 `db:"autostop_requirement_weeks" json:"autostop_requirement_weeks"` AutostartBlockDaysOfWeek int16 `db:"autostart_block_days_of_week" json:"autostart_block_days_of_week"` @@ -6572,8 +6560,6 @@ func (q *sqlQuerier) UpdateTemplateScheduleByID(ctx context.Context, arg UpdateT arg.AllowUserAutostop, arg.DefaultTTL, arg.ActivityBump, - arg.UseMaxTtl, - arg.MaxTTL, arg.AutostopRequirementDaysOfWeek, arg.AutostopRequirementWeeks, arg.AutostartBlockDaysOfWeek, @@ -11907,7 +11893,7 @@ LEFT JOIN LATERAL ( ) latest_build ON TRUE LEFT JOIN LATERAL ( SELECT - id, created_at, updated_at, organization_id, deleted, name, provisioner, active_version_id, description, default_ttl, created_by, icon, user_acl, group_acl, display_name, allow_user_cancel_workspace_jobs, max_ttl, allow_user_autostart, allow_user_autostop, failure_ttl, time_til_dormant, time_til_dormant_autodelete, autostop_requirement_days_of_week, autostop_requirement_weeks, autostart_block_days_of_week, require_active_version, deprecated, use_max_ttl, activity_bump, max_port_sharing_level + id, created_at, updated_at, organization_id, deleted, name, provisioner, active_version_id, description, default_ttl, created_by, icon, user_acl, group_acl, display_name, allow_user_cancel_workspace_jobs, allow_user_autostart, allow_user_autostop, failure_ttl, time_til_dormant, time_til_dormant_autodelete, autostop_requirement_days_of_week, autostop_requirement_weeks, autostart_block_days_of_week, require_active_version, deprecated, activity_bump, max_port_sharing_level FROM templates WHERE diff --git a/coderd/database/queries/templates.sql b/coderd/database/queries/templates.sql index 2202c1b710..d804077319 100644 --- a/coderd/database/queries/templates.sql +++ b/coderd/database/queries/templates.sql @@ -132,14 +132,12 @@ SET allow_user_autostop = $4, default_ttl = $5, activity_bump = $6, - use_max_ttl = $7, - max_ttl = $8, - autostop_requirement_days_of_week = $9, - autostop_requirement_weeks = $10, - autostart_block_days_of_week = $11, - failure_ttl = $12, - time_til_dormant = $13, - time_til_dormant_autodelete = $14 + autostop_requirement_days_of_week = $7, + autostop_requirement_weeks = $8, + autostart_block_days_of_week = $9, + failure_ttl = $10, + time_til_dormant = $11, + time_til_dormant_autodelete = $12 WHERE id = $1 ; diff --git a/coderd/database/sqlc.yaml b/coderd/database/sqlc.yaml index 6ccd4e2239..2884ff76b0 100644 --- a/coderd/database/sqlc.yaml +++ b/coderd/database/sqlc.yaml @@ -88,8 +88,6 @@ sql: group_acl: GroupACL troubleshooting_url: TroubleshootingURL default_ttl: DefaultTTL - max_ttl: MaxTTL - template_max_ttl: TemplateMaxTTL motd_file: MOTDFile uuid: UUID failure_ttl: FailureTTL diff --git a/coderd/provisionerdserver/provisionerdserver_test.go b/coderd/provisionerdserver/provisionerdserver_test.go index 3bd6b5a952..f3a061ce1b 100644 --- a/coderd/provisionerdserver/provisionerdserver_test.go +++ b/coderd/provisionerdserver/provisionerdserver_test.go @@ -1039,248 +1039,6 @@ func TestCompleteJob(t *testing.T) { require.False(t, job.Error.Valid) }) - // TODO(@dean): remove this legacy test for MaxTTL - t.Run("WorkspaceBuildLegacy", func(t *testing.T) { - t.Parallel() - - cases := []struct { - name string - templateAllowAutostop bool - templateDefaultTTL time.Duration - templateMaxTTL time.Duration - workspaceTTL time.Duration - transition database.WorkspaceTransition - // The TTL is actually a deadline time on the workspace_build row, - // so during the test this will be compared to be within 15 seconds - // of the expected value. - expectedTTL time.Duration - expectedMaxTTL time.Duration - }{ - { - name: "OK", - templateAllowAutostop: true, - templateDefaultTTL: 0, - templateMaxTTL: 0, - workspaceTTL: 0, - transition: database.WorkspaceTransitionStart, - expectedTTL: 0, - expectedMaxTTL: 0, - }, - { - name: "Delete", - templateAllowAutostop: true, - templateDefaultTTL: 0, - templateMaxTTL: 0, - workspaceTTL: 0, - transition: database.WorkspaceTransitionDelete, - expectedTTL: 0, - expectedMaxTTL: 0, - }, - { - name: "WorkspaceTTL", - templateAllowAutostop: true, - templateDefaultTTL: 0, - templateMaxTTL: 0, - workspaceTTL: time.Hour, - transition: database.WorkspaceTransitionStart, - expectedTTL: time.Hour, - expectedMaxTTL: 0, - }, - { - name: "TemplateDefaultTTLIgnored", - templateAllowAutostop: true, - templateDefaultTTL: time.Hour, - templateMaxTTL: 0, - workspaceTTL: 0, - transition: database.WorkspaceTransitionStart, - expectedTTL: 0, - expectedMaxTTL: 0, - }, - { - name: "WorkspaceTTLOverridesTemplateDefaultTTL", - templateAllowAutostop: true, - templateDefaultTTL: 2 * time.Hour, - templateMaxTTL: 0, - workspaceTTL: time.Hour, - transition: database.WorkspaceTransitionStart, - expectedTTL: time.Hour, - expectedMaxTTL: 0, - }, - { - name: "TemplateMaxTTL", - templateAllowAutostop: true, - templateDefaultTTL: 0, - templateMaxTTL: time.Hour, - workspaceTTL: 0, - transition: database.WorkspaceTransitionStart, - expectedTTL: time.Hour, - expectedMaxTTL: time.Hour, - }, - { - name: "TemplateMaxTTLOverridesWorkspaceTTL", - templateAllowAutostop: true, - templateDefaultTTL: 0, - templateMaxTTL: 2 * time.Hour, - workspaceTTL: 3 * time.Hour, - transition: database.WorkspaceTransitionStart, - expectedTTL: 2 * time.Hour, - expectedMaxTTL: 2 * time.Hour, - }, - { - name: "TemplateMaxTTLOverridesTemplateDefaultTTL", - templateAllowAutostop: true, - templateDefaultTTL: 3 * time.Hour, - templateMaxTTL: 2 * time.Hour, - workspaceTTL: 0, - transition: database.WorkspaceTransitionStart, - expectedTTL: 2 * time.Hour, - expectedMaxTTL: 2 * time.Hour, - }, - { - name: "TemplateBlockWorkspaceTTL", - templateAllowAutostop: false, - templateDefaultTTL: 3 * time.Hour, - templateMaxTTL: 6 * time.Hour, - workspaceTTL: 4 * time.Hour, - transition: database.WorkspaceTransitionStart, - expectedTTL: 3 * time.Hour, - expectedMaxTTL: 6 * time.Hour, - }, - } - - for _, c := range cases { - c := c - - t.Run(c.name, func(t *testing.T) { - t.Parallel() - - tss := &atomic.Pointer[schedule.TemplateScheduleStore]{} - srv, db, ps, pd := setup(t, false, &overrides{templateScheduleStore: tss}) - - var store schedule.TemplateScheduleStore = schedule.MockTemplateScheduleStore{ - GetFn: func(_ context.Context, _ database.Store, _ uuid.UUID) (schedule.TemplateScheduleOptions, error) { - return schedule.TemplateScheduleOptions{ - UserAutostartEnabled: false, - UserAutostopEnabled: c.templateAllowAutostop, - DefaultTTL: c.templateDefaultTTL, - MaxTTL: c.templateMaxTTL, - UseMaxTTL: true, - }, nil - }, - } - tss.Store(&store) - - user := dbgen.User(t, db, database.User{}) - template := dbgen.Template(t, db, database.Template{ - Name: "template", - Provisioner: database.ProvisionerTypeEcho, - OrganizationID: pd.OrganizationID, - }) - version := dbgen.TemplateVersion(t, db, database.TemplateVersion{ - OrganizationID: pd.OrganizationID, - TemplateID: uuid.NullUUID{ - UUID: template.ID, - Valid: true, - }, - JobID: uuid.New(), - }) - err := db.UpdateTemplateScheduleByID(ctx, database.UpdateTemplateScheduleByIDParams{ - ID: template.ID, - UpdatedAt: dbtime.Now(), - AllowUserAutostart: c.templateAllowAutostop, - DefaultTTL: int64(c.templateDefaultTTL), - MaxTTL: int64(c.templateMaxTTL), - }) - require.NoError(t, err) - file := dbgen.File(t, db, database.File{CreatedBy: user.ID}) - workspaceTTL := sql.NullInt64{} - if c.workspaceTTL != 0 { - workspaceTTL = sql.NullInt64{ - Int64: int64(c.workspaceTTL), - Valid: true, - } - } - workspace := dbgen.Workspace(t, db, database.Workspace{ - TemplateID: template.ID, - Ttl: workspaceTTL, - OrganizationID: pd.OrganizationID, - }) - build := dbgen.WorkspaceBuild(t, db, database.WorkspaceBuild{ - WorkspaceID: workspace.ID, - TemplateVersionID: version.ID, - Transition: c.transition, - Reason: database.BuildReasonInitiator, - }) - job := dbgen.ProvisionerJob(t, db, ps, database.ProvisionerJob{ - OrganizationID: pd.OrganizationID, - FileID: file.ID, - Type: database.ProvisionerJobTypeWorkspaceBuild, - Input: must(json.Marshal(provisionerdserver.WorkspaceProvisionJob{ - WorkspaceBuildID: build.ID, - })), - }) - _, err = db.AcquireProvisionerJob(ctx, database.AcquireProvisionerJobParams{ - OrganizationID: pd.OrganizationID, - WorkerID: uuid.NullUUID{ - UUID: pd.ID, - Valid: true, - }, - Types: []database.ProvisionerType{database.ProvisionerTypeEcho}, - }) - require.NoError(t, err) - - publishedWorkspace := make(chan struct{}) - closeWorkspaceSubscribe, err := ps.Subscribe(codersdk.WorkspaceNotifyChannel(build.WorkspaceID), func(_ context.Context, _ []byte) { - close(publishedWorkspace) - }) - require.NoError(t, err) - defer closeWorkspaceSubscribe() - publishedLogs := make(chan struct{}) - closeLogsSubscribe, err := ps.Subscribe(provisionersdk.ProvisionerJobLogsNotifyChannel(job.ID), func(_ context.Context, _ []byte) { - close(publishedLogs) - }) - require.NoError(t, err) - defer closeLogsSubscribe() - - _, err = srv.CompleteJob(ctx, &proto.CompletedJob{ - JobId: job.ID.String(), - Type: &proto.CompletedJob_WorkspaceBuild_{ - WorkspaceBuild: &proto.CompletedJob_WorkspaceBuild{ - State: []byte{}, - Resources: []*sdkproto.Resource{{ - Name: "example", - Type: "aws_instance", - }}, - }, - }, - }) - require.NoError(t, err) - - <-publishedWorkspace - <-publishedLogs - - workspace, err = db.GetWorkspaceByID(ctx, workspace.ID) - require.NoError(t, err) - require.Equal(t, c.transition == database.WorkspaceTransitionDelete, workspace.Deleted) - - workspaceBuild, err := db.GetWorkspaceBuildByID(ctx, build.ID) - require.NoError(t, err) - - if c.expectedTTL == 0 { - require.True(t, workspaceBuild.Deadline.IsZero()) - } else { - require.WithinDuration(t, time.Now().Add(c.expectedTTL), workspaceBuild.Deadline, 15*time.Second, "deadline does not match expected") - } - if c.expectedMaxTTL == 0 { - require.True(t, workspaceBuild.MaxDeadline.IsZero()) - } else { - require.WithinDuration(t, time.Now().Add(c.expectedMaxTTL), workspaceBuild.MaxDeadline, 15*time.Second, "max deadline does not match expected") - require.GreaterOrEqual(t, workspaceBuild.MaxDeadline.Unix(), workspaceBuild.Deadline.Unix(), "max deadline is smaller than deadline") - } - }) - } - }) - t.Run("WorkspaceBuild", func(t *testing.T) { t.Parallel() @@ -1386,7 +1144,6 @@ func TestCompleteJob(t *testing.T) { UserAutostartEnabled: false, UserAutostopEnabled: true, DefaultTTL: 0, - UseMaxTTL: false, AutostopRequirement: c.templateAutostopRequirement, }, nil }, diff --git a/coderd/schedule/autostop.go b/coderd/schedule/autostop.go index 6ba3e27dfa..eb39ca7638 100644 --- a/coderd/schedule/autostop.go +++ b/coderd/schedule/autostop.go @@ -110,14 +110,8 @@ func CalculateAutostop(ctx context.Context, params CalculateAutostopParams) (Aut } } - // Use the old algorithm for calculating max_deadline if the instance isn't - // configured or entitled to use the new feature flag yet. - if templateSchedule.UseMaxTTL && templateSchedule.MaxTTL > 0 { - autostop.MaxDeadline = now.Add(templateSchedule.MaxTTL) - } - // Otherwise, use the autostop_requirement algorithm. - if !templateSchedule.UseMaxTTL && templateSchedule.AutostopRequirement.DaysOfWeek != 0 { + if templateSchedule.AutostopRequirement.DaysOfWeek != 0 { // The template has a autostop requirement, so determine the max deadline // of this workspace build. diff --git a/coderd/schedule/autostop_test.go b/coderd/schedule/autostop_test.go index 5ea665726a..f932552812 100644 --- a/coderd/schedule/autostop_test.go +++ b/coderd/schedule/autostop_test.go @@ -70,13 +70,10 @@ func TestCalculateAutoStop(t *testing.T) { t.Log("saturdayMidnightAfterDstOut", saturdayMidnightAfterDstOut) cases := []struct { - name string - now time.Time - templateAllowAutostop bool - templateDefaultTTL time.Duration - // TODO(@dean): remove max_ttl tests - useMaxTTL bool - templateMaxTTL time.Duration + name string + now time.Time + templateAllowAutostop bool + templateDefaultTTL time.Duration templateAutostopRequirement schedule.TemplateAutostopRequirement userQuietHoursSchedule string // workspaceTTL is usually copied from the template's TTL when the @@ -367,40 +364,6 @@ func TestCalculateAutoStop(t *testing.T) { // expectedDeadline is copied from expectedMaxDeadline. expectedMaxDeadline: dstOutQuietHoursExpectedTime, }, - - // TODO(@dean): remove max_ttl tests - { - name: "AutostopRequirementIgnoresMaxTTL", - now: fridayEveningSydney.In(time.UTC), - templateAllowAutostop: false, - templateDefaultTTL: 0, - useMaxTTL: false, - templateMaxTTL: time.Hour, // should be ignored - userQuietHoursSchedule: sydneyQuietHours, - templateAutostopRequirement: schedule.TemplateAutostopRequirement{ - DaysOfWeek: 0b00100000, // Saturday - Weeks: 0, // weekly - }, - workspaceTTL: 0, - // expectedDeadline is copied from expectedMaxDeadline. - expectedMaxDeadline: saturdayMidnightSydney.In(time.UTC), - }, - { - name: "MaxTTLIgnoresAutostopRequirement", - now: fridayEveningSydney.In(time.UTC), - templateAllowAutostop: false, - templateDefaultTTL: 0, - useMaxTTL: true, - templateMaxTTL: time.Hour, // should NOT be ignored - userQuietHoursSchedule: sydneyQuietHours, - templateAutostopRequirement: schedule.TemplateAutostopRequirement{ - DaysOfWeek: 0b00100000, // Saturday - Weeks: 0, // weekly - }, - workspaceTTL: 0, - // expectedDeadline is copied from expectedMaxDeadline. - expectedMaxDeadline: fridayEveningSydney.Add(time.Hour).In(time.UTC), - }, } for _, c := range cases { @@ -418,8 +381,6 @@ func TestCalculateAutoStop(t *testing.T) { UserAutostartEnabled: false, UserAutostopEnabled: c.templateAllowAutostop, DefaultTTL: c.templateDefaultTTL, - MaxTTL: c.templateMaxTTL, - UseMaxTTL: c.useMaxTTL, AutostopRequirement: c.templateAutostopRequirement, }, nil }, diff --git a/coderd/schedule/template.go b/coderd/schedule/template.go index b4fa3efd04..b326dd336a 100644 --- a/coderd/schedule/template.go +++ b/coderd/schedule/template.go @@ -120,12 +120,6 @@ type TemplateScheduleOptions struct { // ActivityBump dictates the duration to bump the workspace's deadline by if // Coder detects activity from the user. A value of 0 means no bumping. ActivityBump time.Duration `json:"activity_bump"` - MaxTTL time.Duration `json:"max_ttl"` - // UseMaxTTL dictates whether the max_ttl should be used instead of - // autostop_requirement for this template. This is governed by the template - // and licensing. - // TODO(@dean): remove this when we remove max_tll - UseMaxTTL bool // AutostopRequirement dictates when the workspace must be restarted. This // used to be handled by MaxTTL. AutostopRequirement TemplateAutostopRequirement `json:"autostop_requirement"` @@ -187,8 +181,6 @@ func (*agplTemplateScheduleStore) Get(ctx context.Context, db database.Store, te ActivityBump: time.Duration(tpl.ActivityBump), // Disregard the values in the database, since AutostopRequirement, // FailureTTL, TimeTilDormant, and TimeTilDormantAutoDelete are enterprise features. - UseMaxTTL: false, - MaxTTL: 0, AutostartRequirement: TemplateAutostartRequirement{ // Default to allowing all days for AGPL DaysOfWeek: 0b01111111, @@ -223,8 +215,6 @@ func (*agplTemplateScheduleStore) Set(ctx context.Context, db database.Store, tp ActivityBump: int64(opts.ActivityBump), // Don't allow changing these settings, but keep the value in the DB (to // avoid clearing settings if the license has an issue). - UseMaxTtl: tpl.UseMaxTtl, - MaxTTL: tpl.MaxTTL, AutostopRequirementDaysOfWeek: tpl.AutostopRequirementDaysOfWeek, AutostopRequirementWeeks: tpl.AutostopRequirementWeeks, AutostartBlockDaysOfWeek: tpl.AutostartBlockDaysOfWeek, diff --git a/coderd/telemetry/telemetry.go b/coderd/telemetry/telemetry.go index 8a43c40070..ccb08329b8 100644 --- a/coderd/telemetry/telemetry.go +++ b/coderd/telemetry/telemetry.go @@ -673,8 +673,6 @@ func ConvertTemplate(dbTemplate database.Template) Template { // Some of these fields are meant to be accessed using a specialized // interface (for entitlement purposes), but for telemetry purposes // there's minimal harm accessing them directly. - UseMaxTTL: dbTemplate.UseMaxTtl, - MaxTTLMillis: time.Duration(dbTemplate.MaxTTL).Milliseconds(), DefaultTTLMillis: time.Duration(dbTemplate.DefaultTTL).Milliseconds(), AllowUserCancelWorkspaceJobs: dbTemplate.AllowUserCancelWorkspaceJobs, AllowUserAutostart: dbTemplate.AllowUserAutostart, @@ -919,8 +917,6 @@ type Template struct { Name string `json:"name"` Description bool `json:"description"` - UseMaxTTL bool `json:"use_max_ttl"` - MaxTTLMillis int64 `json:"max_ttl_ms"` DefaultTTLMillis int64 `json:"default_ttl_ms"` AllowUserCancelWorkspaceJobs bool `json:"allow_user_cancel_workspace_jobs"` AllowUserAutostart bool `json:"allow_user_autostart"` diff --git a/coderd/templates.go b/coderd/templates.go index f8631917ed..d1afbbf22e 100644 --- a/coderd/templates.go +++ b/coderd/templates.go @@ -228,7 +228,6 @@ func (api *API) postTemplateByOrganization(rw http.ResponseWriter, r *http.Reque var ( defaultTTL time.Duration activityBump = time.Hour // default - maxTTL time.Duration autostopRequirementDaysOfWeek []string autostartRequirementDaysOfWeek []string autostopRequirementWeeks int64 @@ -273,12 +272,7 @@ func (api *API) postTemplateByOrganization(rw http.ResponseWriter, r *http.Reque if activityBump < 0 { validErrs = append(validErrs, codersdk.ValidationError{Field: "activity_bump_ms", Detail: "Must be a positive integer."}) } - if maxTTL < 0 { - validErrs = append(validErrs, codersdk.ValidationError{Field: "max_ttl_ms", Detail: "Must be a positive integer."}) - } - if maxTTL != 0 && defaultTTL > maxTTL { - validErrs = append(validErrs, codersdk.ValidationError{Field: "default_ttl_ms", Detail: "Must be less than or equal to max_ttl_ms if max_ttl_ms is set."}) - } + if len(autostopRequirementDaysOfWeek) > 0 { autostopRequirementDaysOfWeekParsed, err = codersdk.WeekdaysToBitmap(autostopRequirementDaysOfWeek) if err != nil { @@ -291,12 +285,7 @@ func (api *API) postTemplateByOrganization(rw http.ResponseWriter, r *http.Reque validErrs = append(validErrs, codersdk.ValidationError{Field: "autostart_requirement.days_of_week", Detail: err.Error()}) } } - if createTemplate.MaxTTLMillis != nil { - maxTTL = time.Duration(*createTemplate.MaxTTLMillis) * time.Millisecond - } - if maxTTL != 0 && len(autostopRequirementDaysOfWeek) > 0 { - validErrs = append(validErrs, codersdk.ValidationError{Field: "autostop_requirement.days_of_week", Detail: "Cannot be set if max_ttl_ms is set."}) - } + if autostopRequirementWeeks < 0 { validErrs = append(validErrs, codersdk.ValidationError{Field: "autostop_requirement.weeks", Detail: "Must be a positive integer."}) } @@ -376,10 +365,8 @@ func (api *API) postTemplateByOrganization(rw http.ResponseWriter, r *http.Reque dbTemplate, err = (*api.TemplateScheduleStore.Load()).Set(ctx, tx, dbTemplate, schedule.TemplateScheduleOptions{ UserAutostartEnabled: allowUserAutostart, UserAutostopEnabled: allowUserAutostop, - UseMaxTTL: maxTTL > 0, DefaultTTL: defaultTTL, ActivityBump: activityBump, - MaxTTL: maxTTL, // Some of these values are enterprise-only, but the // TemplateScheduleStore will handle avoiding setting them if // unlicensed. @@ -580,16 +567,7 @@ func (api *API) patchTemplateMeta(rw http.ResponseWriter, r *http.Request) { if req.ActivityBumpMillis < 0 { validErrs = append(validErrs, codersdk.ValidationError{Field: "activity_bump_ms", Detail: "Must be a positive integer."}) } - if req.MaxTTLMillis < 0 { - validErrs = append(validErrs, codersdk.ValidationError{Field: "max_ttl_ms", Detail: "Must be a positive integer."}) - } - if req.MaxTTLMillis != 0 && req.DefaultTTLMillis > req.MaxTTLMillis { - validErrs = append(validErrs, codersdk.ValidationError{Field: "default_ttl_ms", Detail: "Must be less than or equal to max_ttl_ms if max_ttl_ms is set."}) - } - if req.MaxTTLMillis != 0 && req.AutostopRequirement != nil && len(req.AutostopRequirement.DaysOfWeek) > 0 { - validErrs = append(validErrs, codersdk.ValidationError{Field: "autostop_requirement.days_of_week", Detail: "Cannot be set if max_ttl_ms is set."}) - } - useMaxTTL := req.MaxTTLMillis > 0 + if req.AutostopRequirement == nil { req.AutostopRequirement = &codersdk.TemplateAutostopRequirement{ DaysOfWeek: codersdk.BitmapToWeekdays(scheduleOpts.AutostopRequirement.DaysOfWeek), @@ -673,8 +651,6 @@ func (api *API) patchTemplateMeta(rw http.ResponseWriter, r *http.Request) { req.AllowUserCancelWorkspaceJobs == template.AllowUserCancelWorkspaceJobs && req.DefaultTTLMillis == time.Duration(template.DefaultTTL).Milliseconds() && req.ActivityBumpMillis == time.Duration(template.ActivityBump).Milliseconds() && - useMaxTTL == scheduleOpts.UseMaxTTL && - req.MaxTTLMillis == time.Duration(template.MaxTTL).Milliseconds() && autostopRequirementDaysOfWeekParsed == scheduleOpts.AutostopRequirement.DaysOfWeek && autostartRequirementDaysOfWeekParsed == scheduleOpts.AutostartRequirement.DaysOfWeek && req.AutostopRequirement.Weeks == scheduleOpts.AutostopRequirement.Weeks && @@ -746,15 +722,12 @@ func (api *API) patchTemplateMeta(rw http.ResponseWriter, r *http.Request) { defaultTTL := time.Duration(req.DefaultTTLMillis) * time.Millisecond activityBump := time.Duration(req.ActivityBumpMillis) * time.Millisecond - maxTTL := time.Duration(req.MaxTTLMillis) * time.Millisecond failureTTL := time.Duration(req.FailureTTLMillis) * time.Millisecond inactivityTTL := time.Duration(req.TimeTilDormantMillis) * time.Millisecond timeTilDormantAutoDelete := time.Duration(req.TimeTilDormantAutoDeleteMillis) * time.Millisecond if defaultTTL != time.Duration(template.DefaultTTL) || activityBump != time.Duration(template.ActivityBump) || - useMaxTTL != scheduleOpts.UseMaxTTL || - maxTTL != time.Duration(template.MaxTTL) || autostopRequirementDaysOfWeekParsed != scheduleOpts.AutostopRequirement.DaysOfWeek || autostartRequirementDaysOfWeekParsed != scheduleOpts.AutostartRequirement.DaysOfWeek || req.AutostopRequirement.Weeks != scheduleOpts.AutostopRequirement.Weeks || @@ -771,8 +744,6 @@ func (api *API) patchTemplateMeta(rw http.ResponseWriter, r *http.Request) { UserAutostopEnabled: req.AllowUserAutostop, DefaultTTL: defaultTTL, ActivityBump: activityBump, - UseMaxTTL: useMaxTTL, - MaxTTL: maxTTL, AutostopRequirement: schedule.TemplateAutostopRequirement{ DaysOfWeek: autostopRequirementDaysOfWeekParsed, Weeks: req.AutostopRequirement.Weeks, @@ -921,8 +892,6 @@ func (api *API) convertTemplate( Icon: template.Icon, DefaultTTLMillis: time.Duration(template.DefaultTTL).Milliseconds(), ActivityBumpMillis: time.Duration(template.ActivityBump).Milliseconds(), - UseMaxTTL: template.UseMaxTtl, - MaxTTLMillis: time.Duration(template.MaxTTL).Milliseconds(), CreatedByID: template.CreatedBy, CreatedByName: template.CreatedByUsername, AllowUserAutostart: template.AllowUserAutostart, diff --git a/coderd/templates_test.go b/coderd/templates_test.go index c7366d4f9b..13501274f5 100644 --- a/coderd/templates_test.go +++ b/coderd/templates_test.go @@ -273,8 +273,6 @@ func TestPostTemplateByOrganization(t *testing.T) { AllowUserAutostop: options.UserAutostopEnabled, DefaultTTL: int64(options.DefaultTTL), ActivityBump: int64(options.ActivityBump), - UseMaxTtl: options.UseMaxTTL, - MaxTTL: int64(options.MaxTTL), AutostopRequirementDaysOfWeek: int16(options.AutostopRequirement.DaysOfWeek), AutostopRequirementWeeks: options.AutostopRequirement.Weeks, FailureTTL: int64(options.FailureTTL), @@ -302,7 +300,6 @@ func TestPostTemplateByOrganization(t *testing.T) { }) require.NoError(t, err) - require.False(t, got.UseMaxTTL) // default require.EqualValues(t, 1, atomic.LoadInt64(&setCalled)) require.Empty(t, got.AutostopRequirement.DaysOfWeek) require.EqualValues(t, 1, got.AutostopRequirement.Weeks) @@ -326,8 +323,6 @@ func TestPostTemplateByOrganization(t *testing.T) { AllowUserAutostop: options.UserAutostopEnabled, DefaultTTL: int64(options.DefaultTTL), ActivityBump: int64(options.ActivityBump), - UseMaxTtl: options.UseMaxTTL, - MaxTTL: int64(options.MaxTTL), AutostopRequirementDaysOfWeek: int16(options.AutostopRequirement.DaysOfWeek), AutostopRequirementWeeks: options.AutostopRequirement.Weeks, FailureTTL: int64(options.FailureTTL), @@ -360,13 +355,11 @@ func TestPostTemplateByOrganization(t *testing.T) { require.NoError(t, err) require.EqualValues(t, 1, atomic.LoadInt64(&setCalled)) - require.False(t, got.UseMaxTTL) require.Equal(t, []string{"friday", "saturday"}, got.AutostopRequirement.DaysOfWeek) require.EqualValues(t, 2, got.AutostopRequirement.Weeks) got, err = client.Template(ctx, got.ID) require.NoError(t, err) - require.False(t, got.UseMaxTTL) require.Equal(t, []string{"friday", "saturday"}, got.AutostopRequirement.DaysOfWeek) require.EqualValues(t, 2, got.AutostopRequirement.Weeks) }) @@ -391,36 +384,10 @@ func TestPostTemplateByOrganization(t *testing.T) { }) require.NoError(t, err) // ignored and use AGPL defaults - require.False(t, got.UseMaxTTL) require.Empty(t, got.AutostopRequirement.DaysOfWeek) require.EqualValues(t, 1, got.AutostopRequirement.Weeks) }) }) - - t.Run("BothMaxTTLAndAutostopRequirement", func(t *testing.T) { - t.Parallel() - - // Fake template schedule store is unneeded for this test since the - // route fails before it is called. - client := coderdtest.New(t, nil) - user := coderdtest.CreateFirstUser(t, client) - version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, nil) - - ctx, cancel := context.WithTimeout(context.Background(), testutil.WaitLong) - defer cancel() - - _, err := client.CreateTemplate(ctx, user.OrganizationID, codersdk.CreateTemplateRequest{ - Name: "testing", - VersionID: version.ID, - MaxTTLMillis: ptr.Ref(24 * time.Hour.Milliseconds()), - AutostopRequirement: &codersdk.TemplateAutostopRequirement{ - DaysOfWeek: []string{"friday", "saturday"}, - Weeks: 2, - }, - }) - require.Error(t, err) - require.ErrorContains(t, err, "max_ttl_ms") - }) } func TestTemplatesByOrganization(t *testing.T) { @@ -722,136 +689,6 @@ func TestPatchTemplateMeta(t *testing.T) { assert.False(t, updated.Deprecated) }) - t.Run("MaxTTL", func(t *testing.T) { - t.Parallel() - - const ( - defaultTTL = 1 * time.Hour - maxTTL = 24 * time.Hour - ) - - t.Run("OK", func(t *testing.T) { - t.Parallel() - - var setCalled int64 - client := coderdtest.New(t, &coderdtest.Options{ - TemplateScheduleStore: schedule.MockTemplateScheduleStore{ - SetFn: func(ctx context.Context, db database.Store, template database.Template, options schedule.TemplateScheduleOptions) (database.Template, error) { - if atomic.AddInt64(&setCalled, 1) == 2 { - require.Equal(t, maxTTL, options.MaxTTL) - } - - err := db.UpdateTemplateScheduleByID(ctx, database.UpdateTemplateScheduleByIDParams{ - ID: template.ID, - UpdatedAt: dbtime.Now(), - AllowUserAutostart: options.UserAutostartEnabled, - AllowUserAutostop: options.UserAutostopEnabled, - DefaultTTL: int64(options.DefaultTTL), - ActivityBump: int64(options.ActivityBump), - MaxTTL: int64(options.MaxTTL), - UseMaxTtl: options.UseMaxTTL, - AutostopRequirementDaysOfWeek: int16(options.AutostopRequirement.DaysOfWeek), - AutostopRequirementWeeks: options.AutostopRequirement.Weeks, - FailureTTL: int64(options.FailureTTL), - TimeTilDormant: int64(options.TimeTilDormant), - TimeTilDormantAutoDelete: int64(options.TimeTilDormantAutoDelete), - }) - if !assert.NoError(t, err) { - return database.Template{}, err - } - - return db.GetTemplateByID(ctx, template.ID) - }, - }, - }) - 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.DefaultTTLMillis = ptr.Ref(24 * time.Hour.Milliseconds()) - }) - - ctx, cancel := context.WithTimeout(context.Background(), testutil.WaitLong) - defer cancel() - - got, err := client.UpdateTemplateMeta(ctx, template.ID, codersdk.UpdateTemplateMeta{ - Name: template.Name, - DisplayName: template.DisplayName, - Description: template.Description, - Icon: template.Icon, - DefaultTTLMillis: 0, - MaxTTLMillis: maxTTL.Milliseconds(), - AllowUserCancelWorkspaceJobs: template.AllowUserCancelWorkspaceJobs, - }) - require.NoError(t, err) - - require.EqualValues(t, 2, atomic.LoadInt64(&setCalled)) - require.EqualValues(t, 0, got.DefaultTTLMillis) - require.Equal(t, maxTTL.Milliseconds(), got.MaxTTLMillis) - require.Empty(t, got.DeprecationMessage) - require.False(t, got.Deprecated) - }) - - t.Run("DefaultTTLBigger", func(t *testing.T) { - t.Parallel() - - client := coderdtest.New(t, 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.DefaultTTLMillis = ptr.Ref(24 * time.Hour.Milliseconds()) - }) - - ctx, cancel := context.WithTimeout(context.Background(), testutil.WaitLong) - defer cancel() - - _, err := client.UpdateTemplateMeta(ctx, template.ID, codersdk.UpdateTemplateMeta{ - Name: template.Name, - DisplayName: template.DisplayName, - Description: template.Description, - Icon: template.Icon, - DefaultTTLMillis: (maxTTL * 2).Milliseconds(), - MaxTTLMillis: maxTTL.Milliseconds(), - AllowUserCancelWorkspaceJobs: template.AllowUserCancelWorkspaceJobs, - }) - require.Error(t, err) - var sdkErr *codersdk.Error - require.ErrorAs(t, err, &sdkErr) - require.Equal(t, http.StatusBadRequest, sdkErr.StatusCode()) - require.Len(t, sdkErr.Validations, 1) - require.Equal(t, "default_ttl_ms", sdkErr.Validations[0].Field) - require.Contains(t, sdkErr.Validations[0].Detail, "Must be less than or equal to max_ttl_ms") - }) - - t.Run("IgnoredUnlicensed", func(t *testing.T) { - t.Parallel() - - client := coderdtest.New(t, 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.DefaultTTLMillis = ptr.Ref(24 * time.Hour.Milliseconds()) - }) - - ctx, cancel := context.WithTimeout(context.Background(), testutil.WaitLong) - defer cancel() - - got, err := client.UpdateTemplateMeta(ctx, template.ID, codersdk.UpdateTemplateMeta{ - Name: template.Name, - DisplayName: template.DisplayName, - Description: template.Description, - Icon: template.Icon, - DefaultTTLMillis: defaultTTL.Milliseconds(), - MaxTTLMillis: maxTTL.Milliseconds(), - AllowUserCancelWorkspaceJobs: template.AllowUserCancelWorkspaceJobs, - }) - require.NoError(t, err) - require.Equal(t, defaultTTL.Milliseconds(), got.DefaultTTLMillis) - require.Zero(t, got.MaxTTLMillis) - require.Empty(t, got.DeprecationMessage) - require.False(t, got.Deprecated) - }) - }) - t.Run("CleanupTTLs", func(t *testing.T) { t.Parallel() @@ -1148,8 +985,6 @@ func TestPatchTemplateMeta(t *testing.T) { AllowUserAutostop: options.UserAutostopEnabled, DefaultTTL: int64(options.DefaultTTL), ActivityBump: int64(options.ActivityBump), - UseMaxTtl: options.UseMaxTTL, - MaxTTL: int64(options.MaxTTL), AutostopRequirementDaysOfWeek: int16(options.AutostopRequirement.DaysOfWeek), AutostopRequirementWeeks: options.AutostopRequirement.Weeks, FailureTTL: int64(options.FailureTTL), @@ -1221,8 +1056,6 @@ func TestPatchTemplateMeta(t *testing.T) { AllowUserAutostop: options.UserAutostopEnabled, DefaultTTL: int64(options.DefaultTTL), ActivityBump: int64(options.ActivityBump), - UseMaxTtl: options.UseMaxTTL, - MaxTTL: int64(options.MaxTTL), AutostopRequirementDaysOfWeek: int16(options.AutostopRequirement.DaysOfWeek), AutostopRequirementWeeks: options.AutostopRequirement.Weeks, FailureTTL: int64(options.FailureTTL), @@ -1316,38 +1149,6 @@ func TestPatchTemplateMeta(t *testing.T) { require.False(t, template.Deprecated) }) }) - - t.Run("BothMaxTTLAndAutostopRequirement", func(t *testing.T) { - t.Parallel() - - // Fake template schedule store is unneeded for this test since the - // route fails before it is called. - client := coderdtest.New(t, nil) - user := coderdtest.CreateFirstUser(t, client) - version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, nil) - template := coderdtest.CreateTemplate(t, client, user.OrganizationID, version.ID) - - req := codersdk.UpdateTemplateMeta{ - Name: template.Name, - DisplayName: template.DisplayName, - Description: template.Description, - Icon: template.Icon, - AllowUserCancelWorkspaceJobs: template.AllowUserCancelWorkspaceJobs, - DefaultTTLMillis: time.Hour.Milliseconds(), - MaxTTLMillis: time.Hour.Milliseconds(), - AutostopRequirement: &codersdk.TemplateAutostopRequirement{ - DaysOfWeek: []string{"monday"}, - Weeks: 2, - }, - } - - ctx, cancel := context.WithTimeout(context.Background(), testutil.WaitLong) - defer cancel() - - _, err := client.UpdateTemplateMeta(ctx, template.ID, req) - require.Error(t, err) - require.ErrorContains(t, err, "max_ttl_ms") - }) } func TestDeleteTemplate(t *testing.T) { diff --git a/coderd/workspaces.go b/coderd/workspaces.go index 05bf9688e7..b4f5ab8621 100644 --- a/coderd/workspaces.go +++ b/coderd/workspaces.go @@ -480,13 +480,7 @@ func (api *API) postWorkspacesByOrganization(rw http.ResponseWriter, r *http.Req return } - maxTTL := templateSchedule.MaxTTL - if !templateSchedule.UseMaxTTL { - // If we're using autostop requirements, there isn't a max TTL. - maxTTL = 0 - } - - dbTTL, err := validWorkspaceTTLMillis(createWorkspace.TTLMillis, templateSchedule.DefaultTTL, maxTTL) + dbTTL, err := validWorkspaceTTLMillis(createWorkspace.TTLMillis, templateSchedule.DefaultTTL) if err != nil { httpapi.Write(ctx, rw, http.StatusBadRequest, codersdk.Response{ Message: "Invalid Workspace Time to Shutdown.", @@ -857,16 +851,10 @@ func (api *API) putWorkspaceTTL(rw http.ResponseWriter, r *http.Request) { return codersdk.ValidationError{Field: "ttl_ms", Detail: "Custom autostop TTL is not allowed for workspaces using this template."} } - maxTTL := templateSchedule.MaxTTL - if !templateSchedule.UseMaxTTL { - // If we're using autostop requirements, there isn't a max TTL. - maxTTL = 0 - } - // don't override 0 ttl with template default here because it indicates // disabled autostop var validityErr error - dbTTL, validityErr = validWorkspaceTTLMillis(req.TTLMillis, 0, maxTTL) + dbTTL, validityErr = validWorkspaceTTLMillis(req.TTLMillis, 0) if validityErr != nil { return codersdk.ValidationError{Field: "ttl_ms", Detail: validityErr.Error()} } @@ -1685,20 +1673,9 @@ func convertWorkspaceTTLMillis(i sql.NullInt64) *int64 { return &millis } -func validWorkspaceTTLMillis(millis *int64, templateDefault, templateMax time.Duration) (sql.NullInt64, error) { - if templateDefault == 0 && templateMax != 0 || (templateMax > 0 && templateDefault > templateMax) { - templateDefault = templateMax - } - +func validWorkspaceTTLMillis(millis *int64, templateDefault time.Duration) (sql.NullInt64, error) { if ptr.NilOrZero(millis) { if templateDefault == 0 { - if templateMax > 0 { - return sql.NullInt64{ - Int64: int64(templateMax), - Valid: true, - }, nil - } - return sql.NullInt64{}, nil } @@ -1718,10 +1695,6 @@ func validWorkspaceTTLMillis(millis *int64, templateDefault, templateMax time.Du return sql.NullInt64{}, errTTLMax } - if templateMax > 0 && truncated > templateMax { - return sql.NullInt64{}, xerrors.Errorf("time until shutdown must be less than or equal to the template's maximum TTL %q", templateMax.String()) - } - return sql.NullInt64{ Valid: true, Int64: int64(truncated), diff --git a/codersdk/deployment.go b/codersdk/deployment.go index d9de39490f..512e1fd24c 100644 --- a/codersdk/deployment.go +++ b/codersdk/deployment.go @@ -543,7 +543,7 @@ when required by your organization's security policy.`, } deploymentGroupUserQuietHoursSchedule = serpent.Group{ Name: "User Quiet Hours Schedule", - Description: "Allow users to set quiet hours schedules each day for workspaces to avoid workspaces stopping during the day due to template max TTL.", + Description: "Allow users to set quiet hours schedules each day for workspaces to avoid workspaces stopping during the day due to template scheduling.", YAML: "userQuietHoursSchedule", } deploymentGroupDangerous = serpent.Group{ diff --git a/codersdk/organizations.go b/codersdk/organizations.go index 88a710f642..a6a1b927ca 100644 --- a/codersdk/organizations.go +++ b/codersdk/organizations.go @@ -92,12 +92,8 @@ type CreateTemplateRequest struct { // duration for all workspaces created from this template. Defaults to 1h // but can be set to 0 to disable activity bumping. ActivityBumpMillis *int64 `json:"activity_bump_ms,omitempty"` - // TODO(@dean): remove max_ttl once autostop_requirement is matured - // Only one of MaxTTLMillis or AutostopRequirement can be specified. - MaxTTLMillis *int64 `json:"max_ttl_ms,omitempty"` // AutostopRequirement allows optionally specifying the autostop requirement // for workspaces created from this template. This is an enterprise feature. - // Only one of MaxTTLMillis or AutostopRequirement can be specified. AutostopRequirement *TemplateAutostopRequirement `json:"autostop_requirement,omitempty"` // AutostartRequirement allows optionally specifying the autostart allowed days // for workspaces created from this template. This is an enterprise feature. diff --git a/codersdk/templates.go b/codersdk/templates.go index 76fe9f4a4e..541eb4eaca 100644 --- a/codersdk/templates.go +++ b/codersdk/templates.go @@ -32,11 +32,6 @@ type Template struct { Icon string `json:"icon"` DefaultTTLMillis int64 `json:"default_ttl_ms"` ActivityBumpMillis int64 `json:"activity_bump_ms"` - // UseMaxTTL picks whether to use the deprecated max TTL for the template or - // the new autostop requirement. - UseMaxTTL bool `json:"use_max_ttl"` - // TODO(@dean): remove max_ttl once autostop_requirement is matured - MaxTTLMillis int64 `json:"max_ttl_ms"` // AutostopRequirement and AutostartRequirement are enterprise features. Its // value is only used if your license is entitled to use the advanced template // scheduling feature. @@ -214,13 +209,9 @@ type UpdateTemplateMeta struct { // duration for all workspaces created from this template. Defaults to 1h // but can be set to 0 to disable activity bumping. ActivityBumpMillis int64 `json:"activity_bump_ms,omitempty"` - // TODO(@dean): remove max_ttl once autostop_requirement is matured - // Only one of MaxTTLMillis or AutostopRequirement can be specified. - MaxTTLMillis int64 `json:"max_ttl_ms,omitempty"` // AutostopRequirement and AutostartRequirement can only be set if your license // includes the advanced template scheduling feature. If you attempt to set this // value while unlicensed, it will be ignored. - // Only one of MaxTTLMillis or AutostopRequirement can be specified. AutostopRequirement *TemplateAutostopRequirement `json:"autostop_requirement,omitempty"` AutostartRequirement *TemplateAutostartRequirement `json:"autostart_requirement,omitempty"` AllowUserAutostart bool `json:"allow_user_autostart,omitempty"` diff --git a/codersdk/users.go b/codersdk/users.go index 1d50df1472..9fe6d8eb65 100644 --- a/codersdk/users.go +++ b/codersdk/users.go @@ -160,7 +160,7 @@ type UpdateUserQuietHoursScheduleRequest struct { // window is. Schedule must not be empty. For new users, the schedule is set // to 2am in their browser or computer's timezone. The schedule denotes the // beginning of a 4 hour window where the workspace is allowed to - // automatically stop or restart due to maintenance or template max TTL. + // automatically stop or restart due to maintenance or template schedule. // // The schedule must be daily with a single time, and should have a timezone // specified via a CRON_TZ prefix (otherwise UTC will be used). diff --git a/docs/admin/audit-logs.md b/docs/admin/audit-logs.md index 666b8d3d3f..fada57f320 100644 --- a/docs/admin/audit-logs.md +++ b/docs/admin/audit-logs.md @@ -8,22 +8,22 @@ We track the following resources: -| Resource | | -| -------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | -| APIKey
login, logout, register, create, delete |
FieldTracked
created_attrue
expires_attrue
hashed_secretfalse
idfalse
ip_addressfalse
last_usedtrue
lifetime_secondsfalse
login_typefalse
scopefalse
token_namefalse
updated_atfalse
user_idtrue
| -| AuditOAuthConvertState
|
FieldTracked
created_attrue
expires_attrue
from_login_typetrue
to_login_typetrue
user_idtrue
| -| Group
create, write, delete |
FieldTracked
avatar_urltrue
display_nametrue
idtrue
memberstrue
nametrue
organization_idfalse
quota_allowancetrue
sourcefalse
| -| GitSSHKey
create |
FieldTracked
created_atfalse
private_keytrue
public_keytrue
updated_atfalse
user_idtrue
| -| HealthSettings
|
FieldTracked
dismissed_healthcheckstrue
idfalse
| -| License
create, delete |
FieldTracked
exptrue
idfalse
jwtfalse
uploaded_attrue
uuidtrue
| -| OAuth2ProviderApp
|
FieldTracked
callback_urltrue
created_atfalse
icontrue
idfalse
nametrue
updated_atfalse
| -| OAuth2ProviderAppSecret
|
FieldTracked
app_idfalse
created_atfalse
display_secretfalse
hashed_secretfalse
idfalse
last_used_atfalse
secret_prefixfalse
| -| Template
write, delete |
FieldTracked
active_version_idtrue
activity_bumptrue
allow_user_autostarttrue
allow_user_autostoptrue
allow_user_cancel_workspace_jobstrue
autostart_block_days_of_weektrue
autostop_requirement_days_of_weektrue
autostop_requirement_weekstrue
created_atfalse
created_bytrue
created_by_avatar_urlfalse
created_by_usernamefalse
default_ttltrue
deletedfalse
deprecatedtrue
descriptiontrue
display_nametrue
failure_ttltrue
group_acltrue
icontrue
idtrue
max_port_sharing_leveltrue
max_ttltrue
nametrue
organization_idfalse
provisionertrue
require_active_versiontrue
time_til_dormanttrue
time_til_dormant_autodeletetrue
updated_atfalse
use_max_ttltrue
user_acltrue
| -| TemplateVersion
create, write |
FieldTracked
archivedtrue
created_atfalse
created_bytrue
created_by_avatar_urlfalse
created_by_usernamefalse
external_auth_providersfalse
idtrue
job_idfalse
messagefalse
nametrue
organization_idfalse
readmetrue
template_idtrue
updated_atfalse
| -| User
create, write, delete |
FieldTracked
avatar_urlfalse
created_atfalse
deletedtrue
emailtrue
hashed_passwordtrue
idtrue
last_seen_atfalse
login_typetrue
nametrue
quiet_hours_scheduletrue
rbac_rolestrue
statustrue
theme_preferencefalse
updated_atfalse
usernametrue
| -| Workspace
create, write, delete |
FieldTracked
automatic_updatestrue
autostart_scheduletrue
created_atfalse
deletedfalse
deleting_attrue
dormant_attrue
favoritetrue
idtrue
last_used_atfalse
nametrue
organization_idfalse
owner_idtrue
template_idtrue
ttltrue
updated_atfalse
| -| WorkspaceBuild
start, stop |
FieldTracked
build_numberfalse
created_atfalse
daily_costfalse
deadlinefalse
idfalse
initiator_by_avatar_urlfalse
initiator_by_usernamefalse
initiator_idfalse
job_idfalse
max_deadlinefalse
provisioner_statefalse
reasonfalse
template_version_idtrue
transitionfalse
updated_atfalse
workspace_idfalse
| -| WorkspaceProxy
|
FieldTracked
created_attrue
deletedfalse
derp_enabledtrue
derp_onlytrue
display_nametrue
icontrue
idtrue
nametrue
region_idtrue
token_hashed_secrettrue
updated_atfalse
urltrue
versiontrue
wildcard_hostnametrue
| +| Resource | | +| -------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| APIKey
login, logout, register, create, delete |
FieldTracked
created_attrue
expires_attrue
hashed_secretfalse
idfalse
ip_addressfalse
last_usedtrue
lifetime_secondsfalse
login_typefalse
scopefalse
token_namefalse
updated_atfalse
user_idtrue
| +| AuditOAuthConvertState
|
FieldTracked
created_attrue
expires_attrue
from_login_typetrue
to_login_typetrue
user_idtrue
| +| Group
create, write, delete |
FieldTracked
avatar_urltrue
display_nametrue
idtrue
memberstrue
nametrue
organization_idfalse
quota_allowancetrue
sourcefalse
| +| GitSSHKey
create |
FieldTracked
created_atfalse
private_keytrue
public_keytrue
updated_atfalse
user_idtrue
| +| HealthSettings
|
FieldTracked
dismissed_healthcheckstrue
idfalse
| +| License
create, delete |
FieldTracked
exptrue
idfalse
jwtfalse
uploaded_attrue
uuidtrue
| +| OAuth2ProviderApp
|
FieldTracked
callback_urltrue
created_atfalse
icontrue
idfalse
nametrue
updated_atfalse
| +| OAuth2ProviderAppSecret
|
FieldTracked
app_idfalse
created_atfalse
display_secretfalse
hashed_secretfalse
idfalse
last_used_atfalse
secret_prefixfalse
| +| Template
write, delete |
FieldTracked
active_version_idtrue
activity_bumptrue
allow_user_autostarttrue
allow_user_autostoptrue
allow_user_cancel_workspace_jobstrue
autostart_block_days_of_weektrue
autostop_requirement_days_of_weektrue
autostop_requirement_weekstrue
created_atfalse
created_bytrue
created_by_avatar_urlfalse
created_by_usernamefalse
default_ttltrue
deletedfalse
deprecatedtrue
descriptiontrue
display_nametrue
failure_ttltrue
group_acltrue
icontrue
idtrue
max_port_sharing_leveltrue
nametrue
organization_idfalse
provisionertrue
require_active_versiontrue
time_til_dormanttrue
time_til_dormant_autodeletetrue
updated_atfalse
user_acltrue
| +| TemplateVersion
create, write |
FieldTracked
archivedtrue
created_atfalse
created_bytrue
created_by_avatar_urlfalse
created_by_usernamefalse
external_auth_providersfalse
idtrue
job_idfalse
messagefalse
nametrue
organization_idfalse
readmetrue
template_idtrue
updated_atfalse
| +| User
create, write, delete |
FieldTracked
avatar_urlfalse
created_atfalse
deletedtrue
emailtrue
hashed_passwordtrue
idtrue
last_seen_atfalse
login_typetrue
nametrue
quiet_hours_scheduletrue
rbac_rolestrue
statustrue
theme_preferencefalse
updated_atfalse
usernametrue
| +| Workspace
create, write, delete |
FieldTracked
automatic_updatestrue
autostart_scheduletrue
created_atfalse
deletedfalse
deleting_attrue
dormant_attrue
favoritetrue
idtrue
last_used_atfalse
nametrue
organization_idfalse
owner_idtrue
template_idtrue
ttltrue
updated_atfalse
| +| WorkspaceBuild
start, stop |
FieldTracked
build_numberfalse
created_atfalse
daily_costfalse
deadlinefalse
idfalse
initiator_by_avatar_urlfalse
initiator_by_usernamefalse
initiator_idfalse
job_idfalse
max_deadlinefalse
provisioner_statefalse
reasonfalse
template_version_idtrue
transitionfalse
updated_atfalse
workspace_idfalse
| +| WorkspaceProxy
|
FieldTracked
created_attrue
deletedfalse
derp_enabledtrue
derp_onlytrue
display_nametrue
icontrue
idtrue
nametrue
region_idtrue
token_hashed_secrettrue
updated_atfalse
urltrue
versiontrue
wildcard_hostnametrue
| diff --git a/docs/api/schemas.md b/docs/api/schemas.md index 4c77b11383..02cba3ec2c 100644 --- a/docs/api/schemas.md +++ b/docs/api/schemas.md @@ -1414,7 +1414,6 @@ AuthorizationObject can represent a "set" of objects, such as: all workspaces in "dormant_ttl_ms": 0, "failure_ttl_ms": 0, "icon": "string", - "max_ttl_ms": 0, "name": "string", "require_active_version": true, "template_version_id": "0ba39c92-1f1b-4c32-aa3e-9925d7713eb1" @@ -1430,7 +1429,7 @@ AuthorizationObject can represent a "set" of objects, such as: all workspaces in | `allow_user_autostop` | boolean | false | | Allow user autostop allows users to set a custom workspace TTL to use in place of the template's DefaultTTL field. By default this is true. If false, the DefaultTTL will always be used. This can only be disabled when using an enterprise license. | | `allow_user_cancel_workspace_jobs` | boolean | false | | Allow users to cancel in-progress workspace jobs. \*bool as the default value is "true". | | `autostart_requirement` | [codersdk.TemplateAutostartRequirement](#codersdktemplateautostartrequirement) | false | | Autostart requirement allows optionally specifying the autostart allowed days for workspaces created from this template. This is an enterprise feature. | -| `autostop_requirement` | [codersdk.TemplateAutostopRequirement](#codersdktemplateautostoprequirement) | false | | Autostop requirement allows optionally specifying the autostop requirement for workspaces created from this template. This is an enterprise feature. Only one of MaxTTLMillis or AutostopRequirement can be specified. | +| `autostop_requirement` | [codersdk.TemplateAutostopRequirement](#codersdktemplateautostoprequirement) | false | | Autostop requirement allows optionally specifying the autostop requirement for workspaces created from this template. This is an enterprise feature. | | `default_ttl_ms` | integer | false | | Default ttl ms allows optionally specifying the default TTL for all workspaces created from this template. | | `delete_ttl_ms` | integer | false | | Delete ttl ms allows optionally specifying the max lifetime before Coder permanently deletes dormant workspaces created from this template. | | `description` | string | false | | Description is a description of what the template contains. It must be less than 128 bytes. | @@ -1439,7 +1438,6 @@ AuthorizationObject can represent a "set" of objects, such as: all workspaces in | `dormant_ttl_ms` | integer | false | | Dormant ttl ms allows optionally specifying the max lifetime before Coder locks inactive workspaces created from this template. | | `failure_ttl_ms` | integer | false | | Failure ttl ms allows optionally specifying the max lifetime before Coder stops all resources for failed workspaces created from this template. | | `icon` | string | false | | Icon is a relative path or external URL that specifies an icon to be displayed in the dashboard. | -| `max_ttl_ms` | integer | false | | Max ttl ms remove max_ttl once autostop_requirement is matured Only one of MaxTTLMillis or AutostopRequirement can be specified. | | `name` | string | true | | Name is the name of the template. | | `require_active_version` | boolean | false | | Require active version mandates that workspaces are built with the active template version. | | `template_version_id` | string | true | | Template version ID is an in-progress or completed job to use as an initial version of the template. | @@ -5410,15 +5408,13 @@ AuthorizationObject can represent a "set" of objects, such as: all workspaces in "icon": "string", "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", "max_port_share_level": "owner", - "max_ttl_ms": 0, "name": "string", "organization_id": "7c60d51f-b44e-4682-87d6-449835ea4de6", "provisioner": "terraform", "require_active_version": true, "time_til_dormant_autodelete_ms": 0, "time_til_dormant_ms": 0, - "updated_at": "2019-08-24T14:15:22Z", - "use_max_ttl": true + "updated_at": "2019-08-24T14:15:22Z" } ``` @@ -5447,7 +5443,6 @@ AuthorizationObject can represent a "set" of objects, such as: all workspaces in | `icon` | string | false | | | | `id` | string | false | | | | `max_port_share_level` | [codersdk.WorkspaceAgentPortShareLevel](#codersdkworkspaceagentportsharelevel) | false | | | -| `max_ttl_ms` | integer | false | | Max ttl ms remove max_ttl once autostop_requirement is matured | | `name` | string | false | | | | `organization_id` | string | false | | | | `provisioner` | string | false | | | @@ -5455,7 +5450,6 @@ AuthorizationObject can represent a "set" of objects, such as: all workspaces in | `time_til_dormant_autodelete_ms` | integer | false | | | | `time_til_dormant_ms` | integer | false | | | | `updated_at` | string | false | | | -| `use_max_ttl` | boolean | false | | Use max ttl picks whether to use the deprecated max TTL for the template or the new autostop requirement. | #### Enumerated Values @@ -6266,9 +6260,9 @@ AuthorizationObject can represent a "set" of objects, such as: all workspaces in ### Properties -| Name | Type | Required | Restrictions | Description | -| ---------- | ------ | -------- | ------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| `schedule` | string | true | | Schedule is a cron expression that defines when the user's quiet hours window is. Schedule must not be empty. For new users, the schedule is set to 2am in their browser or computer's timezone. The schedule denotes the beginning of a 4 hour window where the workspace is allowed to automatically stop or restart due to maintenance or template max TTL. | +| Name | Type | Required | Restrictions | Description | +| ---------- | ------ | -------- | ------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `schedule` | string | true | | Schedule is a cron expression that defines when the user's quiet hours window is. Schedule must not be empty. For new users, the schedule is set to 2am in their browser or computer's timezone. The schedule denotes the beginning of a 4 hour window where the workspace is allowed to automatically stop or restart due to maintenance or template schedule. | The schedule must be daily with a single time, and should have a timezone specified via a CRON_TZ prefix (otherwise UTC will be used). If the schedule is empty, the user will be updated to use the default schedule.| diff --git a/docs/api/templates.md b/docs/api/templates.md index bc6bfef742..de0498c3de 100644 --- a/docs/api/templates.md +++ b/docs/api/templates.md @@ -61,15 +61,13 @@ curl -X GET http://coder-server:8080/api/v2/organizations/{organization}/templat "icon": "string", "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", "max_port_share_level": "owner", - "max_ttl_ms": 0, "name": "string", "organization_id": "7c60d51f-b44e-4682-87d6-449835ea4de6", "provisioner": "terraform", "require_active_version": true, "time_til_dormant_autodelete_ms": 0, "time_til_dormant_ms": 0, - "updated_at": "2019-08-24T14:15:22Z", - "use_max_ttl": true + "updated_at": "2019-08-24T14:15:22Z" } ] ``` @@ -115,7 +113,6 @@ Status Code **200** | `» icon` | string | false | | | | `» id` | string(uuid) | false | | | | `» max_port_share_level` | [codersdk.WorkspaceAgentPortShareLevel](schemas.md#codersdkworkspaceagentportsharelevel) | false | | | -| `» max_ttl_ms` | integer | false | | Max ttl ms remove max_ttl once autostop_requirement is matured | | `» name` | string | false | | | | `» organization_id` | string(uuid) | false | | | | `» provisioner` | string | false | | | @@ -123,7 +120,6 @@ Status Code **200** | `» time_til_dormant_autodelete_ms` | integer | false | | | | `» time_til_dormant_ms` | integer | false | | | | `» updated_at` | string(date-time) | false | | | -| `» use_max_ttl` | boolean | false | | Use max ttl picks whether to use the deprecated max TTL for the template or the new autostop requirement. | #### Enumerated Values @@ -173,7 +169,6 @@ curl -X POST http://coder-server:8080/api/v2/organizations/{organization}/templa "dormant_ttl_ms": 0, "failure_ttl_ms": 0, "icon": "string", - "max_ttl_ms": 0, "name": "string", "require_active_version": true, "template_version_id": "0ba39c92-1f1b-4c32-aa3e-9925d7713eb1" @@ -228,15 +223,13 @@ curl -X POST http://coder-server:8080/api/v2/organizations/{organization}/templa "icon": "string", "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", "max_port_share_level": "owner", - "max_ttl_ms": 0, "name": "string", "organization_id": "7c60d51f-b44e-4682-87d6-449835ea4de6", "provisioner": "terraform", "require_active_version": true, "time_til_dormant_autodelete_ms": 0, "time_til_dormant_ms": 0, - "updated_at": "2019-08-24T14:15:22Z", - "use_max_ttl": true + "updated_at": "2019-08-24T14:15:22Z" } ``` @@ -369,15 +362,13 @@ curl -X GET http://coder-server:8080/api/v2/organizations/{organization}/templat "icon": "string", "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", "max_port_share_level": "owner", - "max_ttl_ms": 0, "name": "string", "organization_id": "7c60d51f-b44e-4682-87d6-449835ea4de6", "provisioner": "terraform", "require_active_version": true, "time_til_dormant_autodelete_ms": 0, "time_til_dormant_ms": 0, - "updated_at": "2019-08-24T14:15:22Z", - "use_max_ttl": true + "updated_at": "2019-08-24T14:15:22Z" } ``` @@ -686,15 +677,13 @@ curl -X GET http://coder-server:8080/api/v2/templates/{template} \ "icon": "string", "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", "max_port_share_level": "owner", - "max_ttl_ms": 0, "name": "string", "organization_id": "7c60d51f-b44e-4682-87d6-449835ea4de6", "provisioner": "terraform", "require_active_version": true, "time_til_dormant_autodelete_ms": 0, "time_til_dormant_ms": 0, - "updated_at": "2019-08-24T14:15:22Z", - "use_max_ttl": true + "updated_at": "2019-08-24T14:15:22Z" } ``` @@ -810,15 +799,13 @@ curl -X PATCH http://coder-server:8080/api/v2/templates/{template} \ "icon": "string", "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", "max_port_share_level": "owner", - "max_ttl_ms": 0, "name": "string", "organization_id": "7c60d51f-b44e-4682-87d6-449835ea4de6", "provisioner": "terraform", "require_active_version": true, "time_til_dormant_autodelete_ms": 0, "time_til_dormant_ms": 0, - "updated_at": "2019-08-24T14:15:22Z", - "use_max_ttl": true + "updated_at": "2019-08-24T14:15:22Z" } ``` diff --git a/docs/cli/templates_create.md b/docs/cli/templates_create.md index f79609b74d..de15a9fb90 100644 --- a/docs/cli/templates_create.md +++ b/docs/cli/templates_create.md @@ -88,14 +88,6 @@ Specify a duration workspaces may be inactive prior to being moved to the dorman Specify a duration workspaces may be in the dormant state prior to being deleted. This licensed feature's default is 0h (off). Maps to "Dormancy Auto-Deletion" in the UI. -### --max-ttl - -| | | -| ---- | --------------------- | -| Type | duration | - -Edit the template maximum time before shutdown - workspaces created from this template must shutdown within the given duration after starting. This is an enterprise-only feature. - ### --require-active-version | | | diff --git a/docs/cli/templates_edit.md b/docs/cli/templates_edit.md index 9e3bbb9668..45851225f1 100644 --- a/docs/cli/templates_edit.md +++ b/docs/cli/templates_edit.md @@ -68,14 +68,6 @@ Edit the template default time before shutdown - workspaces created from this te Edit the template activity bump - workspaces created from this template will have their shutdown time bumped by this value when activity is detected. Maps to "Activity bump" in the UI. -### --max-ttl - -| | | -| ---- | --------------------- | -| Type | duration | - -Edit the template maximum time before shutdown - workspaces created from this template must shutdown within the given duration after starting, regardless of user activity. This is an enterprise-only feature. Maps to "Max lifetime" in the UI. - ### --autostart-requirement-weekdays | | | @@ -84,6 +76,22 @@ Edit the template maximum time before shutdown - workspaces created from this te Edit the template autostart requirement weekdays - workspaces created from this template can only autostart on the given weekdays. To unset this value for the template (and allow autostart on all days), pass 'all'. +### --autostop-requirement-weekdays + +| | | +| ---- | ------------------------- | +| Type | string-array | + +Edit the template autostop requirement weekdays - workspaces created from this template must be restarted on the given weekdays. To unset this value for the template (and disable the autostop requirement for the template), pass 'none'. + +### --autostop-requirement-weeks + +| | | +| ---- | ---------------- | +| Type | int | + +Edit the template autostop requirement weeks - workspaces created from this template must be restarted on an n-weekly basis. + ### --failure-ttl | | | diff --git a/enterprise/audit/table.go b/enterprise/audit/table.go index f557ded214..f26b4921aa 100644 --- a/enterprise/audit/table.go +++ b/enterprise/audit/table.go @@ -70,8 +70,6 @@ var auditableResourcesTypes = map[any]map[string]Action{ "description": ActionTrack, "icon": ActionTrack, "default_ttl": ActionTrack, - "max_ttl": ActionTrack, - "use_max_ttl": ActionTrack, "autostart_block_days_of_week": ActionTrack, "autostop_requirement_days_of_week": ActionTrack, "autostop_requirement_weeks": ActionTrack, diff --git a/enterprise/cli/templateedit_test.go b/enterprise/cli/templateedit_test.go index 29575e5ab5..fbff3e75df 100644 --- a/enterprise/cli/templateedit_test.go +++ b/enterprise/cli/templateedit_test.go @@ -181,7 +181,6 @@ func TestTemplateEdit(t *testing.T) { expectedDescription = "My description" expectedIcon = "icon.pjg" expectedDefaultTTLMillis = time.Hour.Milliseconds() - expectedMaxTTLMillis = (time.Hour * 24).Milliseconds() expectedAllowAutostart = false expectedAllowAutostop = false expectedFailureTTLMillis = time.Minute.Milliseconds() @@ -204,7 +203,6 @@ func TestTemplateEdit(t *testing.T) { assert.Equal(t, expectedDescription, tpl.Description) assert.Equal(t, expectedIcon, tpl.Icon) assert.Equal(t, expectedDefaultTTLMillis, tpl.DefaultTTLMillis) - assert.Equal(t, expectedMaxTTLMillis, tpl.MaxTTLMillis) assert.Equal(t, expectedAllowAutostart, tpl.AllowUserAutostart) assert.Equal(t, expectedAllowAutostop, tpl.AllowUserAutostop) assert.Equal(t, expectedFailureTTLMillis, tpl.FailureTTLMillis) @@ -225,7 +223,6 @@ func TestTemplateEdit(t *testing.T) { Description: expectedDescription, Icon: expectedIcon, DefaultTTLMillis: expectedDefaultTTLMillis, - MaxTTLMillis: expectedMaxTTLMillis, AllowUserAutostop: expectedAllowAutostop, AllowUserAutostart: expectedAllowAutostart, FailureTTLMillis: expectedFailureTTLMillis, @@ -267,7 +264,6 @@ func TestTemplateEdit(t *testing.T) { expectedAutostartDaysOfWeek = []string{"monday", "wednesday", "friday"} expectedAutoStopDaysOfWeek = []string{"tuesday", "thursday"} expectedAutoStopWeeks = 2 - expectedMaxTTLMillis = 0 template, err = ownerClient.UpdateTemplateMeta(ctx, dbtemplate.ID, codersdk.UpdateTemplateMeta{ Name: expectedName, diff --git a/enterprise/cli/testdata/coder_server_--help.golden b/enterprise/cli/testdata/coder_server_--help.golden index 30c2f778e8..6e717b5735 100644 --- a/enterprise/cli/testdata/coder_server_--help.golden +++ b/enterprise/cli/testdata/coder_server_--help.golden @@ -468,7 +468,7 @@ telemetrywhen required by your organization's security policy. USER QUIET HOURS SCHEDULE OPTIONS: Allow users to set quiet hours schedules each day for workspaces to avoid -workspaces stopping during the day due to template max TTL. +workspaces stopping during the day due to template scheduling. --allow-custom-quiet-hours bool, $CODER_ALLOW_CUSTOM_QUIET_HOURS (default: true) Allow users to set their own quiet hours schedule for workspaces to diff --git a/enterprise/coderd/schedule/template.go b/enterprise/coderd/schedule/template.go index 04744a5ea4..ae5ec1110a 100644 --- a/enterprise/coderd/schedule/template.go +++ b/enterprise/coderd/schedule/template.go @@ -76,8 +76,6 @@ func (*EnterpriseTemplateScheduleStore) Get(ctx context.Context, db database.Sto UserAutostopEnabled: tpl.AllowUserAutostop, DefaultTTL: time.Duration(tpl.DefaultTTL), ActivityBump: time.Duration(tpl.ActivityBump), - MaxTTL: time.Duration(tpl.MaxTTL), - UseMaxTTL: tpl.UseMaxTtl, AutostopRequirement: agpl.TemplateAutostopRequirement{ DaysOfWeek: uint8(tpl.AutostopRequirementDaysOfWeek), Weeks: tpl.AutostopRequirementWeeks, @@ -105,8 +103,6 @@ func (s *EnterpriseTemplateScheduleStore) Set(ctx context.Context, db database.S if int64(opts.DefaultTTL) == tpl.DefaultTTL && int64(opts.ActivityBump) == tpl.ActivityBump && - opts.UseMaxTTL != tpl.UseMaxTtl && - int64(opts.MaxTTL) == tpl.MaxTTL && int16(opts.AutostopRequirement.DaysOfWeek) == tpl.AutostopRequirementDaysOfWeek && opts.AutostartRequirement.DaysOfWeek == tpl.AutostartAllowedDays() && opts.AutostopRequirement.Weeks == tpl.AutostopRequirementWeeks && @@ -141,8 +137,6 @@ func (s *EnterpriseTemplateScheduleStore) Set(ctx context.Context, db database.S AllowUserAutostop: opts.UserAutostopEnabled, DefaultTTL: int64(opts.DefaultTTL), ActivityBump: int64(opts.ActivityBump), - UseMaxTtl: opts.UseMaxTTL, - MaxTTL: int64(opts.MaxTTL), AutostopRequirementDaysOfWeek: int16(opts.AutostopRequirement.DaysOfWeek), AutostopRequirementWeeks: opts.AutostopRequirement.Weeks, // Database stores the inverse of the allowed days of the week. diff --git a/enterprise/coderd/schedule/template_test.go b/enterprise/coderd/schedule/template_test.go index 2eea5d0e9c..dd60805f00 100644 --- a/enterprise/coderd/schedule/template_test.go +++ b/enterprise/coderd/schedule/template_test.go @@ -293,8 +293,6 @@ func TestTemplateUpdateBuildDeadlines(t *testing.T) { UserAutostartEnabled: false, UserAutostopEnabled: false, DefaultTTL: 0, - MaxTTL: 0, - UseMaxTTL: false, AutostopRequirement: autostopReq, FailureTTL: 0, TimeTilDormant: 0, @@ -571,8 +569,6 @@ func TestTemplateUpdateBuildDeadlinesSkip(t *testing.T) { UserAutostartEnabled: false, UserAutostopEnabled: false, DefaultTTL: 0, - MaxTTL: 0, - UseMaxTTL: false, AutostopRequirement: agplschedule.TemplateAutostopRequirement{ // Every day DaysOfWeek: 0b01111111, diff --git a/enterprise/coderd/templates_test.go b/enterprise/coderd/templates_test.go index a6514867ae..c7d9a9cfb3 100644 --- a/enterprise/coderd/templates_test.go +++ b/enterprise/coderd/templates_test.go @@ -29,69 +29,6 @@ import ( func TestTemplates(t *testing.T) { t.Parallel() - // TODO(@dean): remove legacy max_ttl tests - t.Run("CreateUpdateWorkspaceMaxTTL", func(t *testing.T) { - t.Parallel() - client, user := coderdenttest.New(t, &coderdenttest.Options{ - Options: &coderdtest.Options{ - IncludeProvisionerDaemon: true, - }, - LicenseOptions: &coderdenttest.LicenseOptions{ - Features: license.Features{ - codersdk.FeatureAdvancedTemplateScheduling: 1, - }, - }, - }) - anotherClient, _ := coderdtest.CreateAnotherUser(t, client, user.OrganizationID) - - version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, nil) - exp := 24 * time.Hour.Milliseconds() - template := coderdtest.CreateTemplate(t, client, user.OrganizationID, version.ID, func(ctr *codersdk.CreateTemplateRequest) { - ctr.DefaultTTLMillis = &exp - ctr.MaxTTLMillis = &exp - }) - coderdtest.AwaitTemplateVersionJobCompleted(t, client, version.ID) - - ctx, cancel := context.WithTimeout(context.Background(), testutil.WaitLong) - defer cancel() - - // No TTL provided should use template default - req := codersdk.CreateWorkspaceRequest{ - TemplateID: template.ID, - Name: "testing", - } - ws, err := anotherClient.CreateWorkspace(ctx, template.OrganizationID, codersdk.Me, req) - require.NoError(t, err) - require.NotNil(t, ws.TTLMillis) - require.EqualValues(t, exp, *ws.TTLMillis) - - // Editing a workspace to have a higher TTL than the template's max - // should error - exp = exp + time.Minute.Milliseconds() - err = anotherClient.UpdateWorkspaceTTL(ctx, ws.ID, codersdk.UpdateWorkspaceTTLRequest{ - TTLMillis: &exp, - }) - require.Error(t, err) - var apiErr *codersdk.Error - 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.Contains(t, apiErr.Validations[0].Detail, "time until shutdown must be less than or equal to the template's maximum TTL") - - // Creating workspace with TTL higher than max should error - req.Name = "testing2" - req.TTLMillis = &exp - ws, err = anotherClient.CreateWorkspace(ctx, template.OrganizationID, codersdk.Me, req) - require.Error(t, err) - apiErr = nil - 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.Contains(t, apiErr.Validations[0].Detail, "time until shutdown must be less than or equal to the template's maximum TTL") - }) - t.Run("Deprecated", func(t *testing.T) { t.Parallel() @@ -249,60 +186,6 @@ func TestTemplates(t *testing.T) { require.Empty(t, wpsr.Shares) }) - t.Run("BlockDisablingAutoOffWithMaxTTL", func(t *testing.T) { - t.Parallel() - client, user := coderdenttest.New(t, &coderdenttest.Options{ - Options: &coderdtest.Options{ - IncludeProvisionerDaemon: true, - }, - LicenseOptions: &coderdenttest.LicenseOptions{ - Features: license.Features{ - codersdk.FeatureAdvancedTemplateScheduling: 1, - }, - }, - }) - anotherClient, _ := coderdtest.CreateAnotherUser(t, client, user.OrganizationID) - - version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, nil) - exp := 24 * time.Hour.Milliseconds() - template := coderdtest.CreateTemplate(t, client, user.OrganizationID, version.ID, func(ctr *codersdk.CreateTemplateRequest) { - ctr.MaxTTLMillis = &exp - }) - coderdtest.AwaitTemplateVersionJobCompleted(t, client, version.ID) - - ctx, cancel := context.WithTimeout(context.Background(), testutil.WaitLong) - defer cancel() - - // No TTL provided should use template default - req := codersdk.CreateWorkspaceRequest{ - TemplateID: template.ID, - Name: "testing", - } - ws, err := anotherClient.CreateWorkspace(ctx, template.OrganizationID, codersdk.Me, req) - require.NoError(t, err) - require.NotNil(t, ws.TTLMillis) - require.EqualValues(t, exp, *ws.TTLMillis) - - // Editing a workspace to disable the TTL should do nothing - err = anotherClient.UpdateWorkspaceTTL(ctx, ws.ID, codersdk.UpdateWorkspaceTTLRequest{ - TTLMillis: nil, - }) - require.NoError(t, err) - ws, err = anotherClient.Workspace(ctx, ws.ID) - require.NoError(t, err) - require.EqualValues(t, exp, *ws.TTLMillis) - - // Editing a workspace to have a TTL of 0 should do nothing - zero := int64(0) - err = anotherClient.UpdateWorkspaceTTL(ctx, ws.ID, codersdk.UpdateWorkspaceTTLRequest{ - TTLMillis: &zero, - }) - require.NoError(t, err) - ws, err = anotherClient.Workspace(ctx, ws.ID) - require.NoError(t, err) - require.EqualValues(t, exp, *ws.TTLMillis) - }) - t.Run("SetAutostartRequirement", func(t *testing.T) { t.Parallel() diff --git a/site/src/api/typesGenerated.ts b/site/src/api/typesGenerated.ts index 58c260478a..22c3fee9bc 100644 --- a/site/src/api/typesGenerated.ts +++ b/site/src/api/typesGenerated.ts @@ -239,7 +239,6 @@ export interface CreateTemplateRequest { readonly template_version_id: string; readonly default_ttl_ms?: number; readonly activity_bump_ms?: number; - readonly max_ttl_ms?: number; readonly autostop_requirement?: TemplateAutostopRequirement; readonly autostart_requirement?: TemplateAutostartRequirement; readonly allow_user_cancel_workspace_jobs?: boolean; @@ -1165,8 +1164,6 @@ export interface Template { readonly icon: string; readonly default_ttl_ms: number; readonly activity_bump_ms: number; - readonly use_max_ttl: boolean; - readonly max_ttl_ms: number; readonly autostop_requirement: TemplateAutostopRequirement; readonly autostart_requirement: TemplateAutostartRequirement; readonly created_by_id: string; @@ -1425,7 +1422,6 @@ export interface UpdateTemplateMeta { readonly icon?: string; readonly default_ttl_ms?: number; readonly activity_bump_ms?: number; - readonly max_ttl_ms?: number; readonly autostop_requirement?: TemplateAutostopRequirement; readonly autostart_requirement?: TemplateAutostartRequirement; readonly allow_user_autostart?: boolean; diff --git a/site/src/pages/CreateTemplatePage/CreateTemplateForm.tsx b/site/src/pages/CreateTemplatePage/CreateTemplateForm.tsx index 3a470e0115..0ba63ce7de 100644 --- a/site/src/pages/CreateTemplatePage/CreateTemplateForm.tsx +++ b/site/src/pages/CreateTemplatePage/CreateTemplateForm.tsx @@ -41,8 +41,6 @@ export interface CreateTemplateData { description: string; icon: string; default_ttl_hours: number; - use_max_ttl: boolean; - max_ttl_hours: number; autostart_requirement_days_of_week: TemplateAutostartRequirementDaysValue[]; autostop_requirement_days_of_week: TemplateAutostopRequirementDaysValue; autostop_requirement_weeks: number; @@ -70,13 +68,6 @@ const defaultInitialValues: CreateTemplateData = { description: "", icon: "", 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. - use_max_ttl: false, // autostop_requirement is default - max_ttl_hours: 24 * 7, // autostop_requirement is an enterprise-only feature, and the server ignores // the value if you are not licensed. We hide the form value based on // entitlements. @@ -110,7 +101,6 @@ const getInitialValues = ({ if (!allowAdvancedScheduling) { initialValues = { ...initialValues, - max_ttl_hours: 0, autostop_requirement_days_of_week: "off", autostop_requirement_weeks: 1, }; diff --git a/site/src/pages/CreateTemplatePage/utils.ts b/site/src/pages/CreateTemplatePage/utils.ts index 811d9979f2..cc7266d1de 100644 --- a/site/src/pages/CreateTemplatePage/utils.ts +++ b/site/src/pages/CreateTemplatePage/utils.ts @@ -12,18 +12,14 @@ const provisioner: ProvisionerType = typeof (window as any).playwright !== "undefined" ? "echo" : "terraform"; export const newTemplate = (formData: CreateTemplateData) => { - let { - max_ttl_hours, - autostop_requirement_days_of_week, - autostop_requirement_weeks, - } = formData; + const { autostop_requirement_days_of_week, autostop_requirement_weeks } = + formData; const safeTemplateData = { name: formData.name, display_name: formData.display_name, description: formData.description, icon: formData.icon, - use_max_ttl: formData.use_max_ttl, allow_user_autostart: formData.allow_user_autostart, allow_user_autostop: formData.allow_user_autostop, allow_user_cancel_workspace_jobs: formData.allow_user_cancel_workspace_jobs, @@ -31,18 +27,10 @@ export const newTemplate = (formData: CreateTemplateData) => { allow_everyone_group_access: formData.allow_everyone_group_access, }; - if (formData.use_max_ttl) { - autostop_requirement_days_of_week = "off"; - autostop_requirement_weeks = 1; - } else { - max_ttl_hours = 0; - } - return { ...safeTemplateData, disable_everyone_group_access: !formData.allow_everyone_group_access, default_ttl_ms: formData.default_ttl_hours * 60 * 60 * 1000, // Convert hours to ms - max_ttl_ms: max_ttl_hours * 60 * 60 * 1000, // Convert hours to ms autostop_requirement: { days_of_week: calculateAutostopRequirementDaysValue( autostop_requirement_days_of_week, diff --git a/site/src/pages/TemplateSettingsPage/TemplateGeneralSettingsPage/TemplateSettingsPage.test.tsx b/site/src/pages/TemplateSettingsPage/TemplateGeneralSettingsPage/TemplateSettingsPage.test.tsx index a0849dfdd2..beeef1e100 100644 --- a/site/src/pages/TemplateSettingsPage/TemplateGeneralSettingsPage/TemplateSettingsPage.test.tsx +++ b/site/src/pages/TemplateSettingsPage/TemplateGeneralSettingsPage/TemplateSettingsPage.test.tsx @@ -14,7 +14,7 @@ import { TemplateSettingsPage } from "./TemplateSettingsPage"; type FormValues = Required< Omit< UpdateTemplateMeta, - "default_ttl_ms" | "activity_bump_ms" | "max_ttl_ms" | "deprecation_message" + "default_ttl_ms" | "activity_bump_ms" | "deprecation_message" > >; diff --git a/site/src/pages/TemplateSettingsPage/TemplateSchedulePage/TTLHelperText.tsx b/site/src/pages/TemplateSettingsPage/TemplateSchedulePage/TTLHelperText.tsx index c6ec467c16..4114f4d37d 100644 --- a/site/src/pages/TemplateSettingsPage/TemplateSchedulePage/TTLHelperText.tsx +++ b/site/src/pages/TemplateSettingsPage/TemplateSchedulePage/TTLHelperText.tsx @@ -46,26 +46,6 @@ export const ActivityBumpHelperText = (props: { bump?: number }) => { ); }; -export const MaxTTLHelperText = (props: { ttl?: number }) => { - const { ttl = 0 } = props; - - // Error will show once field is considered touched - if (ttl < 0) { - return null; - } - - if (ttl === 0) { - return Workspaces may run indefinitely.; - } - - return ( - - Workspaces must stop within {ttl} {hours(ttl)} of starting, regardless of - any active connections. - - ); -}; - export const FailureTTLHelperText = (props: { ttl?: number }) => { const { ttl = 0 } = props; diff --git a/site/src/pages/TemplateSettingsPage/TemplateSchedulePage/TemplateScheduleForm.tsx b/site/src/pages/TemplateSettingsPage/TemplateSchedulePage/TemplateScheduleForm.tsx index 33b600273d..f3e2f4d440 100644 --- a/site/src/pages/TemplateSettingsPage/TemplateSchedulePage/TemplateScheduleForm.tsx +++ b/site/src/pages/TemplateSettingsPage/TemplateSchedulePage/TemplateScheduleForm.tsx @@ -1,7 +1,6 @@ import { useTheme } from "@emotion/react"; import Checkbox from "@mui/material/Checkbox"; import FormControlLabel from "@mui/material/FormControlLabel"; -import Link from "@mui/material/Link"; import MenuItem from "@mui/material/MenuItem"; import Switch from "@mui/material/Switch"; import TextField from "@mui/material/TextField"; @@ -15,7 +14,6 @@ import { FormFields, } from "components/Form/Form"; import { Stack } from "components/Stack/Stack"; -import { docs } from "utils/docs"; import { getFormHelpers } from "utils/formUtils"; import { calculateAutostopRequirementDaysValue, @@ -38,7 +36,6 @@ import { DormancyAutoDeletionTTLHelperText, DormancyTTLHelperText, FailureTTLHelperText, - MaxTTLHelperText, } from "./TTLHelperText"; import { useWorkspacesToGoDormant, @@ -77,15 +74,6 @@ export const TemplateScheduleForm: FC = ({ // on display, convert from ms => hours default_ttl_ms: template.default_ttl_ms / MS_HOUR_CONVERSION, activity_bump_ms: template.activity_bump_ms / MS_HOUR_CONVERSION, - // the API ignores these values, but to avoid tripping up validation set - // it to zero if the user can't set the field. - use_max_ttl: - template.use_max_ttl === undefined - ? template.max_ttl_ms > 0 - : template.use_max_ttl, - max_ttl_ms: allowAdvancedScheduling - ? template.max_ttl_ms / MS_HOUR_CONVERSION - : 0, failure_ttl_ms: allowAdvancedScheduling ? template.failure_ttl_ms / MS_DAY_CONVERSION : 0, @@ -214,10 +202,6 @@ export const TemplateScheduleForm: FC = ({ activity_bump_ms: form.values.activity_bump_ms ? form.values.activity_bump_ms * MS_HOUR_CONVERSION : undefined, - max_ttl_ms: - form.values.max_ttl_ms && form.values.use_max_ttl - ? form.values.max_ttl_ms * MS_HOUR_CONVERSION - : undefined, failure_ttl_ms: form.values.failure_ttl_ms ? form.values.failure_ttl_ms * MS_DAY_CONVERSION : undefined, @@ -228,14 +212,12 @@ export const TemplateScheduleForm: FC = ({ ? form.values.time_til_dormant_autodelete_ms * MS_DAY_CONVERSION : undefined, - autostop_requirement: form.values.use_max_ttl - ? undefined - : { - days_of_week: calculateAutostopRequirementDaysValue( - form.values.autostop_requirement_days_of_week, - ), - weeks: autostop_requirement_weeks, - }, + autostop_requirement: { + days_of_week: calculateAutostopRequirementDaysValue( + form.values.autostop_requirement_days_of_week, + ), + weeks: autostop_requirement_weeks, + }, autostart_requirement: { days_of_week: form.values.autostart_requirement_days_of_week, }, @@ -330,27 +312,6 @@ export const TemplateScheduleForm: FC = ({ } }; - const handleToggleUseMaxTTL = async () => { - const val = !form.values.use_max_ttl; - if (val) { - // set max_ttl to 1, set autostop_requirement to empty - await form.setValues({ - ...form.values, - use_max_ttl: val, - max_ttl_ms: 1, - autostop_requirement_days_of_week: "off", - autostop_requirement_weeks: 1, - }); - } else { - // set max_ttl to 0 - await form.setValues({ - ...form.values, - use_max_ttl: val, - max_ttl_ms: 0, - }); - } - }; - return ( = ({ /> ), })} - disabled={isSubmitting || form.values.use_max_ttl} + disabled={isSubmitting} fullWidth select value={form.values.autostop_requirement_days_of_week} @@ -433,7 +394,6 @@ export const TemplateScheduleForm: FC = ({ })} disabled={ isSubmitting || - form.values.use_max_ttl || !["saturday", "sunday"].includes( form.values.autostop_requirement_days_of_week || "", ) @@ -446,68 +406,6 @@ export const TemplateScheduleForm: FC = ({ - - - - - } - label={ - - - Use a max lifetime instead of a required autostop schedule. - - - Use a maximum lifetime for workspaces created from this - template instead of an autostop requirement as configured - above. - - - } - /> - - - - ) : ( - <> - You need an enterprise license to use it{" "} - Learn more. - - ), - })} - disabled={ - isSubmitting || - !form.values.use_max_ttl || - !allowAdvancedScheduling - } - fullWidth - inputProps={{ min: 0, step: 1 }} - label="Max lifetime (hours)" - type="number" - /> - - - { ); }); - test("default and max ttl is converted to and from hours", async () => { + test("default is converted to and from hours", async () => { await renderTemplateSchedulePage(); jest.spyOn(API, "updateTemplateMeta").mockResolvedValueOnce({ @@ -176,7 +162,6 @@ describe("TemplateSchedulePage", () => { "test-template", expect.objectContaining({ default_ttl_ms: (validFormValues.default_ttl_ms || 0) * 3600000, - max_ttl_ms: (validFormValues.max_ttl_ms || 0) * 3600000, }), ); }); diff --git a/site/src/pages/TemplateSettingsPage/TemplateSchedulePage/formHelpers.tsx b/site/src/pages/TemplateSettingsPage/TemplateSchedulePage/formHelpers.tsx index a4be76a254..77a2d6d8f1 100644 --- a/site/src/pages/TemplateSettingsPage/TemplateSchedulePage/formHelpers.tsx +++ b/site/src/pages/TemplateSettingsPage/TemplateSchedulePage/formHelpers.tsx @@ -10,7 +10,6 @@ export interface TemplateScheduleFormValues UpdateTemplateMeta, "autostop_requirement" | "autostart_requirement" > { - use_max_ttl: boolean; autostart_requirement_days_of_week: TemplateAutostartRequirementDaysValue[]; autostop_requirement_days_of_week: TemplateAutostopRequirementDaysValue; autostop_requirement_weeks: number; @@ -39,14 +38,6 @@ export const getValidationSchema = (): Yup.AnyObjectSchema => 24 * MAX_TTL_DAYS /* 30 days in hours */, "Please enter an activity bump duration that is less than or equal to 720 hours (30 days).", ), - max_ttl_ms: Yup.number() - .integer() - .required() - .min(0, "Maximum time until autostop must not be less than 0.") - .max( - 24 * MAX_TTL_DAYS /* 30 days in hours */, - "Please enter a limit that is less than or equal to 720 hours (30 days).", - ), failure_ttl_ms: Yup.number() .integer() .required() diff --git a/site/src/testHelpers/entities.ts b/site/src/testHelpers/entities.ts index 6b1391817a..d48cb2aeb3 100644 --- a/site/src/testHelpers/entities.ts +++ b/site/src/testHelpers/entities.ts @@ -475,8 +475,6 @@ export const MockTemplate: TypesGen.Template = { description: "This is a test description.", default_ttl_ms: 24 * 60 * 60 * 1000, activity_bump_ms: 1 * 60 * 60 * 1000, - use_max_ttl: false, - max_ttl_ms: 0, autostop_requirement: { days_of_week: ["sunday"], weeks: 1,