Files
coder/coderd/schedule/template_test.go
Danielle Maywood 40624bf78b fix: update workspace TTL on template TTL change (#15761)
Relates to https://github.com/coder/coder/issues/15390

Currently when a user creates a workspace, their workspace's TTL is
determined by the template's default TTL. If the Coder instance is AGPL,
or if the template has disallowed the user from configuring autostop,
then it is not possible to change the workspace's TTL after creation.
Any changes to the template's default TTL only takes effect on _new_
workspaces.

This PR modifies the behaviour slightly so that on AGPL Coder, or on
enterprise when a template does not allow user's to configure their
workspace's TTL, updating the template's default TTL will also update
any workspace's TTL to match this value.
2024-12-06 11:01:39 +00:00

151 lines
4.8 KiB
Go

package schedule_test
import (
"database/sql"
"testing"
"time"
"github.com/stretchr/testify/require"
"github.com/coder/coder/v2/coderd/database"
"github.com/coder/coder/v2/coderd/database/dbgen"
"github.com/coder/coder/v2/coderd/database/dbtestutil"
"github.com/coder/coder/v2/coderd/database/dbtime"
"github.com/coder/coder/v2/coderd/schedule"
"github.com/coder/coder/v2/testutil"
)
func TestTemplateTTL(t *testing.T) {
t.Parallel()
tests := []struct {
name string
fromTTL time.Duration
toTTL time.Duration
expected sql.NullInt64
}{
{
name: "ModifyTTLDurationDown",
fromTTL: 24 * time.Hour,
toTTL: 1 * time.Hour,
expected: sql.NullInt64{Valid: true, Int64: int64(1 * time.Hour)},
},
{
name: "ModifyTTLDurationUp",
fromTTL: 24 * time.Hour,
toTTL: 36 * time.Hour,
expected: sql.NullInt64{Valid: true, Int64: int64(36 * time.Hour)},
},
{
name: "ModifyTTLDurationSame",
fromTTL: 24 * time.Hour,
toTTL: 24 * time.Hour,
expected: sql.NullInt64{Valid: true, Int64: int64(24 * time.Hour)},
},
{
name: "DisableTTL",
fromTTL: 24 * time.Hour,
toTTL: 0,
expected: sql.NullInt64{},
},
}
for _, tt := range tests {
tt := tt
t.Run(tt.name, func(t *testing.T) {
t.Parallel()
var (
db, _ = dbtestutil.NewDB(t)
ctx = testutil.Context(t, testutil.WaitLong)
user = dbgen.User(t, db, database.User{})
file = dbgen.File(t, db, database.File{CreatedBy: user.ID})
// Create first template
templateJob = dbgen.ProvisionerJob(t, db, nil, database.ProvisionerJob{
FileID: file.ID,
InitiatorID: user.ID,
Tags: database.StringMap{"foo": "bar"},
})
templateVersion = dbgen.TemplateVersion(t, db, database.TemplateVersion{
CreatedBy: user.ID,
JobID: templateJob.ID,
OrganizationID: templateJob.OrganizationID,
})
template = dbgen.Template(t, db, database.Template{
ActiveVersionID: templateVersion.ID,
CreatedBy: user.ID,
OrganizationID: templateJob.OrganizationID,
})
// Create second template
otherTTL = tt.fromTTL + 6*time.Hour
otherTemplateJob = dbgen.ProvisionerJob(t, db, nil, database.ProvisionerJob{
FileID: file.ID,
InitiatorID: user.ID,
Tags: database.StringMap{"foo": "bar"},
})
otherTemplateVersion = dbgen.TemplateVersion(t, db, database.TemplateVersion{
CreatedBy: user.ID,
JobID: otherTemplateJob.ID,
OrganizationID: otherTemplateJob.OrganizationID,
})
otherTemplate = dbgen.Template(t, db, database.Template{
ActiveVersionID: otherTemplateVersion.ID,
CreatedBy: user.ID,
OrganizationID: otherTemplateJob.OrganizationID,
})
)
templateScheduleStore := schedule.NewAGPLTemplateScheduleStore()
// Set both template's default TTL
template, err := templateScheduleStore.Set(ctx, db, template, schedule.TemplateScheduleOptions{
DefaultTTL: tt.fromTTL,
})
require.NoError(t, err)
otherTemplate, err = templateScheduleStore.Set(ctx, db, otherTemplate, schedule.TemplateScheduleOptions{
DefaultTTL: otherTTL,
})
require.NoError(t, err)
// We create two workspaces here, one with the template we're modifying, the
// other with a different template. We want to ensure we only modify one
// of the workspaces.
workspace := dbgen.Workspace(t, db, database.WorkspaceTable{
OwnerID: user.ID,
TemplateID: template.ID,
OrganizationID: templateJob.OrganizationID,
LastUsedAt: dbtime.Now(),
Ttl: sql.NullInt64{Valid: true, Int64: int64(tt.fromTTL)},
})
otherWorkspace := dbgen.Workspace(t, db, database.WorkspaceTable{
OwnerID: user.ID,
TemplateID: otherTemplate.ID,
OrganizationID: otherTemplateJob.OrganizationID,
LastUsedAt: dbtime.Now(),
Ttl: sql.NullInt64{Valid: true, Int64: int64(otherTTL)},
})
// Ensure the workspace's start with the correct TTLs
require.Equal(t, sql.NullInt64{Valid: true, Int64: int64(tt.fromTTL)}, workspace.Ttl)
require.Equal(t, sql.NullInt64{Valid: true, Int64: int64(otherTTL)}, otherWorkspace.Ttl)
// Update _only_ the primary template's TTL
_, err = templateScheduleStore.Set(ctx, db, template, schedule.TemplateScheduleOptions{
DefaultTTL: tt.toTTL,
})
require.NoError(t, err)
// Verify the primary workspace's TTL has been updated.
ws, err := db.GetWorkspaceByID(ctx, workspace.ID)
require.NoError(t, err)
require.Equal(t, tt.expected, ws.Ttl)
// Verify that the other workspace's TTL has not been touched.
ws, err = db.GetWorkspaceByID(ctx, otherWorkspace.ID)
require.NoError(t, err)
require.Equal(t, sql.NullInt64{Valid: true, Int64: int64(otherTTL)}, ws.Ttl)
})
}
}