diff --git a/cli/templatecreate.go b/cli/templatecreate.go index 9a54bf814eb60..38f5d7d0d7fd0 100644 --- a/cli/templatecreate.go +++ b/cli/templatecreate.go @@ -134,7 +134,7 @@ func (r *RootCmd) templateCreate() *clibase.Cmd { VersionID: job.ID, DefaultTTLMillis: ptr.Ref(defaultTTL.Milliseconds()), FailureTTLMillis: ptr.Ref(failureTTL.Milliseconds()), - InactivityTTLMillis: ptr.Ref(inactivityTTL.Milliseconds()), + TimeTilDormantMillis: ptr.Ref(inactivityTTL.Milliseconds()), DisableEveryoneGroupAccess: disableEveryone, } diff --git a/cli/templateedit.go b/cli/templateedit.go index 7ce8fb00daec0..5fcd73c432f58 100644 --- a/cli/templateedit.go +++ b/cli/templateedit.go @@ -104,7 +104,7 @@ func (r *RootCmd) templateEdit() *clibase.Cmd { Weeks: restartRequirementWeeks, }, FailureTTLMillis: failureTTL.Milliseconds(), - InactivityTTLMillis: inactivityTTL.Milliseconds(), + TimeTilDormantMillis: inactivityTTL.Milliseconds(), AllowUserCancelWorkspaceJobs: allowUserCancelWorkspaceJobs, AllowUserAutostart: allowUserAutostart, AllowUserAutostop: allowUserAutostop, diff --git a/cli/templateedit_test.go b/cli/templateedit_test.go index 775a25f91c6bd..0aff5166e9ca8 100644 --- a/cli/templateedit_test.go +++ b/cli/templateedit_test.go @@ -752,7 +752,7 @@ func TestTemplateEdit(t *testing.T) { ctr.DefaultTTLMillis = nil ctr.RestartRequirement = nil ctr.FailureTTLMillis = nil - ctr.InactivityTTLMillis = nil + ctr.TimeTilDormantMillis = nil }) // Test the cli command with --allow-user-autostart. @@ -798,7 +798,7 @@ func TestTemplateEdit(t *testing.T) { assert.Equal(t, template.AllowUserAutostart, updated.AllowUserAutostart) assert.Equal(t, template.AllowUserAutostop, updated.AllowUserAutostop) assert.Equal(t, template.FailureTTLMillis, updated.FailureTTLMillis) - assert.Equal(t, template.InactivityTTLMillis, updated.InactivityTTLMillis) + assert.Equal(t, template.TimeTilDormantMillis, updated.TimeTilDormantMillis) }) t.Run("BlockedNotEntitled", func(t *testing.T) { @@ -892,7 +892,7 @@ func TestTemplateEdit(t *testing.T) { assert.Equal(t, template.AllowUserAutostart, updated.AllowUserAutostart) assert.Equal(t, template.AllowUserAutostop, updated.AllowUserAutostop) assert.Equal(t, template.FailureTTLMillis, updated.FailureTTLMillis) - assert.Equal(t, template.InactivityTTLMillis, updated.InactivityTTLMillis) + assert.Equal(t, template.TimeTilDormantMillis, updated.TimeTilDormantMillis) }) t.Run("Entitled", func(t *testing.T) { t.Parallel() @@ -990,7 +990,7 @@ func TestTemplateEdit(t *testing.T) { assert.Equal(t, template.AllowUserAutostart, updated.AllowUserAutostart) assert.Equal(t, template.AllowUserAutostop, updated.AllowUserAutostop) assert.Equal(t, template.FailureTTLMillis, updated.FailureTTLMillis) - assert.Equal(t, template.InactivityTTLMillis, updated.InactivityTTLMillis) + assert.Equal(t, template.TimeTilDormantMillis, updated.TimeTilDormantMillis) }) }) } diff --git a/cli/testdata/coder_list_--output_json.golden b/cli/testdata/coder_list_--output_json.golden index 49e51d408285c..f680c9e210cbc 100644 --- a/cli/testdata/coder_list_--output_json.golden +++ b/cli/testdata/coder_list_--output_json.golden @@ -52,7 +52,7 @@ "ttl_ms": 28800000, "last_used_at": "[timestamp]", "deleting_at": null, - "locked_at": null, + "dormant_at": null, "health": { "healthy": true, "failing_agents": [] diff --git a/coderd/apidoc/docs.go b/coderd/apidoc/docs.go index 951e1ed04d254..3b46f647bf248 100644 --- a/coderd/apidoc/docs.go +++ b/coderd/apidoc/docs.go @@ -6082,7 +6082,7 @@ const docTemplate = `{ } } }, - "/workspaces/{workspace}/extend": { + "/workspaces/{workspace}/dormant": { "put": { "security": [ { @@ -6098,8 +6098,8 @@ const docTemplate = `{ "tags": [ "Workspaces" ], - "summary": "Extend workspace deadline by ID", - "operationId": "extend-workspace-deadline-by-id", + "summary": "Update workspace dormancy status by id.", + "operationId": "update-workspace-dormancy-status-by-id", "parameters": [ { "type": "string", @@ -6110,12 +6110,12 @@ const docTemplate = `{ "required": true }, { - "description": "Extend deadline update request", + "description": "Make a workspace dormant or active", "name": "request", "in": "body", "required": true, "schema": { - "$ref": "#/definitions/codersdk.PutExtendWorkspaceRequest" + "$ref": "#/definitions/codersdk.UpdateWorkspaceDormancy" } } ], @@ -6123,13 +6123,13 @@ const docTemplate = `{ "200": { "description": "OK", "schema": { - "$ref": "#/definitions/codersdk.Response" + "$ref": "#/definitions/codersdk.Workspace" } } } } }, - "/workspaces/{workspace}/lock": { + "/workspaces/{workspace}/extend": { "put": { "security": [ { @@ -6145,8 +6145,8 @@ const docTemplate = `{ "tags": [ "Workspaces" ], - "summary": "Update workspace lock by id.", - "operationId": "update-workspace-lock-by-id", + "summary": "Extend workspace deadline by ID", + "operationId": "extend-workspace-deadline-by-id", "parameters": [ { "type": "string", @@ -6157,12 +6157,12 @@ const docTemplate = `{ "required": true }, { - "description": "Lock or unlock a workspace", + "description": "Extend deadline update request", "name": "request", "in": "body", "required": true, "schema": { - "$ref": "#/definitions/codersdk.UpdateWorkspaceLock" + "$ref": "#/definitions/codersdk.PutExtendWorkspaceRequest" } } ], @@ -6170,7 +6170,7 @@ const docTemplate = `{ "200": { "description": "OK", "schema": { - "$ref": "#/definitions/codersdk.Workspace" + "$ref": "#/definitions/codersdk.Response" } } } @@ -7391,6 +7391,10 @@ const docTemplate = `{ "description": "DefaultTTLMillis allows optionally specifying the default TTL\nfor all workspaces created from this template.", "type": "integer" }, + "delete_ttl_ms": { + "description": "TimeTilDormantAutoDeleteMillis allows optionally specifying the max lifetime before Coder\npermanently deletes dormant workspaces created from this template.", + "type": "integer" + }, "description": { "description": "Description is a description of what the template contains. It must be\nless than 128 bytes.", "type": "string" @@ -7403,6 +7407,10 @@ const docTemplate = `{ "description": "DisplayName is the displayed name of the template.", "type": "string" }, + "dormant_ttl_ms": { + "description": "TimeTilDormantMillis allows optionally specifying the max lifetime before Coder\nlocks inactive workspaces created from this template.", + "type": "integer" + }, "failure_ttl_ms": { "description": "FailureTTLMillis allows optionally specifying the max lifetime before Coder\nstops all resources for failed workspaces created from this template.", "type": "integer" @@ -7411,14 +7419,6 @@ const docTemplate = `{ "description": "Icon is a relative path or external URL that specifies\nan icon to be displayed in the dashboard.", "type": "string" }, - "inactivity_ttl_ms": { - "description": "InactivityTTLMillis allows optionally specifying the max lifetime before Coder\nlocks inactive workspaces created from this template.", - "type": "integer" - }, - "locked_ttl_ms": { - "description": "LockedTTLMillis allows optionally specifying the max lifetime before Coder\npermanently deletes locked workspaces created from this template.", - "type": "integer" - }, "max_ttl_ms": { "description": "TODO(@dean): remove max_ttl once restart_requirement is matured", "type": "integer" @@ -9534,7 +9534,7 @@ const docTemplate = `{ "type": "string" }, "failure_ttl_ms": { - "description": "FailureTTLMillis, InactivityTTLMillis, and LockedTTLMillis are enterprise-only. Their\nvalues are used if your license is entitled to use the advanced\ntemplate scheduling feature.", + "description": "FailureTTLMillis, TimeTilDormantMillis, and TimeTilDormantAutoDeleteMillis are enterprise-only. Their\nvalues are used if your license is entitled to use the advanced\ntemplate scheduling feature.", "type": "integer" }, "icon": { @@ -9544,12 +9544,6 @@ const docTemplate = `{ "type": "string", "format": "uuid" }, - "inactivity_ttl_ms": { - "type": "integer" - }, - "locked_ttl_ms": { - "type": "integer" - }, "max_ttl_ms": { "description": "TODO(@dean): remove max_ttl once restart_requirement is matured", "type": "integer" @@ -9575,6 +9569,12 @@ const docTemplate = `{ } ] }, + "time_til_dormant_autodelete_ms": { + "type": "integer" + }, + "time_til_dormant_ms": { + "type": "integer" + }, "updated_at": { "type": "string", "format": "date-time" @@ -10248,10 +10248,10 @@ const docTemplate = `{ } } }, - "codersdk.UpdateWorkspaceLock": { + "codersdk.UpdateWorkspaceDormancy": { "type": "object", "properties": { - "lock": { + "dormant": { "type": "boolean" } } @@ -10504,7 +10504,12 @@ const docTemplate = `{ "format": "date-time" }, "deleting_at": { - "description": "DeletingAt indicates the time of the upcoming workspace deletion, if applicable; otherwise it is nil.\nWorkspaces may have impending deletions if Template.InactivityTTL feature is turned on and the workspace is inactive.", + "description": "DeletingAt indicates the time at which the workspace will be permanently deleted.\nA workspace is eligible for deletion if it is dormant (a non-nil dormant_at value)\nand a value has been specified for time_til_dormant_autodelete on its template.", + "type": "string", + "format": "date-time" + }, + "dormant_at": { + "description": "DormantAt being non-nil indicates a workspace that is dormant.\nA dormant workspace is no longer accessible must be activated.\nIt is subject to deletion if it breaches\nthe duration of the time_til_ field on its template.", "type": "string", "format": "date-time" }, @@ -10527,11 +10532,6 @@ const docTemplate = `{ "latest_build": { "$ref": "#/definitions/codersdk.WorkspaceBuild" }, - "locked_at": { - "description": "LockedAt being non-nil indicates a workspace that has been locked.\nA locked workspace is no longer accessible by a user and must be\nunlocked by an admin. It is subject to deletion if it breaches\nthe duration of the locked_ttl field on its template.", - "type": "string", - "format": "date-time" - }, "name": { "type": "string" }, diff --git a/coderd/apidoc/swagger.json b/coderd/apidoc/swagger.json index a0cea25ad07ed..e1cffa5808f07 100644 --- a/coderd/apidoc/swagger.json +++ b/coderd/apidoc/swagger.json @@ -5366,7 +5366,7 @@ } } }, - "/workspaces/{workspace}/extend": { + "/workspaces/{workspace}/dormant": { "put": { "security": [ { @@ -5376,8 +5376,8 @@ "consumes": ["application/json"], "produces": ["application/json"], "tags": ["Workspaces"], - "summary": "Extend workspace deadline by ID", - "operationId": "extend-workspace-deadline-by-id", + "summary": "Update workspace dormancy status by id.", + "operationId": "update-workspace-dormancy-status-by-id", "parameters": [ { "type": "string", @@ -5388,12 +5388,12 @@ "required": true }, { - "description": "Extend deadline update request", + "description": "Make a workspace dormant or active", "name": "request", "in": "body", "required": true, "schema": { - "$ref": "#/definitions/codersdk.PutExtendWorkspaceRequest" + "$ref": "#/definitions/codersdk.UpdateWorkspaceDormancy" } } ], @@ -5401,13 +5401,13 @@ "200": { "description": "OK", "schema": { - "$ref": "#/definitions/codersdk.Response" + "$ref": "#/definitions/codersdk.Workspace" } } } } }, - "/workspaces/{workspace}/lock": { + "/workspaces/{workspace}/extend": { "put": { "security": [ { @@ -5417,8 +5417,8 @@ "consumes": ["application/json"], "produces": ["application/json"], "tags": ["Workspaces"], - "summary": "Update workspace lock by id.", - "operationId": "update-workspace-lock-by-id", + "summary": "Extend workspace deadline by ID", + "operationId": "extend-workspace-deadline-by-id", "parameters": [ { "type": "string", @@ -5429,12 +5429,12 @@ "required": true }, { - "description": "Lock or unlock a workspace", + "description": "Extend deadline update request", "name": "request", "in": "body", "required": true, "schema": { - "$ref": "#/definitions/codersdk.UpdateWorkspaceLock" + "$ref": "#/definitions/codersdk.PutExtendWorkspaceRequest" } } ], @@ -5442,7 +5442,7 @@ "200": { "description": "OK", "schema": { - "$ref": "#/definitions/codersdk.Workspace" + "$ref": "#/definitions/codersdk.Response" } } } @@ -6584,6 +6584,10 @@ "description": "DefaultTTLMillis allows optionally specifying the default TTL\nfor all workspaces created from this template.", "type": "integer" }, + "delete_ttl_ms": { + "description": "TimeTilDormantAutoDeleteMillis allows optionally specifying the max lifetime before Coder\npermanently deletes dormant workspaces created from this template.", + "type": "integer" + }, "description": { "description": "Description is a description of what the template contains. It must be\nless than 128 bytes.", "type": "string" @@ -6596,6 +6600,10 @@ "description": "DisplayName is the displayed name of the template.", "type": "string" }, + "dormant_ttl_ms": { + "description": "TimeTilDormantMillis allows optionally specifying the max lifetime before Coder\nlocks inactive workspaces created from this template.", + "type": "integer" + }, "failure_ttl_ms": { "description": "FailureTTLMillis allows optionally specifying the max lifetime before Coder\nstops all resources for failed workspaces created from this template.", "type": "integer" @@ -6604,14 +6612,6 @@ "description": "Icon is a relative path or external URL that specifies\nan icon to be displayed in the dashboard.", "type": "string" }, - "inactivity_ttl_ms": { - "description": "InactivityTTLMillis allows optionally specifying the max lifetime before Coder\nlocks inactive workspaces created from this template.", - "type": "integer" - }, - "locked_ttl_ms": { - "description": "LockedTTLMillis allows optionally specifying the max lifetime before Coder\npermanently deletes locked workspaces created from this template.", - "type": "integer" - }, "max_ttl_ms": { "description": "TODO(@dean): remove max_ttl once restart_requirement is matured", "type": "integer" @@ -8603,7 +8603,7 @@ "type": "string" }, "failure_ttl_ms": { - "description": "FailureTTLMillis, InactivityTTLMillis, and LockedTTLMillis are enterprise-only. Their\nvalues are used if your license is entitled to use the advanced\ntemplate scheduling feature.", + "description": "FailureTTLMillis, TimeTilDormantMillis, and TimeTilDormantAutoDeleteMillis are enterprise-only. Their\nvalues are used if your license is entitled to use the advanced\ntemplate scheduling feature.", "type": "integer" }, "icon": { @@ -8613,12 +8613,6 @@ "type": "string", "format": "uuid" }, - "inactivity_ttl_ms": { - "type": "integer" - }, - "locked_ttl_ms": { - "type": "integer" - }, "max_ttl_ms": { "description": "TODO(@dean): remove max_ttl once restart_requirement is matured", "type": "integer" @@ -8642,6 +8636,12 @@ } ] }, + "time_til_dormant_autodelete_ms": { + "type": "integer" + }, + "time_til_dormant_ms": { + "type": "integer" + }, "updated_at": { "type": "string", "format": "date-time" @@ -9268,10 +9268,10 @@ } } }, - "codersdk.UpdateWorkspaceLock": { + "codersdk.UpdateWorkspaceDormancy": { "type": "object", "properties": { - "lock": { + "dormant": { "type": "boolean" } } @@ -9506,7 +9506,12 @@ "format": "date-time" }, "deleting_at": { - "description": "DeletingAt indicates the time of the upcoming workspace deletion, if applicable; otherwise it is nil.\nWorkspaces may have impending deletions if Template.InactivityTTL feature is turned on and the workspace is inactive.", + "description": "DeletingAt indicates the time at which the workspace will be permanently deleted.\nA workspace is eligible for deletion if it is dormant (a non-nil dormant_at value)\nand a value has been specified for time_til_dormant_autodelete on its template.", + "type": "string", + "format": "date-time" + }, + "dormant_at": { + "description": "DormantAt being non-nil indicates a workspace that is dormant.\nA dormant workspace is no longer accessible must be activated.\nIt is subject to deletion if it breaches\nthe duration of the time_til_ field on its template.", "type": "string", "format": "date-time" }, @@ -9529,11 +9534,6 @@ "latest_build": { "$ref": "#/definitions/codersdk.WorkspaceBuild" }, - "locked_at": { - "description": "LockedAt being non-nil indicates a workspace that has been locked.\nA locked workspace is no longer accessible by a user and must be\nunlocked by an admin. It is subject to deletion if it breaches\nthe duration of the locked_ttl field on its template.", - "type": "string", - "format": "date-time" - }, "name": { "type": "string" }, diff --git a/coderd/autobuild/lifecycle_executor.go b/coderd/autobuild/lifecycle_executor.go index 3ce0aad5a4202..f603d7895531d 100644 --- a/coderd/autobuild/lifecycle_executor.go +++ b/coderd/autobuild/lifecycle_executor.go @@ -175,35 +175,35 @@ func (e *Executor) runOnce(t time.Time) Stats { } } - // Lock the workspace if it has breached the template's + // Transition the workspace to dormant if it has breached the template's // threshold for inactivity. if reason == database.BuildReasonAutolock { - ws, err = tx.UpdateWorkspaceLockedDeletingAt(e.ctx, database.UpdateWorkspaceLockedDeletingAtParams{ + ws, err = tx.UpdateWorkspaceDormantDeletingAt(e.ctx, database.UpdateWorkspaceDormantDeletingAtParams{ ID: ws.ID, - LockedAt: sql.NullTime{ + DormantAt: sql.NullTime{ Time: database.Now(), Valid: true, }, }) if err != nil { - log.Error(e.ctx, "unable to lock workspace", + log.Error(e.ctx, "unable to transition workspace to dormant", slog.F("transition", nextTransition), slog.Error(err), ) return nil } - log.Info(e.ctx, "locked workspace", + log.Info(e.ctx, "dormant workspace", slog.F("last_used_at", ws.LastUsedAt), - slog.F("inactivity_ttl", templateSchedule.InactivityTTL), + slog.F("time_til_dormant", templateSchedule.TimeTilDormant), slog.F("since_last_used_at", time.Since(ws.LastUsedAt)), ) } if reason == database.BuildReasonAutodelete { log.Info(e.ctx, "deleted workspace", - slog.F("locked_at", ws.LockedAt.Time), - slog.F("locked_ttl", templateSchedule.LockedTTL), + slog.F("dormant_at", ws.DormantAt.Time), + slog.F("time_til_dormant_autodelete", templateSchedule.TimeTilDormantAutoDelete), ) } @@ -246,7 +246,7 @@ func (e *Executor) runOnce(t time.Time) Stats { // for this function to return a nil error as well as an empty transition. // In such cases it means no provisioning should occur but the workspace // may be "transitioning" to a new state (such as an inactive, stopped -// workspace transitioning to the locked state). +// workspace transitioning to the dormant state). func getNextTransition( ws database.Workspace, latestBuild database.WorkspaceBuild, @@ -265,13 +265,13 @@ func getNextTransition( return database.WorkspaceTransitionStart, database.BuildReasonAutostart, nil case isEligibleForFailedStop(latestBuild, latestJob, templateSchedule, currentTick): return database.WorkspaceTransitionStop, database.BuildReasonAutostop, nil - case isEligibleForLockedStop(ws, templateSchedule, currentTick): + case isEligibleForDormantStop(ws, templateSchedule, currentTick): // Only stop started workspaces. if latestBuild.Transition == database.WorkspaceTransitionStart { return database.WorkspaceTransitionStop, database.BuildReasonAutolock, nil } // We shouldn't transition the workspace but we should still - // lock it. + // make it dormant. return "", database.BuildReasonAutolock, nil case isEligibleForDelete(ws, templateSchedule, currentTick): @@ -288,8 +288,8 @@ func isEligibleForAutostart(ws database.Workspace, build database.WorkspaceBuild return false } - // If the workspace is locked we should not autostart it. - if ws.LockedAt.Valid { + // If the workspace is dormant we should not autostart it. + if ws.DormantAt.Valid { return false } @@ -322,8 +322,8 @@ func isEligibleForAutostop(ws database.Workspace, build database.WorkspaceBuild, return false } - // If the workspace is locked we should not autostop it. - if ws.LockedAt.Valid { + // If the workspace is dormant we should not autostop it. + if ws.DormantAt.Valid { return false } @@ -334,23 +334,23 @@ func isEligibleForAutostop(ws database.Workspace, build database.WorkspaceBuild, !currentTick.Before(build.Deadline) } -// isEligibleForLockedStop returns true if the workspace should be locked +// isEligibleForDormantStop returns true if the workspace should be dormant // for breaching the inactivity threshold of the template. -func isEligibleForLockedStop(ws database.Workspace, templateSchedule schedule.TemplateScheduleOptions, currentTick time.Time) bool { - // Only attempt to lock workspaces not already locked. - return !ws.LockedAt.Valid && - // The template must specify an inactivity TTL. - templateSchedule.InactivityTTL > 0 && - // The workspace must breach the inactivity TTL. - currentTick.Sub(ws.LastUsedAt) > templateSchedule.InactivityTTL +func isEligibleForDormantStop(ws database.Workspace, templateSchedule schedule.TemplateScheduleOptions, currentTick time.Time) bool { + // Only attempt against workspaces not already dormant. + return !ws.DormantAt.Valid && + // The template must specify an time_til_dormant value. + templateSchedule.TimeTilDormant > 0 && + // The workspace must breach the time_til_dormant value. + currentTick.Sub(ws.LastUsedAt) > templateSchedule.TimeTilDormant } func isEligibleForDelete(ws database.Workspace, templateSchedule schedule.TemplateScheduleOptions, currentTick time.Time) bool { - // Only attempt to delete locked workspaces. - return ws.LockedAt.Valid && ws.DeletingAt.Valid && - // Locked workspaces should only be deleted if a locked_ttl is specified. - templateSchedule.LockedTTL > 0 && - // The workspace must breach the locked_ttl. + // Only attempt to delete dormant workspaces. + return ws.DormantAt.Valid && ws.DeletingAt.Valid && + // Dormant workspaces should only be deleted if a time_til_dormant_autodelete value is specified. + templateSchedule.TimeTilDormantAutoDelete > 0 && + // The workspace must breach the time_til_dormant_autodelete value. currentTick.After(ws.DeletingAt.Time) } diff --git a/coderd/autobuild/lifecycle_executor_test.go b/coderd/autobuild/lifecycle_executor_test.go index b1b854167e4b2..356926d9bbff9 100644 --- a/coderd/autobuild/lifecycle_executor_test.go +++ b/coderd/autobuild/lifecycle_executor_test.go @@ -737,7 +737,7 @@ func TestExecutorInactiveWorkspace(t *testing.T) { ProvisionApply: echo.ProvisionComplete, }) template := coderdtest.CreateTemplate(t, client, user.OrganizationID, version.ID, func(ctr *codersdk.CreateTemplateRequest) { - ctr.InactivityTTLMillis = ptr.Ref[int64](inactiveTTL.Milliseconds()) + ctr.TimeTilDormantMillis = ptr.Ref[int64](inactiveTTL.Milliseconds()) }) coderdtest.AwaitTemplateVersionJob(t, client, version.ID) ws := coderdtest.CreateWorkspace(t, client, user.OrganizationID, template.ID) diff --git a/coderd/coderd.go b/coderd/coderd.go index 5f3bbab1a0360..f14ce256c8c80 100644 --- a/coderd/coderd.go +++ b/coderd/coderd.go @@ -854,7 +854,7 @@ func New(options *Options) *API { }) r.Get("/watch", api.watchWorkspace) r.Put("/extend", api.putExtendWorkspace) - r.Put("/lock", api.putWorkspaceLock) + r.Put("/dormant", api.putWorkspaceDormant) }) }) r.Route("/workspacebuilds/{workspacebuild}", func(r chi.Router) { diff --git a/coderd/database/dbauthz/dbauthz.go b/coderd/database/dbauthz/dbauthz.go index 1fc1c9782235e..9115e9b5ac184 100644 --- a/coderd/database/dbauthz/dbauthz.go +++ b/coderd/database/dbauthz/dbauthz.go @@ -2636,18 +2636,18 @@ func (q *querier) UpdateWorkspaceDeletedByID(ctx context.Context, arg database.U return deleteQ(q.log, q.auth, fetch, q.db.UpdateWorkspaceDeletedByID)(ctx, arg) } -func (q *querier) UpdateWorkspaceLastUsedAt(ctx context.Context, arg database.UpdateWorkspaceLastUsedAtParams) error { - fetch := func(ctx context.Context, arg database.UpdateWorkspaceLastUsedAtParams) (database.Workspace, error) { +func (q *querier) UpdateWorkspaceDormantDeletingAt(ctx context.Context, arg database.UpdateWorkspaceDormantDeletingAtParams) (database.Workspace, error) { + fetch := func(ctx context.Context, arg database.UpdateWorkspaceDormantDeletingAtParams) (database.Workspace, error) { return q.db.GetWorkspaceByID(ctx, arg.ID) } - return update(q.log, q.auth, fetch, q.db.UpdateWorkspaceLastUsedAt)(ctx, arg) + return updateWithReturn(q.log, q.auth, fetch, q.db.UpdateWorkspaceDormantDeletingAt)(ctx, arg) } -func (q *querier) UpdateWorkspaceLockedDeletingAt(ctx context.Context, arg database.UpdateWorkspaceLockedDeletingAtParams) (database.Workspace, error) { - fetch := func(ctx context.Context, arg database.UpdateWorkspaceLockedDeletingAtParams) (database.Workspace, error) { +func (q *querier) UpdateWorkspaceLastUsedAt(ctx context.Context, arg database.UpdateWorkspaceLastUsedAtParams) error { + fetch := func(ctx context.Context, arg database.UpdateWorkspaceLastUsedAtParams) (database.Workspace, error) { return q.db.GetWorkspaceByID(ctx, arg.ID) } - return updateWithReturn(q.log, q.auth, fetch, q.db.UpdateWorkspaceLockedDeletingAt)(ctx, arg) + return update(q.log, q.auth, fetch, q.db.UpdateWorkspaceLastUsedAt)(ctx, arg) } func (q *querier) UpdateWorkspaceProxy(ctx context.Context, arg database.UpdateWorkspaceProxyParams) (database.WorkspaceProxy, error) { @@ -2671,12 +2671,12 @@ func (q *querier) UpdateWorkspaceTTL(ctx context.Context, arg database.UpdateWor return update(q.log, q.auth, fetch, q.db.UpdateWorkspaceTTL)(ctx, arg) } -func (q *querier) UpdateWorkspacesLockedDeletingAtByTemplateID(ctx context.Context, arg database.UpdateWorkspacesLockedDeletingAtByTemplateIDParams) error { - fetch := func(ctx context.Context, arg database.UpdateWorkspacesLockedDeletingAtByTemplateIDParams) (database.Template, error) { +func (q *querier) UpdateWorkspacesDormantDeletingAtByTemplateID(ctx context.Context, arg database.UpdateWorkspacesDormantDeletingAtByTemplateIDParams) error { + fetch := func(ctx context.Context, arg database.UpdateWorkspacesDormantDeletingAtByTemplateIDParams) (database.Template, error) { return q.db.GetTemplateByID(ctx, arg.TemplateID) } - return fetchAndExec(q.log, q.auth, rbac.ActionUpdate, fetch, q.db.UpdateWorkspacesLockedDeletingAtByTemplateID)(ctx, arg) + return fetchAndExec(q.log, q.auth, rbac.ActionUpdate, fetch, q.db.UpdateWorkspacesDormantDeletingAtByTemplateID)(ctx, arg) } func (q *querier) UpsertAppSecurityKey(ctx context.Context, data string) error { diff --git a/coderd/database/dbfake/dbfake.go b/coderd/database/dbfake/dbfake.go index 605f8000afba4..476b96cff024a 100644 --- a/coderd/database/dbfake/dbfake.go +++ b/coderd/database/dbfake/dbfake.go @@ -341,7 +341,7 @@ func (q *FakeQuerier) convertToWorkspaceRowsNoLock(ctx context.Context, workspac AutostartSchedule: w.AutostartSchedule, Ttl: w.Ttl, LastUsedAt: w.LastUsedAt, - LockedAt: w.LockedAt, + DormantAt: w.DormantAt, DeletingAt: w.DeletingAt, Count: count, } @@ -3735,14 +3735,14 @@ func (q *FakeQuerier) GetWorkspacesEligibleForTransition(ctx context.Context, no if build.Transition == database.WorkspaceTransitionStart && !build.Deadline.IsZero() && build.Deadline.Before(now) && - !workspace.LockedAt.Valid { + !workspace.DormantAt.Valid { workspaces = append(workspaces, workspace) continue } if build.Transition == database.WorkspaceTransitionStop && workspace.AutostartSchedule.Valid && - !workspace.LockedAt.Valid { + !workspace.DormantAt.Valid { workspaces = append(workspaces, workspace) continue } @@ -3760,11 +3760,11 @@ func (q *FakeQuerier) GetWorkspacesEligibleForTransition(ctx context.Context, no if err != nil { return nil, xerrors.Errorf("get template by ID: %w", err) } - if !workspace.LockedAt.Valid && template.InactivityTTL > 0 { + if !workspace.DormantAt.Valid && template.TimeTilDormant > 0 { workspaces = append(workspaces, workspace) continue } - if workspace.LockedAt.Valid && template.LockedTTL > 0 { + if workspace.DormantAt.Valid && template.TimeTilDormantAutoDelete > 0 { workspaces = append(workspaces, workspace) continue } @@ -5128,8 +5128,8 @@ func (q *FakeQuerier) UpdateTemplateScheduleByID(_ context.Context, arg database tpl.RestartRequirementDaysOfWeek = arg.RestartRequirementDaysOfWeek tpl.RestartRequirementWeeks = arg.RestartRequirementWeeks tpl.FailureTTL = arg.FailureTTL - tpl.InactivityTTL = arg.InactivityTTL - tpl.LockedTTL = arg.LockedTTL + tpl.TimeTilDormant = arg.TimeTilDormant + tpl.TimeTilDormantAutoDelete = arg.TimeTilDormantAutoDelete q.templates[idx] = tpl return nil } @@ -5697,27 +5697,7 @@ func (q *FakeQuerier) UpdateWorkspaceDeletedByID(_ context.Context, arg database return sql.ErrNoRows } -func (q *FakeQuerier) UpdateWorkspaceLastUsedAt(_ context.Context, arg database.UpdateWorkspaceLastUsedAtParams) error { - if err := validateDatabaseType(arg); err != nil { - return err - } - - q.mutex.Lock() - defer q.mutex.Unlock() - - for index, workspace := range q.workspaces { - if workspace.ID != arg.ID { - continue - } - workspace.LastUsedAt = arg.LastUsedAt - q.workspaces[index] = workspace - return nil - } - - return sql.ErrNoRows -} - -func (q *FakeQuerier) UpdateWorkspaceLockedDeletingAt(_ context.Context, arg database.UpdateWorkspaceLockedDeletingAtParams) (database.Workspace, error) { +func (q *FakeQuerier) UpdateWorkspaceDormantDeletingAt(_ context.Context, arg database.UpdateWorkspaceDormantDeletingAtParams) (database.Workspace, error) { if err := validateDatabaseType(arg); err != nil { return database.Workspace{}, err } @@ -5727,12 +5707,12 @@ func (q *FakeQuerier) UpdateWorkspaceLockedDeletingAt(_ context.Context, arg dat if workspace.ID != arg.ID { continue } - workspace.LockedAt = arg.LockedAt - if workspace.LockedAt.Time.IsZero() { + workspace.DormantAt = arg.DormantAt + if workspace.DormantAt.Time.IsZero() { workspace.LastUsedAt = database.Now() workspace.DeletingAt = sql.NullTime{} } - if !workspace.LockedAt.Time.IsZero() { + if !workspace.DormantAt.Time.IsZero() { var template database.TemplateTable for _, t := range q.templates { if t.ID == workspace.TemplateID { @@ -5743,10 +5723,10 @@ func (q *FakeQuerier) UpdateWorkspaceLockedDeletingAt(_ context.Context, arg dat if template.ID == uuid.Nil { return database.Workspace{}, xerrors.Errorf("unable to find workspace template") } - if template.LockedTTL > 0 { + if template.TimeTilDormantAutoDelete > 0 { workspace.DeletingAt = sql.NullTime{ Valid: true, - Time: workspace.LockedAt.Time.Add(time.Duration(template.LockedTTL)), + Time: workspace.DormantAt.Time.Add(time.Duration(template.TimeTilDormantAutoDelete)), } } } @@ -5756,6 +5736,26 @@ func (q *FakeQuerier) UpdateWorkspaceLockedDeletingAt(_ context.Context, arg dat return database.Workspace{}, sql.ErrNoRows } +func (q *FakeQuerier) UpdateWorkspaceLastUsedAt(_ context.Context, arg database.UpdateWorkspaceLastUsedAtParams) error { + if err := validateDatabaseType(arg); err != nil { + return err + } + + q.mutex.Lock() + defer q.mutex.Unlock() + + for index, workspace := range q.workspaces { + if workspace.ID != arg.ID { + continue + } + workspace.LastUsedAt = arg.LastUsedAt + q.workspaces[index] = workspace + return nil + } + + return sql.ErrNoRows +} + func (q *FakeQuerier) UpdateWorkspaceProxy(_ context.Context, arg database.UpdateWorkspaceProxyParams) (database.WorkspaceProxy, error) { q.mutex.Lock() defer q.mutex.Unlock() @@ -5816,7 +5816,7 @@ func (q *FakeQuerier) UpdateWorkspaceTTL(_ context.Context, arg database.UpdateW return sql.ErrNoRows } -func (q *FakeQuerier) UpdateWorkspacesLockedDeletingAtByTemplateID(_ context.Context, arg database.UpdateWorkspacesLockedDeletingAtByTemplateIDParams) error { +func (q *FakeQuerier) UpdateWorkspacesDormantDeletingAtByTemplateID(_ context.Context, arg database.UpdateWorkspacesDormantDeletingAtByTemplateIDParams) error { q.mutex.Lock() defer q.mutex.Unlock() @@ -5830,22 +5830,22 @@ func (q *FakeQuerier) UpdateWorkspacesLockedDeletingAtByTemplateID(_ context.Con continue } - if ws.LockedAt.Time.IsZero() { + if ws.DormantAt.Time.IsZero() { continue } - if !arg.LockedAt.IsZero() { - ws.LockedAt = sql.NullTime{ + if !arg.DormantAt.IsZero() { + ws.DormantAt = sql.NullTime{ Valid: true, - Time: arg.LockedAt, + Time: arg.DormantAt, } } deletingAt := sql.NullTime{ - Valid: arg.LockedTtlMs > 0, + Valid: arg.TimeTilDormantAutodeleteMs > 0, } - if arg.LockedTtlMs > 0 { - deletingAt.Time = ws.LockedAt.Time.Add(time.Duration(arg.LockedTtlMs) * time.Millisecond) + if arg.TimeTilDormantAutodeleteMs > 0 { + deletingAt.Time = ws.DormantAt.Time.Add(time.Duration(arg.TimeTilDormantAutodeleteMs) * time.Millisecond) } ws.DeletingAt = deletingAt q.workspaces[i] = ws @@ -6222,12 +6222,12 @@ func (q *FakeQuerier) GetAuthorizedWorkspaces(ctx context.Context, arg database. } // We omit locked workspaces by default. - if arg.LockedAt.IsZero() && workspace.LockedAt.Valid { + if arg.DormantAt.IsZero() && workspace.DormantAt.Valid { continue } // Filter out workspaces that are locked after the timestamp. - if !arg.LockedAt.IsZero() && workspace.LockedAt.Time.Before(arg.LockedAt) { + if !arg.DormantAt.IsZero() && workspace.DormantAt.Time.Before(arg.DormantAt) { continue } diff --git a/coderd/database/dbmetrics/dbmetrics.go b/coderd/database/dbmetrics/dbmetrics.go index 498ca57b504e4..8526eb4da1078 100644 --- a/coderd/database/dbmetrics/dbmetrics.go +++ b/coderd/database/dbmetrics/dbmetrics.go @@ -1607,6 +1607,13 @@ func (m metricsStore) UpdateWorkspaceDeletedByID(ctx context.Context, arg databa return err } +func (m metricsStore) UpdateWorkspaceDormantDeletingAt(ctx context.Context, arg database.UpdateWorkspaceDormantDeletingAtParams) (database.Workspace, error) { + start := time.Now() + ws, r0 := m.s.UpdateWorkspaceDormantDeletingAt(ctx, arg) + m.queryLatencies.WithLabelValues("UpdateWorkspaceDormantDeletingAt").Observe(time.Since(start).Seconds()) + return ws, r0 +} + func (m metricsStore) UpdateWorkspaceLastUsedAt(ctx context.Context, arg database.UpdateWorkspaceLastUsedAtParams) error { start := time.Now() err := m.s.UpdateWorkspaceLastUsedAt(ctx, arg) @@ -1614,13 +1621,6 @@ func (m metricsStore) UpdateWorkspaceLastUsedAt(ctx context.Context, arg databas return err } -func (m metricsStore) UpdateWorkspaceLockedDeletingAt(ctx context.Context, arg database.UpdateWorkspaceLockedDeletingAtParams) (database.Workspace, error) { - start := time.Now() - ws, r0 := m.s.UpdateWorkspaceLockedDeletingAt(ctx, arg) - m.queryLatencies.WithLabelValues("UpdateWorkspaceLockedDeletingAt").Observe(time.Since(start).Seconds()) - return ws, r0 -} - func (m metricsStore) UpdateWorkspaceProxy(ctx context.Context, arg database.UpdateWorkspaceProxyParams) (database.WorkspaceProxy, error) { start := time.Now() proxy, err := m.s.UpdateWorkspaceProxy(ctx, arg) @@ -1642,10 +1642,10 @@ func (m metricsStore) UpdateWorkspaceTTL(ctx context.Context, arg database.Updat return r0 } -func (m metricsStore) UpdateWorkspacesLockedDeletingAtByTemplateID(ctx context.Context, arg database.UpdateWorkspacesLockedDeletingAtByTemplateIDParams) error { +func (m metricsStore) UpdateWorkspacesDormantDeletingAtByTemplateID(ctx context.Context, arg database.UpdateWorkspacesDormantDeletingAtByTemplateIDParams) error { start := time.Now() - r0 := m.s.UpdateWorkspacesLockedDeletingAtByTemplateID(ctx, arg) - m.queryLatencies.WithLabelValues("UpdateWorkspacesLockedDeletingAtByTemplateID").Observe(time.Since(start).Seconds()) + r0 := m.s.UpdateWorkspacesDormantDeletingAtByTemplateID(ctx, arg) + m.queryLatencies.WithLabelValues("UpdateWorkspacesDormantDeletingAtByTemplateID").Observe(time.Since(start).Seconds()) return r0 } diff --git a/coderd/database/dbmock/dbmock.go b/coderd/database/dbmock/dbmock.go index ac1e782e7d398..b0ae7955a458d 100644 --- a/coderd/database/dbmock/dbmock.go +++ b/coderd/database/dbmock/dbmock.go @@ -3379,6 +3379,21 @@ func (mr *MockStoreMockRecorder) UpdateWorkspaceDeletedByID(arg0, arg1 interface return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpdateWorkspaceDeletedByID", reflect.TypeOf((*MockStore)(nil).UpdateWorkspaceDeletedByID), arg0, arg1) } +// UpdateWorkspaceDormantDeletingAt mocks base method. +func (m *MockStore) UpdateWorkspaceDormantDeletingAt(arg0 context.Context, arg1 database.UpdateWorkspaceDormantDeletingAtParams) (database.Workspace, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "UpdateWorkspaceDormantDeletingAt", arg0, arg1) + ret0, _ := ret[0].(database.Workspace) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// UpdateWorkspaceDormantDeletingAt indicates an expected call of UpdateWorkspaceDormantDeletingAt. +func (mr *MockStoreMockRecorder) UpdateWorkspaceDormantDeletingAt(arg0, arg1 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpdateWorkspaceDormantDeletingAt", reflect.TypeOf((*MockStore)(nil).UpdateWorkspaceDormantDeletingAt), arg0, arg1) +} + // UpdateWorkspaceLastUsedAt mocks base method. func (m *MockStore) UpdateWorkspaceLastUsedAt(arg0 context.Context, arg1 database.UpdateWorkspaceLastUsedAtParams) error { m.ctrl.T.Helper() @@ -3393,21 +3408,6 @@ func (mr *MockStoreMockRecorder) UpdateWorkspaceLastUsedAt(arg0, arg1 interface{ return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpdateWorkspaceLastUsedAt", reflect.TypeOf((*MockStore)(nil).UpdateWorkspaceLastUsedAt), arg0, arg1) } -// UpdateWorkspaceLockedDeletingAt mocks base method. -func (m *MockStore) UpdateWorkspaceLockedDeletingAt(arg0 context.Context, arg1 database.UpdateWorkspaceLockedDeletingAtParams) (database.Workspace, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "UpdateWorkspaceLockedDeletingAt", arg0, arg1) - ret0, _ := ret[0].(database.Workspace) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// UpdateWorkspaceLockedDeletingAt indicates an expected call of UpdateWorkspaceLockedDeletingAt. -func (mr *MockStoreMockRecorder) UpdateWorkspaceLockedDeletingAt(arg0, arg1 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpdateWorkspaceLockedDeletingAt", reflect.TypeOf((*MockStore)(nil).UpdateWorkspaceLockedDeletingAt), arg0, arg1) -} - // UpdateWorkspaceProxy mocks base method. func (m *MockStore) UpdateWorkspaceProxy(arg0 context.Context, arg1 database.UpdateWorkspaceProxyParams) (database.WorkspaceProxy, error) { m.ctrl.T.Helper() @@ -3451,18 +3451,18 @@ func (mr *MockStoreMockRecorder) UpdateWorkspaceTTL(arg0, arg1 interface{}) *gom return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpdateWorkspaceTTL", reflect.TypeOf((*MockStore)(nil).UpdateWorkspaceTTL), arg0, arg1) } -// UpdateWorkspacesLockedDeletingAtByTemplateID mocks base method. -func (m *MockStore) UpdateWorkspacesLockedDeletingAtByTemplateID(arg0 context.Context, arg1 database.UpdateWorkspacesLockedDeletingAtByTemplateIDParams) error { +// UpdateWorkspacesDormantDeletingAtByTemplateID mocks base method. +func (m *MockStore) UpdateWorkspacesDormantDeletingAtByTemplateID(arg0 context.Context, arg1 database.UpdateWorkspacesDormantDeletingAtByTemplateIDParams) error { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "UpdateWorkspacesLockedDeletingAtByTemplateID", arg0, arg1) + ret := m.ctrl.Call(m, "UpdateWorkspacesDormantDeletingAtByTemplateID", arg0, arg1) ret0, _ := ret[0].(error) return ret0 } -// UpdateWorkspacesLockedDeletingAtByTemplateID indicates an expected call of UpdateWorkspacesLockedDeletingAtByTemplateID. -func (mr *MockStoreMockRecorder) UpdateWorkspacesLockedDeletingAtByTemplateID(arg0, arg1 interface{}) *gomock.Call { +// UpdateWorkspacesDormantDeletingAtByTemplateID indicates an expected call of UpdateWorkspacesDormantDeletingAtByTemplateID. +func (mr *MockStoreMockRecorder) UpdateWorkspacesDormantDeletingAtByTemplateID(arg0, arg1 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpdateWorkspacesLockedDeletingAtByTemplateID", reflect.TypeOf((*MockStore)(nil).UpdateWorkspacesLockedDeletingAtByTemplateID), arg0, arg1) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpdateWorkspacesDormantDeletingAtByTemplateID", reflect.TypeOf((*MockStore)(nil).UpdateWorkspacesDormantDeletingAtByTemplateID), arg0, arg1) } // UpsertAppSecurityKey mocks base method. diff --git a/coderd/database/dump.sql b/coderd/database/dump.sql index 71106e5da771d..a2767c9cfd5e1 100644 --- a/coderd/database/dump.sql +++ b/coderd/database/dump.sql @@ -635,8 +635,8 @@ CREATE TABLE templates ( allow_user_autostart boolean DEFAULT true NOT NULL, allow_user_autostop boolean DEFAULT true NOT NULL, failure_ttl bigint DEFAULT 0 NOT NULL, - inactivity_ttl bigint DEFAULT 0 NOT NULL, - locked_ttl bigint DEFAULT 0 NOT NULL, + time_til_dormant bigint DEFAULT 0 NOT NULL, + time_til_dormant_autodelete bigint DEFAULT 0 NOT NULL, restart_requirement_days_of_week smallint DEFAULT 0 NOT NULL, restart_requirement_weeks bigint DEFAULT 0 NOT NULL ); @@ -676,8 +676,8 @@ CREATE VIEW template_with_users AS templates.allow_user_autostart, templates.allow_user_autostop, templates.failure_ttl, - templates.inactivity_ttl, - templates.locked_ttl, + templates.time_til_dormant, + templates.time_til_dormant_autodelete, templates.restart_requirement_days_of_week, templates.restart_requirement_weeks, COALESCE(visible_users.avatar_url, ''::text) AS created_by_avatar_url, @@ -1003,7 +1003,7 @@ CREATE TABLE workspaces ( autostart_schedule text, ttl bigint, last_used_at timestamp without time zone DEFAULT '0001-01-01 00:00:00'::timestamp without time zone NOT NULL, - locked_at timestamp with time zone, + dormant_at timestamp with time zone, deleting_at timestamp with time zone ); diff --git a/coderd/database/migrations/000151_rename_locked.down.sql b/coderd/database/migrations/000151_rename_locked.down.sql new file mode 100644 index 0000000000000..4dfb254268fa2 --- /dev/null +++ b/coderd/database/migrations/000151_rename_locked.down.sql @@ -0,0 +1,26 @@ +BEGIN; + +ALTER TABLE templates RENAME COLUMN time_til_dormant TO inactivity_ttl; +ALTER TABLE templates RENAME COLUMN time_til_dormant_autodelete TO locked_ttl; +ALTER TABLE workspaces RENAME COLUMN dormant_at TO locked_at; + +-- Update the template_with_users view; +DROP VIEW template_with_users; +-- If you need to update this view, put 'DROP VIEW template_with_users;' before this. +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.'; + +COMMIT; diff --git a/coderd/database/migrations/000151_rename_locked.up.sql b/coderd/database/migrations/000151_rename_locked.up.sql new file mode 100644 index 0000000000000..ae72c7efa98cb --- /dev/null +++ b/coderd/database/migrations/000151_rename_locked.up.sql @@ -0,0 +1,25 @@ +BEGIN; +ALTER TABLE templates RENAME COLUMN inactivity_ttl TO time_til_dormant; +ALTER TABLE templates RENAME COLUMN locked_ttl TO time_til_dormant_autodelete; +ALTER TABLE workspaces RENAME COLUMN locked_at TO dormant_at; + +-- Update the template_with_users view;a +DROP VIEW template_with_users; +-- If you need to update this view, put 'DROP VIEW template_with_users;' before this. +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.'; + +COMMIT; diff --git a/coderd/database/modelmethods.go b/coderd/database/modelmethods.go index 364b812ac3c94..1cccdd949ecc8 100644 --- a/coderd/database/modelmethods.go +++ b/coderd/database/modelmethods.go @@ -146,8 +146,8 @@ func (w Workspace) RBACObject() rbac.Object { func (w Workspace) ExecutionRBAC() rbac.Object { // If a workspace is locked it cannot be accessed. - if w.LockedAt.Valid { - return w.LockedRBAC() + if w.DormantAt.Valid { + return w.DormantRBAC() } return rbac.ResourceWorkspaceExecution. @@ -158,8 +158,8 @@ func (w Workspace) ExecutionRBAC() rbac.Object { func (w Workspace) ApplicationConnectRBAC() rbac.Object { // If a workspace is locked it cannot be accessed. - if w.LockedAt.Valid { - return w.LockedRBAC() + if w.DormantAt.Valid { + return w.DormantRBAC() } return rbac.ResourceWorkspaceApplicationConnect. @@ -173,9 +173,9 @@ func (w Workspace) WorkspaceBuildRBAC(transition WorkspaceTransition) rbac.Objec // However we need to allow stopping a workspace by a caller once a workspace // is locked (e.g. for autobuild). Additionally, if a user wants to delete // a locked workspace, they shouldn't have to have it unlocked first. - if w.LockedAt.Valid && transition != WorkspaceTransitionStop && + if w.DormantAt.Valid && transition != WorkspaceTransitionStop && transition != WorkspaceTransitionDelete { - return w.LockedRBAC() + return w.DormantRBAC() } return rbac.ResourceWorkspaceBuild. @@ -184,8 +184,8 @@ func (w Workspace) WorkspaceBuildRBAC(transition WorkspaceTransition) rbac.Objec WithOwner(w.OwnerID.String()) } -func (w Workspace) LockedRBAC() rbac.Object { - return rbac.ResourceWorkspaceLocked. +func (w Workspace) DormantRBAC() rbac.Object { + return rbac.ResourceWorkspaceDormant. WithID(w.ID). InOrg(w.OrganizationID). WithOwner(w.OwnerID.String()) @@ -355,7 +355,7 @@ func ConvertWorkspaceRows(rows []GetWorkspacesRow) []Workspace { AutostartSchedule: r.AutostartSchedule, Ttl: r.Ttl, LastUsedAt: r.LastUsedAt, - LockedAt: r.LockedAt, + DormantAt: r.DormantAt, DeletingAt: r.DeletingAt, } } diff --git a/coderd/database/modelqueries.go b/coderd/database/modelqueries.go index 193a046f5cec1..5ccf3282e677c 100644 --- a/coderd/database/modelqueries.go +++ b/coderd/database/modelqueries.go @@ -81,8 +81,8 @@ func (q *sqlQuerier) GetAuthorizedTemplates(ctx context.Context, arg GetTemplate &i.AllowUserAutostart, &i.AllowUserAutostop, &i.FailureTTL, - &i.InactivityTTL, - &i.LockedTTL, + &i.TimeTilDormant, + &i.TimeTilDormantAutoDelete, &i.RestartRequirementDaysOfWeek, &i.RestartRequirementWeeks, &i.CreatedByAvatarURL, @@ -217,7 +217,7 @@ func (q *sqlQuerier) GetAuthorizedWorkspaces(ctx context.Context, arg GetWorkspa arg.Name, arg.HasAgent, arg.AgentInactiveDisconnectTimeoutSeconds, - arg.LockedAt, + arg.DormantAt, arg.LastUsedBefore, arg.LastUsedAfter, arg.Offset, @@ -242,7 +242,7 @@ func (q *sqlQuerier) GetAuthorizedWorkspaces(ctx context.Context, arg GetWorkspa &i.AutostartSchedule, &i.Ttl, &i.LastUsedAt, - &i.LockedAt, + &i.DormantAt, &i.DeletingAt, &i.TemplateName, &i.TemplateVersionID, diff --git a/coderd/database/models.go b/coderd/database/models.go index e795049c16413..85f90020d9fc1 100644 --- a/coderd/database/models.go +++ b/coderd/database/models.go @@ -1729,8 +1729,8 @@ type Template struct { 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"` - InactivityTTL int64 `db:"inactivity_ttl" json:"inactivity_ttl"` - LockedTTL int64 `db:"locked_ttl" json:"locked_ttl"` + TimeTilDormant int64 `db:"time_til_dormant" json:"time_til_dormant"` + TimeTilDormantAutoDelete int64 `db:"time_til_dormant_autodelete" json:"time_til_dormant_autodelete"` RestartRequirementDaysOfWeek int16 `db:"restart_requirement_days_of_week" json:"restart_requirement_days_of_week"` RestartRequirementWeeks int64 `db:"restart_requirement_weeks" json:"restart_requirement_weeks"` CreatedByAvatarURL sql.NullString `db:"created_by_avatar_url" json:"created_by_avatar_url"` @@ -1761,10 +1761,10 @@ type TemplateTable struct { // 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). - AllowUserAutostop bool `db:"allow_user_autostop" json:"allow_user_autostop"` - FailureTTL int64 `db:"failure_ttl" json:"failure_ttl"` - InactivityTTL int64 `db:"inactivity_ttl" json:"inactivity_ttl"` - LockedTTL int64 `db:"locked_ttl" json:"locked_ttl"` + AllowUserAutostop bool `db:"allow_user_autostop" json:"allow_user_autostop"` + FailureTTL int64 `db:"failure_ttl" json:"failure_ttl"` + TimeTilDormant int64 `db:"time_til_dormant" json:"time_til_dormant"` + TimeTilDormantAutoDelete int64 `db:"time_til_dormant_autodelete" json:"time_til_dormant_autodelete"` // A bitmap of days of week to restart the workspace on, starting with Monday as the 0th bit, and Sunday as the 6th bit. The 7th bit is unused. RestartRequirementDaysOfWeek int16 `db:"restart_requirement_days_of_week" json:"restart_requirement_days_of_week"` // The number of weeks between restarts. 0 or 1 weeks means "every week", 2 week means "every second week", etc. Weeks are counted from January 2, 2023, which is the first Monday of 2023. This is to ensure workspaces are started consistently for all customers on the same n-week cycles. @@ -1903,7 +1903,7 @@ type Workspace struct { AutostartSchedule sql.NullString `db:"autostart_schedule" json:"autostart_schedule"` Ttl sql.NullInt64 `db:"ttl" json:"ttl"` LastUsedAt time.Time `db:"last_used_at" json:"last_used_at"` - LockedAt sql.NullTime `db:"locked_at" json:"locked_at"` + DormantAt sql.NullTime `db:"dormant_at" json:"dormant_at"` DeletingAt sql.NullTime `db:"deleting_at" json:"deleting_at"` } diff --git a/coderd/database/querier.go b/coderd/database/querier.go index a217648035e90..520266bd1d25c 100644 --- a/coderd/database/querier.go +++ b/coderd/database/querier.go @@ -292,13 +292,13 @@ type sqlcQuerier interface { UpdateWorkspaceBuildByID(ctx context.Context, arg UpdateWorkspaceBuildByIDParams) error UpdateWorkspaceBuildCostByID(ctx context.Context, arg UpdateWorkspaceBuildCostByIDParams) error UpdateWorkspaceDeletedByID(ctx context.Context, arg UpdateWorkspaceDeletedByIDParams) error + UpdateWorkspaceDormantDeletingAt(ctx context.Context, arg UpdateWorkspaceDormantDeletingAtParams) (Workspace, error) UpdateWorkspaceLastUsedAt(ctx context.Context, arg UpdateWorkspaceLastUsedAtParams) error - UpdateWorkspaceLockedDeletingAt(ctx context.Context, arg UpdateWorkspaceLockedDeletingAtParams) (Workspace, error) // This allows editing the properties of a workspace proxy. UpdateWorkspaceProxy(ctx context.Context, arg UpdateWorkspaceProxyParams) (WorkspaceProxy, error) UpdateWorkspaceProxyDeleted(ctx context.Context, arg UpdateWorkspaceProxyDeletedParams) error UpdateWorkspaceTTL(ctx context.Context, arg UpdateWorkspaceTTLParams) error - UpdateWorkspacesLockedDeletingAtByTemplateID(ctx context.Context, arg UpdateWorkspacesLockedDeletingAtByTemplateIDParams) error + UpdateWorkspacesDormantDeletingAtByTemplateID(ctx context.Context, arg UpdateWorkspacesDormantDeletingAtByTemplateIDParams) error UpsertAppSecurityKey(ctx context.Context, value string) error // The default proxy is implied and not actually stored in the database. // So we need to store it's configuration here for display purposes. diff --git a/coderd/database/queries.sql.go b/coderd/database/queries.sql.go index f1bb80a6e0afd..04860f7127d43 100644 --- a/coderd/database/queries.sql.go +++ b/coderd/database/queries.sql.go @@ -4339,7 +4339,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, inactivity_ttl, locked_ttl, restart_requirement_days_of_week, restart_requirement_weeks, 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, max_ttl, allow_user_autostart, allow_user_autostop, failure_ttl, time_til_dormant, time_til_dormant_autodelete, restart_requirement_days_of_week, restart_requirement_weeks, created_by_avatar_url, created_by_username FROM template_with_users WHERE @@ -4372,8 +4372,8 @@ func (q *sqlQuerier) GetTemplateByID(ctx context.Context, id uuid.UUID) (Templat &i.AllowUserAutostart, &i.AllowUserAutostop, &i.FailureTTL, - &i.InactivityTTL, - &i.LockedTTL, + &i.TimeTilDormant, + &i.TimeTilDormantAutoDelete, &i.RestartRequirementDaysOfWeek, &i.RestartRequirementWeeks, &i.CreatedByAvatarURL, @@ -4384,7 +4384,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, inactivity_ttl, locked_ttl, restart_requirement_days_of_week, restart_requirement_weeks, 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, max_ttl, allow_user_autostart, allow_user_autostop, failure_ttl, time_til_dormant, time_til_dormant_autodelete, restart_requirement_days_of_week, restart_requirement_weeks, created_by_avatar_url, created_by_username FROM template_with_users AS templates WHERE @@ -4425,8 +4425,8 @@ func (q *sqlQuerier) GetTemplateByOrganizationAndName(ctx context.Context, arg G &i.AllowUserAutostart, &i.AllowUserAutostop, &i.FailureTTL, - &i.InactivityTTL, - &i.LockedTTL, + &i.TimeTilDormant, + &i.TimeTilDormantAutoDelete, &i.RestartRequirementDaysOfWeek, &i.RestartRequirementWeeks, &i.CreatedByAvatarURL, @@ -4436,7 +4436,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, inactivity_ttl, locked_ttl, restart_requirement_days_of_week, restart_requirement_weeks, 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, max_ttl, allow_user_autostart, allow_user_autostop, failure_ttl, time_til_dormant, time_til_dormant_autodelete, restart_requirement_days_of_week, restart_requirement_weeks, created_by_avatar_url, created_by_username FROM template_with_users AS templates ORDER BY (name, id) ASC ` @@ -4470,8 +4470,8 @@ func (q *sqlQuerier) GetTemplates(ctx context.Context) ([]Template, error) { &i.AllowUserAutostart, &i.AllowUserAutostop, &i.FailureTTL, - &i.InactivityTTL, - &i.LockedTTL, + &i.TimeTilDormant, + &i.TimeTilDormantAutoDelete, &i.RestartRequirementDaysOfWeek, &i.RestartRequirementWeeks, &i.CreatedByAvatarURL, @@ -4492,7 +4492,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, inactivity_ttl, locked_ttl, restart_requirement_days_of_week, restart_requirement_weeks, 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, max_ttl, allow_user_autostart, allow_user_autostop, failure_ttl, time_til_dormant, time_til_dormant_autodelete, restart_requirement_days_of_week, restart_requirement_weeks, created_by_avatar_url, created_by_username FROM template_with_users AS templates WHERE @@ -4563,8 +4563,8 @@ func (q *sqlQuerier) GetTemplatesWithFilter(ctx context.Context, arg GetTemplate &i.AllowUserAutostart, &i.AllowUserAutostop, &i.FailureTTL, - &i.InactivityTTL, - &i.LockedTTL, + &i.TimeTilDormant, + &i.TimeTilDormantAutoDelete, &i.RestartRequirementDaysOfWeek, &i.RestartRequirementWeeks, &i.CreatedByAvatarURL, @@ -4754,8 +4754,8 @@ SET restart_requirement_days_of_week = $7, restart_requirement_weeks = $8, failure_ttl = $9, - inactivity_ttl = $10, - locked_ttl = $11 + time_til_dormant = $10, + time_til_dormant_autodelete = $11 WHERE id = $1 ` @@ -4770,8 +4770,8 @@ type UpdateTemplateScheduleByIDParams struct { RestartRequirementDaysOfWeek int16 `db:"restart_requirement_days_of_week" json:"restart_requirement_days_of_week"` RestartRequirementWeeks int64 `db:"restart_requirement_weeks" json:"restart_requirement_weeks"` FailureTTL int64 `db:"failure_ttl" json:"failure_ttl"` - InactivityTTL int64 `db:"inactivity_ttl" json:"inactivity_ttl"` - LockedTTL int64 `db:"locked_ttl" json:"locked_ttl"` + TimeTilDormant int64 `db:"time_til_dormant" json:"time_til_dormant"` + TimeTilDormantAutoDelete int64 `db:"time_til_dormant_autodelete" json:"time_til_dormant_autodelete"` } func (q *sqlQuerier) UpdateTemplateScheduleByID(ctx context.Context, arg UpdateTemplateScheduleByIDParams) error { @@ -4785,8 +4785,8 @@ func (q *sqlQuerier) UpdateTemplateScheduleByID(ctx context.Context, arg UpdateT arg.RestartRequirementDaysOfWeek, arg.RestartRequirementWeeks, arg.FailureTTL, - arg.InactivityTTL, - arg.LockedTTL, + arg.TimeTilDormant, + arg.TimeTilDormantAutoDelete, ) return err } @@ -9126,7 +9126,7 @@ func (q *sqlQuerier) GetDeploymentWorkspaceStats(ctx context.Context) (GetDeploy const getWorkspaceByAgentID = `-- name: GetWorkspaceByAgentID :one SELECT - id, created_at, updated_at, owner_id, organization_id, template_id, deleted, name, autostart_schedule, ttl, last_used_at, locked_at, deleting_at + id, created_at, updated_at, owner_id, organization_id, template_id, deleted, name, autostart_schedule, ttl, last_used_at, dormant_at, deleting_at FROM workspaces WHERE @@ -9169,7 +9169,7 @@ func (q *sqlQuerier) GetWorkspaceByAgentID(ctx context.Context, agentID uuid.UUI &i.AutostartSchedule, &i.Ttl, &i.LastUsedAt, - &i.LockedAt, + &i.DormantAt, &i.DeletingAt, ) return i, err @@ -9177,7 +9177,7 @@ func (q *sqlQuerier) GetWorkspaceByAgentID(ctx context.Context, agentID uuid.UUI const getWorkspaceByID = `-- name: GetWorkspaceByID :one SELECT - id, created_at, updated_at, owner_id, organization_id, template_id, deleted, name, autostart_schedule, ttl, last_used_at, locked_at, deleting_at + id, created_at, updated_at, owner_id, organization_id, template_id, deleted, name, autostart_schedule, ttl, last_used_at, dormant_at, deleting_at FROM workspaces WHERE @@ -9201,7 +9201,7 @@ func (q *sqlQuerier) GetWorkspaceByID(ctx context.Context, id uuid.UUID) (Worksp &i.AutostartSchedule, &i.Ttl, &i.LastUsedAt, - &i.LockedAt, + &i.DormantAt, &i.DeletingAt, ) return i, err @@ -9209,7 +9209,7 @@ func (q *sqlQuerier) GetWorkspaceByID(ctx context.Context, id uuid.UUID) (Worksp const getWorkspaceByOwnerIDAndName = `-- name: GetWorkspaceByOwnerIDAndName :one SELECT - id, created_at, updated_at, owner_id, organization_id, template_id, deleted, name, autostart_schedule, ttl, last_used_at, locked_at, deleting_at + id, created_at, updated_at, owner_id, organization_id, template_id, deleted, name, autostart_schedule, ttl, last_used_at, dormant_at, deleting_at FROM workspaces WHERE @@ -9240,7 +9240,7 @@ func (q *sqlQuerier) GetWorkspaceByOwnerIDAndName(ctx context.Context, arg GetWo &i.AutostartSchedule, &i.Ttl, &i.LastUsedAt, - &i.LockedAt, + &i.DormantAt, &i.DeletingAt, ) return i, err @@ -9248,7 +9248,7 @@ func (q *sqlQuerier) GetWorkspaceByOwnerIDAndName(ctx context.Context, arg GetWo const getWorkspaceByWorkspaceAppID = `-- name: GetWorkspaceByWorkspaceAppID :one SELECT - id, created_at, updated_at, owner_id, organization_id, template_id, deleted, name, autostart_schedule, ttl, last_used_at, locked_at, deleting_at + id, created_at, updated_at, owner_id, organization_id, template_id, deleted, name, autostart_schedule, ttl, last_used_at, dormant_at, deleting_at FROM workspaces WHERE @@ -9298,7 +9298,7 @@ func (q *sqlQuerier) GetWorkspaceByWorkspaceAppID(ctx context.Context, workspace &i.AutostartSchedule, &i.Ttl, &i.LastUsedAt, - &i.LockedAt, + &i.DormantAt, &i.DeletingAt, ) return i, err @@ -9306,7 +9306,7 @@ func (q *sqlQuerier) GetWorkspaceByWorkspaceAppID(ctx context.Context, workspace const getWorkspaces = `-- name: GetWorkspaces :many SELECT - workspaces.id, workspaces.created_at, workspaces.updated_at, workspaces.owner_id, workspaces.organization_id, workspaces.template_id, workspaces.deleted, workspaces.name, workspaces.autostart_schedule, workspaces.ttl, workspaces.last_used_at, workspaces.locked_at, workspaces.deleting_at, + workspaces.id, workspaces.created_at, workspaces.updated_at, workspaces.owner_id, workspaces.organization_id, workspaces.template_id, workspaces.deleted, workspaces.name, workspaces.autostart_schedule, workspaces.ttl, workspaces.last_used_at, workspaces.dormant_at, workspaces.deleting_at, COALESCE(template_name.template_name, 'unknown') as template_name, latest_build.template_version_id, latest_build.template_version_name, @@ -9490,13 +9490,13 @@ WHERE ) > 0 ELSE true END - -- Filter by locked workspaces. By default we do not return locked + -- Filter by dormant workspaces. By default we do not return dormant -- workspaces since they are considered soft-deleted. AND CASE WHEN $10 :: timestamptz > '0001-01-01 00:00:00+00'::timestamptz THEN - locked_at IS NOT NULL AND locked_at >= $10 + dormant_at IS NOT NULL AND dormant_at >= $10 ELSE - locked_at IS NULL + dormant_at IS NULL END -- Filter by last_used AND CASE @@ -9537,7 +9537,7 @@ type GetWorkspacesParams struct { Name string `db:"name" json:"name"` HasAgent string `db:"has_agent" json:"has_agent"` AgentInactiveDisconnectTimeoutSeconds int64 `db:"agent_inactive_disconnect_timeout_seconds" json:"agent_inactive_disconnect_timeout_seconds"` - LockedAt time.Time `db:"locked_at" json:"locked_at"` + DormantAt time.Time `db:"dormant_at" json:"dormant_at"` LastUsedBefore time.Time `db:"last_used_before" json:"last_used_before"` LastUsedAfter time.Time `db:"last_used_after" json:"last_used_after"` Offset int32 `db:"offset_" json:"offset_"` @@ -9556,7 +9556,7 @@ type GetWorkspacesRow struct { AutostartSchedule sql.NullString `db:"autostart_schedule" json:"autostart_schedule"` Ttl sql.NullInt64 `db:"ttl" json:"ttl"` LastUsedAt time.Time `db:"last_used_at" json:"last_used_at"` - LockedAt sql.NullTime `db:"locked_at" json:"locked_at"` + DormantAt sql.NullTime `db:"dormant_at" json:"dormant_at"` DeletingAt sql.NullTime `db:"deleting_at" json:"deleting_at"` TemplateName string `db:"template_name" json:"template_name"` TemplateVersionID uuid.UUID `db:"template_version_id" json:"template_version_id"` @@ -9575,7 +9575,7 @@ func (q *sqlQuerier) GetWorkspaces(ctx context.Context, arg GetWorkspacesParams) arg.Name, arg.HasAgent, arg.AgentInactiveDisconnectTimeoutSeconds, - arg.LockedAt, + arg.DormantAt, arg.LastUsedBefore, arg.LastUsedAfter, arg.Offset, @@ -9600,7 +9600,7 @@ func (q *sqlQuerier) GetWorkspaces(ctx context.Context, arg GetWorkspacesParams) &i.AutostartSchedule, &i.Ttl, &i.LastUsedAt, - &i.LockedAt, + &i.DormantAt, &i.DeletingAt, &i.TemplateName, &i.TemplateVersionID, @@ -9622,7 +9622,7 @@ func (q *sqlQuerier) GetWorkspaces(ctx context.Context, arg GetWorkspacesParams) const getWorkspacesEligibleForTransition = `-- name: GetWorkspacesEligibleForTransition :many SELECT - workspaces.id, workspaces.created_at, workspaces.updated_at, workspaces.owner_id, workspaces.organization_id, workspaces.template_id, workspaces.deleted, workspaces.name, workspaces.autostart_schedule, workspaces.ttl, workspaces.last_used_at, workspaces.locked_at, workspaces.deleting_at + workspaces.id, workspaces.created_at, workspaces.updated_at, workspaces.owner_id, workspaces.organization_id, workspaces.template_id, workspaces.deleted, workspaces.name, workspaces.autostart_schedule, workspaces.ttl, workspaces.last_used_at, workspaces.dormant_at, workspaces.deleting_at FROM workspaces LEFT JOIN @@ -9671,17 +9671,17 @@ WHERE ) OR -- If the workspace's template has an inactivity_ttl set - -- it may be eligible for locking. + -- it may be eligible for dormancy. ( - templates.inactivity_ttl > 0 AND - workspaces.locked_at IS NULL + templates.time_til_dormant > 0 AND + workspaces.dormant_at IS NULL ) OR - -- If the workspace's template has a locked_ttl set - -- and the workspace is already locked + -- If the workspace's template has a time_til_dormant_autodelete set + -- and the workspace is already dormant. ( - templates.locked_ttl > 0 AND - workspaces.locked_at IS NOT NULL + templates.time_til_dormant_autodelete > 0 AND + workspaces.dormant_at IS NOT NULL ) ) AND workspaces.deleted = 'false' ` @@ -9707,7 +9707,7 @@ func (q *sqlQuerier) GetWorkspacesEligibleForTransition(ctx context.Context, now &i.AutostartSchedule, &i.Ttl, &i.LastUsedAt, - &i.LockedAt, + &i.DormantAt, &i.DeletingAt, ); err != nil { return nil, err @@ -9738,7 +9738,7 @@ INSERT INTO last_used_at ) VALUES - ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10) RETURNING id, created_at, updated_at, owner_id, organization_id, template_id, deleted, name, autostart_schedule, ttl, last_used_at, locked_at, deleting_at + ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10) RETURNING id, created_at, updated_at, owner_id, organization_id, template_id, deleted, name, autostart_schedule, ttl, last_used_at, dormant_at, deleting_at ` type InsertWorkspaceParams struct { @@ -9780,7 +9780,7 @@ func (q *sqlQuerier) InsertWorkspace(ctx context.Context, arg InsertWorkspacePar &i.AutostartSchedule, &i.Ttl, &i.LastUsedAt, - &i.LockedAt, + &i.DormantAt, &i.DeletingAt, ) return i, err @@ -9812,7 +9812,7 @@ SET WHERE id = $1 AND deleted = false -RETURNING id, created_at, updated_at, owner_id, organization_id, template_id, deleted, name, autostart_schedule, ttl, last_used_at, locked_at, deleting_at +RETURNING id, created_at, updated_at, owner_id, organization_id, template_id, deleted, name, autostart_schedule, ttl, last_used_at, dormant_at, deleting_at ` type UpdateWorkspaceParams struct { @@ -9835,7 +9835,7 @@ func (q *sqlQuerier) UpdateWorkspace(ctx context.Context, arg UpdateWorkspacePar &i.AutostartSchedule, &i.Ttl, &i.LastUsedAt, - &i.LockedAt, + &i.DormantAt, &i.DeletingAt, ) return i, err @@ -9879,52 +9879,33 @@ func (q *sqlQuerier) UpdateWorkspaceDeletedByID(ctx context.Context, arg UpdateW return err } -const updateWorkspaceLastUsedAt = `-- name: UpdateWorkspaceLastUsedAt :exec -UPDATE - workspaces -SET - last_used_at = $2 -WHERE - id = $1 -` - -type UpdateWorkspaceLastUsedAtParams struct { - ID uuid.UUID `db:"id" json:"id"` - LastUsedAt time.Time `db:"last_used_at" json:"last_used_at"` -} - -func (q *sqlQuerier) UpdateWorkspaceLastUsedAt(ctx context.Context, arg UpdateWorkspaceLastUsedAtParams) error { - _, err := q.db.ExecContext(ctx, updateWorkspaceLastUsedAt, arg.ID, arg.LastUsedAt) - return err -} - -const updateWorkspaceLockedDeletingAt = `-- name: UpdateWorkspaceLockedDeletingAt :one +const updateWorkspaceDormantDeletingAt = `-- name: UpdateWorkspaceDormantDeletingAt :one UPDATE workspaces SET - locked_at = $2, - -- When a workspace is unlocked we want to update the last_used_at to avoid the workspace getting re-locked. - -- if we're locking the workspace then we leave it alone. + dormant_at = $2, + -- When a workspace is active we want to update the last_used_at to avoid the workspace going + -- immediately dormant. If we're transition the workspace to dormant then we leave it alone. last_used_at = CASE WHEN $2::timestamptz IS NULL THEN now() at time zone 'utc' ELSE last_used_at END, - -- If locked_at is null (meaning unlocked) or the template-defined locked_ttl is 0 we should set - -- deleting_at to NULL else set it to the locked_at + locked_ttl duration. - deleting_at = CASE WHEN $2::timestamptz IS NULL OR templates.locked_ttl = 0 THEN NULL ELSE $2::timestamptz + INTERVAL '1 milliseconds' * templates.locked_ttl / 1000000 END + -- If dormant_at is null (meaning active) or the template-defined time_til_dormant_autodelete is 0 we should set + -- deleting_at to NULL else set it to the dormant_at + time_til_dormant_autodelete duration. + deleting_at = CASE WHEN $2::timestamptz IS NULL OR templates.time_til_dormant_autodelete = 0 THEN NULL ELSE $2::timestamptz + INTERVAL '1 milliseconds' * templates.time_til_dormant_autodelete / 1000000 END FROM templates WHERE workspaces.template_id = templates.id AND workspaces.id = $1 -RETURNING workspaces.id, workspaces.created_at, workspaces.updated_at, workspaces.owner_id, workspaces.organization_id, workspaces.template_id, workspaces.deleted, workspaces.name, workspaces.autostart_schedule, workspaces.ttl, workspaces.last_used_at, workspaces.locked_at, workspaces.deleting_at +RETURNING workspaces.id, workspaces.created_at, workspaces.updated_at, workspaces.owner_id, workspaces.organization_id, workspaces.template_id, workspaces.deleted, workspaces.name, workspaces.autostart_schedule, workspaces.ttl, workspaces.last_used_at, workspaces.dormant_at, workspaces.deleting_at ` -type UpdateWorkspaceLockedDeletingAtParams struct { - ID uuid.UUID `db:"id" json:"id"` - LockedAt sql.NullTime `db:"locked_at" json:"locked_at"` +type UpdateWorkspaceDormantDeletingAtParams struct { + ID uuid.UUID `db:"id" json:"id"` + DormantAt sql.NullTime `db:"dormant_at" json:"dormant_at"` } -func (q *sqlQuerier) UpdateWorkspaceLockedDeletingAt(ctx context.Context, arg UpdateWorkspaceLockedDeletingAtParams) (Workspace, error) { - row := q.db.QueryRowContext(ctx, updateWorkspaceLockedDeletingAt, arg.ID, arg.LockedAt) +func (q *sqlQuerier) UpdateWorkspaceDormantDeletingAt(ctx context.Context, arg UpdateWorkspaceDormantDeletingAtParams) (Workspace, error) { + row := q.db.QueryRowContext(ctx, updateWorkspaceDormantDeletingAt, arg.ID, arg.DormantAt) var i Workspace err := row.Scan( &i.ID, @@ -9938,12 +9919,31 @@ func (q *sqlQuerier) UpdateWorkspaceLockedDeletingAt(ctx context.Context, arg Up &i.AutostartSchedule, &i.Ttl, &i.LastUsedAt, - &i.LockedAt, + &i.DormantAt, &i.DeletingAt, ) return i, err } +const updateWorkspaceLastUsedAt = `-- name: UpdateWorkspaceLastUsedAt :exec +UPDATE + workspaces +SET + last_used_at = $2 +WHERE + id = $1 +` + +type UpdateWorkspaceLastUsedAtParams struct { + ID uuid.UUID `db:"id" json:"id"` + LastUsedAt time.Time `db:"last_used_at" json:"last_used_at"` +} + +func (q *sqlQuerier) UpdateWorkspaceLastUsedAt(ctx context.Context, arg UpdateWorkspaceLastUsedAtParams) error { + _, err := q.db.ExecContext(ctx, updateWorkspaceLastUsedAt, arg.ID, arg.LastUsedAt) + return err +} + const updateWorkspaceTTL = `-- name: UpdateWorkspaceTTL :exec UPDATE workspaces @@ -9963,28 +9963,28 @@ func (q *sqlQuerier) UpdateWorkspaceTTL(ctx context.Context, arg UpdateWorkspace return err } -const updateWorkspacesLockedDeletingAtByTemplateID = `-- name: UpdateWorkspacesLockedDeletingAtByTemplateID :exec +const updateWorkspacesDormantDeletingAtByTemplateID = `-- name: UpdateWorkspacesDormantDeletingAtByTemplateID :exec UPDATE workspaces SET deleting_at = CASE WHEN $1::bigint = 0 THEN NULL WHEN $2::timestamptz > '0001-01-01 00:00:00+00'::timestamptz THEN ($2::timestamptz) + interval '1 milliseconds' * $1::bigint - ELSE locked_at + interval '1 milliseconds' * $1::bigint + ELSE dormant_at + interval '1 milliseconds' * $1::bigint END, - locked_at = CASE WHEN $2::timestamptz > '0001-01-01 00:00:00+00'::timestamptz THEN $2::timestamptz ELSE locked_at END + dormant_at = CASE WHEN $2::timestamptz > '0001-01-01 00:00:00+00'::timestamptz THEN $2::timestamptz ELSE dormant_at END WHERE template_id = $3 AND - locked_at IS NOT NULL + dormant_at IS NOT NULL ` -type UpdateWorkspacesLockedDeletingAtByTemplateIDParams struct { - LockedTtlMs int64 `db:"locked_ttl_ms" json:"locked_ttl_ms"` - LockedAt time.Time `db:"locked_at" json:"locked_at"` - TemplateID uuid.UUID `db:"template_id" json:"template_id"` +type UpdateWorkspacesDormantDeletingAtByTemplateIDParams struct { + TimeTilDormantAutodeleteMs int64 `db:"time_til_dormant_autodelete_ms" json:"time_til_dormant_autodelete_ms"` + DormantAt time.Time `db:"dormant_at" json:"dormant_at"` + TemplateID uuid.UUID `db:"template_id" json:"template_id"` } -func (q *sqlQuerier) UpdateWorkspacesLockedDeletingAtByTemplateID(ctx context.Context, arg UpdateWorkspacesLockedDeletingAtByTemplateIDParams) error { - _, err := q.db.ExecContext(ctx, updateWorkspacesLockedDeletingAtByTemplateID, arg.LockedTtlMs, arg.LockedAt, arg.TemplateID) +func (q *sqlQuerier) UpdateWorkspacesDormantDeletingAtByTemplateID(ctx context.Context, arg UpdateWorkspacesDormantDeletingAtByTemplateIDParams) error { + _, err := q.db.ExecContext(ctx, updateWorkspacesDormantDeletingAtByTemplateID, arg.TimeTilDormantAutodeleteMs, arg.DormantAt, arg.TemplateID) return err } diff --git a/coderd/database/queries/templates.sql b/coderd/database/queries/templates.sql index 7f4c9ce5de4ab..5387bea009c2d 100644 --- a/coderd/database/queries/templates.sql +++ b/coderd/database/queries/templates.sql @@ -121,8 +121,8 @@ SET restart_requirement_days_of_week = $7, restart_requirement_weeks = $8, failure_ttl = $9, - inactivity_ttl = $10, - locked_ttl = $11 + time_til_dormant = $10, + time_til_dormant_autodelete = $11 WHERE id = $1 ; diff --git a/coderd/database/queries/workspaces.sql b/coderd/database/queries/workspaces.sql index 1ff5971d3266d..0aa073301eb8f 100644 --- a/coderd/database/queries/workspaces.sql +++ b/coderd/database/queries/workspaces.sql @@ -259,13 +259,13 @@ WHERE ) > 0 ELSE true END - -- Filter by locked workspaces. By default we do not return locked + -- Filter by dormant workspaces. By default we do not return dormant -- workspaces since they are considered soft-deleted. AND CASE - WHEN @locked_at :: timestamptz > '0001-01-01 00:00:00+00'::timestamptz THEN - locked_at IS NOT NULL AND locked_at >= @locked_at + WHEN @dormant_at :: timestamptz > '0001-01-01 00:00:00+00'::timestamptz THEN + dormant_at IS NOT NULL AND dormant_at >= @dormant_at ELSE - locked_at IS NULL + dormant_at IS NULL END -- Filter by last_used AND CASE @@ -479,31 +479,31 @@ WHERE ) OR -- If the workspace's template has an inactivity_ttl set - -- it may be eligible for locking. + -- it may be eligible for dormancy. ( - templates.inactivity_ttl > 0 AND - workspaces.locked_at IS NULL + templates.time_til_dormant > 0 AND + workspaces.dormant_at IS NULL ) OR - -- If the workspace's template has a locked_ttl set - -- and the workspace is already locked + -- If the workspace's template has a time_til_dormant_autodelete set + -- and the workspace is already dormant. ( - templates.locked_ttl > 0 AND - workspaces.locked_at IS NOT NULL + templates.time_til_dormant_autodelete > 0 AND + workspaces.dormant_at IS NOT NULL ) ) AND workspaces.deleted = 'false'; --- name: UpdateWorkspaceLockedDeletingAt :one +-- name: UpdateWorkspaceDormantDeletingAt :one UPDATE workspaces SET - locked_at = $2, - -- When a workspace is unlocked we want to update the last_used_at to avoid the workspace getting re-locked. - -- if we're locking the workspace then we leave it alone. + dormant_at = $2, + -- When a workspace is active we want to update the last_used_at to avoid the workspace going + -- immediately dormant. If we're transition the workspace to dormant then we leave it alone. last_used_at = CASE WHEN $2::timestamptz IS NULL THEN now() at time zone 'utc' ELSE last_used_at END, - -- If locked_at is null (meaning unlocked) or the template-defined locked_ttl is 0 we should set - -- deleting_at to NULL else set it to the locked_at + locked_ttl duration. - deleting_at = CASE WHEN $2::timestamptz IS NULL OR templates.locked_ttl = 0 THEN NULL ELSE $2::timestamptz + INTERVAL '1 milliseconds' * templates.locked_ttl / 1000000 END + -- If dormant_at is null (meaning active) or the template-defined time_til_dormant_autodelete is 0 we should set + -- deleting_at to NULL else set it to the dormant_at + time_til_dormant_autodelete duration. + deleting_at = CASE WHEN $2::timestamptz IS NULL OR templates.time_til_dormant_autodelete = 0 THEN NULL ELSE $2::timestamptz + INTERVAL '1 milliseconds' * templates.time_til_dormant_autodelete / 1000000 END FROM templates WHERE @@ -512,19 +512,19 @@ AND workspaces.id = $1 RETURNING workspaces.*; --- name: UpdateWorkspacesLockedDeletingAtByTemplateID :exec +-- name: UpdateWorkspacesDormantDeletingAtByTemplateID :exec UPDATE workspaces SET deleting_at = CASE - WHEN @locked_ttl_ms::bigint = 0 THEN NULL - WHEN @locked_at::timestamptz > '0001-01-01 00:00:00+00'::timestamptz THEN (@locked_at::timestamptz) + interval '1 milliseconds' * @locked_ttl_ms::bigint - ELSE locked_at + interval '1 milliseconds' * @locked_ttl_ms::bigint + WHEN @time_til_dormant_autodelete_ms::bigint = 0 THEN NULL + WHEN @dormant_at::timestamptz > '0001-01-01 00:00:00+00'::timestamptz THEN (@dormant_at::timestamptz) + interval '1 milliseconds' * @time_til_dormant_autodelete_ms::bigint + ELSE dormant_at + interval '1 milliseconds' * @time_til_dormant_autodelete_ms::bigint END, - locked_at = CASE WHEN @locked_at::timestamptz > '0001-01-01 00:00:00+00'::timestamptz THEN @locked_at::timestamptz ELSE locked_at END + dormant_at = CASE WHEN @dormant_at::timestamptz > '0001-01-01 00:00:00+00'::timestamptz THEN @dormant_at::timestamptz ELSE dormant_at END WHERE template_id = @template_id AND - locked_at IS NOT NULL; + dormant_at IS NOT NULL; -- name: UpdateTemplateWorkspacesLastUsedAt :exec UPDATE workspaces diff --git a/coderd/database/sqlc.yaml b/coderd/database/sqlc.yaml index ca1a2324e9ab0..7718b01e0335e 100644 --- a/coderd/database/sqlc.yaml +++ b/coderd/database/sqlc.yaml @@ -67,9 +67,8 @@ overrides: motd_file: MOTDFile uuid: UUID failure_ttl: FailureTTL - inactivity_ttl: InactivityTTL + time_til_dormant_autodelete: TimeTilDormantAutoDelete eof: EOF - locked_ttl: LockedTTL template_ids: TemplateIDs active_user_ids: ActiveUserIDs diff --git a/coderd/rbac/object.go b/coderd/rbac/object.go index 39f57c7fcc6da..1e3f1f45e59ea 100644 --- a/coderd/rbac/object.go +++ b/coderd/rbac/object.go @@ -37,10 +37,10 @@ var ( Type: "workspace_build", } - // ResourceWorkspaceLocked is returned if a workspace is locked. + // ResourceWorkspaceDormant is returned if a workspace is dormant. // It grants restricted permissions on workspace builds. - ResourceWorkspaceLocked = Object{ - Type: "workspace_locked", + ResourceWorkspaceDormant = Object{ + Type: "workspace_dormant", } // ResourceWorkspaceProxy CRUD. Org diff --git a/coderd/rbac/object_gen.go b/coderd/rbac/object_gen.go index 10506b3f719c2..86a03d4552d45 100644 --- a/coderd/rbac/object_gen.go +++ b/coderd/rbac/object_gen.go @@ -26,8 +26,8 @@ func AllResources() []Object { ResourceWorkspace, ResourceWorkspaceApplicationConnect, ResourceWorkspaceBuild, + ResourceWorkspaceDormant, ResourceWorkspaceExecution, - ResourceWorkspaceLocked, ResourceWorkspaceProxy, } } diff --git a/coderd/rbac/roles.go b/coderd/rbac/roles.go index 93aeaca017592..4f159480a0491 100644 --- a/coderd/rbac/roles.go +++ b/coderd/rbac/roles.go @@ -121,7 +121,7 @@ func ReloadBuiltinRoles(opts *RoleOptions) { opts = &RoleOptions{} } - ownerAndAdminExceptions := []Object{ResourceWorkspaceLocked} + ownerAndAdminExceptions := []Object{ResourceWorkspaceDormant} if opts.NoOwnerWorkspaceExec { ownerAndAdminExceptions = append(ownerAndAdminExceptions, ResourceWorkspaceExecution, @@ -150,7 +150,7 @@ func ReloadBuiltinRoles(opts *RoleOptions) { ResourceProvisionerDaemon.Type: {ActionRead}, }), Org: map[string][]Permission{}, - User: append(allPermsExcept(ResourceWorkspaceLocked, ResourceUser, ResourceOrganizationMember), + User: append(allPermsExcept(ResourceWorkspaceDormant, ResourceUser, ResourceOrganizationMember), Permissions(map[string][]Action{ // Users cannot do create/update/delete on themselves, but they // can read their own details. @@ -246,7 +246,7 @@ func ReloadBuiltinRoles(opts *RoleOptions) { Site: []Permission{}, Org: map[string][]Permission{ // Org admins should not have workspace exec perms. - organizationID: allPermsExcept(ResourceWorkspaceExecution, ResourceWorkspaceLocked), + organizationID: allPermsExcept(ResourceWorkspaceExecution, ResourceWorkspaceDormant), }, User: []Permission{}, } diff --git a/coderd/rbac/roles_test.go b/coderd/rbac/roles_test.go index ad03529d81c0f..fc47413fd19f2 100644 --- a/coderd/rbac/roles_test.go +++ b/coderd/rbac/roles_test.go @@ -319,9 +319,9 @@ func TestRolePermissions(t *testing.T) { }, }, { - Name: "WorkspaceLocked", + Name: "WorkspaceDormant", Actions: rbac.AllActions(), - Resource: rbac.ResourceWorkspaceLocked.WithID(uuid.New()).InOrg(orgID).WithOwner(memberMe.Actor.ID), + Resource: rbac.ResourceWorkspaceDormant.WithID(uuid.New()).InOrg(orgID).WithOwner(memberMe.Actor.ID), AuthorizeMap: map[bool][]authSubject{ true: {}, false: {memberMe, orgAdmin, userAdmin, otherOrgAdmin, otherOrgMember, orgMemberMe, owner, templateAdmin}, diff --git a/coderd/schedule/template.go b/coderd/schedule/template.go index 0e3774b798358..9c1b2fa5aa787 100644 --- a/coderd/schedule/template.go +++ b/coderd/schedule/template.go @@ -99,24 +99,24 @@ type TemplateScheduleOptions struct { // FailureTTL dictates the duration after which failed workspaces will be // stopped automatically. FailureTTL time.Duration `json:"failure_ttl"` - // InactivityTTL dictates the duration after which inactive workspaces will - // be locked. - InactivityTTL time.Duration `json:"inactivity_ttl"` - // LockedTTL dictates the duration after which locked workspaces will be + // TimeTilDormant dictates the duration after which inactive workspaces will + // go dormant. + TimeTilDormant time.Duration `json:"time_til_dormant"` + // TimeTilDormantAutoDelete dictates the duration after which dormant workspaces will be // permanently deleted. - LockedTTL time.Duration `json:"locked_ttl"` + TimeTilDormantAutoDelete time.Duration `json:"time_til_dormant_autodelete"` // UpdateWorkspaceLastUsedAt updates the template's workspaces' // last_used_at field. This is useful for preventing updates to the - // templates inactivity_ttl immediately triggering a lock action against + // templates inactivity_ttl immediately triggering a dormant action against // workspaces whose last_used_at field violates the new template // inactivity_ttl threshold. UpdateWorkspaceLastUsedAt bool `json:"update_workspace_last_used_at"` - // UpdateWorkspaceLockedAt updates the template's workspaces' - // locked_at field. This is useful for preventing updates to the + // UpdateWorkspaceDormantAt updates the template's workspaces' + // dormant_at field. This is useful for preventing updates to the // templates locked_ttl immediately triggering a delete action against - // workspaces whose locked_at field violates the new template locked_ttl + // workspaces whose dormant_at field violates the new template time_til_dormant_autodelete // threshold. - UpdateWorkspaceLockedAt bool `json:"update_workspace_locked_at"` + UpdateWorkspaceDormantAt bool `json:"update_workspace_dormant_at"` } // TemplateScheduleStore provides an interface for retrieving template @@ -150,16 +150,16 @@ func (*agplTemplateScheduleStore) Get(ctx context.Context, db database.Store, te UserAutostopEnabled: true, DefaultTTL: time.Duration(tpl.DefaultTTL), // Disregard the values in the database, since RestartRequirement, - // FailureTTL, InactivityTTL, and LockedTTL are enterprise features. + // FailureTTL, TimeTilDormant, and TimeTilDormantAutoDelete are enterprise features. UseRestartRequirement: false, MaxTTL: 0, RestartRequirement: TemplateRestartRequirement{ DaysOfWeek: 0, Weeks: 0, }, - FailureTTL: 0, - InactivityTTL: 0, - LockedTTL: 0, + FailureTTL: 0, + TimeTilDormant: 0, + TimeTilDormantAutoDelete: 0, }, nil } @@ -186,8 +186,8 @@ func (*agplTemplateScheduleStore) Set(ctx context.Context, db database.Store, tp AllowUserAutostart: tpl.AllowUserAutostart, AllowUserAutostop: tpl.AllowUserAutostop, FailureTTL: tpl.FailureTTL, - InactivityTTL: tpl.InactivityTTL, - LockedTTL: tpl.LockedTTL, + TimeTilDormant: tpl.TimeTilDormant, + TimeTilDormantAutoDelete: tpl.TimeTilDormantAutoDelete, }) if err != nil { return xerrors.Errorf("update template schedule: %w", err) diff --git a/coderd/searchquery/search.go b/coderd/searchquery/search.go index 17d1990880727..efdde1bb1d2e7 100644 --- a/coderd/searchquery/search.go +++ b/coderd/searchquery/search.go @@ -114,16 +114,16 @@ func Workspaces(query string, page codersdk.Pagination, agentInactiveDisconnectT filter.Name = parser.String(values, "", "name") filter.Status = string(httpapi.ParseCustom(parser, values, "", "status", httpapi.ParseEnum[database.WorkspaceStatus])) filter.HasAgent = parser.String(values, "", "has-agent") - filter.LockedAt = parser.Time(values, time.Time{}, "locked_at", "2006-01-02") + filter.DormantAt = parser.Time(values, time.Time{}, "dormant_at", "2006-01-02") filter.LastUsedAfter = parser.Time3339Nano(values, time.Time{}, "last_used_after") filter.LastUsedBefore = parser.Time3339Nano(values, time.Time{}, "last_used_before") if _, ok := values["deleting_by"]; ok { postFilter.DeletingBy = ptr.Ref(parser.Time(values, time.Time{}, "deleting_by", "2006-01-02")) - // We want to make sure to grab locked workspaces since they + // We want to make sure to grab dormant workspaces since they // are omitted by default. - if filter.LockedAt.IsZero() { - filter.LockedAt = time.Date(1970, time.January, 1, 0, 0, 0, 0, time.UTC) + if filter.DormantAt.IsZero() { + filter.DormantAt = time.Date(1970, time.January, 1, 0, 0, 0, 0, time.UTC) } } diff --git a/coderd/templates.go b/coderd/templates.go index f51f42668e1a1..7d6dc46d2bf90 100644 --- a/coderd/templates.go +++ b/coderd/templates.go @@ -219,8 +219,8 @@ func (api *API) postTemplateByOrganization(rw http.ResponseWriter, r *http.Reque restartRequirementDaysOfWeek []string restartRequirementWeeks int64 failureTTL time.Duration - inactivityTTL time.Duration - lockedTTL time.Duration + dormantTTL time.Duration + dormantAutoDeletionTTL time.Duration ) if createTemplate.DefaultTTLMillis != nil { defaultTTL = time.Duration(*createTemplate.DefaultTTLMillis) * time.Millisecond @@ -232,11 +232,11 @@ func (api *API) postTemplateByOrganization(rw http.ResponseWriter, r *http.Reque if createTemplate.FailureTTLMillis != nil { failureTTL = time.Duration(*createTemplate.FailureTTLMillis) * time.Millisecond } - if createTemplate.InactivityTTLMillis != nil { - inactivityTTL = time.Duration(*createTemplate.InactivityTTLMillis) * time.Millisecond + if createTemplate.TimeTilDormantMillis != nil { + dormantTTL = time.Duration(*createTemplate.TimeTilDormantMillis) * time.Millisecond } - if createTemplate.LockedTTLMillis != nil { - lockedTTL = time.Duration(*createTemplate.LockedTTLMillis) * time.Millisecond + if createTemplate.TimeTilDormantAutoDeleteMillis != nil { + dormantAutoDeletionTTL = time.Duration(*createTemplate.TimeTilDormantAutoDeleteMillis) * time.Millisecond } var ( @@ -270,11 +270,11 @@ func (api *API) postTemplateByOrganization(rw http.ResponseWriter, r *http.Reque if failureTTL < 0 { validErrs = append(validErrs, codersdk.ValidationError{Field: "failure_ttl_ms", Detail: "Must be a positive integer."}) } - if inactivityTTL < 0 { - validErrs = append(validErrs, codersdk.ValidationError{Field: "inactivity_ttl_ms", Detail: "Must be a positive integer."}) + if dormantTTL < 0 { + validErrs = append(validErrs, codersdk.ValidationError{Field: "time_til_dormant_autodeletion_ms", Detail: "Must be a positive integer."}) } - if lockedTTL < 0 { - validErrs = append(validErrs, codersdk.ValidationError{Field: "locked_ttl_ms", Detail: "Must be a positive integer."}) + if dormantAutoDeletionTTL < 0 { + validErrs = append(validErrs, codersdk.ValidationError{Field: "time_til_dormant_autodeletion_ms", Detail: "Must be a positive integer."}) } if len(validErrs) > 0 { @@ -340,9 +340,9 @@ func (api *API) postTemplateByOrganization(rw http.ResponseWriter, r *http.Reque DaysOfWeek: restartRequirementDaysOfWeekParsed, Weeks: restartRequirementWeeks, }, - FailureTTL: failureTTL, - InactivityTTL: inactivityTTL, - LockedTTL: lockedTTL, + FailureTTL: failureTTL, + TimeTilDormant: dormantTTL, + TimeTilDormantAutoDelete: dormantAutoDeletionTTL, }) if err != nil { return xerrors.Errorf("set template schedule options: %s", err) @@ -533,13 +533,13 @@ func (api *API) patchTemplateMeta(rw http.ResponseWriter, r *http.Request) { if req.FailureTTLMillis < 0 { validErrs = append(validErrs, codersdk.ValidationError{Field: "failure_ttl_ms", Detail: "Must be a positive integer."}) } - if req.InactivityTTLMillis < 0 { + if req.TimeTilDormantMillis < 0 { validErrs = append(validErrs, codersdk.ValidationError{Field: "inactivity_ttl_ms", Detail: "Must be a positive integer."}) } - if req.InactivityTTLMillis < 0 { + if req.TimeTilDormantMillis < 0 { validErrs = append(validErrs, codersdk.ValidationError{Field: "inactivity_ttl_ms", Detail: "Must be a positive integer."}) } - if req.LockedTTLMillis < 0 { + if req.TimeTilDormantAutoDeleteMillis < 0 { validErrs = append(validErrs, codersdk.ValidationError{Field: "locked_ttl_ms", Detail: "Must be a positive integer."}) } @@ -565,8 +565,8 @@ func (api *API) patchTemplateMeta(rw http.ResponseWriter, r *http.Request) { restartRequirementDaysOfWeekParsed == scheduleOpts.RestartRequirement.DaysOfWeek && req.RestartRequirement.Weeks == scheduleOpts.RestartRequirement.Weeks && req.FailureTTLMillis == time.Duration(template.FailureTTL).Milliseconds() && - req.InactivityTTLMillis == time.Duration(template.InactivityTTL).Milliseconds() && - req.LockedTTLMillis == time.Duration(template.LockedTTL).Milliseconds() { + req.TimeTilDormantMillis == time.Duration(template.TimeTilDormant).Milliseconds() && + req.TimeTilDormantAutoDeleteMillis == time.Duration(template.TimeTilDormantAutoDelete).Milliseconds() { return nil } @@ -598,16 +598,16 @@ func (api *API) patchTemplateMeta(rw http.ResponseWriter, r *http.Request) { defaultTTL := time.Duration(req.DefaultTTLMillis) * time.Millisecond maxTTL := time.Duration(req.MaxTTLMillis) * time.Millisecond failureTTL := time.Duration(req.FailureTTLMillis) * time.Millisecond - inactivityTTL := time.Duration(req.InactivityTTLMillis) * time.Millisecond - lockedTTL := time.Duration(req.LockedTTLMillis) * time.Millisecond + inactivityTTL := time.Duration(req.TimeTilDormantMillis) * time.Millisecond + timeTilDormantAutoDelete := time.Duration(req.TimeTilDormantAutoDeleteMillis) * time.Millisecond if defaultTTL != time.Duration(template.DefaultTTL) || maxTTL != time.Duration(template.MaxTTL) || restartRequirementDaysOfWeekParsed != scheduleOpts.RestartRequirement.DaysOfWeek || req.RestartRequirement.Weeks != scheduleOpts.RestartRequirement.Weeks || failureTTL != time.Duration(template.FailureTTL) || - inactivityTTL != time.Duration(template.InactivityTTL) || - lockedTTL != time.Duration(template.LockedTTL) || + inactivityTTL != time.Duration(template.TimeTilDormant) || + timeTilDormantAutoDelete != time.Duration(template.TimeTilDormantAutoDelete) || req.AllowUserAutostart != template.AllowUserAutostart || req.AllowUserAutostop != template.AllowUserAutostop { updated, err = (*api.TemplateScheduleStore.Load()).Set(ctx, tx, updated, schedule.TemplateScheduleOptions{ @@ -623,10 +623,10 @@ func (api *API) patchTemplateMeta(rw http.ResponseWriter, r *http.Request) { Weeks: req.RestartRequirement.Weeks, }, FailureTTL: failureTTL, - InactivityTTL: inactivityTTL, - LockedTTL: lockedTTL, + TimeTilDormant: inactivityTTL, + TimeTilDormantAutoDelete: timeTilDormantAutoDelete, UpdateWorkspaceLastUsedAt: req.UpdateWorkspaceLastUsedAt, - UpdateWorkspaceLockedAt: req.UpdateWorkspaceLockedAt, + UpdateWorkspaceDormantAt: req.UpdateWorkspaceDormantAt, }) if err != nil { return xerrors.Errorf("set template schedule options: %w", err) @@ -738,28 +738,28 @@ func (api *API) convertTemplate( buildTimeStats := api.metricsCache.TemplateBuildTimeStats(template.ID) return codersdk.Template{ - ID: template.ID, - CreatedAt: template.CreatedAt, - UpdatedAt: template.UpdatedAt, - OrganizationID: template.OrganizationID, - Name: template.Name, - DisplayName: template.DisplayName, - Provisioner: codersdk.ProvisionerType(template.Provisioner), - ActiveVersionID: template.ActiveVersionID, - ActiveUserCount: activeCount, - BuildTimeStats: buildTimeStats, - Description: template.Description, - Icon: template.Icon, - DefaultTTLMillis: time.Duration(template.DefaultTTL).Milliseconds(), - MaxTTLMillis: time.Duration(template.MaxTTL).Milliseconds(), - CreatedByID: template.CreatedBy, - CreatedByName: template.CreatedByUsername, - AllowUserAutostart: template.AllowUserAutostart, - AllowUserAutostop: template.AllowUserAutostop, - AllowUserCancelWorkspaceJobs: template.AllowUserCancelWorkspaceJobs, - FailureTTLMillis: time.Duration(template.FailureTTL).Milliseconds(), - InactivityTTLMillis: time.Duration(template.InactivityTTL).Milliseconds(), - LockedTTLMillis: time.Duration(template.LockedTTL).Milliseconds(), + ID: template.ID, + CreatedAt: template.CreatedAt, + UpdatedAt: template.UpdatedAt, + OrganizationID: template.OrganizationID, + Name: template.Name, + DisplayName: template.DisplayName, + Provisioner: codersdk.ProvisionerType(template.Provisioner), + ActiveVersionID: template.ActiveVersionID, + ActiveUserCount: activeCount, + BuildTimeStats: buildTimeStats, + Description: template.Description, + Icon: template.Icon, + DefaultTTLMillis: time.Duration(template.DefaultTTL).Milliseconds(), + MaxTTLMillis: time.Duration(template.MaxTTL).Milliseconds(), + CreatedByID: template.CreatedBy, + CreatedByName: template.CreatedByUsername, + AllowUserAutostart: template.AllowUserAutostart, + AllowUserAutostop: template.AllowUserAutostop, + AllowUserCancelWorkspaceJobs: template.AllowUserCancelWorkspaceJobs, + FailureTTLMillis: time.Duration(template.FailureTTL).Milliseconds(), + TimeTilDormantMillis: time.Duration(template.TimeTilDormant).Milliseconds(), + TimeTilDormantAutoDeleteMillis: time.Duration(template.TimeTilDormantAutoDelete).Milliseconds(), RestartRequirement: codersdk.TemplateRestartRequirement{ DaysOfWeek: codersdk.BitmapToWeekdays(uint8(template.RestartRequirementDaysOfWeek)), Weeks: template.RestartRequirementWeeks, diff --git a/coderd/templates_test.go b/coderd/templates_test.go index 92a67710902dd..fcdb7e64e2e78 100644 --- a/coderd/templates_test.go +++ b/coderd/templates_test.go @@ -270,8 +270,8 @@ func TestPostTemplateByOrganization(t *testing.T) { RestartRequirementDaysOfWeek: int16(options.RestartRequirement.DaysOfWeek), RestartRequirementWeeks: options.RestartRequirement.Weeks, FailureTTL: int64(options.FailureTTL), - InactivityTTL: int64(options.InactivityTTL), - LockedTTL: int64(options.LockedTTL), + TimeTilDormant: int64(options.TimeTilDormant), + TimeTilDormantAutoDelete: int64(options.TimeTilDormantAutoDelete), }) if !assert.NoError(t, err) { return database.Template{}, err @@ -320,8 +320,8 @@ func TestPostTemplateByOrganization(t *testing.T) { RestartRequirementDaysOfWeek: int16(options.RestartRequirement.DaysOfWeek), RestartRequirementWeeks: options.RestartRequirement.Weeks, FailureTTL: int64(options.FailureTTL), - InactivityTTL: int64(options.InactivityTTL), - LockedTTL: int64(options.LockedTTL), + TimeTilDormant: int64(options.TimeTilDormant), + TimeTilDormantAutoDelete: int64(options.TimeTilDormantAutoDelete), }) if !assert.NoError(t, err) { return database.Template{}, err @@ -598,8 +598,8 @@ func TestPatchTemplateMeta(t *testing.T) { RestartRequirementDaysOfWeek: int16(options.RestartRequirement.DaysOfWeek), RestartRequirementWeeks: options.RestartRequirement.Weeks, FailureTTL: int64(options.FailureTTL), - InactivityTTL: int64(options.InactivityTTL), - LockedTTL: int64(options.LockedTTL), + TimeTilDormant: int64(options.TimeTilDormant), + TimeTilDormantAutoDelete: int64(options.TimeTilDormantAutoDelete), }) if !assert.NoError(t, err) { return database.Template{}, err @@ -697,9 +697,9 @@ func TestPatchTemplateMeta(t *testing.T) { t.Parallel() const ( - failureTTL = 7 * 24 * time.Hour - inactivityTTL = 180 * 24 * time.Hour - lockedTTL = 360 * 24 * time.Hour + failureTTL = 7 * 24 * time.Hour + inactivityTTL = 180 * 24 * time.Hour + timeTilDormantAutoDelete = 360 * 24 * time.Hour ) t.Run("OK", func(t *testing.T) { @@ -711,12 +711,12 @@ func TestPatchTemplateMeta(t *testing.T) { 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, failureTTL, options.FailureTTL) - require.Equal(t, inactivityTTL, options.InactivityTTL) - require.Equal(t, lockedTTL, options.LockedTTL) + require.Equal(t, inactivityTTL, options.TimeTilDormant) + require.Equal(t, timeTilDormantAutoDelete, options.TimeTilDormantAutoDelete) } template.FailureTTL = int64(options.FailureTTL) - template.InactivityTTL = int64(options.InactivityTTL) - template.LockedTTL = int64(options.LockedTTL) + template.TimeTilDormant = int64(options.TimeTilDormant) + template.TimeTilDormantAutoDelete = int64(options.TimeTilDormantAutoDelete) return template, nil }, }, @@ -725,31 +725,31 @@ func TestPatchTemplateMeta(t *testing.T) { version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, nil) template := coderdtest.CreateTemplate(t, client, user.OrganizationID, version.ID, func(ctr *codersdk.CreateTemplateRequest) { ctr.FailureTTLMillis = ptr.Ref(0 * time.Hour.Milliseconds()) - ctr.InactivityTTLMillis = ptr.Ref(0 * time.Hour.Milliseconds()) - ctr.LockedTTLMillis = ptr.Ref(0 * time.Hour.Milliseconds()) + ctr.TimeTilDormantMillis = ptr.Ref(0 * time.Hour.Milliseconds()) + ctr.TimeTilDormantAutoDeleteMillis = ptr.Ref(0 * 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, - RestartRequirement: &template.RestartRequirement, - AllowUserCancelWorkspaceJobs: template.AllowUserCancelWorkspaceJobs, - FailureTTLMillis: failureTTL.Milliseconds(), - InactivityTTLMillis: inactivityTTL.Milliseconds(), - LockedTTLMillis: lockedTTL.Milliseconds(), + Name: template.Name, + DisplayName: template.DisplayName, + Description: template.Description, + Icon: template.Icon, + DefaultTTLMillis: 0, + RestartRequirement: &template.RestartRequirement, + AllowUserCancelWorkspaceJobs: template.AllowUserCancelWorkspaceJobs, + FailureTTLMillis: failureTTL.Milliseconds(), + TimeTilDormantMillis: inactivityTTL.Milliseconds(), + TimeTilDormantAutoDeleteMillis: timeTilDormantAutoDelete.Milliseconds(), }) require.NoError(t, err) require.EqualValues(t, 2, atomic.LoadInt64(&setCalled)) require.Equal(t, failureTTL.Milliseconds(), got.FailureTTLMillis) - require.Equal(t, inactivityTTL.Milliseconds(), got.InactivityTTLMillis) - require.Equal(t, lockedTTL.Milliseconds(), got.LockedTTLMillis) + require.Equal(t, inactivityTTL.Milliseconds(), got.TimeTilDormantMillis) + require.Equal(t, timeTilDormantAutoDelete.Milliseconds(), got.TimeTilDormantAutoDeleteMillis) }) t.Run("IgnoredUnlicensed", func(t *testing.T) { @@ -760,29 +760,29 @@ func TestPatchTemplateMeta(t *testing.T) { version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, nil) template := coderdtest.CreateTemplate(t, client, user.OrganizationID, version.ID, func(ctr *codersdk.CreateTemplateRequest) { ctr.FailureTTLMillis = ptr.Ref(0 * time.Hour.Milliseconds()) - ctr.InactivityTTLMillis = ptr.Ref(0 * time.Hour.Milliseconds()) - ctr.LockedTTLMillis = ptr.Ref(0 * time.Hour.Milliseconds()) + ctr.TimeTilDormantMillis = ptr.Ref(0 * time.Hour.Milliseconds()) + ctr.TimeTilDormantAutoDeleteMillis = ptr.Ref(0 * 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: template.DefaultTTLMillis, - RestartRequirement: &template.RestartRequirement, - AllowUserCancelWorkspaceJobs: template.AllowUserCancelWorkspaceJobs, - FailureTTLMillis: failureTTL.Milliseconds(), - InactivityTTLMillis: inactivityTTL.Milliseconds(), - LockedTTLMillis: lockedTTL.Milliseconds(), + Name: template.Name, + DisplayName: template.DisplayName, + Description: template.Description, + Icon: template.Icon, + DefaultTTLMillis: template.DefaultTTLMillis, + RestartRequirement: &template.RestartRequirement, + AllowUserCancelWorkspaceJobs: template.AllowUserCancelWorkspaceJobs, + FailureTTLMillis: failureTTL.Milliseconds(), + TimeTilDormantMillis: inactivityTTL.Milliseconds(), + TimeTilDormantAutoDeleteMillis: timeTilDormantAutoDelete.Milliseconds(), }) require.NoError(t, err) require.Zero(t, got.FailureTTLMillis) - require.Zero(t, got.InactivityTTLMillis) - require.Zero(t, got.LockedTTLMillis) + require.Zero(t, got.TimeTilDormantMillis) + require.Zero(t, got.TimeTilDormantAutoDeleteMillis) }) }) @@ -989,8 +989,8 @@ func TestPatchTemplateMeta(t *testing.T) { RestartRequirementDaysOfWeek: int16(options.RestartRequirement.DaysOfWeek), RestartRequirementWeeks: options.RestartRequirement.Weeks, FailureTTL: int64(options.FailureTTL), - InactivityTTL: int64(options.InactivityTTL), - LockedTTL: int64(options.LockedTTL), + TimeTilDormant: int64(options.TimeTilDormant), + TimeTilDormantAutoDelete: int64(options.TimeTilDormantAutoDelete), }) if !assert.NoError(t, err) { return database.Template{}, err @@ -1058,8 +1058,8 @@ func TestPatchTemplateMeta(t *testing.T) { RestartRequirementDaysOfWeek: int16(options.RestartRequirement.DaysOfWeek), RestartRequirementWeeks: options.RestartRequirement.Weeks, FailureTTL: int64(options.FailureTTL), - InactivityTTL: int64(options.InactivityTTL), - LockedTTL: int64(options.LockedTTL), + TimeTilDormant: int64(options.TimeTilDormant), + TimeTilDormantAutoDelete: int64(options.TimeTilDormantAutoDelete), }) if !assert.NoError(t, err) { return database.Template{}, err diff --git a/coderd/workspaces.go b/coderd/workspaces.go index 0f493e5995b24..92e4c029f3777 100644 --- a/coderd/workspaces.go +++ b/coderd/workspaces.go @@ -765,43 +765,43 @@ func (api *API) putWorkspaceTTL(rw http.ResponseWriter, r *http.Request) { rw.WriteHeader(http.StatusNoContent) } -// @Summary Update workspace lock by id. -// @ID update-workspace-lock-by-id +// @Summary Update workspace dormancy status by id. +// @ID update-workspace-dormancy-status-by-id // @Security CoderSessionToken // @Accept json // @Produce json // @Tags Workspaces // @Param workspace path string true "Workspace ID" format(uuid) -// @Param request body codersdk.UpdateWorkspaceLock true "Lock or unlock a workspace" +// @Param request body codersdk.UpdateWorkspaceDormancy true "Make a workspace dormant or active" // @Success 200 {object} codersdk.Workspace -// @Router /workspaces/{workspace}/lock [put] -func (api *API) putWorkspaceLock(rw http.ResponseWriter, r *http.Request) { +// @Router /workspaces/{workspace}/dormant [put] +func (api *API) putWorkspaceDormant(rw http.ResponseWriter, r *http.Request) { ctx := r.Context() workspace := httpmw.WorkspaceParam(r) - var req codersdk.UpdateWorkspaceLock + var req codersdk.UpdateWorkspaceDormancy if !httpapi.Read(ctx, rw, r, &req) { return } // If the workspace is already in the desired state do nothing! - if workspace.LockedAt.Valid == req.Lock { + if workspace.DormantAt.Valid == req.Dormant { httpapi.Write(ctx, rw, http.StatusNotModified, codersdk.Response{ Message: "Nothing to do!", }) return } - lockedAt := sql.NullTime{ - Valid: req.Lock, + dormantAt := sql.NullTime{ + Valid: req.Dormant, } - if req.Lock { - lockedAt.Time = database.Now() + if req.Dormant { + dormantAt.Time = database.Now() } - workspace, err := api.Database.UpdateWorkspaceLockedDeletingAt(ctx, database.UpdateWorkspaceLockedDeletingAtParams{ - ID: workspace.ID, - LockedAt: lockedAt, + workspace, err := api.Database.UpdateWorkspaceDormantDeletingAt(ctx, database.UpdateWorkspaceDormantDeletingAtParams{ + ID: workspace.ID, + DormantAt: dormantAt, }) if err != nil { httpapi.Write(ctx, rw, http.StatusInternalServerError, codersdk.Response{ @@ -1153,14 +1153,14 @@ func convertWorkspace( autostartSchedule = &workspace.AutostartSchedule.String } - var lockedAt *time.Time - if workspace.LockedAt.Valid { - lockedAt = &workspace.LockedAt.Time + var dormantAt *time.Time + if workspace.DormantAt.Valid { + dormantAt = &workspace.DormantAt.Time } - var deletedAt *time.Time + var deletingAt *time.Time if workspace.DeletingAt.Valid { - deletedAt = &workspace.DeletingAt.Time + deletingAt = &workspace.DeletingAt.Time } failingAgents := []uuid.UUID{} @@ -1192,8 +1192,8 @@ func convertWorkspace( AutostartSchedule: autostartSchedule, TTLMillis: ttlMillis, LastUsedAt: workspace.LastUsedAt, - DeletingAt: deletedAt, - LockedAt: lockedAt, + DeletingAt: deletingAt, + DormantAt: dormantAt, Health: codersdk.WorkspaceHealth{ Healthy: len(failingAgents) == 0, FailingAgents: failingAgents, diff --git a/coderd/workspaces_test.go b/coderd/workspaces_test.go index b42f4517db82d..ebedb8497deae 100644 --- a/coderd/workspaces_test.go +++ b/coderd/workspaces_test.go @@ -1363,9 +1363,9 @@ func TestWorkspaceFilterManual(t *testing.T) { 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 { - assert.Equal(t, inactivityTTL, options.InactivityTTL) + assert.Equal(t, inactivityTTL, options.TimeTilDormant) } - template.InactivityTTL = int64(options.InactivityTTL) + template.TimeTilDormant = int64(options.TimeTilDormant) return template, nil }, }, @@ -1385,11 +1385,11 @@ func TestWorkspaceFilterManual(t *testing.T) { defer cancel() template, err := client.UpdateTemplateMeta(ctx, template.ID, codersdk.UpdateTemplateMeta{ - InactivityTTLMillis: inactivityTTL.Milliseconds(), + TimeTilDormantMillis: inactivityTTL.Milliseconds(), }) assert.NoError(t, err) - assert.Equal(t, inactivityTTL.Milliseconds(), template.InactivityTTLMillis) + assert.Equal(t, inactivityTTL.Milliseconds(), template.TimeTilDormantMillis) workspace := coderdtest.CreateWorkspace(t, client, user.OrganizationID, template.ID) coderdtest.AwaitWorkspaceBuildJob(t, client, workspace.LatestBuild.ID) @@ -1404,11 +1404,11 @@ func TestWorkspaceFilterManual(t *testing.T) { assert.NoError(t, err) // we are expecting that no workspaces are returned as user is unlicensed - // and template.InactivityTTL should be 0 + // and template.TimeTilDormant should be 0 assert.Len(t, res.Workspaces, 0) }) - t.Run("LockedAt", func(t *testing.T) { + t.Run("DormantAt", func(t *testing.T) { // this test has a licensed counterpart in enterprise/coderd/workspaces_test.go: FilterQueryHasDeletingByAndLicensed t.Parallel() client := coderdtest.New(t, &coderdtest.Options{ @@ -1428,24 +1428,24 @@ func TestWorkspaceFilterManual(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), testutil.WaitLong) defer cancel() - lockedWorkspace := coderdtest.CreateWorkspace(t, client, user.OrganizationID, template.ID) - _ = coderdtest.AwaitWorkspaceBuildJob(t, client, lockedWorkspace.LatestBuild.ID) + dormantWorkspace := coderdtest.CreateWorkspace(t, client, user.OrganizationID, template.ID) + _ = coderdtest.AwaitWorkspaceBuildJob(t, client, dormantWorkspace.LatestBuild.ID) - // Create another workspace to validate that we do not return unlocked workspaces. + // Create another workspace to validate that we do not return active workspaces. _ = coderdtest.CreateWorkspace(t, client, user.OrganizationID, template.ID) - _ = coderdtest.AwaitWorkspaceBuildJob(t, client, lockedWorkspace.LatestBuild.ID) + _ = coderdtest.AwaitWorkspaceBuildJob(t, client, dormantWorkspace.LatestBuild.ID) - err := client.UpdateWorkspaceLock(ctx, lockedWorkspace.ID, codersdk.UpdateWorkspaceLock{ - Lock: true, + err := client.UpdateWorkspaceDormancy(ctx, dormantWorkspace.ID, codersdk.UpdateWorkspaceDormancy{ + Dormant: true, }) require.NoError(t, err) res, err := client.Workspaces(ctx, codersdk.WorkspaceFilter{ - FilterQuery: fmt.Sprintf("locked_at:%s", time.Now().Add(-time.Minute).Format("2006-01-02")), + FilterQuery: fmt.Sprintf("dormant_at:%s", time.Now().Add(-time.Minute).Format("2006-01-02")), }) require.NoError(t, err) require.Len(t, res.Workspaces, 1) - require.NotNil(t, res.Workspaces[0].LockedAt) + require.NotNil(t, res.Workspaces[0].DormantAt) }) t.Run("LastUsed", func(t *testing.T) { @@ -2782,21 +2782,21 @@ func TestWorkspaceWithEphemeralRichParameters(t *testing.T) { require.ElementsMatch(t, expectedBuildParameters, workspaceBuildParameters) } -func TestWorkspaceLock(t *testing.T) { +func TestWorkspaceDormant(t *testing.T) { t.Parallel() t.Run("OK", func(t *testing.T) { t.Parallel() var ( - client = coderdtest.New(t, &coderdtest.Options{IncludeProvisionerDaemon: true}) - user = coderdtest.CreateFirstUser(t, client) - version = coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, nil) - _ = coderdtest.AwaitTemplateVersionJob(t, client, version.ID) - lockedTTL = time.Minute + client = coderdtest.New(t, &coderdtest.Options{IncludeProvisionerDaemon: true}) + user = coderdtest.CreateFirstUser(t, client) + version = coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, nil) + _ = coderdtest.AwaitTemplateVersionJob(t, client, version.ID) + timeTilDormantAutoDelete = time.Minute ) template := coderdtest.CreateTemplate(t, client, user.OrganizationID, version.ID, func(ctr *codersdk.CreateTemplateRequest) { - ctr.LockedTTLMillis = ptr.Ref[int64](lockedTTL.Milliseconds()) + ctr.TimeTilDormantAutoDeleteMillis = ptr.Ref[int64](timeTilDormantAutoDelete.Milliseconds()) }) workspace := coderdtest.CreateWorkspace(t, client, user.OrganizationID, template.ID) _ = coderdtest.AwaitWorkspaceBuildJob(t, client, workspace.LatestBuild.ID) @@ -2805,32 +2805,32 @@ func TestWorkspaceLock(t *testing.T) { defer cancel() lastUsedAt := workspace.LastUsedAt - err := client.UpdateWorkspaceLock(ctx, workspace.ID, codersdk.UpdateWorkspaceLock{ - Lock: true, + err := client.UpdateWorkspaceDormancy(ctx, workspace.ID, codersdk.UpdateWorkspaceDormancy{ + Dormant: true, }) require.NoError(t, err) workspace = coderdtest.MustWorkspace(t, client, workspace.ID) require.NoError(t, err, "fetch provisioned workspace") - // The template doesn't have a locked_ttl set so this should be nil. + // The template doesn't have a time_til_dormant_autodelete set so this should be nil. require.Nil(t, workspace.DeletingAt) - require.NotNil(t, workspace.LockedAt) - require.WithinRange(t, *workspace.LockedAt, time.Now().Add(-time.Second*10), time.Now()) + require.NotNil(t, workspace.DormantAt) + require.WithinRange(t, *workspace.DormantAt, time.Now().Add(-time.Second*10), time.Now()) require.Equal(t, lastUsedAt, workspace.LastUsedAt) workspace = coderdtest.MustWorkspace(t, client, workspace.ID) lastUsedAt = workspace.LastUsedAt - err = client.UpdateWorkspaceLock(ctx, workspace.ID, codersdk.UpdateWorkspaceLock{ - Lock: false, + err = client.UpdateWorkspaceDormancy(ctx, workspace.ID, codersdk.UpdateWorkspaceDormancy{ + Dormant: false, }) require.NoError(t, err) workspace, err = client.Workspace(ctx, workspace.ID) require.NoError(t, err, "fetch provisioned workspace") - require.Nil(t, workspace.LockedAt) - // The template doesn't have a locked_ttl set so this should be nil. + require.Nil(t, workspace.DormantAt) + // The template doesn't have a time_til_dormant_autodelete set so this should be nil. require.Nil(t, workspace.DeletingAt) - // The last_used_at should get updated when we unlock the workspace. + // The last_used_at should get updated when we activate the workspace. require.True(t, workspace.LastUsedAt.After(lastUsedAt)) }) @@ -2849,23 +2849,23 @@ func TestWorkspaceLock(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), testutil.WaitLong) defer cancel() - err := client.UpdateWorkspaceLock(ctx, workspace.ID, codersdk.UpdateWorkspaceLock{ - Lock: true, + err := client.UpdateWorkspaceDormancy(ctx, workspace.ID, codersdk.UpdateWorkspaceDormancy{ + Dormant: true, }) require.NoError(t, err) - // Should be able to stop a workspace while it is locked. + // Should be able to stop a workspace while it is dormant. coderdtest.MustTransitionWorkspace(t, client, workspace.ID, database.WorkspaceTransitionStart, database.WorkspaceTransitionStop) - // Should not be able to start a workspace while it is locked. + // Should not be able to start a workspace while it is dormant. _, err = client.CreateWorkspaceBuild(ctx, workspace.ID, codersdk.CreateWorkspaceBuildRequest{ TemplateVersionID: template.ActiveVersionID, Transition: codersdk.WorkspaceTransition(database.WorkspaceTransitionStart), }) require.Error(t, err) - err = client.UpdateWorkspaceLock(ctx, workspace.ID, codersdk.UpdateWorkspaceLock{ - Lock: false, + err = client.UpdateWorkspaceDormancy(ctx, workspace.ID, codersdk.UpdateWorkspaceDormancy{ + Dormant: false, }) require.NoError(t, err) coderdtest.MustTransitionWorkspace(t, client, workspace.ID, database.WorkspaceTransitionStop, database.WorkspaceTransitionStart) diff --git a/codersdk/organizations.go b/codersdk/organizations.go index 96b026a3197a5..0b4af0e67056a 100644 --- a/codersdk/organizations.go +++ b/codersdk/organizations.go @@ -108,12 +108,12 @@ type CreateTemplateRequest struct { // FailureTTLMillis allows optionally specifying the max lifetime before Coder // stops all resources for failed workspaces created from this template. FailureTTLMillis *int64 `json:"failure_ttl_ms,omitempty"` - // InactivityTTLMillis allows optionally specifying the max lifetime before Coder + // TimeTilDormantMillis allows optionally specifying the max lifetime before Coder // locks inactive workspaces created from this template. - InactivityTTLMillis *int64 `json:"inactivity_ttl_ms,omitempty"` - // LockedTTLMillis allows optionally specifying the max lifetime before Coder - // permanently deletes locked workspaces created from this template. - LockedTTLMillis *int64 `json:"locked_ttl_ms,omitempty"` + TimeTilDormantMillis *int64 `json:"dormant_ttl_ms,omitempty"` + // TimeTilDormantAutoDeleteMillis allows optionally specifying the max lifetime before Coder + // permanently deletes dormant workspaces created from this template. + TimeTilDormantAutoDeleteMillis *int64 `json:"delete_ttl_ms,omitempty"` // DisableEveryoneGroupAccess allows optionally disabling the default // behavior of granting the 'everyone' group access to use the template. diff --git a/codersdk/templates.go b/codersdk/templates.go index f566ac4f2ce32..406933c72b75f 100644 --- a/codersdk/templates.go +++ b/codersdk/templates.go @@ -44,12 +44,12 @@ type Template struct { AllowUserAutostop bool `json:"allow_user_autostop"` AllowUserCancelWorkspaceJobs bool `json:"allow_user_cancel_workspace_jobs"` - // FailureTTLMillis, InactivityTTLMillis, and LockedTTLMillis are enterprise-only. Their + // FailureTTLMillis, TimeTilDormantMillis, and TimeTilDormantAutoDeleteMillis are enterprise-only. Their // values are used if your license is entitled to use the advanced // template scheduling feature. - FailureTTLMillis int64 `json:"failure_ttl_ms"` - InactivityTTLMillis int64 `json:"inactivity_ttl_ms"` - LockedTTLMillis int64 `json:"locked_ttl_ms"` + FailureTTLMillis int64 `json:"failure_ttl_ms"` + TimeTilDormantMillis int64 `json:"time_til_dormant_ms"` + TimeTilDormantAutoDeleteMillis int64 `json:"time_til_dormant_autodelete_ms"` } // WeekdaysToBitmap converts a list of weekdays to a bitmap in accordance with @@ -185,22 +185,22 @@ type UpdateTemplateMeta struct { // RestartRequirement 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. - RestartRequirement *TemplateRestartRequirement `json:"restart_requirement,omitempty"` - AllowUserAutostart bool `json:"allow_user_autostart,omitempty"` - AllowUserAutostop bool `json:"allow_user_autostop,omitempty"` - AllowUserCancelWorkspaceJobs bool `json:"allow_user_cancel_workspace_jobs,omitempty"` - FailureTTLMillis int64 `json:"failure_ttl_ms,omitempty"` - InactivityTTLMillis int64 `json:"inactivity_ttl_ms,omitempty"` - LockedTTLMillis int64 `json:"locked_ttl_ms,omitempty"` + RestartRequirement *TemplateRestartRequirement `json:"restart_requirement,omitempty"` + AllowUserAutostart bool `json:"allow_user_autostart,omitempty"` + AllowUserAutostop bool `json:"allow_user_autostop,omitempty"` + AllowUserCancelWorkspaceJobs bool `json:"allow_user_cancel_workspace_jobs,omitempty"` + FailureTTLMillis int64 `json:"failure_ttl_ms,omitempty"` + TimeTilDormantMillis int64 `json:"time_til_dormant_ms,omitempty"` + TimeTilDormantAutoDeleteMillis int64 `json:"time_til_dormant_autodelete_ms,omitempty"` // UpdateWorkspaceLastUsedAt updates the last_used_at field of workspaces // spawned from the template. This is useful for preventing workspaces being // immediately locked when updating the inactivity_ttl field to a new, shorter // value. UpdateWorkspaceLastUsedAt bool `json:"update_workspace_last_used_at"` - // UpdateWorkspaceLockedAt updates the locked_at field of workspaces spawned - // from the template. This is useful for preventing locked workspaces being immediately - // deleted when updating the locked_ttl field to a new, shorter value. - UpdateWorkspaceLockedAt bool `json:"update_workspace_locked_at"` + // UpdateWorkspaceDormant updates the dormant_at field of workspaces spawned + // from the template. This is useful for preventing dormant workspaces being immediately + // deleted when updating the dormant_ttl field to a new, shorter value. + UpdateWorkspaceDormantAt bool `json:"update_workspace_dormant_at"` } type TemplateExample struct { diff --git a/codersdk/workspaces.go b/codersdk/workspaces.go index 0f43143bfe0fb..d7b191c6273b6 100644 --- a/codersdk/workspaces.go +++ b/codersdk/workspaces.go @@ -35,14 +35,15 @@ type Workspace struct { TTLMillis *int64 `json:"ttl_ms,omitempty"` LastUsedAt time.Time `json:"last_used_at" format:"date-time"` - // DeletingAt indicates the time of the upcoming workspace deletion, if applicable; otherwise it is nil. - // Workspaces may have impending deletions if Template.InactivityTTL feature is turned on and the workspace is inactive. + // DeletingAt indicates the time at which the workspace will be permanently deleted. + // A workspace is eligible for deletion if it is dormant (a non-nil dormant_at value) + // and a value has been specified for time_til_dormant_autodelete on its template. DeletingAt *time.Time `json:"deleting_at" format:"date-time"` - // LockedAt being non-nil indicates a workspace that has been locked. - // A locked workspace is no longer accessible by a user and must be - // unlocked by an admin. It is subject to deletion if it breaches - // the duration of the locked_ttl field on its template. - LockedAt *time.Time `json:"locked_at" format:"date-time"` + // DormantAt being non-nil indicates a workspace that is dormant. + // A dormant workspace is no longer accessible must be activated. + // It is subject to deletion if it breaches + // the duration of the time_til_ field on its template. + DormantAt *time.Time `json:"dormant_at" format:"date-time"` // Health shows the health of the workspace and information about // what is causing an unhealthy status. Health WorkspaceHealth `json:"health"` @@ -293,14 +294,16 @@ func (c *Client) PutExtendWorkspace(ctx context.Context, id uuid.UUID, req PutEx return nil } -// UpdateWorkspaceLock is a request to lock or unlock a workspace. -type UpdateWorkspaceLock struct { - Lock bool `json:"lock"` +// UpdateWorkspaceDormancy is a request to activate or make a workspace dormant. +// A value of false will activate a dormant workspace. +type UpdateWorkspaceDormancy struct { + Dormant bool `json:"dormant"` } -// UpdateWorkspaceLock locks or unlocks a workspace. -func (c *Client) UpdateWorkspaceLock(ctx context.Context, id uuid.UUID, req UpdateWorkspaceLock) error { - path := fmt.Sprintf("/api/v2/workspaces/%s/lock", id.String()) +// UpdateWorkspaceDormancy sets a workspace as dormant if dormant=true and activates a dormant workspace +// if dormant=false. +func (c *Client) UpdateWorkspaceDormancy(ctx context.Context, id uuid.UUID, req UpdateWorkspaceDormancy) error { + path := fmt.Sprintf("/api/v2/workspaces/%s/dormant", id.String()) res, err := c.Request(ctx, http.MethodPut, path, req) if err != nil { return xerrors.Errorf("update workspace lock: %w", err) diff --git a/docs/admin/audit-logs.md b/docs/admin/audit-logs.md index 3ad9395e3556f..6d7293731f6cf 100644 --- a/docs/admin/audit-logs.md +++ b/docs/admin/audit-logs.md @@ -8,19 +8,19 @@ 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
| -| License
create, delete |
FieldTracked
exptrue
idfalse
jwtfalse
uploaded_attrue
uuidtrue
| -| Template
write, delete |
FieldTracked
active_version_idtrue
allow_user_autostarttrue
allow_user_autostoptrue
allow_user_cancel_workspace_jobstrue
created_atfalse
created_bytrue
created_by_avatar_urlfalse
created_by_usernamefalse
default_ttltrue
deletedfalse
descriptiontrue
display_nametrue
failure_ttltrue
group_acltrue
icontrue
idtrue
inactivity_ttltrue
locked_ttltrue
max_ttltrue
nametrue
organization_idfalse
provisionertrue
restart_requirement_days_of_weektrue
restart_requirement_weekstrue
updated_atfalse
user_acltrue
| -| TemplateVersion
create, write |
FieldTracked
created_atfalse
created_bytrue
created_by_avatar_urlfalse
created_by_usernamefalse
git_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
quiet_hours_scheduletrue
rbac_rolestrue
statustrue
updated_atfalse
usernametrue
| -| Workspace
create, write, delete |
FieldTracked
autostart_scheduletrue
created_atfalse
deletedfalse
deleting_attrue
idtrue
last_used_atfalse
locked_attrue
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
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
| +| License
create, delete |
FieldTracked
exptrue
idfalse
jwtfalse
uploaded_attrue
uuidtrue
| +| Template
write, delete |
FieldTracked
active_version_idtrue
allow_user_autostarttrue
allow_user_autostoptrue
allow_user_cancel_workspace_jobstrue
created_atfalse
created_bytrue
created_by_avatar_urlfalse
created_by_usernamefalse
default_ttltrue
deletedfalse
descriptiontrue
display_nametrue
failure_ttltrue
group_acltrue
icontrue
idtrue
max_ttltrue
nametrue
organization_idfalse
provisionertrue
restart_requirement_days_of_weektrue
restart_requirement_weekstrue
time_til_dormanttrue
time_til_dormant_autodeletetrue
updated_atfalse
user_acltrue
| +| TemplateVersion
create, write |
FieldTracked
created_atfalse
created_bytrue
created_by_avatar_urlfalse
created_by_usernamefalse
git_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
quiet_hours_scheduletrue
rbac_rolestrue
statustrue
updated_atfalse
usernametrue
| +| Workspace
create, write, delete |
FieldTracked
autostart_scheduletrue
created_atfalse
deletedfalse
deleting_attrue
dormant_attrue
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
wildcard_hostnametrue
| diff --git a/docs/api/schemas.md b/docs/api/schemas.md index b61907f0cd906..68ea0c6d321d9 100644 --- a/docs/api/schemas.md +++ b/docs/api/schemas.md @@ -1477,13 +1477,13 @@ AuthorizationObject can represent a "set" of objects, such as: all workspaces in "allow_user_autostop": true, "allow_user_cancel_workspace_jobs": true, "default_ttl_ms": 0, + "delete_ttl_ms": 0, "description": "string", "disable_everyone_group_access": true, "display_name": "string", + "dormant_ttl_ms": 0, "failure_ttl_ms": 0, "icon": "string", - "inactivity_ttl_ms": 0, - "locked_ttl_ms": 0, "max_ttl_ms": 0, "name": "string", "restart_requirement": { @@ -1502,13 +1502,13 @@ 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". | | `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. | | `disable_everyone_group_access` | boolean | false | | Disable everyone group access allows optionally disabling the default behavior of granting the 'everyone' group access to use the template. If this is set to true, the template will not be available to all users, and must be explicitly granted to users or groups in the permissions settings of the template. | | `display_name` | string | false | | Display name is the displayed name of the template. | +| `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. | -| `inactivity_ttl_ms` | integer | false | | Inactivity ttl ms allows optionally specifying the max lifetime before Coder locks inactive workspaces created from this template. | -| `locked_ttl_ms` | integer | false | | Locked ttl ms allows optionally specifying the max lifetime before Coder permanently deletes locked workspaces created from this template. | | `max_ttl_ms` | integer | false | | Max ttl ms remove max_ttl once restart_requirement is matured | | `name` | string | true | | Name is the name of the template. | | `restart_requirement` | [codersdk.TemplateRestartRequirement](#codersdktemplaterestartrequirement) | false | | Restart requirement allows optionally specifying the restart requirement for workspaces created from this template. This is an enterprise feature. | @@ -4232,8 +4232,6 @@ AuthorizationObject can represent a "set" of objects, such as: all workspaces in "failure_ttl_ms": 0, "icon": "string", "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", - "inactivity_ttl_ms": 0, - "locked_ttl_ms": 0, "max_ttl_ms": 0, "name": "string", "organization_id": "7c60d51f-b44e-4682-87d6-449835ea4de6", @@ -4242,37 +4240,39 @@ AuthorizationObject can represent a "set" of objects, such as: all workspaces in "days_of_week": ["monday"], "weeks": 0 }, + "time_til_dormant_autodelete_ms": 0, + "time_til_dormant_ms": 0, "updated_at": "2019-08-24T14:15:22Z" } ``` ### Properties -| Name | Type | Required | Restrictions | Description | -| ---------------------------------- | -------------------------------------------------------------------------- | -------- | ------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| `active_user_count` | integer | false | | Active user count is set to -1 when loading. | -| `active_version_id` | string | false | | | -| `allow_user_autostart` | boolean | false | | Allow user autostart and AllowUserAutostop are enterprise-only. Their values are only used if your license is entitled to use the advanced template scheduling feature. | -| `allow_user_autostop` | boolean | false | | | -| `allow_user_cancel_workspace_jobs` | boolean | false | | | -| `build_time_stats` | [codersdk.TemplateBuildTimeStats](#codersdktemplatebuildtimestats) | false | | | -| `created_at` | string | false | | | -| `created_by_id` | string | false | | | -| `created_by_name` | string | false | | | -| `default_ttl_ms` | integer | false | | | -| `description` | string | false | | | -| `display_name` | string | false | | | -| `failure_ttl_ms` | integer | false | | Failure ttl ms InactivityTTLMillis, and LockedTTLMillis are enterprise-only. Their values are used if your license is entitled to use the advanced template scheduling feature. | -| `icon` | string | false | | | -| `id` | string | false | | | -| `inactivity_ttl_ms` | integer | false | | | -| `locked_ttl_ms` | integer | false | | | -| `max_ttl_ms` | integer | false | | Max ttl ms remove max_ttl once restart_requirement is matured | -| `name` | string | false | | | -| `organization_id` | string | false | | | -| `provisioner` | string | false | | | -| `restart_requirement` | [codersdk.TemplateRestartRequirement](#codersdktemplaterestartrequirement) | false | | Restart requirement is an enterprise feature. Its value is only used if your license is entitled to use the advanced template scheduling feature. | -| `updated_at` | string | false | | | +| Name | Type | Required | Restrictions | Description | +| ---------------------------------- | -------------------------------------------------------------------------- | -------- | ------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `active_user_count` | integer | false | | Active user count is set to -1 when loading. | +| `active_version_id` | string | false | | | +| `allow_user_autostart` | boolean | false | | Allow user autostart and AllowUserAutostop are enterprise-only. Their values are only used if your license is entitled to use the advanced template scheduling feature. | +| `allow_user_autostop` | boolean | false | | | +| `allow_user_cancel_workspace_jobs` | boolean | false | | | +| `build_time_stats` | [codersdk.TemplateBuildTimeStats](#codersdktemplatebuildtimestats) | false | | | +| `created_at` | string | false | | | +| `created_by_id` | string | false | | | +| `created_by_name` | string | false | | | +| `default_ttl_ms` | integer | false | | | +| `description` | string | false | | | +| `display_name` | string | false | | | +| `failure_ttl_ms` | integer | false | | Failure ttl ms TimeTilDormantMillis, and TimeTilDormantAutoDeleteMillis are enterprise-only. Their values are used if your license is entitled to use the advanced template scheduling feature. | +| `icon` | string | false | | | +| `id` | string | false | | | +| `max_ttl_ms` | integer | false | | Max ttl ms remove max_ttl once restart_requirement is matured | +| `name` | string | false | | | +| `organization_id` | string | false | | | +| `provisioner` | string | false | | | +| `restart_requirement` | [codersdk.TemplateRestartRequirement](#codersdktemplaterestartrequirement) | false | | Restart requirement is an enterprise feature. Its value is only used if your license is entitled to use the advanced template scheduling feature. | +| `time_til_dormant_autodelete_ms` | integer | false | | | +| `time_til_dormant_ms` | integer | false | | | +| `updated_at` | string | false | | | #### Enumerated Values @@ -5044,19 +5044,19 @@ If the schedule is empty, the user will be updated to use the default schedule.| | ---------- | ------ | -------- | ------------ | ----------- | | `schedule` | string | false | | | -## codersdk.UpdateWorkspaceLock +## codersdk.UpdateWorkspaceDormancy ```json { - "lock": true + "dormant": true } ``` ### Properties -| Name | Type | Required | Restrictions | Description | -| ------ | ------- | -------- | ------------ | ----------- | -| `lock` | boolean | false | | | +| Name | Type | Required | Restrictions | Description | +| --------- | ------- | -------- | ------------ | ----------- | +| `dormant` | boolean | false | | | ## codersdk.UpdateWorkspaceRequest @@ -5350,6 +5350,7 @@ If the schedule is empty, the user will be updated to use the default schedule.| "autostart_schedule": "string", "created_at": "2019-08-24T14:15:22Z", "deleting_at": "2019-08-24T14:15:22Z", + "dormant_at": "2019-08-24T14:15:22Z", "health": { "failing_agents": ["497f6eca-6276-4993-bfeb-53cbbbba6f08"], "healthy": false @@ -5484,7 +5485,6 @@ If the schedule is empty, the user will be updated to use the default schedule.| "workspace_owner_id": "e7078695-5279-4c86-8774-3ac2367a2fc7", "workspace_owner_name": "string" }, - "locked_at": "2019-08-24T14:15:22Z", "name": "string", "organization_id": "7c60d51f-b44e-4682-87d6-449835ea4de6", "outdated": true, @@ -5502,28 +5502,28 @@ If the schedule is empty, the user will be updated to use the default schedule.| ### Properties -| Name | Type | Required | Restrictions | Description | -| ------------------------------------------- | ---------------------------------------------------- | -------- | ------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| `autostart_schedule` | string | false | | | -| `created_at` | string | false | | | -| `deleting_at` | string | false | | Deleting at indicates the time of the upcoming workspace deletion, if applicable; otherwise it is nil. Workspaces may have impending deletions if Template.InactivityTTL feature is turned on and the workspace is inactive. | -| `health` | [codersdk.WorkspaceHealth](#codersdkworkspacehealth) | false | | Health shows the health of the workspace and information about what is causing an unhealthy status. | -| `id` | string | false | | | -| `last_used_at` | string | false | | | -| `latest_build` | [codersdk.WorkspaceBuild](#codersdkworkspacebuild) | false | | | -| `locked_at` | string | false | | Locked at being non-nil indicates a workspace that has been locked. A locked workspace is no longer accessible by a user and must be unlocked by an admin. It is subject to deletion if it breaches the duration of the locked_ttl field on its template. | -| `name` | string | false | | | -| `organization_id` | string | false | | | -| `outdated` | boolean | false | | | -| `owner_id` | string | false | | | -| `owner_name` | string | false | | | -| `template_allow_user_cancel_workspace_jobs` | boolean | false | | | -| `template_display_name` | string | false | | | -| `template_icon` | string | false | | | -| `template_id` | string | false | | | -| `template_name` | string | false | | | -| `ttl_ms` | integer | false | | | -| `updated_at` | string | false | | | +| Name | Type | Required | Restrictions | Description | +| ------------------------------------------- | ---------------------------------------------------- | -------- | ------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `autostart_schedule` | string | false | | | +| `created_at` | string | false | | | +| `deleting_at` | string | false | | Deleting at indicates the time at which the workspace will be permanently deleted. A workspace is eligible for deletion if it is dormant (a non-nil dormant_at value) and a value has been specified for time_til_dormant_autodelete on its template. | +| `dormant_at` | string | false | | Dormant at being non-nil indicates a workspace that is dormant. A dormant workspace is no longer accessible must be activated. It is subject to deletion if it breaches the duration of the time*til* field on its template. | +| `health` | [codersdk.WorkspaceHealth](#codersdkworkspacehealth) | false | | Health shows the health of the workspace and information about what is causing an unhealthy status. | +| `id` | string | false | | | +| `last_used_at` | string | false | | | +| `latest_build` | [codersdk.WorkspaceBuild](#codersdkworkspacebuild) | false | | | +| `name` | string | false | | | +| `organization_id` | string | false | | | +| `outdated` | boolean | false | | | +| `owner_id` | string | false | | | +| `owner_name` | string | false | | | +| `template_allow_user_cancel_workspace_jobs` | boolean | false | | | +| `template_display_name` | string | false | | | +| `template_icon` | string | false | | | +| `template_id` | string | false | | | +| `template_name` | string | false | | | +| `ttl_ms` | integer | false | | | +| `updated_at` | string | false | | | ## codersdk.WorkspaceAgent @@ -6484,6 +6484,7 @@ If the schedule is empty, the user will be updated to use the default schedule.| "autostart_schedule": "string", "created_at": "2019-08-24T14:15:22Z", "deleting_at": "2019-08-24T14:15:22Z", + "dormant_at": "2019-08-24T14:15:22Z", "health": { "failing_agents": ["497f6eca-6276-4993-bfeb-53cbbbba6f08"], "healthy": false @@ -6614,7 +6615,6 @@ If the schedule is empty, the user will be updated to use the default schedule.| "workspace_owner_id": "e7078695-5279-4c86-8774-3ac2367a2fc7", "workspace_owner_name": "string" }, - "locked_at": "2019-08-24T14:15:22Z", "name": "string", "organization_id": "7c60d51f-b44e-4682-87d6-449835ea4de6", "outdated": true, diff --git a/docs/api/templates.md b/docs/api/templates.md index 407ab84eba439..bb67535e786c2 100644 --- a/docs/api/templates.md +++ b/docs/api/templates.md @@ -50,8 +50,6 @@ curl -X GET http://coder-server:8080/api/v2/organizations/{organization}/templat "failure_ttl_ms": 0, "icon": "string", "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", - "inactivity_ttl_ms": 0, - "locked_ttl_ms": 0, "max_ttl_ms": 0, "name": "string", "organization_id": "7c60d51f-b44e-4682-87d6-449835ea4de6", @@ -60,6 +58,8 @@ curl -X GET http://coder-server:8080/api/v2/organizations/{organization}/templat "days_of_week": ["monday"], "weeks": 0 }, + "time_til_dormant_autodelete_ms": 0, + "time_til_dormant_ms": 0, "updated_at": "2019-08-24T14:15:22Z" } ] @@ -93,11 +93,9 @@ Status Code **200** | `» default_ttl_ms` | integer | false | | | | `» description` | string | false | | | | `» display_name` | string | false | | | -| `» failure_ttl_ms` | integer | false | | Failure ttl ms InactivityTTLMillis, and LockedTTLMillis are enterprise-only. Their values are used if your license is entitled to use the advanced template scheduling feature. | +| `» failure_ttl_ms` | integer | false | | Failure ttl ms TimeTilDormantMillis, and TimeTilDormantAutoDeleteMillis are enterprise-only. Their values are used if your license is entitled to use the advanced template scheduling feature. | | `» icon` | string | false | | | | `» id` | string(uuid) | false | | | -| `» inactivity_ttl_ms` | integer | false | | | -| `» locked_ttl_ms` | integer | false | | | | `» max_ttl_ms` | integer | false | | Max ttl ms remove max_ttl once restart_requirement is matured | | `» name` | string | false | | | | `» organization_id` | string(uuid) | false | | | @@ -106,6 +104,8 @@ Status Code **200** | `»» days_of_week` | array | false | | »days of week is a list of days of the week on which restarts are required. Restarts happen within the user's quiet hours (in their configured timezone). If no days are specified, restarts are not required. Weekdays cannot be specified twice. | | Restarts will only happen on weekdays in this list on weeks which line up with Weeks. | | `»» weeks` | integer | false | | Weeks is the number of weeks between required restarts. Weeks are synced across all workspaces (and Coder deployments) using modulo math on a hardcoded epoch week of January 2nd, 2023 (the first Monday of 2023). Values of 0 or 1 indicate weekly restarts. Values of 2 indicate fortnightly restarts, etc. | +| `» time_til_dormant_autodelete_ms` | integer | false | | | +| `» time_til_dormant_ms` | integer | false | | | | `» updated_at` | string(date-time) | false | | | #### Enumerated Values @@ -138,13 +138,13 @@ curl -X POST http://coder-server:8080/api/v2/organizations/{organization}/templa "allow_user_autostop": true, "allow_user_cancel_workspace_jobs": true, "default_ttl_ms": 0, + "delete_ttl_ms": 0, "description": "string", "disable_everyone_group_access": true, "display_name": "string", + "dormant_ttl_ms": 0, "failure_ttl_ms": 0, "icon": "string", - "inactivity_ttl_ms": 0, - "locked_ttl_ms": 0, "max_ttl_ms": 0, "name": "string", "restart_requirement": { @@ -192,8 +192,6 @@ curl -X POST http://coder-server:8080/api/v2/organizations/{organization}/templa "failure_ttl_ms": 0, "icon": "string", "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", - "inactivity_ttl_ms": 0, - "locked_ttl_ms": 0, "max_ttl_ms": 0, "name": "string", "organization_id": "7c60d51f-b44e-4682-87d6-449835ea4de6", @@ -202,6 +200,8 @@ curl -X POST http://coder-server:8080/api/v2/organizations/{organization}/templa "days_of_week": ["monday"], "weeks": 0 }, + "time_til_dormant_autodelete_ms": 0, + "time_til_dormant_ms": 0, "updated_at": "2019-08-24T14:15:22Z" } ``` @@ -324,8 +324,6 @@ curl -X GET http://coder-server:8080/api/v2/organizations/{organization}/templat "failure_ttl_ms": 0, "icon": "string", "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", - "inactivity_ttl_ms": 0, - "locked_ttl_ms": 0, "max_ttl_ms": 0, "name": "string", "organization_id": "7c60d51f-b44e-4682-87d6-449835ea4de6", @@ -334,6 +332,8 @@ curl -X GET http://coder-server:8080/api/v2/organizations/{organization}/templat "days_of_week": ["monday"], "weeks": 0 }, + "time_til_dormant_autodelete_ms": 0, + "time_til_dormant_ms": 0, "updated_at": "2019-08-24T14:15:22Z" } ``` @@ -629,8 +629,6 @@ curl -X GET http://coder-server:8080/api/v2/templates/{template} \ "failure_ttl_ms": 0, "icon": "string", "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", - "inactivity_ttl_ms": 0, - "locked_ttl_ms": 0, "max_ttl_ms": 0, "name": "string", "organization_id": "7c60d51f-b44e-4682-87d6-449835ea4de6", @@ -639,6 +637,8 @@ curl -X GET http://coder-server:8080/api/v2/templates/{template} \ "days_of_week": ["monday"], "weeks": 0 }, + "time_til_dormant_autodelete_ms": 0, + "time_til_dormant_ms": 0, "updated_at": "2019-08-24T14:15:22Z" } ``` @@ -744,8 +744,6 @@ curl -X PATCH http://coder-server:8080/api/v2/templates/{template} \ "failure_ttl_ms": 0, "icon": "string", "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", - "inactivity_ttl_ms": 0, - "locked_ttl_ms": 0, "max_ttl_ms": 0, "name": "string", "organization_id": "7c60d51f-b44e-4682-87d6-449835ea4de6", @@ -754,6 +752,8 @@ curl -X PATCH http://coder-server:8080/api/v2/templates/{template} \ "days_of_week": ["monday"], "weeks": 0 }, + "time_til_dormant_autodelete_ms": 0, + "time_til_dormant_ms": 0, "updated_at": "2019-08-24T14:15:22Z" } ``` diff --git a/docs/api/workspaces.md b/docs/api/workspaces.md index 01b85e21b3527..7c4e9319cd2b8 100644 --- a/docs/api/workspaces.md +++ b/docs/api/workspaces.md @@ -48,6 +48,7 @@ curl -X POST http://coder-server:8080/api/v2/organizations/{organization}/member "autostart_schedule": "string", "created_at": "2019-08-24T14:15:22Z", "deleting_at": "2019-08-24T14:15:22Z", + "dormant_at": "2019-08-24T14:15:22Z", "health": { "failing_agents": ["497f6eca-6276-4993-bfeb-53cbbbba6f08"], "healthy": false @@ -182,7 +183,6 @@ curl -X POST http://coder-server:8080/api/v2/organizations/{organization}/member "workspace_owner_id": "e7078695-5279-4c86-8774-3ac2367a2fc7", "workspace_owner_name": "string" }, - "locked_at": "2019-08-24T14:15:22Z", "name": "string", "organization_id": "7c60d51f-b44e-4682-87d6-449835ea4de6", "outdated": true, @@ -236,6 +236,7 @@ curl -X GET http://coder-server:8080/api/v2/users/{user}/workspace/{workspacenam "autostart_schedule": "string", "created_at": "2019-08-24T14:15:22Z", "deleting_at": "2019-08-24T14:15:22Z", + "dormant_at": "2019-08-24T14:15:22Z", "health": { "failing_agents": ["497f6eca-6276-4993-bfeb-53cbbbba6f08"], "healthy": false @@ -370,7 +371,6 @@ curl -X GET http://coder-server:8080/api/v2/users/{user}/workspace/{workspacenam "workspace_owner_id": "e7078695-5279-4c86-8774-3ac2367a2fc7", "workspace_owner_name": "string" }, - "locked_at": "2019-08-24T14:15:22Z", "name": "string", "organization_id": "7c60d51f-b44e-4682-87d6-449835ea4de6", "outdated": true, @@ -427,6 +427,7 @@ curl -X GET http://coder-server:8080/api/v2/workspaces \ "autostart_schedule": "string", "created_at": "2019-08-24T14:15:22Z", "deleting_at": "2019-08-24T14:15:22Z", + "dormant_at": "2019-08-24T14:15:22Z", "health": { "failing_agents": ["497f6eca-6276-4993-bfeb-53cbbbba6f08"], "healthy": false @@ -557,7 +558,6 @@ curl -X GET http://coder-server:8080/api/v2/workspaces \ "workspace_owner_id": "e7078695-5279-4c86-8774-3ac2367a2fc7", "workspace_owner_name": "string" }, - "locked_at": "2019-08-24T14:15:22Z", "name": "string", "organization_id": "7c60d51f-b44e-4682-87d6-449835ea4de6", "outdated": true, @@ -612,6 +612,7 @@ curl -X GET http://coder-server:8080/api/v2/workspaces/{workspace} \ "autostart_schedule": "string", "created_at": "2019-08-24T14:15:22Z", "deleting_at": "2019-08-24T14:15:22Z", + "dormant_at": "2019-08-24T14:15:22Z", "health": { "failing_agents": ["497f6eca-6276-4993-bfeb-53cbbbba6f08"], "healthy": false @@ -746,7 +747,6 @@ curl -X GET http://coder-server:8080/api/v2/workspaces/{workspace} \ "workspace_owner_id": "e7078695-5279-4c86-8774-3ac2367a2fc7", "workspace_owner_name": "string" }, - "locked_at": "2019-08-24T14:15:22Z", "name": "string", "organization_id": "7c60d51f-b44e-4682-87d6-449835ea4de6", "outdated": true, @@ -842,88 +842,34 @@ curl -X PUT http://coder-server:8080/api/v2/workspaces/{workspace}/autostart \ To perform this operation, you must be authenticated. [Learn more](authentication.md). -## Extend workspace deadline by ID - -### Code samples - -```shell -# Example request using curl -curl -X PUT http://coder-server:8080/api/v2/workspaces/{workspace}/extend \ - -H 'Content-Type: application/json' \ - -H 'Accept: application/json' \ - -H 'Coder-Session-Token: API_KEY' -``` - -`PUT /workspaces/{workspace}/extend` - -> Body parameter - -```json -{ - "deadline": "2019-08-24T14:15:22Z" -} -``` - -### Parameters - -| Name | In | Type | Required | Description | -| ----------- | ---- | ---------------------------------------------------------------------------------- | -------- | ------------------------------ | -| `workspace` | path | string(uuid) | true | Workspace ID | -| `body` | body | [codersdk.PutExtendWorkspaceRequest](schemas.md#codersdkputextendworkspacerequest) | true | Extend deadline update request | - -### Example responses - -> 200 Response - -```json -{ - "detail": "string", - "message": "string", - "validations": [ - { - "detail": "string", - "field": "string" - } - ] -} -``` - -### Responses - -| Status | Meaning | Description | Schema | -| ------ | ------------------------------------------------------- | ----------- | ------------------------------------------------ | -| 200 | [OK](https://tools.ietf.org/html/rfc7231#section-6.3.1) | OK | [codersdk.Response](schemas.md#codersdkresponse) | - -To perform this operation, you must be authenticated. [Learn more](authentication.md). - -## Update workspace lock by id. +## Update workspace dormancy status by id. ### Code samples ```shell # Example request using curl -curl -X PUT http://coder-server:8080/api/v2/workspaces/{workspace}/lock \ +curl -X PUT http://coder-server:8080/api/v2/workspaces/{workspace}/dormant \ -H 'Content-Type: application/json' \ -H 'Accept: application/json' \ -H 'Coder-Session-Token: API_KEY' ``` -`PUT /workspaces/{workspace}/lock` +`PUT /workspaces/{workspace}/dormant` > Body parameter ```json { - "lock": true + "dormant": true } ``` ### Parameters -| Name | In | Type | Required | Description | -| ----------- | ---- | ---------------------------------------------------------------------- | -------- | -------------------------- | -| `workspace` | path | string(uuid) | true | Workspace ID | -| `body` | body | [codersdk.UpdateWorkspaceLock](schemas.md#codersdkupdateworkspacelock) | true | Lock or unlock a workspace | +| Name | In | Type | Required | Description | +| ----------- | ---- | ------------------------------------------------------------------------------ | -------- | ---------------------------------- | +| `workspace` | path | string(uuid) | true | Workspace ID | +| `body` | body | [codersdk.UpdateWorkspaceDormancy](schemas.md#codersdkupdateworkspacedormancy) | true | Make a workspace dormant or active | ### Example responses @@ -934,6 +880,7 @@ curl -X PUT http://coder-server:8080/api/v2/workspaces/{workspace}/lock \ "autostart_schedule": "string", "created_at": "2019-08-24T14:15:22Z", "deleting_at": "2019-08-24T14:15:22Z", + "dormant_at": "2019-08-24T14:15:22Z", "health": { "failing_agents": ["497f6eca-6276-4993-bfeb-53cbbbba6f08"], "healthy": false @@ -1068,7 +1015,6 @@ curl -X PUT http://coder-server:8080/api/v2/workspaces/{workspace}/lock \ "workspace_owner_id": "e7078695-5279-4c86-8774-3ac2367a2fc7", "workspace_owner_name": "string" }, - "locked_at": "2019-08-24T14:15:22Z", "name": "string", "organization_id": "7c60d51f-b44e-4682-87d6-449835ea4de6", "outdated": true, @@ -1092,6 +1038,60 @@ curl -X PUT http://coder-server:8080/api/v2/workspaces/{workspace}/lock \ To perform this operation, you must be authenticated. [Learn more](authentication.md). +## Extend workspace deadline by ID + +### Code samples + +```shell +# Example request using curl +curl -X PUT http://coder-server:8080/api/v2/workspaces/{workspace}/extend \ + -H 'Content-Type: application/json' \ + -H 'Accept: application/json' \ + -H 'Coder-Session-Token: API_KEY' +``` + +`PUT /workspaces/{workspace}/extend` + +> Body parameter + +```json +{ + "deadline": "2019-08-24T14:15:22Z" +} +``` + +### Parameters + +| Name | In | Type | Required | Description | +| ----------- | ---- | ---------------------------------------------------------------------------------- | -------- | ------------------------------ | +| `workspace` | path | string(uuid) | true | Workspace ID | +| `body` | body | [codersdk.PutExtendWorkspaceRequest](schemas.md#codersdkputextendworkspacerequest) | true | Extend deadline update request | + +### Example responses + +> 200 Response + +```json +{ + "detail": "string", + "message": "string", + "validations": [ + { + "detail": "string", + "field": "string" + } + ] +} +``` + +### Responses + +| Status | Meaning | Description | Schema | +| ------ | ------------------------------------------------------- | ----------- | ------------------------------------------------ | +| 200 | [OK](https://tools.ietf.org/html/rfc7231#section-6.3.1) | OK | [codersdk.Response](schemas.md#codersdkresponse) | + +To perform this operation, you must be authenticated. [Learn more](authentication.md). + ## Update workspace TTL by ID ### Code samples diff --git a/enterprise/audit/table.go b/enterprise/audit/table.go index 2e732a1d53a1d..a1dfef2d053d3 100644 --- a/enterprise/audit/table.go +++ b/enterprise/audit/table.go @@ -82,8 +82,8 @@ var auditableResourcesTypes = map[any]map[string]Action{ "allow_user_autostop": ActionTrack, "allow_user_cancel_workspace_jobs": ActionTrack, "failure_ttl": ActionTrack, - "inactivity_ttl": ActionTrack, - "locked_ttl": ActionTrack, + "time_til_dormant": ActionTrack, + "time_til_dormant_autodelete": ActionTrack, }, &database.TemplateVersion{}: { "id": ActionTrack, @@ -127,7 +127,7 @@ var auditableResourcesTypes = map[any]map[string]Action{ "autostart_schedule": ActionTrack, "ttl": ActionTrack, "last_used_at": ActionIgnore, - "locked_at": ActionTrack, + "dormant_at": ActionTrack, "deleting_at": ActionTrack, }, &database.WorkspaceBuild{}: { diff --git a/enterprise/coderd/schedule/template.go b/enterprise/coderd/schedule/template.go index c5613c44e7880..f37d9ded8d187 100644 --- a/enterprise/coderd/schedule/template.go +++ b/enterprise/coderd/schedule/template.go @@ -83,9 +83,9 @@ func (s *EnterpriseTemplateScheduleStore) Get(ctx context.Context, db database.S DaysOfWeek: uint8(tpl.RestartRequirementDaysOfWeek), Weeks: tpl.RestartRequirementWeeks, }, - FailureTTL: time.Duration(tpl.FailureTTL), - InactivityTTL: time.Duration(tpl.InactivityTTL), - LockedTTL: time.Duration(tpl.LockedTTL), + FailureTTL: time.Duration(tpl.FailureTTL), + TimeTilDormant: time.Duration(tpl.TimeTilDormant), + TimeTilDormantAutoDelete: time.Duration(tpl.TimeTilDormantAutoDelete), }, nil } @@ -99,8 +99,8 @@ func (s *EnterpriseTemplateScheduleStore) Set(ctx context.Context, db database.S int16(opts.RestartRequirement.DaysOfWeek) == tpl.RestartRequirementDaysOfWeek && opts.RestartRequirement.Weeks == tpl.RestartRequirementWeeks && int64(opts.FailureTTL) == tpl.FailureTTL && - int64(opts.InactivityTTL) == tpl.InactivityTTL && - int64(opts.LockedTTL) == tpl.LockedTTL && + int64(opts.TimeTilDormant) == tpl.TimeTilDormant && + int64(opts.TimeTilDormantAutoDelete) == tpl.TimeTilDormantAutoDelete && opts.UserAutostartEnabled == tpl.AllowUserAutostart && opts.UserAutostopEnabled == tpl.AllowUserAutostop { // Avoid updating the UpdatedAt timestamp if nothing will be changed. @@ -127,29 +127,29 @@ func (s *EnterpriseTemplateScheduleStore) Set(ctx context.Context, db database.S RestartRequirementDaysOfWeek: int16(opts.RestartRequirement.DaysOfWeek), RestartRequirementWeeks: opts.RestartRequirement.Weeks, FailureTTL: int64(opts.FailureTTL), - InactivityTTL: int64(opts.InactivityTTL), - LockedTTL: int64(opts.LockedTTL), + TimeTilDormant: int64(opts.TimeTilDormant), + TimeTilDormantAutoDelete: int64(opts.TimeTilDormantAutoDelete), }) if err != nil { return xerrors.Errorf("update template schedule: %w", err) } - var lockedAt time.Time - if opts.UpdateWorkspaceLockedAt { - lockedAt = database.Now() + var dormantAt time.Time + if opts.UpdateWorkspaceDormantAt { + dormantAt = database.Now() } - // If we updated the locked_ttl we need to update all the workspaces deleting_at + // If we updated the time_til_dormant_autodelete we need to update all the workspaces deleting_at // to ensure workspaces are being cleaned up correctly. Similarly if we are // disabling it (by passing 0), then we want to delete nullify the deleting_at // fields of all the template workspaces. - err = tx.UpdateWorkspacesLockedDeletingAtByTemplateID(ctx, database.UpdateWorkspacesLockedDeletingAtByTemplateIDParams{ - TemplateID: tpl.ID, - LockedTtlMs: opts.LockedTTL.Milliseconds(), - LockedAt: lockedAt, + err = tx.UpdateWorkspacesDormantDeletingAtByTemplateID(ctx, database.UpdateWorkspacesDormantDeletingAtByTemplateIDParams{ + TemplateID: tpl.ID, + TimeTilDormantAutodeleteMs: opts.TimeTilDormantAutoDelete.Milliseconds(), + DormantAt: dormantAt, }) if err != nil { - return xerrors.Errorf("update deleting_at of all workspaces for new locked_ttl %q: %w", opts.LockedTTL, err) + return xerrors.Errorf("update deleting_at of all workspaces for new time_til_dormant_autodelete %q: %w", opts.TimeTilDormantAutoDelete, err) } if opts.UpdateWorkspaceLastUsedAt { diff --git a/enterprise/coderd/schedule/template_test.go b/enterprise/coderd/schedule/template_test.go index aff7e2364fcec..14f7a384b0c12 100644 --- a/enterprise/coderd/schedule/template_test.go +++ b/enterprise/coderd/schedule/template_test.go @@ -225,9 +225,9 @@ func TestTemplateUpdateBuildDeadlines(t *testing.T) { DaysOfWeek: 0b01111111, Weeks: 0, }, - FailureTTL: 0, - InactivityTTL: 0, - LockedTTL: 0, + FailureTTL: 0, + TimeTilDormant: 0, + TimeTilDormantAutoDelete: 0, }) require.NoError(t, err) @@ -500,9 +500,9 @@ func TestTemplateUpdateBuildDeadlinesSkip(t *testing.T) { DaysOfWeek: 0b01111111, Weeks: 0, }, - FailureTTL: 0, - InactivityTTL: 0, - LockedTTL: 0, + FailureTTL: 0, + TimeTilDormant: 0, + TimeTilDormantAutoDelete: 0, }) require.NoError(t, err) diff --git a/enterprise/coderd/templates_test.go b/enterprise/coderd/templates_test.go index af364d3578b1c..52d29acf76cd6 100644 --- a/enterprise/coderd/templates_test.go +++ b/enterprise/coderd/templates_test.go @@ -202,41 +202,41 @@ func TestTemplates(t *testing.T) { version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, nil) coderdtest.AwaitTemplateVersionJob(t, client, version.ID) template := coderdtest.CreateTemplate(t, client, user.OrganizationID, version.ID) - require.EqualValues(t, 0, template.InactivityTTLMillis) + require.EqualValues(t, 0, template.TimeTilDormantMillis) require.EqualValues(t, 0, template.FailureTTLMillis) - require.EqualValues(t, 0, template.LockedTTLMillis) + require.EqualValues(t, 0, template.TimeTilDormantAutoDeleteMillis) var ( failureTTL int64 = 1 inactivityTTL int64 = 2 - lockedTTL int64 = 3 + dormantTTL int64 = 3 ) updated, err := client.UpdateTemplateMeta(ctx, template.ID, codersdk.UpdateTemplateMeta{ - Name: template.Name, - DisplayName: template.DisplayName, - Description: template.Description, - Icon: template.Icon, - AllowUserCancelWorkspaceJobs: template.AllowUserCancelWorkspaceJobs, - InactivityTTLMillis: inactivityTTL, - FailureTTLMillis: failureTTL, - LockedTTLMillis: lockedTTL, + Name: template.Name, + DisplayName: template.DisplayName, + Description: template.Description, + Icon: template.Icon, + AllowUserCancelWorkspaceJobs: template.AllowUserCancelWorkspaceJobs, + TimeTilDormantMillis: inactivityTTL, + FailureTTLMillis: failureTTL, + TimeTilDormantAutoDeleteMillis: dormantTTL, }) require.NoError(t, err) require.Equal(t, failureTTL, updated.FailureTTLMillis) - require.Equal(t, inactivityTTL, updated.InactivityTTLMillis) - require.Equal(t, lockedTTL, updated.LockedTTLMillis) + require.Equal(t, inactivityTTL, updated.TimeTilDormantMillis) + require.Equal(t, dormantTTL, updated.TimeTilDormantAutoDeleteMillis) // Validate fetching the template returns the same values as updating // the template. template, err = client.Template(ctx, template.ID) require.NoError(t, err) require.Equal(t, failureTTL, updated.FailureTTLMillis) - require.Equal(t, inactivityTTL, updated.InactivityTTLMillis) - require.Equal(t, lockedTTL, updated.LockedTTLMillis) + require.Equal(t, inactivityTTL, updated.TimeTilDormantMillis) + require.Equal(t, dormantTTL, updated.TimeTilDormantAutoDeleteMillis) }) - t.Run("UpdateLockedTTL", func(t *testing.T) { + t.Run("UpdateTimeTilDormantAutoDelete", func(t *testing.T) { t.Parallel() ctx := testutil.Context(t, testutil.WaitMedium) @@ -254,62 +254,62 @@ func TestTemplates(t *testing.T) { coderdtest.AwaitTemplateVersionJob(t, client, version.ID) template := coderdtest.CreateTemplate(t, client, user.OrganizationID, version.ID) - unlockedWorkspace := coderdtest.CreateWorkspace(t, client, user.OrganizationID, template.ID) - lockedWorkspace := coderdtest.CreateWorkspace(t, client, user.OrganizationID, template.ID) - require.Nil(t, unlockedWorkspace.DeletingAt) - require.Nil(t, lockedWorkspace.DeletingAt) + activeWS := coderdtest.CreateWorkspace(t, client, user.OrganizationID, template.ID) + dormantWS := coderdtest.CreateWorkspace(t, client, user.OrganizationID, template.ID) + require.Nil(t, activeWS.DeletingAt) + require.Nil(t, dormantWS.DeletingAt) - _ = coderdtest.AwaitWorkspaceBuildJob(t, client, unlockedWorkspace.LatestBuild.ID) - _ = coderdtest.AwaitWorkspaceBuildJob(t, client, lockedWorkspace.LatestBuild.ID) + _ = coderdtest.AwaitWorkspaceBuildJob(t, client, activeWS.LatestBuild.ID) + _ = coderdtest.AwaitWorkspaceBuildJob(t, client, dormantWS.LatestBuild.ID) - err := client.UpdateWorkspaceLock(ctx, lockedWorkspace.ID, codersdk.UpdateWorkspaceLock{ - Lock: true, + err := client.UpdateWorkspaceDormancy(ctx, dormantWS.ID, codersdk.UpdateWorkspaceDormancy{ + Dormant: true, }) require.NoError(t, err) - lockedWorkspace = coderdtest.MustWorkspace(t, client, lockedWorkspace.ID) - require.NotNil(t, lockedWorkspace.LockedAt) - // The deleting_at field should be nil since there is no template locked_ttl set. - require.Nil(t, lockedWorkspace.DeletingAt) + dormantWS = coderdtest.MustWorkspace(t, client, dormantWS.ID) + require.NotNil(t, dormantWS.DormantAt) + // The deleting_at field should be nil since there is no template time_til_dormant_autodelete set. + require.Nil(t, dormantWS.DeletingAt) - lockedTTL := time.Minute + dormantTTL := time.Minute updated, err := client.UpdateTemplateMeta(ctx, template.ID, codersdk.UpdateTemplateMeta{ - LockedTTLMillis: lockedTTL.Milliseconds(), + TimeTilDormantAutoDeleteMillis: dormantTTL.Milliseconds(), }) require.NoError(t, err) - require.Equal(t, lockedTTL.Milliseconds(), updated.LockedTTLMillis) + require.Equal(t, dormantTTL.Milliseconds(), updated.TimeTilDormantAutoDeleteMillis) - unlockedWorkspace = coderdtest.MustWorkspace(t, client, unlockedWorkspace.ID) - require.Nil(t, unlockedWorkspace.LockedAt) - require.Nil(t, unlockedWorkspace.DeletingAt) + activeWS = coderdtest.MustWorkspace(t, client, activeWS.ID) + require.Nil(t, activeWS.DormantAt) + require.Nil(t, activeWS.DeletingAt) - updatedLockedWorkspace := coderdtest.MustWorkspace(t, client, lockedWorkspace.ID) - require.NotNil(t, updatedLockedWorkspace.LockedAt) - require.NotNil(t, updatedLockedWorkspace.DeletingAt) - require.Equal(t, updatedLockedWorkspace.LockedAt.Add(lockedTTL), *updatedLockedWorkspace.DeletingAt) - require.Equal(t, updatedLockedWorkspace.LockedAt, lockedWorkspace.LockedAt) + updatedDormantWorkspace := coderdtest.MustWorkspace(t, client, dormantWS.ID) + require.NotNil(t, updatedDormantWorkspace.DormantAt) + require.NotNil(t, updatedDormantWorkspace.DeletingAt) + require.Equal(t, updatedDormantWorkspace.DormantAt.Add(dormantTTL), *updatedDormantWorkspace.DeletingAt) + require.Equal(t, updatedDormantWorkspace.DormantAt, dormantWS.DormantAt) - // Disable the locked_ttl on the template, then we can assert that the workspaces + // Disable the time_til_dormant_auto_delete on the template, then we can assert that the workspaces // no longer have a deleting_at field. updated, err = client.UpdateTemplateMeta(ctx, template.ID, codersdk.UpdateTemplateMeta{ - LockedTTLMillis: 0, + TimeTilDormantAutoDeleteMillis: 0, }) require.NoError(t, err) - require.EqualValues(t, 0, updated.LockedTTLMillis) + require.EqualValues(t, 0, updated.TimeTilDormantAutoDeleteMillis) - // The unlocked workspace should remain unchanged. - unlockedWorkspace = coderdtest.MustWorkspace(t, client, unlockedWorkspace.ID) - require.Nil(t, unlockedWorkspace.LockedAt) - require.Nil(t, unlockedWorkspace.DeletingAt) + // The active workspace should remain unchanged. + activeWS = coderdtest.MustWorkspace(t, client, activeWS.ID) + require.Nil(t, activeWS.DormantAt) + require.Nil(t, activeWS.DeletingAt) - // Fetch the locked workspace. It should still be locked, but it should no + // Fetch the dormant workspace. It should still be dormant, but it should no // longer be scheduled for deletion. - lockedWorkspace = coderdtest.MustWorkspace(t, client, lockedWorkspace.ID) - require.NotNil(t, lockedWorkspace.LockedAt) - require.Nil(t, lockedWorkspace.DeletingAt) + dormantWS = coderdtest.MustWorkspace(t, client, dormantWS.ID) + require.NotNil(t, dormantWS.DormantAt) + require.Nil(t, dormantWS.DeletingAt) }) - t.Run("UpdateLockedAt", func(t *testing.T) { + t.Run("UpdateDormantAt", func(t *testing.T) { t.Parallel() ctx := testutil.Context(t, testutil.WaitMedium) @@ -327,42 +327,42 @@ func TestTemplates(t *testing.T) { coderdtest.AwaitTemplateVersionJob(t, client, version.ID) template := coderdtest.CreateTemplate(t, client, user.OrganizationID, version.ID) - unlockedWorkspace := coderdtest.CreateWorkspace(t, client, user.OrganizationID, template.ID) - lockedWorkspace := coderdtest.CreateWorkspace(t, client, user.OrganizationID, template.ID) - require.Nil(t, unlockedWorkspace.DeletingAt) - require.Nil(t, lockedWorkspace.DeletingAt) + activeWS := coderdtest.CreateWorkspace(t, client, user.OrganizationID, template.ID) + dormantWS := coderdtest.CreateWorkspace(t, client, user.OrganizationID, template.ID) + require.Nil(t, activeWS.DeletingAt) + require.Nil(t, dormantWS.DeletingAt) - _ = coderdtest.AwaitWorkspaceBuildJob(t, client, unlockedWorkspace.LatestBuild.ID) - _ = coderdtest.AwaitWorkspaceBuildJob(t, client, lockedWorkspace.LatestBuild.ID) + _ = coderdtest.AwaitWorkspaceBuildJob(t, client, activeWS.LatestBuild.ID) + _ = coderdtest.AwaitWorkspaceBuildJob(t, client, dormantWS.LatestBuild.ID) - err := client.UpdateWorkspaceLock(ctx, lockedWorkspace.ID, codersdk.UpdateWorkspaceLock{ - Lock: true, + err := client.UpdateWorkspaceDormancy(ctx, dormantWS.ID, codersdk.UpdateWorkspaceDormancy{ + Dormant: true, }) require.NoError(t, err) - lockedWorkspace = coderdtest.MustWorkspace(t, client, lockedWorkspace.ID) - require.NotNil(t, lockedWorkspace.LockedAt) - // The deleting_at field should be nil since there is no template locked_ttl set. - require.Nil(t, lockedWorkspace.DeletingAt) + dormantWS = coderdtest.MustWorkspace(t, client, dormantWS.ID) + require.NotNil(t, dormantWS.DormantAt) + // The deleting_at field should be nil since there is no template time_til_dormant_autodelete set. + require.Nil(t, dormantWS.DeletingAt) - lockedTTL := time.Minute + dormantTTL := time.Minute updated, err := client.UpdateTemplateMeta(ctx, template.ID, codersdk.UpdateTemplateMeta{ - LockedTTLMillis: lockedTTL.Milliseconds(), - UpdateWorkspaceLockedAt: true, + TimeTilDormantAutoDeleteMillis: dormantTTL.Milliseconds(), + UpdateWorkspaceDormantAt: true, }) require.NoError(t, err) - require.Equal(t, lockedTTL.Milliseconds(), updated.LockedTTLMillis) + require.Equal(t, dormantTTL.Milliseconds(), updated.TimeTilDormantAutoDeleteMillis) - unlockedWorkspace = coderdtest.MustWorkspace(t, client, unlockedWorkspace.ID) - require.Nil(t, unlockedWorkspace.LockedAt) - require.Nil(t, unlockedWorkspace.DeletingAt) + activeWS = coderdtest.MustWorkspace(t, client, activeWS.ID) + require.Nil(t, activeWS.DormantAt) + require.Nil(t, activeWS.DeletingAt) - updatedLockedWorkspace := coderdtest.MustWorkspace(t, client, lockedWorkspace.ID) - require.NotNil(t, updatedLockedWorkspace.LockedAt) - require.NotNil(t, updatedLockedWorkspace.DeletingAt) - // Validate that the workspace locked_at value is updated. - require.True(t, updatedLockedWorkspace.LockedAt.After(*lockedWorkspace.LockedAt)) - require.Equal(t, updatedLockedWorkspace.LockedAt.Add(lockedTTL), *updatedLockedWorkspace.DeletingAt) + updatedDormantWorkspace := coderdtest.MustWorkspace(t, client, dormantWS.ID) + require.NotNil(t, updatedDormantWorkspace.DormantAt) + require.NotNil(t, updatedDormantWorkspace.DeletingAt) + // Validate that the workspace dormant_at value is updated. + require.True(t, updatedDormantWorkspace.DormantAt.After(*dormantWS.DormantAt)) + require.Equal(t, updatedDormantWorkspace.DormantAt.Add(dormantTTL), *updatedDormantWorkspace.DeletingAt) }) t.Run("UpdateLastUsedAt", func(t *testing.T) { @@ -383,43 +383,43 @@ func TestTemplates(t *testing.T) { coderdtest.AwaitTemplateVersionJob(t, client, version.ID) template := coderdtest.CreateTemplate(t, client, user.OrganizationID, version.ID) - unlockedWorkspace := coderdtest.CreateWorkspace(t, client, user.OrganizationID, template.ID) - lockedWorkspace := coderdtest.CreateWorkspace(t, client, user.OrganizationID, template.ID) - require.Nil(t, unlockedWorkspace.DeletingAt) - require.Nil(t, lockedWorkspace.DeletingAt) + activeWorkspace := coderdtest.CreateWorkspace(t, client, user.OrganizationID, template.ID) + dormantWorkspace := coderdtest.CreateWorkspace(t, client, user.OrganizationID, template.ID) + require.Nil(t, activeWorkspace.DeletingAt) + require.Nil(t, dormantWorkspace.DeletingAt) - _ = coderdtest.AwaitWorkspaceBuildJob(t, client, unlockedWorkspace.LatestBuild.ID) - _ = coderdtest.AwaitWorkspaceBuildJob(t, client, lockedWorkspace.LatestBuild.ID) + _ = coderdtest.AwaitWorkspaceBuildJob(t, client, activeWorkspace.LatestBuild.ID) + _ = coderdtest.AwaitWorkspaceBuildJob(t, client, dormantWorkspace.LatestBuild.ID) - err := client.UpdateWorkspaceLock(ctx, lockedWorkspace.ID, codersdk.UpdateWorkspaceLock{ - Lock: true, + err := client.UpdateWorkspaceDormancy(ctx, dormantWorkspace.ID, codersdk.UpdateWorkspaceDormancy{ + Dormant: true, }) require.NoError(t, err) - lockedWorkspace = coderdtest.MustWorkspace(t, client, lockedWorkspace.ID) - require.NotNil(t, lockedWorkspace.LockedAt) - // The deleting_at field should be nil since there is no template locked_ttl set. - require.Nil(t, lockedWorkspace.DeletingAt) + dormantWorkspace = coderdtest.MustWorkspace(t, client, dormantWorkspace.ID) + require.NotNil(t, dormantWorkspace.DormantAt) + // The deleting_at field should be nil since there is no template time_til_dormant_autodelete set. + require.Nil(t, dormantWorkspace.DeletingAt) inactivityTTL := time.Minute updated, err := client.UpdateTemplateMeta(ctx, template.ID, codersdk.UpdateTemplateMeta{ - InactivityTTLMillis: inactivityTTL.Milliseconds(), + TimeTilDormantMillis: inactivityTTL.Milliseconds(), UpdateWorkspaceLastUsedAt: true, }) require.NoError(t, err) - require.Equal(t, inactivityTTL.Milliseconds(), updated.InactivityTTLMillis) + require.Equal(t, inactivityTTL.Milliseconds(), updated.TimeTilDormantMillis) - updatedUnlockedWS := coderdtest.MustWorkspace(t, client, unlockedWorkspace.ID) - require.Nil(t, updatedUnlockedWS.LockedAt) - require.Nil(t, updatedUnlockedWS.DeletingAt) - require.True(t, updatedUnlockedWS.LastUsedAt.After(unlockedWorkspace.LastUsedAt)) + updatedActiveWS := coderdtest.MustWorkspace(t, client, activeWorkspace.ID) + require.Nil(t, updatedActiveWS.DormantAt) + require.Nil(t, updatedActiveWS.DeletingAt) + require.True(t, updatedActiveWS.LastUsedAt.After(activeWorkspace.LastUsedAt)) - updatedLockedWorkspace := coderdtest.MustWorkspace(t, client, lockedWorkspace.ID) - require.NotNil(t, updatedLockedWorkspace.LockedAt) - require.Nil(t, updatedLockedWorkspace.DeletingAt) - // Validate that the workspace locked_at value is updated. - require.Equal(t, updatedLockedWorkspace.LockedAt, lockedWorkspace.LockedAt) - require.True(t, updatedLockedWorkspace.LastUsedAt.After(lockedWorkspace.LastUsedAt)) + updatedDormantWS := coderdtest.MustWorkspace(t, client, dormantWorkspace.ID) + require.NotNil(t, updatedDormantWS.DormantAt) + require.Nil(t, updatedDormantWS.DeletingAt) + // Validate that the workspace dormant_at value is updated. + require.Equal(t, updatedDormantWS.DormantAt, dormantWorkspace.DormantAt) + require.True(t, updatedDormantWS.LastUsedAt.After(dormantWorkspace.LastUsedAt)) }) } diff --git a/enterprise/coderd/workspaces_test.go b/enterprise/coderd/workspaces_test.go index c5a8aec6b8535..373b79c78d59f 100644 --- a/enterprise/coderd/workspaces_test.go +++ b/enterprise/coderd/workspaces_test.go @@ -217,9 +217,9 @@ func TestWorkspaceAutobuild(t *testing.T) { }) // Create a template without setting a failure_ttl. template := coderdtest.CreateTemplate(t, client, user.OrganizationID, version.ID) - require.Zero(t, template.InactivityTTLMillis) + require.Zero(t, template.TimeTilDormantMillis) require.Zero(t, template.FailureTTLMillis) - require.Zero(t, template.LockedTTLMillis) + require.Zero(t, template.TimeTilDormantAutoDeleteMillis) coderdtest.AwaitTemplateVersionJob(t, client, version.ID) ws := coderdtest.CreateWorkspace(t, client, user.OrganizationID, template.ID) @@ -259,7 +259,7 @@ func TestWorkspaceAutobuild(t *testing.T) { ProvisionApply: echo.ProvisionComplete, }) template := coderdtest.CreateTemplate(t, client, user.OrganizationID, version.ID, func(ctr *codersdk.CreateTemplateRequest) { - ctr.InactivityTTLMillis = ptr.Ref[int64](inactiveTTL.Milliseconds()) + ctr.TimeTilDormantMillis = ptr.Ref[int64](inactiveTTL.Milliseconds()) }) coderdtest.AwaitTemplateVersionJob(t, client, version.ID) @@ -275,12 +275,12 @@ func TestWorkspaceAutobuild(t *testing.T) { require.Len(t, stats.Transitions, 1) require.Equal(t, stats.Transitions[ws.ID], database.WorkspaceTransitionStop) - // The workspace should be locked. + // The workspace should be dormant. ws = coderdtest.MustWorkspace(t, client, ws.ID) - require.NotNil(t, ws.LockedAt) + require.NotNil(t, ws.DormantAt) lastUsedAt := ws.LastUsedAt - err := client.UpdateWorkspaceLock(ctx, ws.ID, codersdk.UpdateWorkspaceLock{Lock: false}) + err := client.UpdateWorkspaceDormancy(ctx, ws.ID, codersdk.UpdateWorkspaceDormancy{Dormant: false}) require.NoError(t, err) // Assert that we updated our last_used_at so that we don't immediately @@ -315,7 +315,7 @@ func TestWorkspaceAutobuild(t *testing.T) { ProvisionApply: echo.ProvisionComplete, }) template := coderdtest.CreateTemplate(t, client, user.OrganizationID, version.ID, func(ctr *codersdk.CreateTemplateRequest) { - ctr.InactivityTTLMillis = ptr.Ref[int64](inactiveTTL.Milliseconds()) + ctr.TimeTilDormantMillis = ptr.Ref[int64](inactiveTTL.Milliseconds()) }) coderdtest.AwaitTemplateVersionJob(t, client, version.ID) ws := coderdtest.CreateWorkspace(t, client, user.OrganizationID, template.ID) @@ -331,13 +331,13 @@ func TestWorkspaceAutobuild(t *testing.T) { // This is kind of a dumb test but it exists to offer some marginal // confidence that a bug in the auto-deletion logic doesn't delete running // workspaces. - t.Run("UnlockedWorkspacesNotDeleted", func(t *testing.T) { + t.Run("ActiveWorkspacesNotDeleted", func(t *testing.T) { t.Parallel() var ( - ticker = make(chan time.Time) - statCh = make(chan autobuild.Stats) - lockedTTL = time.Minute + ticker = make(chan time.Time) + statCh = make(chan autobuild.Stats) + autoDeleteTTL = time.Minute ) client, user := coderdenttest.New(t, &coderdenttest.Options{ @@ -357,16 +357,16 @@ func TestWorkspaceAutobuild(t *testing.T) { ProvisionApply: echo.ProvisionComplete, }) template := coderdtest.CreateTemplate(t, client, user.OrganizationID, version.ID, func(ctr *codersdk.CreateTemplateRequest) { - ctr.LockedTTLMillis = ptr.Ref[int64](lockedTTL.Milliseconds()) + ctr.TimeTilDormantAutoDeleteMillis = ptr.Ref[int64](autoDeleteTTL.Milliseconds()) }) coderdtest.AwaitTemplateVersionJob(t, client, version.ID) ws := coderdtest.CreateWorkspace(t, client, user.OrganizationID, template.ID) build := coderdtest.AwaitWorkspaceBuildJob(t, client, ws.LatestBuild.ID) - require.Nil(t, ws.LockedAt) + require.Nil(t, ws.DormantAt) require.Equal(t, codersdk.WorkspaceStatusRunning, build.Status) - ticker <- ws.LastUsedAt.Add(lockedTTL * 2) + ticker <- ws.LastUsedAt.Add(autoDeleteTTL * 2) stats := <-statCh - // Expect no transitions since workspace is unlocked. + // Expect no transitions since workspace is active. require.Len(t, stats.Transitions, 0) }) @@ -399,7 +399,7 @@ func TestWorkspaceAutobuild(t *testing.T) { ProvisionApply: echo.ProvisionComplete, }) template := coderdtest.CreateTemplate(t, client, user.OrganizationID, version.ID, func(ctr *codersdk.CreateTemplateRequest) { - ctr.InactivityTTLMillis = ptr.Ref[int64](inactiveTTL.Milliseconds()) + ctr.TimeTilDormantMillis = ptr.Ref[int64](inactiveTTL.Milliseconds()) }) coderdtest.AwaitTemplateVersionJob(t, client, version.ID) @@ -417,13 +417,13 @@ func TestWorkspaceAutobuild(t *testing.T) { // Expect no transitions since workspace is stopped. require.Len(t, stats.Transitions, 0) ws = coderdtest.MustWorkspace(t, client, ws.ID) - // The workspace should still be locked even though we didn't + // The workspace should still be dormant even though we didn't // transition the workspace. - require.NotNil(t, ws.LockedAt) + require.NotNil(t, ws.DormantAt) }) // Test the flow of a workspace transitioning from - // inactive -> locked -> deleted. + // inactive -> dormant -> deleted. t.Run("WorkspaceInactiveDeleteTransition", func(t *testing.T) { t.Parallel() @@ -451,8 +451,8 @@ func TestWorkspaceAutobuild(t *testing.T) { ProvisionApply: echo.ProvisionComplete, }) template := coderdtest.CreateTemplate(t, client, user.OrganizationID, version.ID, func(ctr *codersdk.CreateTemplateRequest) { - ctr.InactivityTTLMillis = ptr.Ref[int64](transitionTTL.Milliseconds()) - ctr.LockedTTLMillis = ptr.Ref[int64](transitionTTL.Milliseconds()) + ctr.TimeTilDormantMillis = ptr.Ref[int64](transitionTTL.Milliseconds()) + ctr.TimeTilDormantAutoDeleteMillis = ptr.Ref[int64](transitionTTL.Milliseconds()) }) coderdtest.AwaitTemplateVersionJob(t, client, version.ID) @@ -469,14 +469,14 @@ func TestWorkspaceAutobuild(t *testing.T) { require.Equal(t, stats.Transitions[ws.ID], database.WorkspaceTransitionStop) ws = coderdtest.MustWorkspace(t, client, ws.ID) - // The workspace should be locked. - require.NotNil(t, ws.LockedAt) + // The workspace should be dormant. + require.NotNil(t, ws.DormantAt) // Wait for the autobuilder to stop the workspace. _ = coderdtest.AwaitWorkspaceBuildJob(t, client, ws.LatestBuild.ID) - // Simulate the workspace being locked beyond the threshold. - ticker <- ws.LockedAt.Add(2 * transitionTTL) + // Simulate the workspace being dormant beyond the threshold. + ticker <- ws.DormantAt.Add(2 * transitionTTL) stats = <-statCh require.Len(t, stats.Transitions, 1) // The workspace should be scheduled for deletion. @@ -494,13 +494,13 @@ func TestWorkspaceAutobuild(t *testing.T) { require.Equal(t, http.StatusGone, cerr.StatusCode()) }) - t.Run("LockedTTTooEarly", func(t *testing.T) { + t.Run("DormantTTLTooEarly", func(t *testing.T) { t.Parallel() var ( - ticker = make(chan time.Time) - statCh = make(chan autobuild.Stats) - lockedTTL = time.Minute + ticker = make(chan time.Time) + statCh = make(chan autobuild.Stats) + dormantTTL = time.Minute ) client, user := coderdenttest.New(t, &coderdenttest.Options{ @@ -520,7 +520,7 @@ func TestWorkspaceAutobuild(t *testing.T) { ProvisionApply: echo.ProvisionComplete, }) template := coderdtest.CreateTemplate(t, client, user.OrganizationID, version.ID, func(ctr *codersdk.CreateTemplateRequest) { - ctr.LockedTTLMillis = ptr.Ref[int64](lockedTTL.Milliseconds()) + ctr.TimeTilDormantAutoDeleteMillis = ptr.Ref[int64](dormantTTL.Milliseconds()) }) coderdtest.AwaitTemplateVersionJob(t, client, version.ID) ws := coderdtest.CreateWorkspace(t, client, user.OrganizationID, template.ID) @@ -528,34 +528,34 @@ func TestWorkspaceAutobuild(t *testing.T) { require.Equal(t, codersdk.WorkspaceStatusRunning, build.Status) ctx := testutil.Context(t, testutil.WaitMedium) - err := client.UpdateWorkspaceLock(ctx, ws.ID, codersdk.UpdateWorkspaceLock{ - Lock: true, + err := client.UpdateWorkspaceDormancy(ctx, ws.ID, codersdk.UpdateWorkspaceDormancy{ + Dormant: true, }) require.NoError(t, err) ws = coderdtest.MustWorkspace(t, client, ws.ID) - require.NotNil(t, ws.LockedAt) + require.NotNil(t, ws.DormantAt) // Ensure we haven't breached our threshold. - ticker <- ws.LockedAt.Add(-lockedTTL * 2) + ticker <- ws.DormantAt.Add(-dormantTTL * 2) stats := <-statCh // Expect no transitions since not enough time has elapsed. require.Len(t, stats.Transitions, 0) _, err = client.UpdateTemplateMeta(ctx, template.ID, codersdk.UpdateTemplateMeta{ - LockedTTLMillis: lockedTTL.Milliseconds(), + TimeTilDormantAutoDeleteMillis: dormantTTL.Milliseconds(), }) require.NoError(t, err) // Simlute the workspace breaching the threshold. - ticker <- ws.LockedAt.Add(lockedTTL * 2) + ticker <- ws.DormantAt.Add(dormantTTL * 2) stats = <-statCh require.Len(t, stats.Transitions, 1) require.Equal(t, database.WorkspaceTransitionDelete, stats.Transitions[ws.ID]) }) - // Assert that a locked workspace does not autostart. - t.Run("LockedNoAutostart", func(t *testing.T) { + // Assert that a dormant workspace does not autostart. + t.Run("DormantNoAutostart", func(t *testing.T) { t.Parallel() var ( @@ -594,7 +594,7 @@ func TestWorkspaceAutobuild(t *testing.T) { coderdtest.AwaitWorkspaceBuildJob(t, client, ws.LatestBuild.ID) coderdtest.MustTransitionWorkspace(t, client, ws.ID, database.WorkspaceTransitionStart, database.WorkspaceTransitionStop) - // Assert that autostart works when the workspace isn't locked.. + // Assert that autostart works when the workspace isn't dormant.. tickCh <- sched.Next(ws.LatestBuild.CreatedAt) stats := <-statsCh require.NoError(t, stats.Error) @@ -606,9 +606,9 @@ func TestWorkspaceAutobuild(t *testing.T) { coderdtest.AwaitWorkspaceBuildJob(t, client, ws.LatestBuild.ID) // Now that we've validated that the workspace is eligible for autostart - // lets cause it to become locked. + // lets cause it to become dormant. _, err = client.UpdateTemplateMeta(ctx, template.ID, codersdk.UpdateTemplateMeta{ - InactivityTTLMillis: inactiveTTL.Milliseconds(), + TimeTilDormantMillis: inactiveTTL.Milliseconds(), }) require.NoError(t, err) @@ -620,12 +620,12 @@ func TestWorkspaceAutobuild(t *testing.T) { require.Contains(t, stats.Transitions, ws.ID) require.Equal(t, database.WorkspaceTransitionStop, stats.Transitions[ws.ID]) - // The workspace should be locked now. + // The workspace should be dormant now. ws = coderdtest.MustWorkspace(t, client, ws.ID) coderdtest.AwaitWorkspaceBuildJob(t, client, ws.LatestBuild.ID) - require.NotNil(t, ws.LockedAt) + require.NotNil(t, ws.DormantAt) - // Assert that autostart is no longer triggered since workspace is locked. + // Assert that autostart is no longer triggered since workspace is dormant. tickCh <- sched.Next(ws.LatestBuild.CreatedAt) stats = <-statsCh require.Len(t, stats.Transitions, 0) @@ -638,7 +638,7 @@ func TestWorkspacesFiltering(t *testing.T) { t.Run("DeletingBy", func(t *testing.T) { t.Parallel() - lockedTTL := 24 * time.Hour + dormantTTL := 24 * time.Hour client, user := coderdenttest.New(t, &coderdenttest.Options{ Options: &coderdtest.Options{ @@ -660,10 +660,10 @@ func TestWorkspacesFiltering(t *testing.T) { defer cancel() template, err := client.UpdateTemplateMeta(ctx, template.ID, codersdk.UpdateTemplateMeta{ - LockedTTLMillis: lockedTTL.Milliseconds(), + TimeTilDormantAutoDeleteMillis: dormantTTL.Milliseconds(), }) require.NoError(t, err) - require.Equal(t, lockedTTL.Milliseconds(), template.LockedTTLMillis) + require.Equal(t, dormantTTL.Milliseconds(), template.TimeTilDormantAutoDeleteMillis) workspace := coderdtest.CreateWorkspace(t, client, user.OrganizationID, template.ID) _ = coderdtest.AwaitWorkspaceBuildJob(t, client, workspace.LatestBuild.ID) @@ -671,8 +671,8 @@ func TestWorkspacesFiltering(t *testing.T) { // stop build so workspace is inactive stopBuild := coderdtest.CreateWorkspaceBuild(t, client, workspace, database.WorkspaceTransitionStop) coderdtest.AwaitWorkspaceBuildJob(t, client, stopBuild.ID) - err = client.UpdateWorkspaceLock(ctx, workspace.ID, codersdk.UpdateWorkspaceLock{ - Lock: true, + err = client.UpdateWorkspaceDormancy(ctx, workspace.ID, codersdk.UpdateWorkspaceDormancy{ + Dormant: true, }) require.NoError(t, err) workspace = coderdtest.MustWorkspace(t, client, workspace.ID) @@ -680,7 +680,7 @@ func TestWorkspacesFiltering(t *testing.T) { res, err := client.Workspaces(ctx, codersdk.WorkspaceFilter{ // adding a second to time.Now() to give some buffer in case test runs quickly - FilterQuery: fmt.Sprintf("deleting_by:%s", time.Now().Add(time.Second).Add(lockedTTL).Format("2006-01-02")), + FilterQuery: fmt.Sprintf("deleting_by:%s", time.Now().Add(time.Second).Add(dormantTTL).Format("2006-01-02")), }) require.NoError(t, err) require.Len(t, res.Workspaces, 1) @@ -746,7 +746,7 @@ func TestWorkspacesWithoutTemplatePerms(t *testing.T) { func TestWorkspaceLock(t *testing.T) { t.Parallel() - t.Run("TemplateLockedTTL", func(t *testing.T) { + t.Run("TemplateTimeTilDormantAutoDelete", func(t *testing.T) { t.Parallel() var ( client, user = coderdenttest.New(t, &coderdenttest.Options{ @@ -761,13 +761,13 @@ func TestWorkspaceLock(t *testing.T) { }, }) - version = coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, nil) - _ = coderdtest.AwaitTemplateVersionJob(t, client, version.ID) - lockedTTL = time.Minute + version = coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, nil) + _ = coderdtest.AwaitTemplateVersionJob(t, client, version.ID) + dormantTTL = time.Minute ) template := coderdtest.CreateTemplate(t, client, user.OrganizationID, version.ID, func(ctr *codersdk.CreateTemplateRequest) { - ctr.LockedTTLMillis = ptr.Ref[int64](lockedTTL.Milliseconds()) + ctr.TimeTilDormantAutoDeleteMillis = ptr.Ref[int64](dormantTTL.Milliseconds()) }) workspace := coderdtest.CreateWorkspace(t, client, user.OrganizationID, template.ID) @@ -777,30 +777,30 @@ func TestWorkspaceLock(t *testing.T) { defer cancel() lastUsedAt := workspace.LastUsedAt - err := client.UpdateWorkspaceLock(ctx, workspace.ID, codersdk.UpdateWorkspaceLock{ - Lock: true, + err := client.UpdateWorkspaceDormancy(ctx, workspace.ID, codersdk.UpdateWorkspaceDormancy{ + Dormant: true, }) require.NoError(t, err) workspace = coderdtest.MustWorkspace(t, client, workspace.ID) require.NoError(t, err, "fetch provisioned workspace") require.NotNil(t, workspace.DeletingAt) - require.NotNil(t, workspace.LockedAt) - require.Equal(t, workspace.LockedAt.Add(lockedTTL), *workspace.DeletingAt) - require.WithinRange(t, *workspace.LockedAt, time.Now().Add(-time.Second*10), time.Now()) + require.NotNil(t, workspace.DormantAt) + require.Equal(t, workspace.DormantAt.Add(dormantTTL), *workspace.DeletingAt) + require.WithinRange(t, *workspace.DormantAt, time.Now().Add(-time.Second*10), time.Now()) // Locking a workspace shouldn't update the last_used_at. require.Equal(t, lastUsedAt, workspace.LastUsedAt) workspace = coderdtest.MustWorkspace(t, client, workspace.ID) lastUsedAt = workspace.LastUsedAt - err = client.UpdateWorkspaceLock(ctx, workspace.ID, codersdk.UpdateWorkspaceLock{ - Lock: false, + err = client.UpdateWorkspaceDormancy(ctx, workspace.ID, codersdk.UpdateWorkspaceDormancy{ + Dormant: false, }) require.NoError(t, err) workspace, err = client.Workspace(ctx, workspace.ID) require.NoError(t, err, "fetch provisioned workspace") - require.Nil(t, workspace.LockedAt) + require.Nil(t, workspace.DormantAt) // Unlocking a workspace should cause the deleting_at to be unset. require.Nil(t, workspace.DeletingAt) // The last_used_at should get updated when we unlock the workspace. diff --git a/site/src/api/api.ts b/site/src/api/api.ts index eefbcfba275e7..3567e4f977332 100644 --- a/site/src/api/api.ts +++ b/site/src/api/api.ts @@ -554,16 +554,16 @@ export const cancelWorkspaceBuild = async ( return response.data } -export const updateWorkspaceLock = async ( +export const updateWorkspaceDormancy = async ( workspaceId: string, - lock: boolean, + dormant: boolean, ): Promise => { - const data: TypesGen.UpdateWorkspaceLock = { - lock: lock, + const data: TypesGen.UpdateWorkspaceDormancy = { + dormant: dormant, } const response = await axios.put( - `/api/v2/workspaces/${workspaceId}/lock`, + `/api/v2/workspaces/${workspaceId}/dormant`, data, ) return response.data diff --git a/site/src/api/typesGenerated.ts b/site/src/api/typesGenerated.ts index ebe8fb61218e2..fb15fedc8ada0 100644 --- a/site/src/api/typesGenerated.ts +++ b/site/src/api/typesGenerated.ts @@ -200,8 +200,8 @@ export interface CreateTemplateRequest { readonly allow_user_autostart?: boolean readonly allow_user_autostop?: boolean readonly failure_ttl_ms?: number - readonly inactivity_ttl_ms?: number - readonly locked_ttl_ms?: number + readonly dormant_ttl_ms?: number + readonly delete_ttl_ms?: number readonly disable_everyone_group_access: boolean } @@ -916,8 +916,8 @@ export interface Template { readonly allow_user_autostop: boolean readonly allow_user_cancel_workspace_jobs: boolean readonly failure_ttl_ms: number - readonly inactivity_ttl_ms: number - readonly locked_ttl_ms: number + readonly time_til_dormant_ms: number + readonly time_til_dormant_autodelete_ms: number } // From codersdk/templates.go @@ -1151,10 +1151,10 @@ export interface UpdateTemplateMeta { readonly allow_user_autostop?: boolean readonly allow_user_cancel_workspace_jobs?: boolean readonly failure_ttl_ms?: number - readonly inactivity_ttl_ms?: number - readonly locked_ttl_ms?: number + readonly time_til_dormant_ms?: number + readonly time_til_dormant_autodelete_ms?: number readonly update_workspace_last_used_at: boolean - readonly update_workspace_locked_at: boolean + readonly update_workspace_dormant_at: boolean } // From codersdk/users.go @@ -1179,8 +1179,8 @@ export interface UpdateWorkspaceAutostartRequest { } // From codersdk/workspaces.go -export interface UpdateWorkspaceLock { - readonly lock: boolean +export interface UpdateWorkspaceDormancy { + readonly dormant: boolean } // From codersdk/workspaceproxy.go @@ -1309,7 +1309,7 @@ export interface Workspace { readonly ttl_ms?: number readonly last_used_at: string readonly deleting_at?: string - readonly locked_at?: string + readonly dormant_at?: string readonly health: WorkspaceHealth } diff --git a/site/src/components/Dialogs/ConfirmDialog/ConfirmDialog.tsx b/site/src/components/Dialogs/ConfirmDialog/ConfirmDialog.tsx index 5a1cfcc80060e..b16437ea62375 100644 --- a/site/src/components/Dialogs/ConfirmDialog/ConfirmDialog.tsx +++ b/site/src/components/Dialogs/ConfirmDialog/ConfirmDialog.tsx @@ -160,7 +160,7 @@ export interface ScheduleDialogProps extends ConfirmDialogProps { readonly inactiveWorkspacesToGoDormantInWeek: number readonly dormantWorkspacesToBeDeleted: number readonly dormantWorkspacesToBeDeletedInWeek: number - readonly updateLockedWorkspaces: (confirm: boolean) => void + readonly updateDormantWorkspaces: (confirm: boolean) => void readonly updateInactiveWorkspaces: (confirm: boolean) => void readonly dormantValueChanged: boolean readonly deletionValueChanged: boolean @@ -180,7 +180,7 @@ export const ScheduleDialog: FC> = ({ inactiveWorkspacesToGoDormantInWeek, dormantWorkspacesToBeDeleted, dormantWorkspacesToBeDeletedInWeek, - updateLockedWorkspaces, + updateDormantWorkspaces, updateInactiveWorkspaces, dormantValueChanged, deletionValueChanged, @@ -250,7 +250,7 @@ export const ScheduleDialog: FC> = ({ { - updateLockedWorkspaces(e.target.checked) + updateDormantWorkspaces(e.target.checked) }} /> } diff --git a/site/src/components/WorkspaceActions/constants.ts b/site/src/components/WorkspaceActions/constants.ts index 1d2eeb9d4811e..5e8b1345ee898 100644 --- a/site/src/components/WorkspaceActions/constants.ts +++ b/site/src/components/WorkspaceActions/constants.ts @@ -34,7 +34,7 @@ export const actionsByWorkspaceStatus = ( workspace: Workspace, status: WorkspaceStatus, ): WorkspaceAbilities => { - if (workspace.locked_at) { + if (workspace.dormant_at) { return { actions: [ButtonTypesEnum.activate], canCancel: false, diff --git a/site/src/components/WorkspaceDeletion/ImpendingDeletionBadge.tsx b/site/src/components/WorkspaceDeletion/ImpendingDeletionBadge.tsx deleted file mode 100644 index dc09566fc69e9..0000000000000 --- a/site/src/components/WorkspaceDeletion/ImpendingDeletionBadge.tsx +++ /dev/null @@ -1,17 +0,0 @@ -import { Workspace } from "api/typesGenerated" -import { useIsWorkspaceActionsEnabled } from "components/Dashboard/DashboardProvider" -import { Pill } from "components/Pill/Pill" -import LockIcon from "@mui/icons-material/Lock" - -export const LockedBadge = ({ - workspace, -}: { - workspace: Workspace -}): JSX.Element | null => { - const experimentEnabled = useIsWorkspaceActionsEnabled() - if (!workspace.locked_at || !experimentEnabled) { - return null - } - - return } text="Locked" type="error" /> -} diff --git a/site/src/components/WorkspaceDeletion/ImpendingDeletionBanner.tsx b/site/src/components/WorkspaceDeletion/ImpendingDeletionBanner.tsx index b65f3a5cdc28b..f49ec057d0878 100644 --- a/site/src/components/WorkspaceDeletion/ImpendingDeletionBanner.tsx +++ b/site/src/components/WorkspaceDeletion/ImpendingDeletionBanner.tsx @@ -27,8 +27,8 @@ export const DormantWorkspaceBanner = ({ return null } - const hasLockedWorkspaces = workspaces.find( - (workspace) => workspace.locked_at, + const hasDormantWorkspaces = workspaces.find( + (workspace) => workspace.dormant_at, ) const hasDeletionScheduledWorkspaces = workspaces.find( @@ -38,7 +38,7 @@ export const DormantWorkspaceBanner = ({ if ( // Only show this if the experiment is included. !experimentEnabled || - !hasLockedWorkspaces || + !hasDormantWorkspaces || // Banners should be redisplayed after dismissal when additional workspaces are newly scheduled for deletion !shouldRedisplayBanner ) { @@ -59,16 +59,16 @@ export const DormantWorkspaceBanner = ({ if ( hasDeletionScheduledWorkspaces && hasDeletionScheduledWorkspaces.deleting_at && - hasDeletionScheduledWorkspaces.locked_at + hasDeletionScheduledWorkspaces.dormant_at ) { return `This workspace has been dormant for ${formatDistanceToNow( - Date.parse(hasDeletionScheduledWorkspaces.locked_at), + Date.parse(hasDeletionScheduledWorkspaces.dormant_at), )} and is scheduled to be deleted on ${formatDate( hasDeletionScheduledWorkspaces.deleting_at, )} . To keep it you must activate the workspace.` - } else if (hasLockedWorkspaces && hasLockedWorkspaces.locked_at) { + } else if (hasDormantWorkspaces && hasDormantWorkspaces.dormant_at) { return `This workspace has been dormant for ${formatDistanceToNow( - Date.parse(hasLockedWorkspaces.locked_at), + Date.parse(hasDormantWorkspaces.dormant_at), )} and cannot be interacted with. Dormant workspaces are eligible for @@ -88,7 +88,7 @@ export const DormantWorkspaceBanner = ({ There are{" "} workspaces {" "} diff --git a/site/src/components/WorkspaceDeletion/index.ts b/site/src/components/WorkspaceDeletion/index.ts index d58af8e83ac57..a8c14bd12da69 100644 --- a/site/src/components/WorkspaceDeletion/index.ts +++ b/site/src/components/WorkspaceDeletion/index.ts @@ -1,4 +1,3 @@ export * from "./ImpendingDeletionStat" -export * from "./ImpendingDeletionBadge" export * from "./ImpendingDeletionText" export * from "./ImpendingDeletionBanner" diff --git a/site/src/pages/TemplateSettingsPage/TemplateGeneralSettingsPage/TemplateSettingsForm.tsx b/site/src/pages/TemplateSettingsPage/TemplateGeneralSettingsPage/TemplateSettingsForm.tsx index 4ae79d145baaa..dd4535bc29b89 100644 --- a/site/src/pages/TemplateSettingsPage/TemplateGeneralSettingsPage/TemplateSettingsForm.tsx +++ b/site/src/pages/TemplateSettingsPage/TemplateGeneralSettingsPage/TemplateSettingsForm.tsx @@ -73,7 +73,7 @@ export const TemplateSettingsForm: FC = ({ allow_user_cancel_workspace_jobs: template.allow_user_cancel_workspace_jobs, update_workspace_last_used_at: false, - update_workspace_locked_at: false, + update_workspace_dormant_at: false, }, validationSchema, onSubmit, diff --git a/site/src/pages/TemplateSettingsPage/TemplateGeneralSettingsPage/TemplateSettingsPage.test.tsx b/site/src/pages/TemplateSettingsPage/TemplateGeneralSettingsPage/TemplateSettingsPage.test.tsx index 9f9f0d231f9c4..9c43034905e68 100644 --- a/site/src/pages/TemplateSettingsPage/TemplateGeneralSettingsPage/TemplateSettingsPage.test.tsx +++ b/site/src/pages/TemplateSettingsPage/TemplateGeneralSettingsPage/TemplateSettingsPage.test.tsx @@ -31,10 +31,10 @@ const validFormValues: FormValues = { weeks: 1, }, failure_ttl_ms: 0, - inactivity_ttl_ms: 0, - locked_ttl_ms: 0, + time_til_dormant_ms: 0, + time_til_dormant_autodelete_ms: 0, update_workspace_last_used_at: false, - update_workspace_locked_at: false, + update_workspace_dormant_at: false, } const renderTemplateSettingsPage = async () => { diff --git a/site/src/pages/TemplateSettingsPage/TemplateSchedulePage/TemplateScheduleForm/InactivityDialog.stories.tsx b/site/src/pages/TemplateSettingsPage/TemplateSchedulePage/TemplateScheduleForm/InactivityDialog.stories.tsx deleted file mode 100644 index 6128700299e28..0000000000000 --- a/site/src/pages/TemplateSettingsPage/TemplateSchedulePage/TemplateScheduleForm/InactivityDialog.stories.tsx +++ /dev/null @@ -1,19 +0,0 @@ -import type { Meta, StoryObj } from "@storybook/react" -import { InactivityDialog } from "./InactivityDialog" - -const meta: Meta = { - title: "InactivityDialog", - component: InactivityDialog, -} - -export default meta -type Story = StoryObj - -export const OpenDialog: Story = { - args: { - submitValues: () => null, - isInactivityDialogOpen: true, - setIsInactivityDialogOpen: () => null, - workspacesToBeLockedToday: 2, - }, -} diff --git a/site/src/pages/TemplateSettingsPage/TemplateSchedulePage/TemplateScheduleForm/InactivityDialog.tsx b/site/src/pages/TemplateSettingsPage/TemplateSchedulePage/TemplateScheduleForm/InactivityDialog.tsx deleted file mode 100644 index a9407f6d5eab5..0000000000000 --- a/site/src/pages/TemplateSettingsPage/TemplateSchedulePage/TemplateScheduleForm/InactivityDialog.tsx +++ /dev/null @@ -1,59 +0,0 @@ -import { ConfirmDialog } from "components/Dialogs/ConfirmDialog/ConfirmDialog" - -export const InactivityDialog = ({ - submitValues, - isInactivityDialogOpen, - setIsInactivityDialogOpen, - workspacesToBeLockedToday, -}: { - submitValues: () => void - isInactivityDialogOpen: boolean - setIsInactivityDialogOpen: (arg0: boolean) => void - workspacesToBeLockedToday: number -}) => { - return ( - { - submitValues() - setIsInactivityDialogOpen(false) - }} - onClose={() => setIsInactivityDialogOpen(false)} - title="Lock inactive workspaces" - confirmText="Lock Workspaces" - description={`There are ${ - workspacesToBeLockedToday ? workspacesToBeLockedToday : "" - } workspaces that already match this filter and will be locked upon form submission. Are you sure you want to proceed?`} - /> - ) -} - -export const DeleteLockedDialog = ({ - submitValues, - isLockedDialogOpen, - setIsLockedDialogOpen, - workspacesToBeDeletedToday, -}: { - submitValues: () => void - isLockedDialogOpen: boolean - setIsLockedDialogOpen: (arg0: boolean) => void - workspacesToBeDeletedToday: number -}) => { - return ( - { - submitValues() - setIsLockedDialogOpen(false) - }} - onClose={() => setIsLockedDialogOpen(false)} - title="Delete Locked Workspaces" - confirmText="Delete Workspaces" - description={`There are ${ - workspacesToBeDeletedToday ? workspacesToBeDeletedToday : "" - } workspaces that already match this filter and will be deleted upon form submission. Are you sure you want to proceed?`} - /> - ) -} diff --git a/site/src/pages/TemplateSettingsPage/TemplateSchedulePage/TemplateScheduleForm/TemplateScheduleForm.tsx b/site/src/pages/TemplateSettingsPage/TemplateSchedulePage/TemplateScheduleForm/TemplateScheduleForm.tsx index 180d81df978b8..e13535d68270b 100644 --- a/site/src/pages/TemplateSettingsPage/TemplateSchedulePage/TemplateScheduleForm/TemplateScheduleForm.tsx +++ b/site/src/pages/TemplateSettingsPage/TemplateSchedulePage/TemplateScheduleForm/TemplateScheduleForm.tsx @@ -17,7 +17,7 @@ import Checkbox from "@mui/material/Checkbox" import FormControlLabel from "@mui/material/FormControlLabel" import Switch from "@mui/material/Switch" import { - useWorkspacesToBeLocked, + useWorkspacesToGoDormant, useWorkspacesToBeDeleted, } from "./useWorkspacesToBeDeleted" import { TemplateScheduleFormValues, getValidationSchema } from "./formHelpers" @@ -29,7 +29,7 @@ const MS_HOUR_CONVERSION = 3600000 const MS_DAY_CONVERSION = 86400000 const FAILURE_CLEANUP_DEFAULT = 7 const INACTIVITY_CLEANUP_DEFAULT = 180 -const LOCKED_CLEANUP_DEFAULT = 30 +const DORMANT_AUTODELETION_DEFAULT = 30 export interface TemplateScheduleForm { template: Template @@ -67,11 +67,11 @@ export const TemplateScheduleForm: FC = ({ failure_ttl_ms: allowAdvancedScheduling ? template.failure_ttl_ms / MS_DAY_CONVERSION : 0, - inactivity_ttl_ms: allowAdvancedScheduling - ? template.inactivity_ttl_ms / MS_DAY_CONVERSION + time_til_dormant_ms: allowAdvancedScheduling + ? template.time_til_dormant_ms / MS_DAY_CONVERSION : 0, - locked_ttl_ms: allowAdvancedScheduling - ? template.locked_ttl_ms / MS_DAY_CONVERSION + time_til_dormant_autodelete_ms: allowAdvancedScheduling + ? template.time_til_dormant_autodelete_ms / MS_DAY_CONVERSION : 0, restart_requirement: { @@ -84,18 +84,21 @@ export const TemplateScheduleForm: FC = ({ failure_cleanup_enabled: allowAdvancedScheduling && Boolean(template.failure_ttl_ms), inactivity_cleanup_enabled: - allowAdvancedScheduling && Boolean(template.inactivity_ttl_ms), - locked_cleanup_enabled: - allowAdvancedScheduling && Boolean(template.locked_ttl_ms), + allowAdvancedScheduling && Boolean(template.time_til_dormant_ms), + dormant_autodeletion_cleanup_enabled: + allowAdvancedScheduling && + Boolean(template.time_til_dormant_autodelete_ms), update_workspace_last_used_at: false, - update_workspace_locked_at: false, + update_workspace_dormant_at: false, }, validationSchema, onSubmit: () => { const dormancyChanged = - form.initialValues.inactivity_ttl_ms !== form.values.inactivity_ttl_ms + form.initialValues.time_til_dormant_ms !== + form.values.time_til_dormant_ms const deletionChanged = - form.initialValues.locked_ttl_ms !== form.values.locked_ttl_ms + form.initialValues.time_til_dormant_autodelete_ms !== + form.values.time_til_dormant_autodelete_ms const dormancyScheduleChanged = form.values.inactivity_cleanup_enabled && @@ -128,13 +131,13 @@ export const TemplateScheduleForm: FC = ({ const weekFromNow = new Date(now) weekFromNow.setDate(now.getDate() + 7) - const workspacesToDormancyNow = useWorkspacesToBeLocked( + const workspacesToDormancyNow = useWorkspacesToGoDormant( template, form.values, now, ) - const workspacesToDormancyInWeek = useWorkspacesToBeLocked( + const workspacesToDormancyInWeek = useWorkspacesToGoDormant( template, form.values, weekFromNow, @@ -175,17 +178,17 @@ export const TemplateScheduleForm: FC = ({ failure_ttl_ms: form.values.failure_ttl_ms ? form.values.failure_ttl_ms * MS_DAY_CONVERSION : undefined, - inactivity_ttl_ms: form.values.inactivity_ttl_ms - ? form.values.inactivity_ttl_ms * MS_DAY_CONVERSION + time_til_dormant_ms: form.values.time_til_dormant_ms + ? form.values.time_til_dormant_ms * MS_DAY_CONVERSION : undefined, - locked_ttl_ms: form.values.locked_ttl_ms - ? form.values.locked_ttl_ms * MS_DAY_CONVERSION + time_til_dormant_autodelete_ms: form.values.time_til_dormant_autodelete_ms + ? form.values.time_til_dormant_autodelete_ms * MS_DAY_CONVERSION : undefined, allow_user_autostart: form.values.allow_user_autostart, allow_user_autostop: form.values.allow_user_autostop, update_workspace_last_used_at: form.values.update_workspace_last_used_at, - update_workspace_locked_at: form.values.update_workspace_locked_at, + update_workspace_dormant_at: form.values.update_workspace_dormant_at, }) } @@ -211,37 +214,37 @@ export const TemplateScheduleForm: FC = ({ const handleToggleInactivityCleanup = async (e: ChangeEvent) => { form.handleChange(e) if (!form.values.inactivity_cleanup_enabled) { - // fill inactivity_ttl_ms with defaults + // fill time_til_dormant_ms with defaults await form.setValues({ ...form.values, inactivity_cleanup_enabled: true, - inactivity_ttl_ms: INACTIVITY_CLEANUP_DEFAULT, + time_til_dormant_ms: INACTIVITY_CLEANUP_DEFAULT, }) } else { - // clear inactivity_ttl_ms + // clear time_til_dormant_ms await form.setValues({ ...form.values, inactivity_cleanup_enabled: false, - inactivity_ttl_ms: 0, + time_til_dormant_ms: 0, }) } } - const handleToggleLockedCleanup = async (e: ChangeEvent) => { + const handleToggleDormantAutoDeletion = async (e: ChangeEvent) => { form.handleChange(e) - if (!form.values.locked_cleanup_enabled) { + if (!form.values.dormant_autodeletion_cleanup_enabled) { // fill failure_ttl_ms with defaults await form.setValues({ ...form.values, - locked_cleanup_enabled: true, - locked_ttl_ms: LOCKED_CLEANUP_DEFAULT, + dormant_autodeletion_cleanup_enabled: true, + time_til_dormant_autodelete_ms: DORMANT_AUTODELETION_DEFAULT, }) } else { // clear failure_ttl_ms await form.setValues({ ...form.values, - locked_cleanup_enabled: false, - locked_ttl_ms: 0, + dormant_autodeletion_cleanup_enabled: false, + time_til_dormant_autodelete_ms: 0, }) } } @@ -398,10 +401,10 @@ export const TemplateScheduleForm: FC = ({ /> , )} disabled={ @@ -423,21 +426,24 @@ export const TemplateScheduleForm: FC = ({ control={ } label="Enable Dormancy Auto-Deletion" /> , )} - disabled={isSubmitting || !form.values.locked_cleanup_enabled} + disabled={ + isSubmitting || + !form.values.dormant_autodeletion_cleanup_enabled + } fullWidth inputProps={{ min: 0, step: "any" }} label="Time until deletion (days)" @@ -455,7 +461,7 @@ export const TemplateScheduleForm: FC = ({ // These fields are request-scoped so they should be reset // after every submission. form - .setFieldValue("update_workspace_locked_at", false) + .setFieldValue("update_workspace_dormant_at", false) .catch((error) => { throw error }) @@ -478,18 +484,19 @@ export const TemplateScheduleForm: FC = ({ setIsScheduleDialogOpen(false) }} title="Workspace Scheduling" - updateLockedWorkspaces={(update: boolean) => - form.setFieldValue("update_workspace_locked_at", update) + updateDormantWorkspaces={(update: boolean) => + form.setFieldValue("update_workspace_dormant_at", update) } updateInactiveWorkspaces={(update: boolean) => form.setFieldValue("update_workspace_last_used_at", update) } dormantValueChanged={ - form.initialValues.inactivity_ttl_ms !== - form.values.inactivity_ttl_ms + form.initialValues.time_til_dormant_ms !== + form.values.time_til_dormant_ms } deletionValueChanged={ - form.initialValues.locked_ttl_ms !== form.values.locked_ttl_ms + form.initialValues.time_til_dormant_autodelete_ms !== + form.values.time_til_dormant_autodelete_ms } /> )} diff --git a/site/src/pages/TemplateSettingsPage/TemplateSchedulePage/TemplateScheduleForm/formHelpers.tsx b/site/src/pages/TemplateSettingsPage/TemplateSchedulePage/TemplateScheduleForm/formHelpers.tsx index d36fcd85b021c..8480b933c44da 100644 --- a/site/src/pages/TemplateSettingsPage/TemplateSchedulePage/TemplateScheduleForm/formHelpers.tsx +++ b/site/src/pages/TemplateSettingsPage/TemplateSchedulePage/TemplateScheduleForm/formHelpers.tsx @@ -5,7 +5,7 @@ import i18next from "i18next" export interface TemplateScheduleFormValues extends UpdateTemplateMeta { failure_cleanup_enabled: boolean inactivity_cleanup_enabled: boolean - locked_cleanup_enabled: boolean + dormant_autodeletion_cleanup_enabled: boolean } const MAX_TTL_DAYS = 30 @@ -50,7 +50,7 @@ export const getValidationSchema = (): Yup.AnyObjectSchema => } }, ), - inactivity_ttl_ms: Yup.number() + time_til_dormant_ms: Yup.number() .min(0, "Dormancy threshold days must not be less than 0.") .test( "positive-if-enabled", @@ -64,14 +64,14 @@ export const getValidationSchema = (): Yup.AnyObjectSchema => } }, ), - locked_ttl_ms: Yup.number() + time_til_dormant_autodelete_ms: Yup.number() .min(0, "Dormancy auto-deletion days must not be less than 0.") .test( "positive-if-enabled", "Dormancy auto-deletion days must be greater than zero when enabled.", function (value) { const parent = this.parent as TemplateScheduleFormValues - if (parent.locked_cleanup_enabled) { + if (parent.dormant_autodeletion_cleanup_enabled) { return Boolean(value) } else { return true diff --git a/site/src/pages/TemplateSettingsPage/TemplateSchedulePage/TemplateScheduleForm/useWorkspacesToBeDeleted.ts b/site/src/pages/TemplateSettingsPage/TemplateSchedulePage/TemplateScheduleForm/useWorkspacesToBeDeleted.ts index 346d371f02951..b04cf02e7d618 100644 --- a/site/src/pages/TemplateSettingsPage/TemplateSchedulePage/TemplateScheduleForm/useWorkspacesToBeDeleted.ts +++ b/site/src/pages/TemplateSettingsPage/TemplateSchedulePage/TemplateScheduleForm/useWorkspacesToBeDeleted.ts @@ -3,7 +3,7 @@ import { Workspace, Template } from "api/typesGenerated" import { TemplateScheduleFormValues } from "./formHelpers" import { useWorkspacesData } from "pages/WorkspacesPage/data" -export const useWorkspacesToBeLocked = ( +export const useWorkspacesToGoDormant = ( template: Template, formValues: TemplateScheduleFormValues, fromDate: Date, @@ -15,17 +15,17 @@ export const useWorkspacesToBeLocked = ( }) return data?.workspaces?.filter((workspace: Workspace) => { - if (!formValues.inactivity_ttl_ms) { + if (!formValues.time_til_dormant_ms) { return } - if (workspace.locked_at) { + if (workspace.dormant_at) { return } const proposedLocking = new Date( new Date(workspace.last_used_at).getTime() + - formValues.inactivity_ttl_ms * DayInMS, + formValues.time_til_dormant_ms * DayInMS, ) if (compareAsc(proposedLocking, fromDate) < 1) { @@ -44,16 +44,16 @@ export const useWorkspacesToBeDeleted = ( const { data } = useWorkspacesData({ page: 0, limit: 0, - query: "template:" + template.name + " locked_at:1970-01-01", + query: "template:" + template.name + " dormant_at:1970-01-01", }) return data?.workspaces?.filter((workspace: Workspace) => { - if (!workspace.locked_at || !formValues.locked_ttl_ms) { + if (!workspace.dormant_at || !formValues.time_til_dormant_autodelete_ms) { return false } const proposedLocking = new Date( - new Date(workspace.locked_at).getTime() + - formValues.locked_ttl_ms * DayInMS, + new Date(workspace.dormant_at).getTime() + + formValues.time_til_dormant_autodelete_ms * DayInMS, ) if (compareAsc(proposedLocking, fromDate) < 1) { diff --git a/site/src/pages/TemplateSettingsPage/TemplateSchedulePage/TemplateSchedulePage.test.tsx b/site/src/pages/TemplateSettingsPage/TemplateSchedulePage/TemplateSchedulePage.test.tsx index dd03610717829..476d5085d0f4f 100644 --- a/site/src/pages/TemplateSettingsPage/TemplateSchedulePage/TemplateSchedulePage.test.tsx +++ b/site/src/pages/TemplateSettingsPage/TemplateSchedulePage/TemplateSchedulePage.test.tsx @@ -21,10 +21,10 @@ const validFormValues = { default_ttl_ms: 1, max_ttl_ms: 2, failure_ttl_ms: 7, - inactivity_ttl_ms: 180, - locked_ttl_ms: 30, + time_til_dormant_ms: 180, + time_til_dormant_autodelete_ms: 30, update_workspace_last_used_at: false, - update_workspace_locked_at: false, + update_workspace_dormant_at: false, } const renderTemplateSchedulePage = async () => { @@ -39,14 +39,14 @@ const fillAndSubmitForm = async ({ default_ttl_ms, max_ttl_ms, failure_ttl_ms, - inactivity_ttl_ms, - locked_ttl_ms, + time_til_dormant_ms, + time_til_dormant_autodelete_ms, }: { default_ttl_ms: number max_ttl_ms: number failure_ttl_ms: number - inactivity_ttl_ms: number - locked_ttl_ms: number + time_til_dormant_ms: number + time_til_dormant_autodelete_ms: number }) => { const user = userEvent.setup() const defaultTtlLabel = t("defaultTtlLabel", { ns: "templateSettingsPage" }) @@ -67,19 +67,22 @@ const fillAndSubmitForm = async ({ const inactivityTtlField = screen.getByRole("checkbox", { name: /Dormancy Threshold/i, }) - await user.type(inactivityTtlField, inactivity_ttl_ms.toString()) + await user.type(inactivityTtlField, time_til_dormant_ms.toString()) - const lockedTtlField = screen.getByRole("checkbox", { + const dormancyAutoDeletionField = screen.getByRole("checkbox", { name: /Dormancy Auto-Deletion/i, }) - await user.type(lockedTtlField, locked_ttl_ms.toString()) + await user.type( + dormancyAutoDeletionField, + time_til_dormant_autodelete_ms.toString(), + ) const submitButton = await screen.findByText( FooterFormLanguage.defaultSubmitLabel, ) await user.click(submitButton) - // User needs to confirm inactivity and locked ttl + // User needs to confirm dormancy and autodeletion fields. const confirmButton = await screen.findByTestId("confirm-button") await user.click(confirmButton) } @@ -140,8 +143,9 @@ describe("TemplateSchedulePage", () => { "test-template", expect.objectContaining({ failure_ttl_ms: validFormValues.failure_ttl_ms * 86400000, - inactivity_ttl_ms: validFormValues.inactivity_ttl_ms * 86400000, - locked_ttl_ms: validFormValues.locked_ttl_ms * 86400000, + time_til_dormant_ms: validFormValues.time_til_dormant_ms * 86400000, + time_til_dormant_autodelete_ms: + validFormValues.time_til_dormant_autodelete_ms * 86400000, }), ), ) @@ -217,7 +221,7 @@ describe("TemplateSchedulePage", () => { it("allows an inactivity ttl of 7 days", () => { const values: UpdateTemplateMeta = { ...validFormValues, - inactivity_ttl_ms: 86400000 * 7, + time_til_dormant_ms: 86400000 * 7, } const validate = () => getValidationSchema().validateSync(values) expect(validate).not.toThrowError() @@ -226,7 +230,7 @@ describe("TemplateSchedulePage", () => { it("allows an inactivity ttl of 0", () => { const values: UpdateTemplateMeta = { ...validFormValues, - inactivity_ttl_ms: 0, + time_til_dormant_ms: 0, } const validate = () => getValidationSchema().validateSync(values) expect(validate).not.toThrowError() @@ -235,7 +239,7 @@ describe("TemplateSchedulePage", () => { it("disallows a negative inactivity ttl", () => { const values: UpdateTemplateMeta = { ...validFormValues, - inactivity_ttl_ms: -1, + time_til_dormant_ms: -1, } const validate = () => getValidationSchema().validateSync(values) expect(validate).toThrowError( @@ -246,7 +250,7 @@ describe("TemplateSchedulePage", () => { it("allows a dormancy ttl of 7 days", () => { const values: UpdateTemplateMeta = { ...validFormValues, - locked_ttl_ms: 86400000 * 7, + time_til_dormant_autodelete_ms: 86400000 * 7, } const validate = () => getValidationSchema().validateSync(values) expect(validate).not.toThrowError() @@ -255,7 +259,7 @@ describe("TemplateSchedulePage", () => { it("allows a dormancy ttl of 0", () => { const values: UpdateTemplateMeta = { ...validFormValues, - locked_ttl_ms: 0, + time_til_dormant_autodelete_ms: 0, } const validate = () => getValidationSchema().validateSync(values) expect(validate).not.toThrowError() @@ -264,7 +268,7 @@ describe("TemplateSchedulePage", () => { it("disallows a negative inactivity ttl", () => { const values: UpdateTemplateMeta = { ...validFormValues, - locked_ttl_ms: -1, + time_til_dormant_autodelete_ms: -1, } const validate = () => getValidationSchema().validateSync(values) expect(validate).toThrowError( diff --git a/site/src/pages/WorkspacesPage/WorkspacesPage.tsx b/site/src/pages/WorkspacesPage/WorkspacesPage.tsx index ed810e36769b3..9684e156c8ce7 100644 --- a/site/src/pages/WorkspacesPage/WorkspacesPage.tsx +++ b/site/src/pages/WorkspacesPage/WorkspacesPage.tsx @@ -23,7 +23,7 @@ import { displayError } from "components/GlobalSnackbar/utils" import { getErrorMessage } from "api/errors" const WorkspacesPage: FC = () => { - const [lockedWorkspaces, setLockedWorkspaces] = useState([]) + const [dormantWorkspaces, setDormantWorkspaces] = useState([]) // If we use a useSearchParams for each hook, the values will not be in sync. // So we have to use a single one, centralizing the values, and pass it to // each hook. @@ -36,23 +36,23 @@ const WorkspacesPage: FC = () => { }) const experimentEnabled = useIsWorkspaceActionsEnabled() - // If workspace actions are enabled we need to fetch the locked + // If workspace actions are enabled we need to fetch the dormant // workspaces as well. This lets us determine whether we should // show a banner to the user indicating that some of their workspaces // are at risk of being deleted. useEffect(() => { if (experimentEnabled) { - const includesLocked = filterProps.filter.query.includes("locked_at") - const lockedQuery = includesLocked + const includesDormant = filterProps.filter.query.includes("dormant_at") + const dormantQuery = includesDormant ? filterProps.filter.query - : filterProps.filter.query + " locked_at:1970-01-01" + : filterProps.filter.query + " dormant_at:1970-01-01" - if (includesLocked && data) { - setLockedWorkspaces(data.workspaces) + if (includesDormant && data) { + setDormantWorkspaces(data.workspaces) } else { - getWorkspaces({ q: lockedQuery }) + getWorkspaces({ q: dormantQuery }) .then((resp) => { - setLockedWorkspaces(resp.workspaces) + setDormantWorkspaces(resp.workspaces) }) .catch(() => { // TODO @@ -60,8 +60,8 @@ const WorkspacesPage: FC = () => { } } else { // If the experiment isn't included then we'll pretend - // like locked workspaces don't exist. - setLockedWorkspaces([]) + // like dormant workspaces don't exist. + setDormantWorkspaces([]) } }, [experimentEnabled, data, filterProps.filter.query]) const updateWorkspace = useWorkspaceUpdate(queryKey) @@ -90,7 +90,7 @@ const WorkspacesPage: FC = () => { checkedWorkspaces={checkedWorkspaces} onCheckChange={setCheckedWorkspaces} workspaces={data?.workspaces} - lockedWorkspaces={lockedWorkspaces} + dormantWorkspaces={dormantWorkspaces} error={error} count={data?.count} page={pagination.page} diff --git a/site/src/pages/WorkspacesPage/WorkspacesPageView.tsx b/site/src/pages/WorkspacesPage/WorkspacesPageView.tsx index 9190db98c9575..cb3a8cd2f2e62 100644 --- a/site/src/pages/WorkspacesPage/WorkspacesPageView.tsx +++ b/site/src/pages/WorkspacesPage/WorkspacesPageView.tsx @@ -38,7 +38,7 @@ export const Language = { export interface WorkspacesPageViewProps { error: unknown workspaces?: Workspace[] - lockedWorkspaces?: Workspace[] + dormantWorkspaces?: Workspace[] checkedWorkspaces: Workspace[] count?: number filterProps: ComponentProps @@ -55,7 +55,7 @@ export const WorkspacesPageView: FC< React.PropsWithChildren > = ({ workspaces, - lockedWorkspaces, + dormantWorkspaces, error, limit, count, @@ -70,12 +70,12 @@ export const WorkspacesPageView: FC< }) => { const { saveLocal } = useLocalStorage() - const workspacesDeletionScheduled = lockedWorkspaces + const workspacesDeletionScheduled = dormantWorkspaces ?.filter((workspace) => workspace.deleting_at) .map((workspace) => workspace.id) - const hasLockedWorkspace = - lockedWorkspaces !== undefined && lockedWorkspaces.length > 0 + const hasDormantWorkspace = + dormantWorkspaces !== undefined && dormantWorkspaces.length > 0 return ( @@ -102,8 +102,8 @@ export const WorkspacesPageView: FC< {/* determines its own visibility */} saveLocal( "dismissedWorkspaceList", diff --git a/site/src/pages/WorkspacesPage/filter/filter.tsx b/site/src/pages/WorkspacesPage/filter/filter.tsx index 8d98c0b03182f..2a1b519a0ceec 100644 --- a/site/src/pages/WorkspacesPage/filter/filter.tsx +++ b/site/src/pages/WorkspacesPage/filter/filter.tsx @@ -47,8 +47,8 @@ export const WorkspacesFilter = ({ const presets = [...PRESET_FILTERS] if (useIsWorkspaceActionsEnabled()) { presets.push({ - query: workspaceFilterQuery.locked, - name: "Locked workspaces", + query: workspaceFilterQuery.dormant, + name: "Dormant workspaces", }) } diff --git a/site/src/testHelpers/entities.ts b/site/src/testHelpers/entities.ts index 2a77a174e8831..2e3438c1fe293 100644 --- a/site/src/testHelpers/entities.ts +++ b/site/src/testHelpers/entities.ts @@ -445,8 +445,8 @@ export const MockTemplate: TypesGen.Template = { icon: "/icon/code.svg", allow_user_cancel_workspace_jobs: true, failure_ttl_ms: 0, - inactivity_ttl_ms: 0, - locked_ttl_ms: 0, + time_til_dormant_ms: 0, + time_til_dormant_autodelete_ms: 0, allow_user_autostart: false, allow_user_autostop: false, } diff --git a/site/src/utils/filters.ts b/site/src/utils/filters.ts index 27f32130aafe6..8f477278b578c 100644 --- a/site/src/utils/filters.ts +++ b/site/src/utils/filters.ts @@ -14,7 +14,7 @@ export const workspaceFilterQuery = { all: "", running: "status:running", failed: "status:failed", - locked: "locked_at:1970-01-01", + dormant: "dormant_at:1970-01-01", } export const userFilterQuery = { diff --git a/site/src/xServices/workspace/workspaceXService.ts b/site/src/xServices/workspace/workspaceXService.ts index d3e9f7204e25c..1706d1af7304f 100644 --- a/site/src/xServices/workspace/workspaceXService.ts +++ b/site/src/xServices/workspace/workspaceXService.ts @@ -697,7 +697,7 @@ export const workspaceMachine = createMachine( }, activateWorkspace: (context) => async (send) => { if (context.workspace) { - const activateWorkspacePromise = await API.updateWorkspaceLock( + const activateWorkspacePromise = await API.updateWorkspaceDormancy( context.workspace.id, false, )