mirror of
https://github.com/coder/coder.git
synced 2025-07-15 22:20:27 +00:00
feat: add deleted_at field to workspace model (#7475)
* added impending_deletion workspace field * gen docs * update golden files * added test * PR comments
This commit is contained in:
5
coderd/apidoc/docs.go
generated
5
coderd/apidoc/docs.go
generated
@ -9455,6 +9455,11 @@ const docTemplate = `{
|
||||
"type": "string",
|
||||
"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.",
|
||||
"type": "string",
|
||||
"format": "date-time"
|
||||
},
|
||||
"id": {
|
||||
"type": "string",
|
||||
"format": "uuid"
|
||||
|
5
coderd/apidoc/swagger.json
generated
5
coderd/apidoc/swagger.json
generated
@ -8499,6 +8499,11 @@
|
||||
"type": "string",
|
||||
"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.",
|
||||
"type": "string",
|
||||
"format": "date-time"
|
||||
},
|
||||
"id": {
|
||||
"type": "string",
|
||||
"format": "uuid"
|
||||
|
@ -1169,7 +1169,10 @@ func convertWorkspace(
|
||||
autostartSchedule = &workspace.AutostartSchedule.String
|
||||
}
|
||||
|
||||
ttlMillis := convertWorkspaceTTLMillis(workspace.Ttl)
|
||||
var (
|
||||
ttlMillis = convertWorkspaceTTLMillis(workspace.Ttl)
|
||||
deletingAt = calculateDeletingAt(workspace, template)
|
||||
)
|
||||
return codersdk.Workspace{
|
||||
ID: workspace.ID,
|
||||
CreatedAt: workspace.CreatedAt,
|
||||
@ -1188,6 +1191,7 @@ func convertWorkspace(
|
||||
AutostartSchedule: autostartSchedule,
|
||||
TTLMillis: ttlMillis,
|
||||
LastUsedAt: workspace.LastUsedAt,
|
||||
DeletingAt: deletingAt,
|
||||
}
|
||||
}
|
||||
|
||||
@ -1200,6 +1204,22 @@ func convertWorkspaceTTLMillis(i sql.NullInt64) *int64 {
|
||||
return &millis
|
||||
}
|
||||
|
||||
// Calculate the time of the upcoming workspace deletion, if applicable; otherwise, return nil.
|
||||
// Workspaces may have impending deletions if InactivityTTL feature is turned on and the workspace is inactive.
|
||||
func calculateDeletingAt(workspace database.Workspace, template database.Template) *time.Time {
|
||||
var (
|
||||
year, month, day = time.Now().Date()
|
||||
beginningOfToday = time.Date(year, month, day, 0, 0, 0, 0, time.Now().Location())
|
||||
)
|
||||
// If InactivityTTL is turned off (set to 0), if the workspace has already been deleted,
|
||||
// or if the workspace was used sometime within the last day, there is no impending deletion
|
||||
if template.InactivityTTL == 0 || workspace.Deleted || workspace.LastUsedAt.After(beginningOfToday) {
|
||||
return nil
|
||||
}
|
||||
|
||||
return ptr.Ref(workspace.LastUsedAt.Add(time.Duration(template.InactivityTTL) * time.Nanosecond))
|
||||
}
|
||||
|
||||
func validWorkspaceTTLMillis(millis *int64, templateDefault, templateMax time.Duration) (sql.NullInt64, error) {
|
||||
if templateDefault == 0 && templateMax != 0 || (templateMax > 0 && templateDefault > templateMax) {
|
||||
templateDefault = templateMax
|
||||
|
82
coderd/workspaces_internal_test.go
Normal file
82
coderd/workspaces_internal_test.go
Normal file
@ -0,0 +1,82 @@
|
||||
package coderd
|
||||
|
||||
import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/coder/coder/coderd/database"
|
||||
"github.com/coder/coder/coderd/util/ptr"
|
||||
)
|
||||
|
||||
func Test_calculateDeletingAt(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
testCases := []struct {
|
||||
name string
|
||||
workspace database.Workspace
|
||||
template database.Template
|
||||
expected *time.Time
|
||||
}{
|
||||
{
|
||||
name: "DeletingAt",
|
||||
workspace: database.Workspace{
|
||||
Deleted: false,
|
||||
LastUsedAt: time.Now().Add(time.Duration(-10) * time.Hour * 24), // 10 days ago
|
||||
},
|
||||
template: database.Template{
|
||||
InactivityTTL: int64(9 * 24 * time.Hour), // 9 days
|
||||
},
|
||||
expected: ptr.Ref(time.Now().Add(time.Duration(-1) * time.Hour * 24)), // yesterday
|
||||
},
|
||||
{
|
||||
name: "InactivityTTLUnset",
|
||||
workspace: database.Workspace{
|
||||
Deleted: false,
|
||||
LastUsedAt: time.Now().Add(time.Duration(-10) * time.Hour * 24),
|
||||
},
|
||||
template: database.Template{
|
||||
InactivityTTL: 0,
|
||||
},
|
||||
expected: nil,
|
||||
},
|
||||
{
|
||||
name: "DeletedWorkspace",
|
||||
workspace: database.Workspace{
|
||||
Deleted: true,
|
||||
LastUsedAt: time.Now().Add(time.Duration(-10) * time.Hour * 24),
|
||||
},
|
||||
template: database.Template{
|
||||
InactivityTTL: int64(9 * 24 * time.Hour),
|
||||
},
|
||||
expected: nil,
|
||||
},
|
||||
{
|
||||
name: "ActiveWorkspace",
|
||||
workspace: database.Workspace{
|
||||
Deleted: true,
|
||||
LastUsedAt: time.Now().Add(time.Duration(-5) * time.Hour), // 5 hours ago
|
||||
},
|
||||
template: database.Template{
|
||||
InactivityTTL: int64(1 * 24 * time.Hour), // 1 day
|
||||
},
|
||||
expected: nil,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
tc := tc
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
found := calculateDeletingAt(tc.workspace, tc.template)
|
||||
if tc.expected == nil {
|
||||
require.Nil(t, found, "impending deletion should be nil")
|
||||
} else {
|
||||
require.NotNil(t, found)
|
||||
require.WithinDuration(t, *tc.expected, *found, time.Second, "incorrect impending deletion")
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user