From 8ab63aa3eb0be47137d6c849c074123c0a691b6f Mon Sep 17 00:00:00 2001 From: Garrett Date: Tue, 1 Nov 2022 19:42:10 +0000 Subject: [PATCH 01/18] fix: remove unused min autostart feature --- cli/templateedit.go | 19 +++-- coderd/database/databasefake/databasefake.go | 30 ++++---- coderd/database/queries.sql.go | 50 ++++++------- coderd/database/queries/templates.sql | 8 +-- coderd/templates.go | 75 ++++++++------------ coderd/templates_test.go | 24 +++---- codersdk/templates.go | 9 ++- site/src/api/typesGenerated.ts | 1 - 8 files changed, 87 insertions(+), 129 deletions(-) diff --git a/cli/templateedit.go b/cli/templateedit.go index e0e4cf57a7196..09bdf5e069960 100644 --- a/cli/templateedit.go +++ b/cli/templateedit.go @@ -13,11 +13,10 @@ import ( func templateEdit() *cobra.Command { var ( - name string - description string - icon string - maxTTL time.Duration - minAutostartInterval time.Duration + name string + description string + icon string + maxTTL time.Duration ) cmd := &cobra.Command{ @@ -40,11 +39,10 @@ func templateEdit() *cobra.Command { // NOTE: coderd will ignore empty fields. req := codersdk.UpdateTemplateMeta{ - Name: name, - Description: description, - Icon: icon, - MaxTTLMillis: maxTTL.Milliseconds(), - MinAutostartIntervalMillis: minAutostartInterval.Milliseconds(), + Name: name, + Description: description, + Icon: icon, + MaxTTLMillis: maxTTL.Milliseconds(), } _, err = client.UpdateTemplateMeta(cmd.Context(), template.ID, req) @@ -60,7 +58,6 @@ func templateEdit() *cobra.Command { cmd.Flags().StringVarP(&description, "description", "", "", "Edit the template description") cmd.Flags().StringVarP(&icon, "icon", "", "", "Edit the template icon path") cmd.Flags().DurationVarP(&maxTTL, "max-ttl", "", 0, "Edit the template maximum time before shutdown - workspaces created from this template cannot stay running longer than this.") - cmd.Flags().DurationVarP(&minAutostartInterval, "min-autostart-interval", "", 0, "Edit the template minimum autostart interval - workspaces created from this template must wait at least this long between autostarts.") cliui.AllowSkipPrompt(cmd) return cmd diff --git a/coderd/database/databasefake/databasefake.go b/coderd/database/databasefake/databasefake.go index e2b9f09f3c37f..a2806b45120db 100644 --- a/coderd/database/databasefake/databasefake.go +++ b/coderd/database/databasefake/databasefake.go @@ -1456,7 +1456,6 @@ func (q *fakeQuerier) UpdateTemplateMetaByID(_ context.Context, arg database.Upd tpl.Description = arg.Description tpl.Icon = arg.Icon tpl.MaxTtl = arg.MaxTtl - tpl.MinAutostartInterval = arg.MinAutostartInterval q.templates[idx] = tpl return tpl, nil } @@ -2227,25 +2226,20 @@ func (q *fakeQuerier) InsertTemplate(_ context.Context, arg database.InsertTempl q.mutex.Lock() defer q.mutex.Unlock() - if arg.MinAutostartInterval == 0 { - arg.MinAutostartInterval = int64(time.Hour) - } - //nolint:gosimple template := database.Template{ - ID: arg.ID, - CreatedAt: arg.CreatedAt, - UpdatedAt: arg.UpdatedAt, - OrganizationID: arg.OrganizationID, - Name: arg.Name, - Provisioner: arg.Provisioner, - ActiveVersionID: arg.ActiveVersionID, - Description: arg.Description, - MaxTtl: arg.MaxTtl, - MinAutostartInterval: arg.MinAutostartInterval, - CreatedBy: arg.CreatedBy, - UserACL: arg.UserACL, - GroupACL: arg.GroupACL, + ID: arg.ID, + CreatedAt: arg.CreatedAt, + UpdatedAt: arg.UpdatedAt, + OrganizationID: arg.OrganizationID, + Name: arg.Name, + Provisioner: arg.Provisioner, + ActiveVersionID: arg.ActiveVersionID, + Description: arg.Description, + MaxTtl: arg.MaxTtl, + CreatedBy: arg.CreatedBy, + UserACL: arg.UserACL, + GroupACL: arg.GroupACL, } q.templates = append(q.templates, template) return template, nil diff --git a/coderd/database/queries.sql.go b/coderd/database/queries.sql.go index 7c1bf4bfcd34d..6f89f8fc22767 100644 --- a/coderd/database/queries.sql.go +++ b/coderd/database/queries.sql.go @@ -3229,31 +3229,29 @@ INSERT INTO active_version_id, description, max_ttl, - min_autostart_interval, created_by, icon, user_acl, group_acl ) VALUES - ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14) RETURNING id, created_at, updated_at, organization_id, deleted, name, provisioner, active_version_id, description, max_ttl, min_autostart_interval, created_by, icon, user_acl, group_acl + ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13) RETURNING id, created_at, updated_at, organization_id, deleted, name, provisioner, active_version_id, description, max_ttl, min_autostart_interval, created_by, icon, user_acl, group_acl ` type InsertTemplateParams struct { - ID uuid.UUID `db:"id" json:"id"` - CreatedAt time.Time `db:"created_at" json:"created_at"` - UpdatedAt time.Time `db:"updated_at" json:"updated_at"` - OrganizationID uuid.UUID `db:"organization_id" json:"organization_id"` - Name string `db:"name" json:"name"` - Provisioner ProvisionerType `db:"provisioner" json:"provisioner"` - ActiveVersionID uuid.UUID `db:"active_version_id" json:"active_version_id"` - Description string `db:"description" json:"description"` - MaxTtl int64 `db:"max_ttl" json:"max_ttl"` - MinAutostartInterval int64 `db:"min_autostart_interval" json:"min_autostart_interval"` - CreatedBy uuid.UUID `db:"created_by" json:"created_by"` - Icon string `db:"icon" json:"icon"` - UserACL TemplateACL `db:"user_acl" json:"user_acl"` - GroupACL TemplateACL `db:"group_acl" json:"group_acl"` + ID uuid.UUID `db:"id" json:"id"` + CreatedAt time.Time `db:"created_at" json:"created_at"` + UpdatedAt time.Time `db:"updated_at" json:"updated_at"` + OrganizationID uuid.UUID `db:"organization_id" json:"organization_id"` + Name string `db:"name" json:"name"` + Provisioner ProvisionerType `db:"provisioner" json:"provisioner"` + ActiveVersionID uuid.UUID `db:"active_version_id" json:"active_version_id"` + Description string `db:"description" json:"description"` + MaxTtl int64 `db:"max_ttl" json:"max_ttl"` + CreatedBy uuid.UUID `db:"created_by" json:"created_by"` + Icon string `db:"icon" json:"icon"` + UserACL TemplateACL `db:"user_acl" json:"user_acl"` + GroupACL TemplateACL `db:"group_acl" json:"group_acl"` } func (q *sqlQuerier) InsertTemplate(ctx context.Context, arg InsertTemplateParams) (Template, error) { @@ -3267,7 +3265,6 @@ func (q *sqlQuerier) InsertTemplate(ctx context.Context, arg InsertTemplateParam arg.ActiveVersionID, arg.Description, arg.MaxTtl, - arg.MinAutostartInterval, arg.CreatedBy, arg.Icon, arg.UserACL, @@ -3384,9 +3381,8 @@ SET updated_at = $2, description = $3, max_ttl = $4, - min_autostart_interval = $5, - name = $6, - icon = $7 + name = $5, + icon = $6 WHERE id = $1 RETURNING @@ -3394,13 +3390,12 @@ RETURNING ` type UpdateTemplateMetaByIDParams struct { - ID uuid.UUID `db:"id" json:"id"` - UpdatedAt time.Time `db:"updated_at" json:"updated_at"` - Description string `db:"description" json:"description"` - MaxTtl int64 `db:"max_ttl" json:"max_ttl"` - MinAutostartInterval int64 `db:"min_autostart_interval" json:"min_autostart_interval"` - Name string `db:"name" json:"name"` - Icon string `db:"icon" json:"icon"` + ID uuid.UUID `db:"id" json:"id"` + UpdatedAt time.Time `db:"updated_at" json:"updated_at"` + Description string `db:"description" json:"description"` + MaxTtl int64 `db:"max_ttl" json:"max_ttl"` + Name string `db:"name" json:"name"` + Icon string `db:"icon" json:"icon"` } func (q *sqlQuerier) UpdateTemplateMetaByID(ctx context.Context, arg UpdateTemplateMetaByIDParams) (Template, error) { @@ -3409,7 +3404,6 @@ func (q *sqlQuerier) UpdateTemplateMetaByID(ctx context.Context, arg UpdateTempl arg.UpdatedAt, arg.Description, arg.MaxTtl, - arg.MinAutostartInterval, arg.Name, arg.Icon, ) diff --git a/coderd/database/queries/templates.sql b/coderd/database/queries/templates.sql index 6eb73af288b81..64df1cd25a440 100644 --- a/coderd/database/queries/templates.sql +++ b/coderd/database/queries/templates.sql @@ -66,14 +66,13 @@ INSERT INTO active_version_id, description, max_ttl, - min_autostart_interval, created_by, icon, user_acl, group_acl ) VALUES - ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14) RETURNING *; + ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13) RETURNING *; -- name: UpdateTemplateActiveVersionByID :exec UPDATE @@ -100,9 +99,8 @@ SET updated_at = $2, description = $3, max_ttl = $4, - min_autostart_interval = $5, - name = $6, - icon = $7 + name = $5, + icon = $6 WHERE id = $1 RETURNING diff --git a/coderd/templates.go b/coderd/templates.go index 163464db39b12..d342c990e8be7 100644 --- a/coderd/templates.go +++ b/coderd/templates.go @@ -22,7 +22,6 @@ import ( "github.com/coder/coder/coderd/httpmw" "github.com/coder/coder/coderd/rbac" "github.com/coder/coder/coderd/telemetry" - "github.com/coder/coder/coderd/util/ptr" "github.com/coder/coder/codersdk" ) @@ -236,28 +235,22 @@ func (api *API) postTemplateByOrganization(rw http.ResponseWriter, r *http.Reque return } - minAutostartInterval := minAutostartIntervalDefault - if !ptr.NilOrZero(createTemplate.MinAutostartIntervalMillis) { - minAutostartInterval = time.Duration(*createTemplate.MinAutostartIntervalMillis) * time.Millisecond - } - var dbTemplate database.Template var template codersdk.Template err = api.Database.InTx(func(tx database.Store) error { now := database.Now() dbTemplate, err = tx.InsertTemplate(ctx, database.InsertTemplateParams{ - ID: uuid.New(), - CreatedAt: now, - UpdatedAt: now, - OrganizationID: organization.ID, - Name: createTemplate.Name, - Provisioner: importJob.Provisioner, - ActiveVersionID: templateVersion.ID, - Description: createTemplate.Description, - MaxTtl: int64(maxTTL), - MinAutostartInterval: int64(minAutostartInterval), - CreatedBy: apiKey.UserID, - UserACL: database.TemplateACL{}, + ID: uuid.New(), + CreatedAt: now, + UpdatedAt: now, + OrganizationID: organization.ID, + Name: createTemplate.Name, + Provisioner: importJob.Provisioner, + ActiveVersionID: templateVersion.ID, + Description: createTemplate.Description, + MaxTtl: int64(maxTTL), + CreatedBy: apiKey.UserID, + UserACL: database.TemplateACL{}, GroupACL: database.TemplateACL{ organization.ID.String(): []rbac.Action{rbac.ActionRead}, }, @@ -467,9 +460,6 @@ func (api *API) patchTemplateMeta(rw http.ResponseWriter, r *http.Request) { if req.MaxTTLMillis < 0 { validErrs = append(validErrs, codersdk.ValidationError{Field: "max_ttl_ms", Detail: "Must be a positive integer."}) } - if req.MinAutostartIntervalMillis < 0 { - validErrs = append(validErrs, codersdk.ValidationError{Field: "min_autostart_interval_ms", Detail: "Must be a positive integer."}) - } if req.MaxTTLMillis > maxTTLDefault.Milliseconds() { validErrs = append(validErrs, codersdk.ValidationError{Field: "max_ttl_ms", Detail: "Cannot be greater than " + maxTTLDefault.String()}) } @@ -501,8 +491,7 @@ func (api *API) patchTemplateMeta(rw http.ResponseWriter, r *http.Request) { if req.Name == template.Name && req.Description == template.Description && req.Icon == template.Icon && - req.MaxTTLMillis == time.Duration(template.MaxTtl).Milliseconds() && - req.MinAutostartIntervalMillis == time.Duration(template.MinAutostartInterval).Milliseconds() { + req.MaxTTLMillis == time.Duration(template.MaxTtl).Milliseconds() { return nil } @@ -511,7 +500,6 @@ func (api *API) patchTemplateMeta(rw http.ResponseWriter, r *http.Request) { desc := req.Description icon := req.Icon maxTTL := time.Duration(req.MaxTTLMillis) * time.Millisecond - minAutostartInterval := time.Duration(req.MinAutostartIntervalMillis) * time.Millisecond if name == "" { name = template.Name @@ -519,18 +507,14 @@ func (api *API) patchTemplateMeta(rw http.ResponseWriter, r *http.Request) { if desc == "" { desc = template.Description } - if minAutostartInterval == 0 { - minAutostartInterval = time.Duration(template.MinAutostartInterval) - } updated, err = tx.UpdateTemplateMetaByID(ctx, database.UpdateTemplateMetaByIDParams{ - ID: template.ID, - UpdatedAt: database.Now(), - Name: name, - Description: desc, - Icon: icon, - MaxTtl: int64(maxTTL), - MinAutostartInterval: int64(minAutostartInterval), + ID: template.ID, + UpdatedAt: database.Now(), + Name: name, + Description: desc, + Icon: icon, + MaxTtl: int64(maxTTL), }) if err != nil { return err @@ -666,18 +650,17 @@ func (api *API) autoImportTemplate(ctx context.Context, opts autoImportTemplateO // Create template template, err = tx.InsertTemplate(ctx, database.InsertTemplateParams{ - ID: uuid.New(), - CreatedAt: now, - UpdatedAt: now, - OrganizationID: opts.orgID, - Name: opts.name, - Provisioner: job.Provisioner, - ActiveVersionID: templateVersion.ID, - Description: "This template was auto-imported by Coder.", - MaxTtl: int64(maxTTLDefault), - MinAutostartInterval: int64(minAutostartIntervalDefault), - CreatedBy: opts.userID, - UserACL: database.TemplateACL{}, + ID: uuid.New(), + CreatedAt: now, + UpdatedAt: now, + OrganizationID: opts.orgID, + Name: opts.name, + Provisioner: job.Provisioner, + ActiveVersionID: templateVersion.ID, + Description: "This template was auto-imported by Coder.", + MaxTtl: int64(maxTTLDefault), + CreatedBy: opts.userID, + UserACL: database.TemplateACL{}, GroupACL: database.TemplateACL{ opts.orgID.String(): []rbac.Action{rbac.ActionRead}, }, diff --git a/coderd/templates_test.go b/coderd/templates_test.go index bb602d0b05406..b72e98515435b 100644 --- a/coderd/templates_test.go +++ b/coderd/templates_test.go @@ -310,11 +310,10 @@ func TestPatchTemplateMeta(t *testing.T) { ctr.MinAutostartIntervalMillis = ptr.Ref(time.Hour.Milliseconds()) }) req := codersdk.UpdateTemplateMeta{ - Name: "new-template-name", - Description: "lorem ipsum dolor sit amet et cetera", - Icon: "/icons/new-icon.png", - MaxTTLMillis: 12 * time.Hour.Milliseconds(), - MinAutostartIntervalMillis: time.Minute.Milliseconds(), + Name: "new-template-name", + Description: "lorem ipsum dolor sit amet et cetera", + Icon: "/icons/new-icon.png", + MaxTTLMillis: 12 * time.Hour.Milliseconds(), } // It is unfortunate we need to sleep, but the test can fail if the // updatedAt is too close together. @@ -330,7 +329,6 @@ func TestPatchTemplateMeta(t *testing.T) { assert.Equal(t, req.Description, updated.Description) assert.Equal(t, req.Icon, updated.Icon) assert.Equal(t, req.MaxTTLMillis, updated.MaxTTLMillis) - assert.Equal(t, req.MinAutostartIntervalMillis, updated.MinAutostartIntervalMillis) // Extra paranoid: did it _really_ happen? updated, err = client.Template(ctx, template.ID) @@ -340,7 +338,6 @@ func TestPatchTemplateMeta(t *testing.T) { assert.Equal(t, req.Description, updated.Description) assert.Equal(t, req.Icon, updated.Icon) assert.Equal(t, req.MaxTTLMillis, updated.MaxTTLMillis) - assert.Equal(t, req.MinAutostartIntervalMillis, updated.MinAutostartIntervalMillis) require.Len(t, auditor.AuditLogs, 4) assert.Equal(t, database.AuditActionWrite, auditor.AuditLogs[3].Action) @@ -444,11 +441,10 @@ func TestPatchTemplateMeta(t *testing.T) { defer cancel() req := codersdk.UpdateTemplateMeta{ - Name: template.Name, - Description: template.Description, - Icon: template.Icon, - MaxTTLMillis: template.MaxTTLMillis, - MinAutostartIntervalMillis: template.MinAutostartIntervalMillis, + Name: template.Name, + Description: template.Description, + Icon: template.Icon, + MaxTTLMillis: template.MaxTTLMillis, } _, err := client.UpdateTemplateMeta(ctx, template.ID, req) require.ErrorContains(t, err, "not modified") @@ -478,8 +474,7 @@ func TestPatchTemplateMeta(t *testing.T) { defer cancel() req := codersdk.UpdateTemplateMeta{ - MaxTTLMillis: -int64(time.Hour), - MinAutostartIntervalMillis: -int64(time.Hour), + MaxTTLMillis: -int64(time.Hour), } _, err := client.UpdateTemplateMeta(ctx, template.ID, req) var apiErr *codersdk.Error @@ -487,7 +482,6 @@ func TestPatchTemplateMeta(t *testing.T) { require.Contains(t, apiErr.Message, "Invalid request") require.Len(t, apiErr.Validations, 2) assert.Equal(t, apiErr.Validations[0].Field, "max_ttl_ms") - assert.Equal(t, apiErr.Validations[1].Field, "min_autostart_interval_ms") updated, err := client.Template(ctx, template.ID) require.NoError(t, err) diff --git a/codersdk/templates.go b/codersdk/templates.go index 22e707050aed3..6a723a665b3ab 100644 --- a/codersdk/templates.go +++ b/codersdk/templates.go @@ -72,11 +72,10 @@ type UpdateTemplateACL struct { } type UpdateTemplateMeta struct { - Name string `json:"name,omitempty" validate:"omitempty,username"` - Description string `json:"description,omitempty"` - Icon string `json:"icon,omitempty"` - MaxTTLMillis int64 `json:"max_ttl_ms,omitempty"` - MinAutostartIntervalMillis int64 `json:"min_autostart_interval_ms,omitempty"` + Name string `json:"name,omitempty" validate:"omitempty,username"` + Description string `json:"description,omitempty"` + Icon string `json:"icon,omitempty"` + MaxTTLMillis int64 `json:"max_ttl_ms,omitempty"` } // Template returns a single template. diff --git a/site/src/api/typesGenerated.ts b/site/src/api/typesGenerated.ts index b38ed2ea6c040..59796976b3ab1 100644 --- a/site/src/api/typesGenerated.ts +++ b/site/src/api/typesGenerated.ts @@ -701,7 +701,6 @@ export interface UpdateTemplateMeta { readonly description?: string readonly icon?: string readonly max_ttl_ms?: number - readonly min_autostart_interval_ms?: number } // From codersdk/users.go From 04fd355738d79baa3267f3de03ee86548a6a2e99 Mon Sep 17 00:00:00 2001 From: Garrett Date: Tue, 1 Nov 2022 19:48:11 +0000 Subject: [PATCH 02/18] drop column from db --- coderd/database/migrations/000069_remove_min_autostart.down.sql | 2 ++ coderd/database/migrations/000069_remove_min_autostart.up.sql | 2 ++ 2 files changed, 4 insertions(+) create mode 100644 coderd/database/migrations/000069_remove_min_autostart.down.sql create mode 100644 coderd/database/migrations/000069_remove_min_autostart.up.sql diff --git a/coderd/database/migrations/000069_remove_min_autostart.down.sql b/coderd/database/migrations/000069_remove_min_autostart.down.sql new file mode 100644 index 0000000000000..8f7f7743a4a9f --- /dev/null +++ b/coderd/database/migrations/000069_remove_min_autostart.down.sql @@ -0,0 +1,2 @@ +-- add "slug" min_autostart_interval to "templates" table +ALTER TABLE "templates" ADD COLUMN "min_autostart_interval" int DEFAULT 0; diff --git a/coderd/database/migrations/000069_remove_min_autostart.up.sql b/coderd/database/migrations/000069_remove_min_autostart.up.sql new file mode 100644 index 0000000000000..cf2c8a7981a2c --- /dev/null +++ b/coderd/database/migrations/000069_remove_min_autostart.up.sql @@ -0,0 +1,2 @@ +-- drop "min_autostart_interval" column from "templates" table +ALTER TABLE "templates" DROP COLUMN "min_autostart_interval"; From 21a5c8e21fc83e8d61872bb8fa305b04d8a51fef Mon Sep 17 00:00:00 2001 From: Garrett Date: Tue, 1 Nov 2022 19:48:53 +0000 Subject: [PATCH 03/18] lint --- coderd/templates.go | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/coderd/templates.go b/coderd/templates.go index d342c990e8be7..29fee72e9bdb7 100644 --- a/coderd/templates.go +++ b/coderd/templates.go @@ -26,8 +26,7 @@ import ( ) var ( - maxTTLDefault = 24 * 7 * time.Hour - minAutostartIntervalDefault = time.Hour + maxTTLDefault = 24 * 7 * time.Hour ) // Auto-importable templates. These can be auto-imported after the first user From 71875fd308f8a5d8e01b5a0870662e114b573ca9 Mon Sep 17 00:00:00 2001 From: Garrett Date: Tue, 1 Nov 2022 19:50:58 +0000 Subject: [PATCH 04/18] go tests --- coderd/templates_test.go | 2 +- coderd/workspaces_test.go | 26 -------------------------- 2 files changed, 1 insertion(+), 27 deletions(-) diff --git a/coderd/templates_test.go b/coderd/templates_test.go index b72e98515435b..9a6e18f28cdb7 100644 --- a/coderd/templates_test.go +++ b/coderd/templates_test.go @@ -480,7 +480,7 @@ func TestPatchTemplateMeta(t *testing.T) { var apiErr *codersdk.Error require.ErrorAs(t, err, &apiErr) require.Contains(t, apiErr.Message, "Invalid request") - require.Len(t, apiErr.Validations, 2) + require.Len(t, apiErr.Validations, 1) assert.Equal(t, apiErr.Validations[0].Field, "max_ttl_ms") updated, err := client.Template(ctx, template.ID) diff --git a/coderd/workspaces_test.go b/coderd/workspaces_test.go index ca92537da0e27..36ac0565600e8 100644 --- a/coderd/workspaces_test.go +++ b/coderd/workspaces_test.go @@ -325,32 +325,6 @@ func TestPostWorkspacesByOrganization(t *testing.T) { require.Equal(t, "time until shutdown must be less than 7 days", apiErr.Validations[0].Detail) }) }) - - t.Run("InvalidAutostart", func(t *testing.T) { - t.Parallel() - client := coderdtest.New(t, &coderdtest.Options{IncludeProvisionerDaemon: true}) - user := coderdtest.CreateFirstUser(t, client) - version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, nil) - template := coderdtest.CreateTemplate(t, client, user.OrganizationID, version.ID) - coderdtest.AwaitTemplateVersionJob(t, client, version.ID) - - ctx, cancel := context.WithTimeout(context.Background(), testutil.WaitLong) - defer cancel() - - req := codersdk.CreateWorkspaceRequest{ - TemplateID: template.ID, - Name: "testing", - AutostartSchedule: ptr.Ref("CRON_TZ=US/Central * * * * *"), - } - _, err := client.CreateWorkspace(ctx, template.OrganizationID, codersdk.Me, req) - require.Error(t, err) - var apiErr *codersdk.Error - require.ErrorAs(t, err, &apiErr) - require.Equal(t, http.StatusBadRequest, apiErr.StatusCode()) - require.Len(t, apiErr.Validations, 1) - require.Equal(t, apiErr.Validations[0].Field, "schedule") - require.Equal(t, apiErr.Validations[0].Detail, "Minimum autostart interval 1m0s below template minimum 1h0m0s") - }) } func TestWorkspaceByOwnerAndName(t *testing.T) { From 6fdd314db69006c4bde31e43e76afb4aaaabad8c Mon Sep 17 00:00:00 2001 From: Garrett Date: Tue, 1 Nov 2022 19:55:37 +0000 Subject: [PATCH 05/18] make gen --- coderd/database/dump.sql | 1 - coderd/database/models.go | 29 +++++++++++----------- coderd/database/queries.sql.go | 21 ++++++---------- coderd/templates.go | 31 ++++++++++++----------- coderd/workspaces.go | 21 +++------------- enterprise/audit/diff_internal_test.go | 34 ++++++++++++-------------- 6 files changed, 56 insertions(+), 81 deletions(-) diff --git a/coderd/database/dump.sql b/coderd/database/dump.sql index a32ea8af471f1..e097fcc306da8 100644 --- a/coderd/database/dump.sql +++ b/coderd/database/dump.sql @@ -350,7 +350,6 @@ CREATE TABLE templates ( active_version_id uuid NOT NULL, description character varying(128) DEFAULT ''::character varying NOT NULL, max_ttl bigint DEFAULT '604800000000000'::bigint NOT NULL, - min_autostart_interval bigint DEFAULT '3600000000000'::bigint NOT NULL, created_by uuid NOT NULL, icon character varying(256) DEFAULT ''::character varying NOT NULL, user_acl jsonb DEFAULT '{}'::jsonb NOT NULL, diff --git a/coderd/database/models.go b/coderd/database/models.go index b3da222afa827..b5d911be8c423 100644 --- a/coderd/database/models.go +++ b/coderd/database/models.go @@ -574,21 +574,20 @@ type SiteConfig struct { } type Template struct { - ID uuid.UUID `db:"id" json:"id"` - CreatedAt time.Time `db:"created_at" json:"created_at"` - UpdatedAt time.Time `db:"updated_at" json:"updated_at"` - OrganizationID uuid.UUID `db:"organization_id" json:"organization_id"` - Deleted bool `db:"deleted" json:"deleted"` - Name string `db:"name" json:"name"` - Provisioner ProvisionerType `db:"provisioner" json:"provisioner"` - ActiveVersionID uuid.UUID `db:"active_version_id" json:"active_version_id"` - Description string `db:"description" json:"description"` - MaxTtl int64 `db:"max_ttl" json:"max_ttl"` - MinAutostartInterval int64 `db:"min_autostart_interval" json:"min_autostart_interval"` - CreatedBy uuid.UUID `db:"created_by" json:"created_by"` - Icon string `db:"icon" json:"icon"` - UserACL TemplateACL `db:"user_acl" json:"user_acl"` - GroupACL TemplateACL `db:"group_acl" json:"group_acl"` + ID uuid.UUID `db:"id" json:"id"` + CreatedAt time.Time `db:"created_at" json:"created_at"` + UpdatedAt time.Time `db:"updated_at" json:"updated_at"` + OrganizationID uuid.UUID `db:"organization_id" json:"organization_id"` + Deleted bool `db:"deleted" json:"deleted"` + Name string `db:"name" json:"name"` + Provisioner ProvisionerType `db:"provisioner" json:"provisioner"` + ActiveVersionID uuid.UUID `db:"active_version_id" json:"active_version_id"` + Description string `db:"description" json:"description"` + MaxTtl int64 `db:"max_ttl" json:"max_ttl"` + CreatedBy uuid.UUID `db:"created_by" json:"created_by"` + Icon string `db:"icon" json:"icon"` + UserACL TemplateACL `db:"user_acl" json:"user_acl"` + GroupACL TemplateACL `db:"group_acl" json:"group_acl"` } type TemplateVersion struct { diff --git a/coderd/database/queries.sql.go b/coderd/database/queries.sql.go index 6f89f8fc22767..0924d5b20fc76 100644 --- a/coderd/database/queries.sql.go +++ b/coderd/database/queries.sql.go @@ -3019,7 +3019,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, max_ttl, min_autostart_interval, created_by, icon, user_acl, group_acl + id, created_at, updated_at, organization_id, deleted, name, provisioner, active_version_id, description, max_ttl, created_by, icon, user_acl, group_acl FROM templates WHERE @@ -3042,7 +3042,6 @@ func (q *sqlQuerier) GetTemplateByID(ctx context.Context, id uuid.UUID) (Templat &i.ActiveVersionID, &i.Description, &i.MaxTtl, - &i.MinAutostartInterval, &i.CreatedBy, &i.Icon, &i.UserACL, @@ -3053,7 +3052,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, max_ttl, min_autostart_interval, created_by, icon, user_acl, group_acl + id, created_at, updated_at, organization_id, deleted, name, provisioner, active_version_id, description, max_ttl, created_by, icon, user_acl, group_acl FROM templates WHERE @@ -3084,7 +3083,6 @@ func (q *sqlQuerier) GetTemplateByOrganizationAndName(ctx context.Context, arg G &i.ActiveVersionID, &i.Description, &i.MaxTtl, - &i.MinAutostartInterval, &i.CreatedBy, &i.Icon, &i.UserACL, @@ -3094,7 +3092,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, max_ttl, min_autostart_interval, created_by, icon, user_acl, group_acl FROM templates +SELECT id, created_at, updated_at, organization_id, deleted, name, provisioner, active_version_id, description, max_ttl, created_by, icon, user_acl, group_acl FROM templates ORDER BY (name, id) ASC ` @@ -3118,7 +3116,6 @@ func (q *sqlQuerier) GetTemplates(ctx context.Context) ([]Template, error) { &i.ActiveVersionID, &i.Description, &i.MaxTtl, - &i.MinAutostartInterval, &i.CreatedBy, &i.Icon, &i.UserACL, @@ -3139,7 +3136,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, max_ttl, min_autostart_interval, created_by, icon, user_acl, group_acl + id, created_at, updated_at, organization_id, deleted, name, provisioner, active_version_id, description, max_ttl, created_by, icon, user_acl, group_acl FROM templates WHERE @@ -3198,7 +3195,6 @@ func (q *sqlQuerier) GetTemplatesWithFilter(ctx context.Context, arg GetTemplate &i.ActiveVersionID, &i.Description, &i.MaxTtl, - &i.MinAutostartInterval, &i.CreatedBy, &i.Icon, &i.UserACL, @@ -3235,7 +3231,7 @@ INSERT INTO group_acl ) VALUES - ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13) RETURNING id, created_at, updated_at, organization_id, deleted, name, provisioner, active_version_id, description, max_ttl, min_autostart_interval, created_by, icon, user_acl, group_acl + ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13) RETURNING id, created_at, updated_at, organization_id, deleted, name, provisioner, active_version_id, description, max_ttl, created_by, icon, user_acl, group_acl ` type InsertTemplateParams struct { @@ -3282,7 +3278,6 @@ func (q *sqlQuerier) InsertTemplate(ctx context.Context, arg InsertTemplateParam &i.ActiveVersionID, &i.Description, &i.MaxTtl, - &i.MinAutostartInterval, &i.CreatedBy, &i.Icon, &i.UserACL, @@ -3300,7 +3295,7 @@ SET WHERE id = $3 RETURNING - id, created_at, updated_at, organization_id, deleted, name, provisioner, active_version_id, description, max_ttl, min_autostart_interval, created_by, icon, user_acl, group_acl + id, created_at, updated_at, organization_id, deleted, name, provisioner, active_version_id, description, max_ttl, created_by, icon, user_acl, group_acl ` type UpdateTemplateACLByIDParams struct { @@ -3323,7 +3318,6 @@ func (q *sqlQuerier) UpdateTemplateACLByID(ctx context.Context, arg UpdateTempla &i.ActiveVersionID, &i.Description, &i.MaxTtl, - &i.MinAutostartInterval, &i.CreatedBy, &i.Icon, &i.UserACL, @@ -3386,7 +3380,7 @@ SET WHERE id = $1 RETURNING - id, created_at, updated_at, organization_id, deleted, name, provisioner, active_version_id, description, max_ttl, min_autostart_interval, created_by, icon, user_acl, group_acl + id, created_at, updated_at, organization_id, deleted, name, provisioner, active_version_id, description, max_ttl, created_by, icon, user_acl, group_acl ` type UpdateTemplateMetaByIDParams struct { @@ -3419,7 +3413,6 @@ func (q *sqlQuerier) UpdateTemplateMetaByID(ctx context.Context, arg UpdateTempl &i.ActiveVersionID, &i.Description, &i.MaxTtl, - &i.MinAutostartInterval, &i.CreatedBy, &i.Icon, &i.UserACL, diff --git a/coderd/templates.go b/coderd/templates.go index 29fee72e9bdb7..9da7617f99bcf 100644 --- a/coderd/templates.go +++ b/coderd/templates.go @@ -750,21 +750,20 @@ 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, - Provisioner: codersdk.ProvisionerType(template.Provisioner), - ActiveVersionID: template.ActiveVersionID, - WorkspaceOwnerCount: workspaceOwnerCount, - ActiveUserCount: activeCount, - BuildTimeStats: buildTimeStats, - Description: template.Description, - Icon: template.Icon, - MaxTTLMillis: time.Duration(template.MaxTtl).Milliseconds(), - MinAutostartIntervalMillis: time.Duration(template.MinAutostartInterval).Milliseconds(), - CreatedByID: template.CreatedBy, - CreatedByName: createdByName, + ID: template.ID, + CreatedAt: template.CreatedAt, + UpdatedAt: template.UpdatedAt, + OrganizationID: template.OrganizationID, + Name: template.Name, + Provisioner: codersdk.ProvisionerType(template.Provisioner), + ActiveVersionID: template.ActiveVersionID, + WorkspaceOwnerCount: workspaceOwnerCount, + ActiveUserCount: activeCount, + BuildTimeStats: buildTimeStats, + Description: template.Description, + Icon: template.Icon, + MaxTTLMillis: time.Duration(template.MaxTtl).Milliseconds(), + CreatedByID: template.CreatedBy, + CreatedByName: createdByName, } } diff --git a/coderd/workspaces.go b/coderd/workspaces.go index 38d53107363dd..28d5a1124e701 100644 --- a/coderd/workspaces.go +++ b/coderd/workspaces.go @@ -333,7 +333,7 @@ func (api *API) postWorkspacesByOrganization(rw http.ResponseWriter, r *http.Req return } - dbAutostartSchedule, err := validWorkspaceSchedule(createWorkspace.AutostartSchedule, time.Duration(template.MinAutostartInterval)) + dbAutostartSchedule, err := validWorkspaceSchedule(createWorkspace.AutostartSchedule) if err != nil { httpapi.Write(ctx, rw, http.StatusBadRequest, codersdk.Response{ Message: "Invalid Autostart Schedule.", @@ -666,16 +666,7 @@ func (api *API) putWorkspaceAutostart(rw http.ResponseWriter, r *http.Request) { return } - template, err := api.Database.GetTemplateByID(ctx, workspace.TemplateID) - if err != nil { - api.Logger.Error(ctx, "fetch workspace template", slog.F("workspace_id", workspace.ID), slog.F("template_id", workspace.TemplateID), slog.Error(err)) - httpapi.Write(ctx, rw, http.StatusInternalServerError, codersdk.Response{ - Message: "Error fetching workspace template.", - }) - return - } - - dbSched, err := validWorkspaceSchedule(req.Schedule, time.Duration(template.MinAutostartInterval)) + dbSched, err := validWorkspaceSchedule(req.Schedule) if err != nil { httpapi.Write(ctx, rw, http.StatusBadRequest, codersdk.Response{ Message: "Invalid autostart schedule.", @@ -1149,20 +1140,16 @@ func validWorkspaceDeadline(startedAt, newDeadline time.Time, max time.Duration) return nil } -func validWorkspaceSchedule(s *string, min time.Duration) (sql.NullString, error) { +func validWorkspaceSchedule(s *string) (sql.NullString, error) { if ptr.NilOrEmpty(s) { return sql.NullString{}, nil } - sched, err := schedule.Weekly(*s) + _, err := schedule.Weekly(*s) if err != nil { return sql.NullString{}, err } - if schedMin := sched.Min(); schedMin < min { - return sql.NullString{}, xerrors.Errorf("Minimum autostart interval %s below template minimum %s", schedMin, min) - } - return sql.NullString{ Valid: true, String: *s, diff --git a/enterprise/audit/diff_internal_test.go b/enterprise/audit/diff_internal_test.go index d5ee1f0e369c5..ea4bab2fff7c0 100644 --- a/enterprise/audit/diff_internal_test.go +++ b/enterprise/audit/diff_internal_test.go @@ -242,26 +242,24 @@ func Test_diff(t *testing.T) { name: "Create", left: audit.Empty[database.Template](), right: database.Template{ - ID: uuid.UUID{1}, - CreatedAt: time.Now(), - UpdatedAt: time.Now(), - OrganizationID: uuid.UUID{2}, - Deleted: false, - Name: "rust", - Provisioner: database.ProvisionerTypeTerraform, - ActiveVersionID: uuid.UUID{3}, - MaxTtl: int64(time.Hour), - MinAutostartInterval: int64(time.Minute), - CreatedBy: uuid.UUID{4}, + ID: uuid.UUID{1}, + CreatedAt: time.Now(), + UpdatedAt: time.Now(), + OrganizationID: uuid.UUID{2}, + Deleted: false, + Name: "rust", + Provisioner: database.ProvisionerTypeTerraform, + ActiveVersionID: uuid.UUID{3}, + MaxTtl: int64(time.Hour), + CreatedBy: uuid.UUID{4}, }, exp: audit.Map{ - "id": audit.OldNew{Old: "", New: uuid.UUID{1}.String()}, - "name": audit.OldNew{Old: "", New: "rust"}, - "provisioner": audit.OldNew{Old: database.ProvisionerType(""), New: database.ProvisionerTypeTerraform}, - "active_version_id": audit.OldNew{Old: "", New: uuid.UUID{3}.String()}, - "max_ttl": audit.OldNew{Old: int64(0), New: int64(time.Hour)}, - "min_autostart_interval": audit.OldNew{Old: int64(0), New: int64(time.Minute)}, - "created_by": audit.OldNew{Old: "", New: uuid.UUID{4}.String()}, + "id": audit.OldNew{Old: "", New: uuid.UUID{1}.String()}, + "name": audit.OldNew{Old: "", New: "rust"}, + "provisioner": audit.OldNew{Old: database.ProvisionerType(""), New: database.ProvisionerTypeTerraform}, + "active_version_id": audit.OldNew{Old: "", New: uuid.UUID{3}.String()}, + "max_ttl": audit.OldNew{Old: int64(0), New: int64(time.Hour)}, + "created_by": audit.OldNew{Old: "", New: uuid.UUID{4}.String()}, }, }, }) From 0f5e78916974a41960aa1f64c238a96f9bb7f3dc Mon Sep 17 00:00:00 2001 From: Garrett Date: Tue, 1 Nov 2022 20:12:52 +0000 Subject: [PATCH 06/18] change max-ttl to default --- cli/templatecreate.go | 11 ++- cli/templatecreate_test.go | 1 - cli/templateedit.go | 12 +-- cli/templateedit_test.go | 20 ++--- cli/templates.go | 2 +- coderd/database/databasefake/databasefake.go | 4 +- coderd/database/dump.sql | 2 +- .../000069_remove_min_autostart.down.sql | 3 + .../000069_remove_min_autostart.up.sql | 3 + coderd/database/models.go | 2 +- coderd/database/queries.sql.go | 40 +++++----- coderd/database/queries/templates.sql | 4 +- coderd/templates.go | 32 ++++---- coderd/templates_test.go | 73 +++++++++---------- coderd/workspaces.go | 6 +- coderd/workspaces_test.go | 28 +++---- codersdk/organizations.go | 9 +-- codersdk/templates.go | 10 +-- enterprise/audit/diff_internal_test.go | 2 +- 19 files changed, 128 insertions(+), 136 deletions(-) diff --git a/cli/templatecreate.go b/cli/templatecreate.go index 458cb1c2015b0..c53316a66edff 100644 --- a/cli/templatecreate.go +++ b/cli/templatecreate.go @@ -27,7 +27,7 @@ func templateCreate() *cobra.Command { directory string provisioner string parameterFile string - maxTTL time.Duration + defaultTTL time.Duration minAutostartInterval time.Duration ) cmd := &cobra.Command{ @@ -108,10 +108,9 @@ func templateCreate() *cobra.Command { } createReq := codersdk.CreateTemplateRequest{ - Name: templateName, - VersionID: job.ID, - MaxTTLMillis: ptr.Ref(maxTTL.Milliseconds()), - MinAutostartIntervalMillis: ptr.Ref(minAutostartInterval.Milliseconds()), + Name: templateName, + VersionID: job.ID, + DefaultTTLMillis: ptr.Ref(defaultTTL.Milliseconds()), } _, err = client.CreateTemplate(cmd.Context(), organization.ID, createReq) @@ -133,7 +132,7 @@ func templateCreate() *cobra.Command { cmd.Flags().StringVarP(&directory, "directory", "d", currentDirectory, "Specify the directory to create from") cmd.Flags().StringVarP(&provisioner, "test.provisioner", "", "terraform", "Customize the provisioner backend") cmd.Flags().StringVarP(¶meterFile, "parameter-file", "", "", "Specify a file path with parameter values.") - cmd.Flags().DurationVarP(&maxTTL, "max-ttl", "", 24*time.Hour, "Specify a maximum TTL for workspaces created from this template.") + cmd.Flags().DurationVarP(&defaultTTL, "default-ttl", "", 24*time.Hour, "Specify a default TTL for workspaces created from this template.") cmd.Flags().DurationVarP(&minAutostartInterval, "min-autostart-interval", "", time.Hour, "Specify a minimum autostart interval for workspaces created from this template.") // This is for testing! err := cmd.Flags().MarkHidden("test.provisioner") diff --git a/cli/templatecreate_test.go b/cli/templatecreate_test.go index ccffb45fc7114..cca46c29ee737 100644 --- a/cli/templatecreate_test.go +++ b/cli/templatecreate_test.go @@ -53,7 +53,6 @@ func TestTemplateCreate(t *testing.T) { "--directory", source, "--test.provisioner", string(database.ProvisionerTypeEcho), "--max-ttl", "24h", - "--min-autostart-interval", "2h", } cmd, root := clitest.New(t, args...) clitest.SetupConfig(t, client, root) diff --git a/cli/templateedit.go b/cli/templateedit.go index 09bdf5e069960..867cb41d208a7 100644 --- a/cli/templateedit.go +++ b/cli/templateedit.go @@ -16,7 +16,7 @@ func templateEdit() *cobra.Command { name string description string icon string - maxTTL time.Duration + defaultTTL time.Duration ) cmd := &cobra.Command{ @@ -39,10 +39,10 @@ func templateEdit() *cobra.Command { // NOTE: coderd will ignore empty fields. req := codersdk.UpdateTemplateMeta{ - Name: name, - Description: description, - Icon: icon, - MaxTTLMillis: maxTTL.Milliseconds(), + Name: name, + Description: description, + Icon: icon, + DefaultTTLMillis: defaultTTL.Milliseconds(), } _, err = client.UpdateTemplateMeta(cmd.Context(), template.ID, req) @@ -57,7 +57,7 @@ func templateEdit() *cobra.Command { cmd.Flags().StringVarP(&name, "name", "", "", "Edit the template name") cmd.Flags().StringVarP(&description, "description", "", "", "Edit the template description") cmd.Flags().StringVarP(&icon, "icon", "", "", "Edit the template icon path") - cmd.Flags().DurationVarP(&maxTTL, "max-ttl", "", 0, "Edit the template maximum time before shutdown - workspaces created from this template cannot stay running longer than this.") + cmd.Flags().DurationVarP(&defaultTTL, "default-ttl", "", 0, "Edit the template default time before shutdown - workspaces created from this template to this value.") cliui.AllowSkipPrompt(cmd) return cmd diff --git a/cli/templateedit_test.go b/cli/templateedit_test.go index 61437764021f6..3b66dd6d34b41 100644 --- a/cli/templateedit_test.go +++ b/cli/templateedit_test.go @@ -26,16 +26,14 @@ func TestTemplateEdit(t *testing.T) { template := coderdtest.CreateTemplate(t, client, user.OrganizationID, version.ID, func(ctr *codersdk.CreateTemplateRequest) { ctr.Description = "original description" ctr.Icon = "/icons/default-icon.png" - ctr.MaxTTLMillis = ptr.Ref(24 * time.Hour.Milliseconds()) - ctr.MinAutostartIntervalMillis = ptr.Ref(time.Hour.Milliseconds()) + ctr.DefaultTTLMillis = ptr.Ref(24 * time.Hour.Milliseconds()) }) // Test the cli command. name := "new-template-name" desc := "lorem ipsum dolor sit amet et cetera" icon := "/icons/new-icon.png" - maxTTL := 12 * time.Hour - minAutostartInterval := time.Minute + defaultTTL := 12 * time.Hour cmdArgs := []string{ "templates", "edit", @@ -43,8 +41,7 @@ func TestTemplateEdit(t *testing.T) { "--name", name, "--description", desc, "--icon", icon, - "--max-ttl", maxTTL.String(), - "--min-autostart-interval", minAutostartInterval.String(), + "--default-ttl", defaultTTL.String(), } cmd, root := clitest.New(t, cmdArgs...) clitest.SetupConfig(t, client, root) @@ -59,8 +56,7 @@ func TestTemplateEdit(t *testing.T) { assert.Equal(t, name, updated.Name) assert.Equal(t, desc, updated.Description) assert.Equal(t, icon, updated.Icon) - assert.Equal(t, maxTTL.Milliseconds(), updated.MaxTTLMillis) - assert.Equal(t, minAutostartInterval.Milliseconds(), updated.MinAutostartIntervalMillis) + assert.Equal(t, defaultTTL.Milliseconds(), updated.DefaultTTLMillis) }) t.Run("NotModified", func(t *testing.T) { @@ -72,8 +68,7 @@ func TestTemplateEdit(t *testing.T) { template := coderdtest.CreateTemplate(t, client, user.OrganizationID, version.ID, func(ctr *codersdk.CreateTemplateRequest) { ctr.Description = "original description" ctr.Icon = "/icons/default-icon.png" - ctr.MaxTTLMillis = ptr.Ref(24 * time.Hour.Milliseconds()) - ctr.MinAutostartIntervalMillis = ptr.Ref(time.Hour.Milliseconds()) + ctr.DefaultTTLMillis = ptr.Ref(24 * time.Hour.Milliseconds()) }) // Test the cli command. @@ -84,8 +79,7 @@ func TestTemplateEdit(t *testing.T) { "--name", template.Name, "--description", template.Description, "--icon", template.Icon, - "--max-ttl", (time.Duration(template.MaxTTLMillis) * time.Millisecond).String(), - "--min-autostart-interval", (time.Duration(template.MinAutostartIntervalMillis) * time.Millisecond).String(), + "--default-ttl", (time.Duration(template.DefaultTTLMillis) * time.Millisecond).String(), } cmd, root := clitest.New(t, cmdArgs...) clitest.SetupConfig(t, client, root) @@ -100,7 +94,7 @@ func TestTemplateEdit(t *testing.T) { assert.Equal(t, template.Name, updated.Name) assert.Equal(t, template.Description, updated.Description) assert.Equal(t, template.Icon, updated.Icon) - assert.Equal(t, template.MaxTTLMillis, updated.MaxTTLMillis) + assert.Equal(t, template.DefaultTTLMillis, updated.DefaultTTLMillis) assert.Equal(t, template.MinAutostartIntervalMillis, updated.MinAutostartIntervalMillis) }) } diff --git a/cli/templates.go b/cli/templates.go index 4c85606d70c6f..5d96805a153f8 100644 --- a/cli/templates.go +++ b/cli/templates.go @@ -75,7 +75,7 @@ func displayTemplates(filterColumns []string, templates ...codersdk.Template) (s Provisioner: template.Provisioner, ActiveVersionID: template.ActiveVersionID, UsedBy: cliui.Styles.Fuchsia.Render(formatActiveDevelopers(template.ActiveUserCount)), - MaxTTL: (time.Duration(template.MaxTTLMillis) * time.Millisecond), + MaxTTL: (time.Duration(template.DefaultTTLMillis) * time.Millisecond), MinAutostartInterval: (time.Duration(template.MinAutostartIntervalMillis) * time.Millisecond), } } diff --git a/coderd/database/databasefake/databasefake.go b/coderd/database/databasefake/databasefake.go index a2806b45120db..91525b5eb09af 100644 --- a/coderd/database/databasefake/databasefake.go +++ b/coderd/database/databasefake/databasefake.go @@ -1455,7 +1455,7 @@ func (q *fakeQuerier) UpdateTemplateMetaByID(_ context.Context, arg database.Upd tpl.Name = arg.Name tpl.Description = arg.Description tpl.Icon = arg.Icon - tpl.MaxTtl = arg.MaxTtl + tpl.DefaultTtl = arg.DefaultTtl q.templates[idx] = tpl return tpl, nil } @@ -2236,7 +2236,7 @@ func (q *fakeQuerier) InsertTemplate(_ context.Context, arg database.InsertTempl Provisioner: arg.Provisioner, ActiveVersionID: arg.ActiveVersionID, Description: arg.Description, - MaxTtl: arg.MaxTtl, + DefaultTtl: arg.DefaultTtl, CreatedBy: arg.CreatedBy, UserACL: arg.UserACL, GroupACL: arg.GroupACL, diff --git a/coderd/database/dump.sql b/coderd/database/dump.sql index e097fcc306da8..cb62c42fae208 100644 --- a/coderd/database/dump.sql +++ b/coderd/database/dump.sql @@ -349,7 +349,7 @@ CREATE TABLE templates ( provisioner provisioner_type NOT NULL, active_version_id uuid NOT NULL, description character varying(128) DEFAULT ''::character varying NOT NULL, - max_ttl bigint DEFAULT '604800000000000'::bigint NOT NULL, + default_ttl bigint DEFAULT '604800000000000'::bigint NOT NULL, created_by uuid NOT NULL, icon character varying(256) DEFAULT ''::character varying NOT NULL, user_acl jsonb DEFAULT '{}'::jsonb NOT NULL, diff --git a/coderd/database/migrations/000069_remove_min_autostart.down.sql b/coderd/database/migrations/000069_remove_min_autostart.down.sql index 8f7f7743a4a9f..0b7b72c9fc974 100644 --- a/coderd/database/migrations/000069_remove_min_autostart.down.sql +++ b/coderd/database/migrations/000069_remove_min_autostart.down.sql @@ -1,2 +1,5 @@ -- add "slug" min_autostart_interval to "templates" table ALTER TABLE "templates" ADD COLUMN "min_autostart_interval" int DEFAULT 0; + +-- rename "default_ttl" to "max_ttl" on "templates" table +ALTER TABLE "templates" RENAME COLUMN "default_ttl" TO "max_ttl"; diff --git a/coderd/database/migrations/000069_remove_min_autostart.up.sql b/coderd/database/migrations/000069_remove_min_autostart.up.sql index cf2c8a7981a2c..900e0afd59525 100644 --- a/coderd/database/migrations/000069_remove_min_autostart.up.sql +++ b/coderd/database/migrations/000069_remove_min_autostart.up.sql @@ -1,2 +1,5 @@ -- drop "min_autostart_interval" column from "templates" table ALTER TABLE "templates" DROP COLUMN "min_autostart_interval"; + +-- rename "max_ttl" to "default_ttl" on "templates" table +ALTER TABLE "templates" RENAME COLUMN "max_ttl" TO "default_ttl"; diff --git a/coderd/database/models.go b/coderd/database/models.go index b5d911be8c423..3d2b809b04d82 100644 --- a/coderd/database/models.go +++ b/coderd/database/models.go @@ -583,7 +583,7 @@ type Template struct { Provisioner ProvisionerType `db:"provisioner" json:"provisioner"` ActiveVersionID uuid.UUID `db:"active_version_id" json:"active_version_id"` Description string `db:"description" json:"description"` - MaxTtl int64 `db:"max_ttl" json:"max_ttl"` + DefaultTtl int64 `db:"default_ttl" json:"default_ttl"` CreatedBy uuid.UUID `db:"created_by" json:"created_by"` Icon string `db:"icon" json:"icon"` UserACL TemplateACL `db:"user_acl" json:"user_acl"` diff --git a/coderd/database/queries.sql.go b/coderd/database/queries.sql.go index 0924d5b20fc76..074bbb8a7eac3 100644 --- a/coderd/database/queries.sql.go +++ b/coderd/database/queries.sql.go @@ -3019,7 +3019,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, max_ttl, created_by, icon, user_acl, group_acl + id, created_at, updated_at, organization_id, deleted, name, provisioner, active_version_id, description, default_ttl, created_by, icon, user_acl, group_acl FROM templates WHERE @@ -3041,7 +3041,7 @@ func (q *sqlQuerier) GetTemplateByID(ctx context.Context, id uuid.UUID) (Templat &i.Provisioner, &i.ActiveVersionID, &i.Description, - &i.MaxTtl, + &i.DefaultTtl, &i.CreatedBy, &i.Icon, &i.UserACL, @@ -3052,7 +3052,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, max_ttl, created_by, icon, user_acl, group_acl + id, created_at, updated_at, organization_id, deleted, name, provisioner, active_version_id, description, default_ttl, created_by, icon, user_acl, group_acl FROM templates WHERE @@ -3082,7 +3082,7 @@ func (q *sqlQuerier) GetTemplateByOrganizationAndName(ctx context.Context, arg G &i.Provisioner, &i.ActiveVersionID, &i.Description, - &i.MaxTtl, + &i.DefaultTtl, &i.CreatedBy, &i.Icon, &i.UserACL, @@ -3092,7 +3092,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, max_ttl, created_by, icon, user_acl, group_acl FROM 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 FROM templates ORDER BY (name, id) ASC ` @@ -3115,7 +3115,7 @@ func (q *sqlQuerier) GetTemplates(ctx context.Context) ([]Template, error) { &i.Provisioner, &i.ActiveVersionID, &i.Description, - &i.MaxTtl, + &i.DefaultTtl, &i.CreatedBy, &i.Icon, &i.UserACL, @@ -3136,7 +3136,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, max_ttl, created_by, icon, user_acl, group_acl + id, created_at, updated_at, organization_id, deleted, name, provisioner, active_version_id, description, default_ttl, created_by, icon, user_acl, group_acl FROM templates WHERE @@ -3194,7 +3194,7 @@ func (q *sqlQuerier) GetTemplatesWithFilter(ctx context.Context, arg GetTemplate &i.Provisioner, &i.ActiveVersionID, &i.Description, - &i.MaxTtl, + &i.DefaultTtl, &i.CreatedBy, &i.Icon, &i.UserACL, @@ -3224,14 +3224,14 @@ INSERT INTO provisioner, active_version_id, description, - max_ttl, + default_ttl, created_by, icon, user_acl, group_acl ) VALUES - ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13) RETURNING id, created_at, updated_at, organization_id, deleted, name, provisioner, active_version_id, description, max_ttl, created_by, icon, user_acl, group_acl + ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13) RETURNING id, created_at, updated_at, organization_id, deleted, name, provisioner, active_version_id, description, default_ttl, created_by, icon, user_acl, group_acl ` type InsertTemplateParams struct { @@ -3243,7 +3243,7 @@ type InsertTemplateParams struct { Provisioner ProvisionerType `db:"provisioner" json:"provisioner"` ActiveVersionID uuid.UUID `db:"active_version_id" json:"active_version_id"` Description string `db:"description" json:"description"` - MaxTtl int64 `db:"max_ttl" json:"max_ttl"` + DefaultTtl int64 `db:"default_ttl" json:"default_ttl"` CreatedBy uuid.UUID `db:"created_by" json:"created_by"` Icon string `db:"icon" json:"icon"` UserACL TemplateACL `db:"user_acl" json:"user_acl"` @@ -3260,7 +3260,7 @@ func (q *sqlQuerier) InsertTemplate(ctx context.Context, arg InsertTemplateParam arg.Provisioner, arg.ActiveVersionID, arg.Description, - arg.MaxTtl, + arg.DefaultTtl, arg.CreatedBy, arg.Icon, arg.UserACL, @@ -3277,7 +3277,7 @@ func (q *sqlQuerier) InsertTemplate(ctx context.Context, arg InsertTemplateParam &i.Provisioner, &i.ActiveVersionID, &i.Description, - &i.MaxTtl, + &i.DefaultTtl, &i.CreatedBy, &i.Icon, &i.UserACL, @@ -3295,7 +3295,7 @@ SET WHERE id = $3 RETURNING - id, created_at, updated_at, organization_id, deleted, name, provisioner, active_version_id, description, max_ttl, created_by, icon, user_acl, group_acl + id, created_at, updated_at, organization_id, deleted, name, provisioner, active_version_id, description, default_ttl, created_by, icon, user_acl, group_acl ` type UpdateTemplateACLByIDParams struct { @@ -3317,7 +3317,7 @@ func (q *sqlQuerier) UpdateTemplateACLByID(ctx context.Context, arg UpdateTempla &i.Provisioner, &i.ActiveVersionID, &i.Description, - &i.MaxTtl, + &i.DefaultTtl, &i.CreatedBy, &i.Icon, &i.UserACL, @@ -3374,20 +3374,20 @@ UPDATE SET updated_at = $2, description = $3, - max_ttl = $4, + default_ttl = $4, name = $5, icon = $6 WHERE id = $1 RETURNING - id, created_at, updated_at, organization_id, deleted, name, provisioner, active_version_id, description, max_ttl, created_by, icon, user_acl, group_acl + id, created_at, updated_at, organization_id, deleted, name, provisioner, active_version_id, description, default_ttl, created_by, icon, user_acl, group_acl ` type UpdateTemplateMetaByIDParams struct { ID uuid.UUID `db:"id" json:"id"` UpdatedAt time.Time `db:"updated_at" json:"updated_at"` Description string `db:"description" json:"description"` - MaxTtl int64 `db:"max_ttl" json:"max_ttl"` + DefaultTtl int64 `db:"default_ttl" json:"default_ttl"` Name string `db:"name" json:"name"` Icon string `db:"icon" json:"icon"` } @@ -3397,7 +3397,7 @@ func (q *sqlQuerier) UpdateTemplateMetaByID(ctx context.Context, arg UpdateTempl arg.ID, arg.UpdatedAt, arg.Description, - arg.MaxTtl, + arg.DefaultTtl, arg.Name, arg.Icon, ) @@ -3412,7 +3412,7 @@ func (q *sqlQuerier) UpdateTemplateMetaByID(ctx context.Context, arg UpdateTempl &i.Provisioner, &i.ActiveVersionID, &i.Description, - &i.MaxTtl, + &i.DefaultTtl, &i.CreatedBy, &i.Icon, &i.UserACL, diff --git a/coderd/database/queries/templates.sql b/coderd/database/queries/templates.sql index 64df1cd25a440..7063b87075c6f 100644 --- a/coderd/database/queries/templates.sql +++ b/coderd/database/queries/templates.sql @@ -65,7 +65,7 @@ INSERT INTO provisioner, active_version_id, description, - max_ttl, + default_ttl, created_by, icon, user_acl, @@ -98,7 +98,7 @@ UPDATE SET updated_at = $2, description = $3, - max_ttl = $4, + default_ttl = $4, name = $5, icon = $6 WHERE diff --git a/coderd/templates.go b/coderd/templates.go index 9da7617f99bcf..1703884bf35f9 100644 --- a/coderd/templates.go +++ b/coderd/templates.go @@ -26,7 +26,7 @@ import ( ) var ( - maxTTLDefault = 24 * 7 * time.Hour + defaultTTL = 24 * 7 * time.Hour ) // Auto-importable templates. These can be auto-imported after the first user @@ -210,9 +210,9 @@ func (api *API) postTemplateByOrganization(rw http.ResponseWriter, r *http.Reque return } - maxTTL := maxTTLDefault - if createTemplate.MaxTTLMillis != nil { - maxTTL = time.Duration(*createTemplate.MaxTTLMillis) * time.Millisecond + maxTTL := defaultTTL + if createTemplate.DefaultTTLMillis != nil { + maxTTL = time.Duration(*createTemplate.DefaultTTLMillis) * time.Millisecond } if maxTTL < 0 { httpapi.Write(ctx, rw, http.StatusBadRequest, codersdk.Response{ @@ -224,11 +224,11 @@ func (api *API) postTemplateByOrganization(rw http.ResponseWriter, r *http.Reque return } - if maxTTL > maxTTLDefault { + if maxTTL > defaultTTL { httpapi.Write(ctx, rw, http.StatusBadRequest, codersdk.Response{ Message: "Invalid create template request.", Validations: []codersdk.ValidationError{ - {Field: "max_ttl_ms", Detail: "Cannot be greater than " + maxTTLDefault.String()}, + {Field: "max_ttl_ms", Detail: "Cannot be greater than " + defaultTTL.String()}, }, }) return @@ -247,7 +247,7 @@ func (api *API) postTemplateByOrganization(rw http.ResponseWriter, r *http.Reque Provisioner: importJob.Provisioner, ActiveVersionID: templateVersion.ID, Description: createTemplate.Description, - MaxTtl: int64(maxTTL), + DefaultTtl: int64(maxTTL), CreatedBy: apiKey.UserID, UserACL: database.TemplateACL{}, GroupACL: database.TemplateACL{ @@ -456,11 +456,11 @@ func (api *API) patchTemplateMeta(rw http.ResponseWriter, r *http.Request) { } var validErrs []codersdk.ValidationError - if req.MaxTTLMillis < 0 { - validErrs = append(validErrs, codersdk.ValidationError{Field: "max_ttl_ms", Detail: "Must be a positive integer."}) + if req.DefaultTTLMillis < 0 { + validErrs = append(validErrs, codersdk.ValidationError{Field: "default_ttl_ms", Detail: "Must be a positive integer."}) } - if req.MaxTTLMillis > maxTTLDefault.Milliseconds() { - validErrs = append(validErrs, codersdk.ValidationError{Field: "max_ttl_ms", Detail: "Cannot be greater than " + maxTTLDefault.String()}) + if req.DefaultTTLMillis > defaultTTL.Milliseconds() { + validErrs = append(validErrs, codersdk.ValidationError{Field: "default_ttl_ms", Detail: "Cannot be greater than " + defaultTTL.String()}) } if len(validErrs) > 0 { @@ -490,7 +490,7 @@ func (api *API) patchTemplateMeta(rw http.ResponseWriter, r *http.Request) { if req.Name == template.Name && req.Description == template.Description && req.Icon == template.Icon && - req.MaxTTLMillis == time.Duration(template.MaxTtl).Milliseconds() { + req.DefaultTTLMillis == time.Duration(template.DefaultTtl).Milliseconds() { return nil } @@ -498,7 +498,7 @@ func (api *API) patchTemplateMeta(rw http.ResponseWriter, r *http.Request) { name := req.Name desc := req.Description icon := req.Icon - maxTTL := time.Duration(req.MaxTTLMillis) * time.Millisecond + maxTTL := time.Duration(req.DefaultTTLMillis) * time.Millisecond if name == "" { name = template.Name @@ -513,7 +513,7 @@ func (api *API) patchTemplateMeta(rw http.ResponseWriter, r *http.Request) { Name: name, Description: desc, Icon: icon, - MaxTtl: int64(maxTTL), + DefaultTtl: int64(maxTTL), }) if err != nil { return err @@ -657,7 +657,7 @@ func (api *API) autoImportTemplate(ctx context.Context, opts autoImportTemplateO Provisioner: job.Provisioner, ActiveVersionID: templateVersion.ID, Description: "This template was auto-imported by Coder.", - MaxTtl: int64(maxTTLDefault), + DefaultTtl: int64(defaultTTL), CreatedBy: opts.userID, UserACL: database.TemplateACL{}, GroupACL: database.TemplateACL{ @@ -762,7 +762,7 @@ func (api *API) convertTemplate( BuildTimeStats: buildTimeStats, Description: template.Description, Icon: template.Icon, - MaxTTLMillis: time.Duration(template.MaxTtl).Milliseconds(), + DefaultTTLMillis: time.Duration(template.DefaultTtl).Milliseconds(), CreatedByID: template.CreatedBy, CreatedByName: createdByName, } diff --git a/coderd/templates_test.go b/coderd/templates_test.go index 9a6e18f28cdb7..d66cd4fb47b6e 100644 --- a/coderd/templates_test.go +++ b/coderd/templates_test.go @@ -131,9 +131,9 @@ func TestPostTemplateByOrganization(t *testing.T) { defer cancel() _, err := client.CreateTemplate(ctx, user.OrganizationID, codersdk.CreateTemplateRequest{ - Name: "testing", - VersionID: version.ID, - MaxTTLMillis: ptr.Ref(int64(-1)), + Name: "testing", + VersionID: version.ID, + DefaultTTLMillis: ptr.Ref(int64(-1)), }) var apiErr *codersdk.Error require.ErrorAs(t, err, &apiErr) @@ -151,9 +151,9 @@ func TestPostTemplateByOrganization(t *testing.T) { defer cancel() _, err := client.CreateTemplate(ctx, user.OrganizationID, codersdk.CreateTemplateRequest{ - Name: "testing", - VersionID: version.ID, - MaxTTLMillis: ptr.Ref(365 * 24 * time.Hour.Milliseconds()), + Name: "testing", + VersionID: version.ID, + DefaultTTLMillis: ptr.Ref(365 * 24 * time.Hour.Milliseconds()), }) var apiErr *codersdk.Error require.ErrorAs(t, err, &apiErr) @@ -171,12 +171,12 @@ func TestPostTemplateByOrganization(t *testing.T) { defer cancel() got, err := client.CreateTemplate(ctx, user.OrganizationID, codersdk.CreateTemplateRequest{ - Name: "testing", - VersionID: version.ID, - MaxTTLMillis: ptr.Ref(int64(0)), + Name: "testing", + VersionID: version.ID, + DefaultTTLMillis: ptr.Ref(int64(0)), }) require.NoError(t, err) - require.Zero(t, got.MaxTTLMillis) + require.Zero(t, got.DefaultTTLMillis) }) t.Run("Unauthorized", func(t *testing.T) { @@ -306,14 +306,13 @@ func TestPatchTemplateMeta(t *testing.T) { template := coderdtest.CreateTemplate(t, client, user.OrganizationID, version.ID, func(ctr *codersdk.CreateTemplateRequest) { ctr.Description = "original description" ctr.Icon = "/icons/original-icon.png" - ctr.MaxTTLMillis = ptr.Ref(24 * time.Hour.Milliseconds()) - ctr.MinAutostartIntervalMillis = ptr.Ref(time.Hour.Milliseconds()) + ctr.DefaultTTLMillis = ptr.Ref(24 * time.Hour.Milliseconds()) }) req := codersdk.UpdateTemplateMeta{ - Name: "new-template-name", - Description: "lorem ipsum dolor sit amet et cetera", - Icon: "/icons/new-icon.png", - MaxTTLMillis: 12 * time.Hour.Milliseconds(), + Name: "new-template-name", + Description: "lorem ipsum dolor sit amet et cetera", + Icon: "/icons/new-icon.png", + DefaultTTLMillis: 12 * time.Hour.Milliseconds(), } // It is unfortunate we need to sleep, but the test can fail if the // updatedAt is too close together. @@ -328,7 +327,7 @@ func TestPatchTemplateMeta(t *testing.T) { assert.Equal(t, req.Name, updated.Name) assert.Equal(t, req.Description, updated.Description) assert.Equal(t, req.Icon, updated.Icon) - assert.Equal(t, req.MaxTTLMillis, updated.MaxTTLMillis) + assert.Equal(t, req.DefaultTTLMillis, updated.DefaultTTLMillis) // Extra paranoid: did it _really_ happen? updated, err = client.Template(ctx, template.ID) @@ -337,7 +336,7 @@ func TestPatchTemplateMeta(t *testing.T) { assert.Equal(t, req.Name, updated.Name) assert.Equal(t, req.Description, updated.Description) assert.Equal(t, req.Icon, updated.Icon) - assert.Equal(t, req.MaxTTLMillis, updated.MaxTTLMillis) + assert.Equal(t, req.DefaultTTLMillis, updated.DefaultTTLMillis) require.Len(t, auditor.AuditLogs, 4) assert.Equal(t, database.AuditActionWrite, auditor.AuditLogs[3].Action) @@ -350,10 +349,10 @@ func TestPatchTemplateMeta(t *testing.T) { user := coderdtest.CreateFirstUser(t, client) version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, nil) template := coderdtest.CreateTemplate(t, client, user.OrganizationID, version.ID, func(ctr *codersdk.CreateTemplateRequest) { - ctr.MaxTTLMillis = ptr.Ref(24 * time.Hour.Milliseconds()) + ctr.DefaultTTLMillis = ptr.Ref(24 * time.Hour.Milliseconds()) }) req := codersdk.UpdateTemplateMeta{ - MaxTTLMillis: 0, + DefaultTTLMillis: 0, } // We're too fast! Sleep so we can be sure that updatedAt is greater @@ -369,7 +368,7 @@ func TestPatchTemplateMeta(t *testing.T) { updated, err := client.Template(ctx, template.ID) require.NoError(t, err) assert.Greater(t, updated.UpdatedAt, template.UpdatedAt) - assert.Equal(t, req.MaxTTLMillis, updated.MaxTTLMillis) + assert.Equal(t, req.DefaultTTLMillis, updated.DefaultTTLMillis) }) t.Run("MaxTTLTooLow", func(t *testing.T) { @@ -379,10 +378,10 @@ func TestPatchTemplateMeta(t *testing.T) { user := coderdtest.CreateFirstUser(t, client) version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, nil) template := coderdtest.CreateTemplate(t, client, user.OrganizationID, version.ID, func(ctr *codersdk.CreateTemplateRequest) { - ctr.MaxTTLMillis = ptr.Ref(24 * time.Hour.Milliseconds()) + ctr.DefaultTTLMillis = ptr.Ref(24 * time.Hour.Milliseconds()) }) req := codersdk.UpdateTemplateMeta{ - MaxTTLMillis: -1, + DefaultTTLMillis: -1, } ctx, cancel := context.WithTimeout(context.Background(), testutil.WaitLong) @@ -395,7 +394,7 @@ func TestPatchTemplateMeta(t *testing.T) { updated, err := client.Template(ctx, template.ID) require.NoError(t, err) assert.Equal(t, updated.UpdatedAt, template.UpdatedAt) - assert.Equal(t, updated.MaxTTLMillis, template.MaxTTLMillis) + assert.Equal(t, updated.DefaultTTLMillis, template.DefaultTTLMillis) }) t.Run("MaxTTLTooHigh", func(t *testing.T) { @@ -405,10 +404,10 @@ func TestPatchTemplateMeta(t *testing.T) { user := coderdtest.CreateFirstUser(t, client) version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, nil) template := coderdtest.CreateTemplate(t, client, user.OrganizationID, version.ID, func(ctr *codersdk.CreateTemplateRequest) { - ctr.MaxTTLMillis = ptr.Ref(24 * time.Hour.Milliseconds()) + ctr.DefaultTTLMillis = ptr.Ref(24 * time.Hour.Milliseconds()) }) req := codersdk.UpdateTemplateMeta{ - MaxTTLMillis: 365 * 24 * time.Hour.Milliseconds(), + DefaultTTLMillis: 365 * 24 * time.Hour.Milliseconds(), } ctx, cancel := context.WithTimeout(context.Background(), testutil.WaitLong) @@ -421,7 +420,7 @@ func TestPatchTemplateMeta(t *testing.T) { updated, err := client.Template(ctx, template.ID) require.NoError(t, err) assert.Equal(t, updated.UpdatedAt, template.UpdatedAt) - assert.Equal(t, updated.MaxTTLMillis, template.MaxTTLMillis) + assert.Equal(t, updated.DefaultTTLMillis, template.DefaultTTLMillis) }) t.Run("NotModified", func(t *testing.T) { @@ -433,18 +432,17 @@ func TestPatchTemplateMeta(t *testing.T) { template := coderdtest.CreateTemplate(t, client, user.OrganizationID, version.ID, func(ctr *codersdk.CreateTemplateRequest) { ctr.Description = "original description" ctr.Icon = "/icons/original-icon.png" - ctr.MaxTTLMillis = ptr.Ref(24 * time.Hour.Milliseconds()) - ctr.MinAutostartIntervalMillis = ptr.Ref(time.Hour.Milliseconds()) + ctr.DefaultTTLMillis = ptr.Ref(24 * time.Hour.Milliseconds()) }) ctx, cancel := context.WithTimeout(context.Background(), testutil.WaitLong) defer cancel() req := codersdk.UpdateTemplateMeta{ - Name: template.Name, - Description: template.Description, - Icon: template.Icon, - MaxTTLMillis: template.MaxTTLMillis, + Name: template.Name, + Description: template.Description, + Icon: template.Icon, + DefaultTTLMillis: template.DefaultTTLMillis, } _, err := client.UpdateTemplateMeta(ctx, template.ID, req) require.ErrorContains(t, err, "not modified") @@ -454,7 +452,7 @@ func TestPatchTemplateMeta(t *testing.T) { assert.Equal(t, template.Name, updated.Name) assert.Equal(t, template.Description, updated.Description) assert.Equal(t, template.Icon, updated.Icon) - assert.Equal(t, template.MaxTTLMillis, updated.MaxTTLMillis) + assert.Equal(t, template.DefaultTTLMillis, updated.DefaultTTLMillis) assert.Equal(t, template.MinAutostartIntervalMillis, updated.MinAutostartIntervalMillis) }) @@ -466,15 +464,14 @@ 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.Description = "original description" - ctr.MaxTTLMillis = ptr.Ref(24 * time.Hour.Milliseconds()) - ctr.MinAutostartIntervalMillis = ptr.Ref(time.Hour.Milliseconds()) + ctr.DefaultTTLMillis = ptr.Ref(24 * time.Hour.Milliseconds()) }) ctx, cancel := context.WithTimeout(context.Background(), testutil.WaitLong) defer cancel() req := codersdk.UpdateTemplateMeta{ - MaxTTLMillis: -int64(time.Hour), + DefaultTTLMillis: -int64(time.Hour), } _, err := client.UpdateTemplateMeta(ctx, template.ID, req) var apiErr *codersdk.Error @@ -489,7 +486,7 @@ func TestPatchTemplateMeta(t *testing.T) { assert.Equal(t, template.Name, updated.Name) assert.Equal(t, template.Description, updated.Description) assert.Equal(t, template.Icon, updated.Icon) - assert.Equal(t, template.MaxTTLMillis, updated.MaxTTLMillis) + assert.Equal(t, template.DefaultTTLMillis, updated.DefaultTTLMillis) assert.Equal(t, template.MinAutostartIntervalMillis, updated.MinAutostartIntervalMillis) }) diff --git a/coderd/workspaces.go b/coderd/workspaces.go index 28d5a1124e701..5fe0e1463c3fb 100644 --- a/coderd/workspaces.go +++ b/coderd/workspaces.go @@ -342,7 +342,7 @@ func (api *API) postWorkspacesByOrganization(rw http.ResponseWriter, r *http.Req return } - dbTTL, err := validWorkspaceTTLMillis(createWorkspace.TTLMillis, time.Duration(template.MaxTtl)) + dbTTL, err := validWorkspaceTTLMillis(createWorkspace.TTLMillis, time.Duration(template.DefaultTtl)) if err != nil { httpapi.Write(ctx, rw, http.StatusBadRequest, codersdk.Response{ Message: "Invalid Workspace Time to Shutdown.", @@ -730,7 +730,7 @@ func (api *API) putWorkspaceTTL(rw http.ResponseWriter, r *http.Request) { return xerrors.Errorf("fetch workspace template: %w", err) } - dbTTL, err = validWorkspaceTTLMillis(req.TTLMillis, time.Duration(template.MaxTtl)) + dbTTL, err = validWorkspaceTTLMillis(req.TTLMillis, time.Duration(template.DefaultTtl)) if err != nil { return codersdk.ValidationError{Field: "ttl_ms", Detail: err.Error()} } @@ -824,7 +824,7 @@ func (api *API) putExtendWorkspace(rw http.ResponseWriter, r *http.Request) { } newDeadline := req.Deadline.UTC() - if err := validWorkspaceDeadline(job.CompletedAt.Time, newDeadline, time.Duration(template.MaxTtl)); err != nil { + if err := validWorkspaceDeadline(job.CompletedAt.Time, newDeadline, time.Duration(template.DefaultTtl)); err != nil { // NOTE(Cian): Putting the error in the Message field on request from the FE folks. // Normally, we would put the validation error in Validations, but this endpoint is // not tied to a form or specific named user input on the FE. diff --git a/coderd/workspaces_test.go b/coderd/workspaces_test.go index 36ac0565600e8..b807037b96d05 100644 --- a/coderd/workspaces_test.go +++ b/coderd/workspaces_test.go @@ -239,10 +239,10 @@ func TestPostWorkspacesByOrganization(t *testing.T) { user := coderdtest.CreateFirstUser(t, client) version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, nil) template := coderdtest.CreateTemplate(t, client, user.OrganizationID, version.ID, func(ctr *codersdk.CreateTemplateRequest) { - ctr.MaxTTLMillis = ptr.Ref(int64(0)) + ctr.DefaultTTLMillis = ptr.Ref(int64(0)) }) // Given: the template has no max TTL set - require.Zero(t, template.MaxTTLMillis) + require.Zero(t, template.DefaultTTLMillis) coderdtest.AwaitTemplateVersionJob(t, client, version.ID) // When: we create a workspace with autostop not enabled @@ -260,15 +260,15 @@ func TestPostWorkspacesByOrganization(t *testing.T) { version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, nil) templateTTL := 24 * time.Hour.Milliseconds() template := coderdtest.CreateTemplate(t, client, user.OrganizationID, version.ID, func(ctr *codersdk.CreateTemplateRequest) { - ctr.MaxTTLMillis = ptr.Ref(templateTTL) + ctr.DefaultTTLMillis = ptr.Ref(templateTTL) }) coderdtest.AwaitTemplateVersionJob(t, client, version.ID) workspace := coderdtest.CreateWorkspace(t, client, user.OrganizationID, template.ID, func(cwr *codersdk.CreateWorkspaceRequest) { cwr.TTLMillis = nil // ensure that no default TTL is set }) // TTL should be set by the template - require.Equal(t, template.MaxTTLMillis, templateTTL) - require.Equal(t, template.MaxTTLMillis, template.MaxTTLMillis, workspace.TTLMillis) + require.Equal(t, template.DefaultTTLMillis, templateTTL) + require.Equal(t, template.DefaultTTLMillis, template.DefaultTTLMillis, workspace.TTLMillis) }) t.Run("InvalidTTL", func(t *testing.T) { @@ -313,7 +313,7 @@ func TestPostWorkspacesByOrganization(t *testing.T) { req := codersdk.CreateWorkspaceRequest{ TemplateID: template.ID, Name: "testing", - TTLMillis: ptr.Ref(template.MaxTTLMillis + time.Minute.Milliseconds()), + TTLMillis: ptr.Ref(template.DefaultTTLMillis + time.Minute.Milliseconds()), } _, err := client.CreateWorkspace(ctx, template.OrganizationID, codersdk.Me, req) require.Error(t, err) @@ -1162,21 +1162,23 @@ func TestWorkspaceUpdateTTL(t *testing.T) { expectedError: "time until shutdown must be less than 7 days", }, { - name: "above template maximum ttl", - ttlMillis: ptr.Ref((12 * time.Hour).Milliseconds()), - expectedError: "ttl_ms: time until shutdown must be below template maximum 8h0m0s", - modifyTemplate: func(ctr *codersdk.CreateTemplateRequest) { ctr.MaxTTLMillis = ptr.Ref((8 * time.Hour).Milliseconds()) }, + name: "above template maximum ttl", + ttlMillis: ptr.Ref((12 * time.Hour).Milliseconds()), + expectedError: "ttl_ms: time until shutdown must be below template maximum 8h0m0s", + modifyTemplate: func(ctr *codersdk.CreateTemplateRequest) { + ctr.DefaultTTLMillis = ptr.Ref((8 * time.Hour).Milliseconds()) + }, }, { name: "no template maximum ttl", ttlMillis: ptr.Ref((7 * 24 * time.Hour).Milliseconds()), - modifyTemplate: func(ctr *codersdk.CreateTemplateRequest) { ctr.MaxTTLMillis = ptr.Ref(int64(0)) }, + modifyTemplate: func(ctr *codersdk.CreateTemplateRequest) { ctr.DefaultTTLMillis = ptr.Ref(int64(0)) }, }, { name: "above maximum ttl even with no template max", ttlMillis: ptr.Ref((365 * 24 * time.Hour).Milliseconds()), expectedError: "ttl_ms: time until shutdown must be less than 7 days", - modifyTemplate: func(ctr *codersdk.CreateTemplateRequest) { ctr.MaxTTLMillis = ptr.Ref(int64(0)) }, + modifyTemplate: func(ctr *codersdk.CreateTemplateRequest) { ctr.DefaultTTLMillis = ptr.Ref(int64(0)) }, }, } @@ -1297,7 +1299,7 @@ func TestWorkspaceExtend(t *testing.T) { require.ErrorContains(t, err, "unexpected status code 400: Cannot extend workspace: new deadline must be at least 30 minutes in the future", "setting a deadline less than 30 minutes in the future should fail") // And with a deadline greater than the template max_ttl should also fail - deadlineExceedsMaxTTL := time.Now().Add(time.Duration(template.MaxTTLMillis) * time.Millisecond).Add(time.Minute) + deadlineExceedsMaxTTL := time.Now().Add(time.Duration(template.DefaultTTLMillis) * time.Millisecond).Add(time.Minute) err = client.PutExtendWorkspace(ctx, workspace.ID, codersdk.PutExtendWorkspaceRequest{ Deadline: deadlineExceedsMaxTTL, }) diff --git a/codersdk/organizations.go b/codersdk/organizations.go index de5e42122ce28..1cb0ca5f975e2 100644 --- a/codersdk/organizations.go +++ b/codersdk/organizations.go @@ -66,14 +66,9 @@ type CreateTemplateRequest struct { VersionID uuid.UUID `json:"template_version_id" validate:"required"` ParameterValues []CreateParameterRequest `json:"parameter_values,omitempty"` - // MaxTTLMillis allows optionally specifying the maximum allowable TTL + // DefaultTTLMillis allows optionally specifying the default TTL // for all workspaces created from this template. - MaxTTLMillis *int64 `json:"max_ttl_ms,omitempty"` - - // MinAutostartIntervalMillis allows optionally specifying the minimum - // allowable duration between autostarts for all workspaces created from - // this template. - MinAutostartIntervalMillis *int64 `json:"min_autostart_interval_ms,omitempty"` + DefaultTTLMillis *int64 `json:"default_ttl_ms,omitempty"` } // CreateWorkspaceRequest provides options for creating a new workspace. diff --git a/codersdk/templates.go b/codersdk/templates.go index 6a723a665b3ab..567a807131eb8 100644 --- a/codersdk/templates.go +++ b/codersdk/templates.go @@ -27,7 +27,7 @@ type Template struct { BuildTimeStats TemplateBuildTimeStats `json:"build_time_stats"` Description string `json:"description"` Icon string `json:"icon"` - MaxTTLMillis int64 `json:"max_ttl_ms"` + DefaultTTLMillis int64 `json:"default_ttl_ms"` MinAutostartIntervalMillis int64 `json:"min_autostart_interval_ms"` CreatedByID uuid.UUID `json:"created_by_id"` CreatedByName string `json:"created_by_name"` @@ -72,10 +72,10 @@ type UpdateTemplateACL struct { } type UpdateTemplateMeta struct { - Name string `json:"name,omitempty" validate:"omitempty,username"` - Description string `json:"description,omitempty"` - Icon string `json:"icon,omitempty"` - MaxTTLMillis int64 `json:"max_ttl_ms,omitempty"` + Name string `json:"name,omitempty" validate:"omitempty,username"` + Description string `json:"description,omitempty"` + Icon string `json:"icon,omitempty"` + DefaultTTLMillis int64 `json:"default_ttl_ms,omitempty"` } // Template returns a single template. diff --git a/enterprise/audit/diff_internal_test.go b/enterprise/audit/diff_internal_test.go index ea4bab2fff7c0..e257c9606c342 100644 --- a/enterprise/audit/diff_internal_test.go +++ b/enterprise/audit/diff_internal_test.go @@ -250,7 +250,7 @@ func Test_diff(t *testing.T) { Name: "rust", Provisioner: database.ProvisionerTypeTerraform, ActiveVersionID: uuid.UUID{3}, - MaxTtl: int64(time.Hour), + DefaultTtl: int64(time.Hour), CreatedBy: uuid.UUID{4}, }, exp: audit.Map{ From f1039526fd78d30afed9397bafade6a26972e1d6 Mon Sep 17 00:00:00 2001 From: Garrett Date: Tue, 1 Nov 2022 20:13:36 +0000 Subject: [PATCH 07/18] make gen --- site/src/api/typesGenerated.ts | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/site/src/api/typesGenerated.ts b/site/src/api/typesGenerated.ts index 59796976b3ab1..b36e458d6443f 100644 --- a/site/src/api/typesGenerated.ts +++ b/site/src/api/typesGenerated.ts @@ -179,8 +179,7 @@ export interface CreateTemplateRequest { readonly icon?: string readonly template_version_id: string readonly parameter_values?: CreateParameterRequest[] - readonly max_ttl_ms?: number - readonly min_autostart_interval_ms?: number + readonly default_ttl_ms?: number } // From codersdk/templateversions.go @@ -620,7 +619,7 @@ export interface Template { readonly build_time_stats: TemplateBuildTimeStats readonly description: string readonly icon: string - readonly max_ttl_ms: number + readonly default_ttl_ms: number readonly min_autostart_interval_ms: number readonly created_by_id: string readonly created_by_name: string @@ -700,7 +699,7 @@ export interface UpdateTemplateMeta { readonly name?: string readonly description?: string readonly icon?: string - readonly max_ttl_ms?: number + readonly default_ttl_ms?: number } // From codersdk/users.go From 6291976472add0938e4d22257bde1dc993c4466a Mon Sep 17 00:00:00 2001 From: Garrett Date: Wed, 2 Nov 2022 15:35:28 +0000 Subject: [PATCH 08/18] more tests --- coderd/templates.go | 29 +++----------- coderd/templates_test.go | 52 ++------------------------ coderd/workspaces.go | 16 +------- coderd/workspaces_test.go | 34 ----------------- enterprise/audit/diff_internal_test.go | 2 +- enterprise/audit/table.go | 2 +- 6 files changed, 13 insertions(+), 122 deletions(-) diff --git a/coderd/templates.go b/coderd/templates.go index 1703884bf35f9..8becfd31c04df 100644 --- a/coderd/templates.go +++ b/coderd/templates.go @@ -25,10 +25,6 @@ import ( "github.com/coder/coder/codersdk" ) -var ( - defaultTTL = 24 * 7 * time.Hour -) - // Auto-importable templates. These can be auto-imported after the first user // has been created. type AutoImportTemplate string @@ -210,25 +206,15 @@ func (api *API) postTemplateByOrganization(rw http.ResponseWriter, r *http.Reque return } - maxTTL := defaultTTL + var ttl time.Duration if createTemplate.DefaultTTLMillis != nil { - maxTTL = time.Duration(*createTemplate.DefaultTTLMillis) * time.Millisecond + ttl = time.Duration(*createTemplate.DefaultTTLMillis) * time.Millisecond } - if maxTTL < 0 { + if ttl < 0 { httpapi.Write(ctx, rw, http.StatusBadRequest, codersdk.Response{ Message: "Invalid create template request.", Validations: []codersdk.ValidationError{ - {Field: "max_ttl_ms", Detail: "Must be a positive integer."}, - }, - }) - return - } - - if maxTTL > defaultTTL { - httpapi.Write(ctx, rw, http.StatusBadRequest, codersdk.Response{ - Message: "Invalid create template request.", - Validations: []codersdk.ValidationError{ - {Field: "max_ttl_ms", Detail: "Cannot be greater than " + defaultTTL.String()}, + {Field: "default_ttl_ms", Detail: "Must be a positive integer."}, }, }) return @@ -247,7 +233,7 @@ func (api *API) postTemplateByOrganization(rw http.ResponseWriter, r *http.Reque Provisioner: importJob.Provisioner, ActiveVersionID: templateVersion.ID, Description: createTemplate.Description, - DefaultTtl: int64(maxTTL), + DefaultTtl: int64(ttl), CreatedBy: apiKey.UserID, UserACL: database.TemplateACL{}, GroupACL: database.TemplateACL{ @@ -459,9 +445,6 @@ func (api *API) patchTemplateMeta(rw http.ResponseWriter, r *http.Request) { if req.DefaultTTLMillis < 0 { validErrs = append(validErrs, codersdk.ValidationError{Field: "default_ttl_ms", Detail: "Must be a positive integer."}) } - if req.DefaultTTLMillis > defaultTTL.Milliseconds() { - validErrs = append(validErrs, codersdk.ValidationError{Field: "default_ttl_ms", Detail: "Cannot be greater than " + defaultTTL.String()}) - } if len(validErrs) > 0 { httpapi.Write(ctx, rw, http.StatusBadRequest, codersdk.Response{ @@ -657,7 +640,7 @@ func (api *API) autoImportTemplate(ctx context.Context, opts autoImportTemplateO Provisioner: job.Provisioner, ActiveVersionID: templateVersion.ID, Description: "This template was auto-imported by Coder.", - DefaultTtl: int64(defaultTTL), + DefaultTtl: 0, CreatedBy: opts.userID, UserACL: database.TemplateACL{}, GroupACL: database.TemplateACL{ diff --git a/coderd/templates_test.go b/coderd/templates_test.go index d66cd4fb47b6e..110c6bcba445d 100644 --- a/coderd/templates_test.go +++ b/coderd/templates_test.go @@ -138,27 +138,7 @@ func TestPostTemplateByOrganization(t *testing.T) { var apiErr *codersdk.Error require.ErrorAs(t, err, &apiErr) require.Equal(t, http.StatusBadRequest, apiErr.StatusCode()) - require.Contains(t, err.Error(), "max_ttl_ms: Must be a positive integer") - }) - - t.Run("MaxTTLTooHigh", func(t *testing.T) { - t.Parallel() - client := coderdtest.New(t, nil) - user := coderdtest.CreateFirstUser(t, client) - version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, nil) - - ctx, cancel := context.WithTimeout(context.Background(), testutil.WaitLong) - defer cancel() - - _, err := client.CreateTemplate(ctx, user.OrganizationID, codersdk.CreateTemplateRequest{ - Name: "testing", - VersionID: version.ID, - DefaultTTLMillis: ptr.Ref(365 * 24 * time.Hour.Milliseconds()), - }) - var apiErr *codersdk.Error - require.ErrorAs(t, err, &apiErr) - require.Equal(t, http.StatusBadRequest, apiErr.StatusCode()) - require.Contains(t, err.Error(), "max_ttl_ms: Cannot be greater than") + require.Contains(t, err.Error(), "default_ttl_ms: Must be a positive integer") }) t.Run("NoMaxTTL", func(t *testing.T) { @@ -388,33 +368,7 @@ func TestPatchTemplateMeta(t *testing.T) { defer cancel() _, err := client.UpdateTemplateMeta(ctx, template.ID, req) - require.ErrorContains(t, err, "max_ttl_ms: Must be a positive integer") - - // Ensure no update occurred - updated, err := client.Template(ctx, template.ID) - require.NoError(t, err) - assert.Equal(t, updated.UpdatedAt, template.UpdatedAt) - assert.Equal(t, updated.DefaultTTLMillis, template.DefaultTTLMillis) - }) - - t.Run("MaxTTLTooHigh", func(t *testing.T) { - t.Parallel() - - client := coderdtest.New(t, nil) - user := coderdtest.CreateFirstUser(t, client) - version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, nil) - template := coderdtest.CreateTemplate(t, client, user.OrganizationID, version.ID, func(ctr *codersdk.CreateTemplateRequest) { - ctr.DefaultTTLMillis = ptr.Ref(24 * time.Hour.Milliseconds()) - }) - req := codersdk.UpdateTemplateMeta{ - DefaultTTLMillis: 365 * 24 * time.Hour.Milliseconds(), - } - - ctx, cancel := context.WithTimeout(context.Background(), testutil.WaitLong) - defer cancel() - - _, err := client.UpdateTemplateMeta(ctx, template.ID, req) - require.ErrorContains(t, err, "max_ttl_ms: Cannot be greater than") + require.ErrorContains(t, err, "default_ttl_ms: Must be a positive integer") // Ensure no update occurred updated, err := client.Template(ctx, template.ID) @@ -478,7 +432,7 @@ func TestPatchTemplateMeta(t *testing.T) { require.ErrorAs(t, err, &apiErr) require.Contains(t, apiErr.Message, "Invalid request") require.Len(t, apiErr.Validations, 1) - assert.Equal(t, apiErr.Validations[0].Field, "max_ttl_ms") + assert.Equal(t, apiErr.Validations[0].Field, "default_ttl_ms") updated, err := client.Template(ctx, template.ID) require.NoError(t, err) diff --git a/coderd/workspaces.go b/coderd/workspaces.go index 5fe0e1463c3fb..b8eabe809a6fb 100644 --- a/coderd/workspaces.go +++ b/coderd/workspaces.go @@ -784,13 +784,6 @@ func (api *API) putExtendWorkspace(rw http.ResponseWriter, r *http.Request) { resp := codersdk.Response{} err := api.Database.InTx(func(s database.Store) error { - template, err := s.GetTemplateByID(ctx, workspace.TemplateID) - if err != nil { - code = http.StatusInternalServerError - resp.Message = "Error fetching workspace template!" - return xerrors.Errorf("get workspace template: %w", err) - } - build, err := s.GetLatestWorkspaceBuildByWorkspaceID(ctx, workspace.ID) if err != nil { code = http.StatusInternalServerError @@ -824,7 +817,7 @@ func (api *API) putExtendWorkspace(rw http.ResponseWriter, r *http.Request) { } newDeadline := req.Deadline.UTC() - if err := validWorkspaceDeadline(job.CompletedAt.Time, newDeadline, time.Duration(template.DefaultTtl)); err != nil { + if err := validWorkspaceDeadline(job.CompletedAt.Time, newDeadline); err != nil { // NOTE(Cian): Putting the error in the Message field on request from the FE folks. // Normally, we would put the validation error in Validations, but this endpoint is // not tied to a form or specific named user input on the FE. @@ -1121,7 +1114,7 @@ func validWorkspaceTTLMillis(millis *int64, max time.Duration) (sql.NullInt64, e }, nil } -func validWorkspaceDeadline(startedAt, newDeadline time.Time, max time.Duration) error { +func validWorkspaceDeadline(startedAt, newDeadline time.Time) error { soon := time.Now().Add(29 * time.Minute) if newDeadline.Before(soon) { return errDeadlineTooSoon @@ -1132,11 +1125,6 @@ func validWorkspaceDeadline(startedAt, newDeadline time.Time, max time.Duration) return errDeadlineBeforeStart } - delta := newDeadline.Sub(startedAt) - if delta > max { - return errDeadlineOverTemplateMax - } - return nil } diff --git a/coderd/workspaces_test.go b/coderd/workspaces_test.go index b807037b96d05..d4c7e9b7aef76 100644 --- a/coderd/workspaces_test.go +++ b/coderd/workspaces_test.go @@ -298,32 +298,6 @@ func TestPostWorkspacesByOrganization(t *testing.T) { require.Equal(t, apiErr.Validations[0].Field, "ttl_ms") require.Equal(t, "time until shutdown must be at least one minute", apiErr.Validations[0].Detail) }) - - t.Run("AboveMax", func(t *testing.T) { - t.Parallel() - client := coderdtest.New(t, &coderdtest.Options{IncludeProvisionerDaemon: true}) - user := coderdtest.CreateFirstUser(t, client) - version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, nil) - template := coderdtest.CreateTemplate(t, client, user.OrganizationID, version.ID) - coderdtest.AwaitTemplateVersionJob(t, client, version.ID) - - ctx, cancel := context.WithTimeout(context.Background(), testutil.WaitLong) - defer cancel() - - req := codersdk.CreateWorkspaceRequest{ - TemplateID: template.ID, - Name: "testing", - TTLMillis: ptr.Ref(template.DefaultTTLMillis + time.Minute.Milliseconds()), - } - _, err := client.CreateWorkspace(ctx, template.OrganizationID, codersdk.Me, req) - require.Error(t, err) - var apiErr *codersdk.Error - require.ErrorAs(t, err, &apiErr) - require.Equal(t, http.StatusBadRequest, apiErr.StatusCode()) - require.Len(t, apiErr.Validations, 1) - require.Equal(t, apiErr.Validations[0].Field, "ttl_ms") - require.Equal(t, "time until shutdown must be less than 7 days", apiErr.Validations[0].Detail) - }) }) } @@ -1298,14 +1272,6 @@ func TestWorkspaceExtend(t *testing.T) { }) require.ErrorContains(t, err, "unexpected status code 400: Cannot extend workspace: new deadline must be at least 30 minutes in the future", "setting a deadline less than 30 minutes in the future should fail") - // And with a deadline greater than the template max_ttl should also fail - deadlineExceedsMaxTTL := time.Now().Add(time.Duration(template.DefaultTTLMillis) * time.Millisecond).Add(time.Minute) - err = client.PutExtendWorkspace(ctx, workspace.ID, codersdk.PutExtendWorkspaceRequest{ - Deadline: deadlineExceedsMaxTTL, - }) - - require.ErrorContains(t, err, "unexpected status code 400: Cannot extend workspace: new deadline is greater than template allows", "setting a deadline greater than that allowed by the template should fail") - // Updating with a deadline 30 minutes in the future should succeed deadlineJustSoonEnough := time.Now().Add(30 * time.Minute) err = client.PutExtendWorkspace(ctx, workspace.ID, codersdk.PutExtendWorkspaceRequest{ diff --git a/enterprise/audit/diff_internal_test.go b/enterprise/audit/diff_internal_test.go index e257c9606c342..226fc44b4131c 100644 --- a/enterprise/audit/diff_internal_test.go +++ b/enterprise/audit/diff_internal_test.go @@ -258,7 +258,7 @@ func Test_diff(t *testing.T) { "name": audit.OldNew{Old: "", New: "rust"}, "provisioner": audit.OldNew{Old: database.ProvisionerType(""), New: database.ProvisionerTypeTerraform}, "active_version_id": audit.OldNew{Old: "", New: uuid.UUID{3}.String()}, - "max_ttl": audit.OldNew{Old: int64(0), New: int64(time.Hour)}, + "default_ttl": audit.OldNew{Old: int64(0), New: int64(time.Hour)}, "created_by": audit.OldNew{Old: "", New: uuid.UUID{4}.String()}, }, }, diff --git a/enterprise/audit/table.go b/enterprise/audit/table.go index 2578ae7437844..72774ced4e142 100644 --- a/enterprise/audit/table.go +++ b/enterprise/audit/table.go @@ -58,7 +58,7 @@ var AuditableResources = auditMap(map[any]map[string]Action{ "active_version_id": ActionTrack, "description": ActionTrack, "icon": ActionTrack, - "max_ttl": ActionTrack, + "default_ttl": ActionTrack, "min_autostart_interval": ActionTrack, "created_by": ActionTrack, "is_private": ActionTrack, From e1ec10714a36908ea2d56b2bc43c414d9e3a9213 Mon Sep 17 00:00:00 2001 From: Garrett Date: Wed, 2 Nov 2022 15:41:24 +0000 Subject: [PATCH 09/18] drop field from sdk --- cli/templateedit_test.go | 1 - cli/templates.go | 34 ++++++++++++++++------------------ coderd/templates_test.go | 2 -- codersdk/templates.go | 15 +++++++-------- site/src/api/typesGenerated.ts | 1 - 5 files changed, 23 insertions(+), 30 deletions(-) diff --git a/cli/templateedit_test.go b/cli/templateedit_test.go index 3b66dd6d34b41..fbfc77d26fdf5 100644 --- a/cli/templateedit_test.go +++ b/cli/templateedit_test.go @@ -95,6 +95,5 @@ func TestTemplateEdit(t *testing.T) { assert.Equal(t, template.Description, updated.Description) assert.Equal(t, template.Icon, updated.Icon) assert.Equal(t, template.DefaultTTLMillis, updated.DefaultTTLMillis) - assert.Equal(t, template.MinAutostartIntervalMillis, updated.MinAutostartIntervalMillis) }) } diff --git a/cli/templates.go b/cli/templates.go index 5d96805a153f8..7f8aa7e861792 100644 --- a/cli/templates.go +++ b/cli/templates.go @@ -50,15 +50,14 @@ func templates() *cobra.Command { } type templateTableRow struct { - Name string `table:"name"` - CreatedAt string `table:"created at"` - LastUpdated string `table:"last updated"` - OrganizationID uuid.UUID `table:"organization id"` - Provisioner codersdk.ProvisionerType `table:"provisioner"` - ActiveVersionID uuid.UUID `table:"active version id"` - UsedBy string `table:"used by"` - MaxTTL time.Duration `table:"max ttl"` - MinAutostartInterval time.Duration `table:"min autostart"` + Name string `table:"name"` + CreatedAt string `table:"created at"` + LastUpdated string `table:"last updated"` + OrganizationID uuid.UUID `table:"organization id"` + Provisioner codersdk.ProvisionerType `table:"provisioner"` + ActiveVersionID uuid.UUID `table:"active version id"` + UsedBy string `table:"used by"` + DefaultTTL time.Duration `table:"default ttl"` } // displayTemplates will return a table displaying all templates passed in. @@ -68,15 +67,14 @@ func displayTemplates(filterColumns []string, templates ...codersdk.Template) (s rows := make([]templateTableRow, len(templates)) for i, template := range templates { rows[i] = templateTableRow{ - Name: template.Name, - CreatedAt: template.CreatedAt.Format("January 2, 2006"), - LastUpdated: template.UpdatedAt.Format("January 2, 2006"), - OrganizationID: template.OrganizationID, - Provisioner: template.Provisioner, - ActiveVersionID: template.ActiveVersionID, - UsedBy: cliui.Styles.Fuchsia.Render(formatActiveDevelopers(template.ActiveUserCount)), - MaxTTL: (time.Duration(template.DefaultTTLMillis) * time.Millisecond), - MinAutostartInterval: (time.Duration(template.MinAutostartIntervalMillis) * time.Millisecond), + Name: template.Name, + CreatedAt: template.CreatedAt.Format("January 2, 2006"), + LastUpdated: template.UpdatedAt.Format("January 2, 2006"), + OrganizationID: template.OrganizationID, + Provisioner: template.Provisioner, + ActiveVersionID: template.ActiveVersionID, + UsedBy: cliui.Styles.Fuchsia.Render(formatActiveDevelopers(template.ActiveUserCount)), + DefaultTTL: (time.Duration(template.DefaultTTLMillis) * time.Millisecond), } } diff --git a/coderd/templates_test.go b/coderd/templates_test.go index 110c6bcba445d..df047993b5061 100644 --- a/coderd/templates_test.go +++ b/coderd/templates_test.go @@ -407,7 +407,6 @@ func TestPatchTemplateMeta(t *testing.T) { assert.Equal(t, template.Description, updated.Description) assert.Equal(t, template.Icon, updated.Icon) assert.Equal(t, template.DefaultTTLMillis, updated.DefaultTTLMillis) - assert.Equal(t, template.MinAutostartIntervalMillis, updated.MinAutostartIntervalMillis) }) t.Run("Invalid", func(t *testing.T) { @@ -441,7 +440,6 @@ func TestPatchTemplateMeta(t *testing.T) { assert.Equal(t, template.Description, updated.Description) assert.Equal(t, template.Icon, updated.Icon) assert.Equal(t, template.DefaultTTLMillis, updated.DefaultTTLMillis) - assert.Equal(t, template.MinAutostartIntervalMillis, updated.MinAutostartIntervalMillis) }) t.Run("RemoveIcon", func(t *testing.T) { diff --git a/codersdk/templates.go b/codersdk/templates.go index 567a807131eb8..05e845240be8f 100644 --- a/codersdk/templates.go +++ b/codersdk/templates.go @@ -23,14 +23,13 @@ type Template struct { ActiveVersionID uuid.UUID `json:"active_version_id"` WorkspaceOwnerCount uint32 `json:"workspace_owner_count"` // ActiveUserCount is set to -1 when loading. - ActiveUserCount int `json:"active_user_count"` - BuildTimeStats TemplateBuildTimeStats `json:"build_time_stats"` - Description string `json:"description"` - Icon string `json:"icon"` - DefaultTTLMillis int64 `json:"default_ttl_ms"` - MinAutostartIntervalMillis int64 `json:"min_autostart_interval_ms"` - CreatedByID uuid.UUID `json:"created_by_id"` - CreatedByName string `json:"created_by_name"` + ActiveUserCount int `json:"active_user_count"` + BuildTimeStats TemplateBuildTimeStats `json:"build_time_stats"` + Description string `json:"description"` + Icon string `json:"icon"` + DefaultTTLMillis int64 `json:"default_ttl_ms"` + CreatedByID uuid.UUID `json:"created_by_id"` + CreatedByName string `json:"created_by_name"` } type TemplateBuildTimeStats struct { diff --git a/site/src/api/typesGenerated.ts b/site/src/api/typesGenerated.ts index b36e458d6443f..bb8d4fa38c16f 100644 --- a/site/src/api/typesGenerated.ts +++ b/site/src/api/typesGenerated.ts @@ -620,7 +620,6 @@ export interface Template { readonly description: string readonly icon: string readonly default_ttl_ms: number - readonly min_autostart_interval_ms: number readonly created_by_id: string readonly created_by_name: string } From b9c9d7bc9c818f758d3ef5d9ea67dccf95f88999 Mon Sep 17 00:00:00 2001 From: Garrett Date: Wed, 2 Nov 2022 15:50:34 +0000 Subject: [PATCH 10/18] fix cli test --- cli/templatecreate_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cli/templatecreate_test.go b/cli/templatecreate_test.go index cca46c29ee737..98af8158241d3 100644 --- a/cli/templatecreate_test.go +++ b/cli/templatecreate_test.go @@ -52,7 +52,7 @@ func TestTemplateCreate(t *testing.T) { "my-template", "--directory", source, "--test.provisioner", string(database.ProvisionerTypeEcho), - "--max-ttl", "24h", + "--default-ttl", "24h", } cmd, root := clitest.New(t, args...) clitest.SetupConfig(t, client, root) From a450ed87f4d2f7cc676cd726217830e9286afaab Mon Sep 17 00:00:00 2001 From: Garrett Date: Fri, 4 Nov 2022 18:58:21 +0000 Subject: [PATCH 11/18] fix migrations --- ...in_autostart.down.sql => 000070_remove_min_autostart.down.sql} | 0 ...ve_min_autostart.up.sql => 000070_remove_min_autostart.up.sql} | 0 2 files changed, 0 insertions(+), 0 deletions(-) rename coderd/database/migrations/{000069_remove_min_autostart.down.sql => 000070_remove_min_autostart.down.sql} (100%) rename coderd/database/migrations/{000069_remove_min_autostart.up.sql => 000070_remove_min_autostart.up.sql} (100%) diff --git a/coderd/database/migrations/000069_remove_min_autostart.down.sql b/coderd/database/migrations/000070_remove_min_autostart.down.sql similarity index 100% rename from coderd/database/migrations/000069_remove_min_autostart.down.sql rename to coderd/database/migrations/000070_remove_min_autostart.down.sql diff --git a/coderd/database/migrations/000069_remove_min_autostart.up.sql b/coderd/database/migrations/000070_remove_min_autostart.up.sql similarity index 100% rename from coderd/database/migrations/000069_remove_min_autostart.up.sql rename to coderd/database/migrations/000070_remove_min_autostart.up.sql From 7b54fc71274b97c31d2346d01ffc71c3dcaf5233 Mon Sep 17 00:00:00 2001 From: Garrett Date: Fri, 4 Nov 2022 19:01:09 +0000 Subject: [PATCH 12/18] lint --- coderd/workspaces.go | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/coderd/workspaces.go b/coderd/workspaces.go index b8eabe809a6fb..3c57cb462801d 100644 --- a/coderd/workspaces.go +++ b/coderd/workspaces.go @@ -37,11 +37,10 @@ var ( ttlMin = time.Minute //nolint:revive // min here means 'minimum' not 'minutes' ttlMax = 7 * 24 * time.Hour - errTTLMin = xerrors.New("time until shutdown must be at least one minute") - errTTLMax = xerrors.New("time until shutdown must be less than 7 days") - errDeadlineTooSoon = xerrors.New("new deadline must be at least 30 minutes in the future") - errDeadlineBeforeStart = xerrors.New("new deadline must be before workspace start time") - errDeadlineOverTemplateMax = xerrors.New("new deadline is greater than template allows") + errTTLMin = xerrors.New("time until shutdown must be at least one minute") + errTTLMax = xerrors.New("time until shutdown must be less than 7 days") + errDeadlineTooSoon = xerrors.New("new deadline must be at least 30 minutes in the future") + errDeadlineBeforeStart = xerrors.New("new deadline must be before workspace start time") ) func (api *API) workspace(rw http.ResponseWriter, r *http.Request) { From a1673649759a1e1b515013be78d4c11dbb51f460 Mon Sep 17 00:00:00 2001 From: Garrett Date: Fri, 4 Nov 2022 20:10:47 +0000 Subject: [PATCH 13/18] add tests for default values --- coderd/workspaces.go | 20 ++++++++------- coderd/workspaces_test.go | 51 ++++++++++++++++++++++++--------------- 2 files changed, 43 insertions(+), 28 deletions(-) diff --git a/coderd/workspaces.go b/coderd/workspaces.go index 3c57cb462801d..0002c65178cc6 100644 --- a/coderd/workspaces.go +++ b/coderd/workspaces.go @@ -341,7 +341,7 @@ func (api *API) postWorkspacesByOrganization(rw http.ResponseWriter, r *http.Req return } - dbTTL, err := validWorkspaceTTLMillis(createWorkspace.TTLMillis, time.Duration(template.DefaultTtl)) + dbTTL, err := validWorkspaceTTLMillis(createWorkspace.TTLMillis, template.DefaultTtl) if err != nil { httpapi.Write(ctx, rw, http.StatusBadRequest, codersdk.Response{ Message: "Invalid Workspace Time to Shutdown.", @@ -729,7 +729,7 @@ func (api *API) putWorkspaceTTL(rw http.ResponseWriter, r *http.Request) { return xerrors.Errorf("fetch workspace template: %w", err) } - dbTTL, err = validWorkspaceTTLMillis(req.TTLMillis, time.Duration(template.DefaultTtl)) + dbTTL, err = validWorkspaceTTLMillis(req.TTLMillis, template.DefaultTtl) if err != nil { return codersdk.ValidationError{Field: "ttl_ms", Detail: err.Error()} } @@ -1087,9 +1087,16 @@ func convertWorkspaceTTLMillis(i sql.NullInt64) *int64 { return &millis } -func validWorkspaceTTLMillis(millis *int64, max time.Duration) (sql.NullInt64, error) { +func validWorkspaceTTLMillis(millis *int64, def int64) (sql.NullInt64, error) { if ptr.NilOrZero(millis) { - return sql.NullInt64{}, nil + if def == 0 { + return sql.NullInt64{}, nil + } + + return sql.NullInt64{ + Int64: def, + Valid: true, + }, nil } dur := time.Duration(*millis) * time.Millisecond @@ -1102,11 +1109,6 @@ func validWorkspaceTTLMillis(millis *int64, max time.Duration) (sql.NullInt64, e return sql.NullInt64{}, errTTLMax } - // template level - if max > 0 && truncated > max { - return sql.NullInt64{}, xerrors.Errorf("time until shutdown must be below template maximum %s", max.String()) - } - return sql.NullInt64{ Valid: true, Int64: int64(truncated), diff --git a/coderd/workspaces_test.go b/coderd/workspaces_test.go index d4c7e9b7aef76..bc8a6b05664cb 100644 --- a/coderd/workspaces_test.go +++ b/coderd/workspaces_test.go @@ -299,6 +299,38 @@ func TestPostWorkspacesByOrganization(t *testing.T) { require.Equal(t, "time until shutdown must be at least one minute", apiErr.Validations[0].Detail) }) }) + + t.Run("TemplateDefaultTTL", func(t *testing.T) { + t.Parallel() + client := coderdtest.New(t, &coderdtest.Options{IncludeProvisionerDaemon: true}) + user := coderdtest.CreateFirstUser(t, client) + version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, nil) + exp := 24 * time.Hour.Milliseconds() + template := coderdtest.CreateTemplate(t, client, user.OrganizationID, version.ID, func(ctr *codersdk.CreateTemplateRequest) { + ctr.DefaultTTLMillis = &exp + }) + coderdtest.AwaitTemplateVersionJob(t, client, version.ID) + + ctx, cancel := context.WithTimeout(context.Background(), testutil.WaitLong) + defer cancel() + + // no TTL provided should use template default + req := codersdk.CreateWorkspaceRequest{ + TemplateID: template.ID, + Name: "testing", + } + ws, err := client.CreateWorkspace(ctx, template.OrganizationID, codersdk.Me, req) + require.NoError(t, err) + require.EqualValues(t, exp, *ws.TTLMillis) + + // TTL provided should override template default + req.Name = "testing2" + exp = 1 * time.Hour.Milliseconds() + req.TTLMillis = &exp + ws, err = client.CreateWorkspace(ctx, template.OrganizationID, codersdk.Me, req) + require.NoError(t, err) + require.EqualValues(t, exp, *ws.TTLMillis) + }) } func TestWorkspaceByOwnerAndName(t *testing.T) { @@ -1135,25 +1167,6 @@ func TestWorkspaceUpdateTTL(t *testing.T) { ttlMillis: ptr.Ref((24*7*time.Hour + time.Minute).Milliseconds()), expectedError: "time until shutdown must be less than 7 days", }, - { - name: "above template maximum ttl", - ttlMillis: ptr.Ref((12 * time.Hour).Milliseconds()), - expectedError: "ttl_ms: time until shutdown must be below template maximum 8h0m0s", - modifyTemplate: func(ctr *codersdk.CreateTemplateRequest) { - ctr.DefaultTTLMillis = ptr.Ref((8 * time.Hour).Milliseconds()) - }, - }, - { - name: "no template maximum ttl", - ttlMillis: ptr.Ref((7 * 24 * time.Hour).Milliseconds()), - modifyTemplate: func(ctr *codersdk.CreateTemplateRequest) { ctr.DefaultTTLMillis = ptr.Ref(int64(0)) }, - }, - { - name: "above maximum ttl even with no template max", - ttlMillis: ptr.Ref((365 * 24 * time.Hour).Milliseconds()), - expectedError: "ttl_ms: time until shutdown must be less than 7 days", - modifyTemplate: func(ctr *codersdk.CreateTemplateRequest) { ctr.DefaultTTLMillis = ptr.Ref(int64(0)) }, - }, } for _, testCase := range testCases { From 323bddd52092abbf18afe827769322b996e57f2d Mon Sep 17 00:00:00 2001 From: Garrett Date: Mon, 7 Nov 2022 17:35:39 +0000 Subject: [PATCH 14/18] fix migration --- ...in_autostart.down.sql => 000072_remove_min_autostart.down.sql} | 0 ...ve_min_autostart.up.sql => 000072_remove_min_autostart.up.sql} | 0 2 files changed, 0 insertions(+), 0 deletions(-) rename coderd/database/migrations/{000070_remove_min_autostart.down.sql => 000072_remove_min_autostart.down.sql} (100%) rename coderd/database/migrations/{000070_remove_min_autostart.up.sql => 000072_remove_min_autostart.up.sql} (100%) diff --git a/coderd/database/migrations/000070_remove_min_autostart.down.sql b/coderd/database/migrations/000072_remove_min_autostart.down.sql similarity index 100% rename from coderd/database/migrations/000070_remove_min_autostart.down.sql rename to coderd/database/migrations/000072_remove_min_autostart.down.sql diff --git a/coderd/database/migrations/000070_remove_min_autostart.up.sql b/coderd/database/migrations/000072_remove_min_autostart.up.sql similarity index 100% rename from coderd/database/migrations/000070_remove_min_autostart.up.sql rename to coderd/database/migrations/000072_remove_min_autostart.up.sql From ef2ea57cd9bef61c00125cf22c65f79b40f3ed66 Mon Sep 17 00:00:00 2001 From: Garrett Date: Mon, 7 Nov 2022 21:40:20 +0000 Subject: [PATCH 15/18] change to default naing in fe --- .../TemplateSettingsForm.tsx | 22 +++++++++---------- .../TemplateSettingsPage.test.tsx | 18 +++++++-------- site/src/testHelpers/entities.ts | 2 +- site/src/util/schedule.test.ts | 8 +++---- site/src/util/schedule.ts | 2 +- 5 files changed, 26 insertions(+), 26 deletions(-) diff --git a/site/src/pages/TemplateSettingsPage/TemplateSettingsForm.tsx b/site/src/pages/TemplateSettingsPage/TemplateSettingsForm.tsx index 7ef9f758fc498..2b30f6fb803dc 100644 --- a/site/src/pages/TemplateSettingsPage/TemplateSettingsForm.tsx +++ b/site/src/pages/TemplateSettingsPage/TemplateSettingsForm.tsx @@ -19,7 +19,7 @@ import * as Yup from "yup" export const Language = { nameLabel: "Name", descriptionLabel: "Description", - maxTtlLabel: "Auto-stop limit", + defaultTtlLabel: "Auto-stop default", iconLabel: "Icon", formAriaLabel: "Template settings form", selectEmoji: "Select emoji", @@ -28,7 +28,7 @@ export const Language = { descriptionMaxError: "Please enter a description that is less than or equal to 128 characters.", ttlHelperText: (ttl: number): string => - `Workspaces created from this template may not remain running longer than ${ttl} hours.`, + `Workspaces created from this template will default to stopping after ${ttl} hours.`, } const MAX_DESCRIPTION_CHAR_LIMIT = 128 @@ -41,7 +41,7 @@ export const validationSchema = Yup.object({ MAX_DESCRIPTION_CHAR_LIMIT, Language.descriptionMaxError, ), - max_ttl_ms: Yup.number() + default_ttl_ms: Yup.number() .integer() .min(0) .max(24 * MAX_TTL_DAYS /* 7 days in hours */, Language.ttlMaxError), @@ -72,7 +72,7 @@ export const TemplateSettingsForm: FC = ({ name: template.name, description: template.description, // on display, convert from ms => hours - max_ttl_ms: template.max_ttl_ms / MS_HOUR_CONVERSION, + default_ttl_ms: template.default_ttl_ms / MS_HOUR_CONVERSION, icon: template.icon, }, validationSchema, @@ -80,8 +80,8 @@ export const TemplateSettingsForm: FC = ({ // on submit, convert from hours => ms onSubmit({ ...formData, - max_ttl_ms: formData.max_ttl_ms - ? formData.max_ttl_ms * MS_HOUR_CONVERSION + default_ttl_ms: formData.default_ttl_ms + ? formData.default_ttl_ms * MS_HOUR_CONVERSION : undefined, }) }, @@ -176,20 +176,20 @@ export const TemplateSettingsForm: FC = ({ - {/* If a value for max_ttl_ms has been entered and + {/* If a value for default_ttl_ms has been entered and there are no validation errors for that field, display helper text. We do not use the MUI helper-text prop because it overrides the validation error */} - {form.values.max_ttl_ms && !form.errors.max_ttl_ms && ( + {form.values.default_ttl_ms && !form.errors.default_ttl_ms && ( - {Language.ttlHelperText(form.values.max_ttl_ms)} + {Language.ttlHelperText(form.values.default_ttl_ms)} )} diff --git a/site/src/pages/TemplateSettingsPage/TemplateSettingsPage.test.tsx b/site/src/pages/TemplateSettingsPage/TemplateSettingsPage.test.tsx index f6db0bde004d9..080a848c76238 100644 --- a/site/src/pages/TemplateSettingsPage/TemplateSettingsPage.test.tsx +++ b/site/src/pages/TemplateSettingsPage/TemplateSettingsPage.test.tsx @@ -26,13 +26,13 @@ const validFormValues = { name: "Name", description: "A description", icon: "A string", - max_ttl_ms: 1, + default_ttl_ms: 1, } const fillAndSubmitForm = async ({ name, description, - max_ttl_ms, + default_ttl_ms, icon, }: Omit, "min_autostart_interval_ms">) => { const nameField = await screen.findByLabelText(FormLanguage.nameLabel) @@ -49,9 +49,9 @@ const fillAndSubmitForm = async ({ await userEvent.clear(iconField) await userEvent.type(iconField, icon) - const maxTtlField = await screen.findByLabelText(FormLanguage.maxTtlLabel) + const maxTtlField = await screen.findByLabelText(FormLanguage.defaultTtlLabel) await userEvent.clear(maxTtlField) - await userEvent.type(maxTtlField, max_ttl_ms.toString()) + await userEvent.type(maxTtlField, default_ttl_ms.toString()) const submitButton = await screen.findByText( FooterFormLanguage.defaultSubmitLabel, @@ -87,7 +87,7 @@ describe("TemplateSettingsPage", () => { }) await fillAndSubmitForm(validFormValues) - expect(screen.getByDisplayValue(1)).toBeInTheDocument() // the max_ttl_ms + expect(screen.getByDisplayValue(1)).toBeInTheDocument() // the default_ttl_ms await waitFor(() => expect(API.updateTemplateMeta).toBeCalledTimes(1)) await waitFor(() => @@ -95,7 +95,7 @@ describe("TemplateSettingsPage", () => { "test-template", expect.objectContaining({ ...validFormValues, - max_ttl_ms: 3600000, // the max_ttl_ms to ms + default_ttl_ms: 3600000, // the default_ttl_ms to ms }), ), ) @@ -104,7 +104,7 @@ describe("TemplateSettingsPage", () => { it("allows a ttl of 7 days", () => { const values: UpdateTemplateMeta = { ...validFormValues, - max_ttl_ms: 24 * 7, + default_ttl_ms: 24 * 7, } const validate = () => validationSchema.validateSync(values) expect(validate).not.toThrowError() @@ -113,7 +113,7 @@ describe("TemplateSettingsPage", () => { it("allows ttl of 0", () => { const values: UpdateTemplateMeta = { ...validFormValues, - max_ttl_ms: 0, + default_ttl_ms: 0, } const validate = () => validationSchema.validateSync(values) expect(validate).not.toThrowError() @@ -122,7 +122,7 @@ describe("TemplateSettingsPage", () => { it("disallows a ttl of 7 days + 1 hour", () => { const values: UpdateTemplateMeta = { ...validFormValues, - max_ttl_ms: 24 * 7 + 1, + default_ttl_ms: 24 * 7 + 1, } const validate = () => validationSchema.validateSync(values) expect(validate).toThrowError(FormLanguage.ttlMaxError) diff --git a/site/src/testHelpers/entities.ts b/site/src/testHelpers/entities.ts index 343f2dd414c12..1772d2a514553 100644 --- a/site/src/testHelpers/entities.ts +++ b/site/src/testHelpers/entities.ts @@ -198,7 +198,7 @@ export const MockTemplate: TypesGen.Template = { delete_ms: 3000, }, description: "This is a test description.", - max_ttl_ms: 24 * 60 * 60 * 1000, + default_ttl_ms: 24 * 60 * 60 * 1000, min_autostart_interval_ms: 60 * 60 * 1000, created_by_id: "test-creator-id", created_by_name: "test_creator", diff --git a/site/src/util/schedule.test.ts b/site/src/util/schedule.test.ts index 9cd3dd1b50177..452727624a4e6 100644 --- a/site/src/util/schedule.test.ts +++ b/site/src/util/schedule.test.ts @@ -52,7 +52,7 @@ describe("maxDeadline", () => { it("should be never be greater than global max deadline", () => { const template: Template = { ...Mocks.MockTemplate, - max_ttl_ms: 25 * 60 * 60 * 1000, + default_ttl_ms: 25 * 60 * 60 * 1000, } // Then: deadlineMinusDisabled should be falsy @@ -65,7 +65,7 @@ describe("maxDeadline", () => { it("should be never be greater than global max deadline", () => { const template: Template = { ...Mocks.MockTemplate, - max_ttl_ms: 4 * 60 * 60 * 1000, + default_ttl_ms: 4 * 60 * 60 * 1000, } // Then: deadlineMinusDisabled should be falsy @@ -95,7 +95,7 @@ describe("canExtendDeadline", () => { it("should be falsy if the deadline is more than the template max_ttl", () => { const tooFarAhead = dayjs().add( - dayjs.duration(Mocks.MockTemplate.max_ttl_ms, "milliseconds"), + dayjs.duration(Mocks.MockTemplate.default_ttl_ms, "milliseconds"), ) expect( canExtendDeadline(tooFarAhead, Mocks.MockWorkspace, Mocks.MockTemplate), @@ -104,7 +104,7 @@ describe("canExtendDeadline", () => { it("should be truth if the deadline is within the template max_ttl", () => { const okDeadline = dayjs().add( - dayjs.duration(Mocks.MockTemplate.max_ttl_ms / 2, "milliseconds"), + dayjs.duration(Mocks.MockTemplate.default_ttl_ms / 2, "milliseconds"), ) expect( canExtendDeadline(okDeadline, Mocks.MockWorkspace, Mocks.MockTemplate), diff --git a/site/src/util/schedule.ts b/site/src/util/schedule.ts index c96ba02daa47f..4070f8101e388 100644 --- a/site/src/util/schedule.ts +++ b/site/src/util/schedule.ts @@ -139,7 +139,7 @@ export function getMaxDeadline( } const startedAt = dayjs(ws.latest_build.updated_at) const maxTemplateDeadline = startedAt.add( - dayjs.duration(tpl.max_ttl_ms, "milliseconds"), + dayjs.duration(tpl.default_ttl_ms, "milliseconds"), ) const maxGlobalDeadline = startedAt.add(deadlineExtensionMax) return dayjs.min(maxTemplateDeadline, maxGlobalDeadline) From 0cd5b85a970a6479efa885edbd3f0a5dc84fc2ca Mon Sep 17 00:00:00 2001 From: Garrett Date: Tue, 8 Nov 2022 15:48:26 +0000 Subject: [PATCH 16/18] fix js tests --- .../pages/TemplateSettingsPage/TemplateSettingsPage.test.tsx | 2 +- site/src/testHelpers/entities.ts | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/site/src/pages/TemplateSettingsPage/TemplateSettingsPage.test.tsx b/site/src/pages/TemplateSettingsPage/TemplateSettingsPage.test.tsx index 080a848c76238..95aed343c0d01 100644 --- a/site/src/pages/TemplateSettingsPage/TemplateSettingsPage.test.tsx +++ b/site/src/pages/TemplateSettingsPage/TemplateSettingsPage.test.tsx @@ -34,7 +34,7 @@ const fillAndSubmitForm = async ({ description, default_ttl_ms, icon, -}: Omit, "min_autostart_interval_ms">) => { +}: Required) => { const nameField = await screen.findByLabelText(FormLanguage.nameLabel) await userEvent.clear(nameField) await userEvent.type(nameField, name) diff --git a/site/src/testHelpers/entities.ts b/site/src/testHelpers/entities.ts index 1772d2a514553..69361d68b9222 100644 --- a/site/src/testHelpers/entities.ts +++ b/site/src/testHelpers/entities.ts @@ -199,7 +199,6 @@ export const MockTemplate: TypesGen.Template = { }, description: "This is a test description.", default_ttl_ms: 24 * 60 * 60 * 1000, - min_autostart_interval_ms: 60 * 60 * 1000, created_by_id: "test-creator-id", created_by_name: "test_creator", icon: "/icon/code.svg", From b622dc1295a7b44d510018c7beef7231695450f7 Mon Sep 17 00:00:00 2001 From: Garrett Date: Wed, 9 Nov 2022 18:47:13 +0000 Subject: [PATCH 17/18] pr comments --- cli/templatecreate.go | 10 ++++------ coderd/database/dump.sql | 2 ++ .../migrations/000072_remove_min_autostart.up.sql | 1 + coderd/database/models.go | 11 ++++++----- 4 files changed, 13 insertions(+), 11 deletions(-) diff --git a/cli/templatecreate.go b/cli/templatecreate.go index c53316a66edff..1f8833d0c957a 100644 --- a/cli/templatecreate.go +++ b/cli/templatecreate.go @@ -24,11 +24,10 @@ import ( func templateCreate() *cobra.Command { var ( - directory string - provisioner string - parameterFile string - defaultTTL time.Duration - minAutostartInterval time.Duration + directory string + provisioner string + parameterFile string + defaultTTL time.Duration ) cmd := &cobra.Command{ Use: "create [name]", @@ -133,7 +132,6 @@ func templateCreate() *cobra.Command { cmd.Flags().StringVarP(&provisioner, "test.provisioner", "", "terraform", "Customize the provisioner backend") cmd.Flags().StringVarP(¶meterFile, "parameter-file", "", "", "Specify a file path with parameter values.") cmd.Flags().DurationVarP(&defaultTTL, "default-ttl", "", 24*time.Hour, "Specify a default TTL for workspaces created from this template.") - cmd.Flags().DurationVarP(&minAutostartInterval, "min-autostart-interval", "", time.Hour, "Specify a minimum autostart interval for workspaces created from this template.") // This is for testing! err := cmd.Flags().MarkHidden("test.provisioner") if err != nil { diff --git a/coderd/database/dump.sql b/coderd/database/dump.sql index cb62c42fae208..2b369cf167fe3 100644 --- a/coderd/database/dump.sql +++ b/coderd/database/dump.sql @@ -356,6 +356,8 @@ CREATE TABLE templates ( group_acl jsonb DEFAULT '{}'::jsonb NOT NULL ); +COMMENT ON COLUMN templates.default_ttl IS 'The default duration for auto-stop for workspaces created from this template.'; + CREATE TABLE user_links ( user_id uuid NOT NULL, login_type login_type NOT NULL, diff --git a/coderd/database/migrations/000072_remove_min_autostart.up.sql b/coderd/database/migrations/000072_remove_min_autostart.up.sql index 900e0afd59525..cc13b5f1746e6 100644 --- a/coderd/database/migrations/000072_remove_min_autostart.up.sql +++ b/coderd/database/migrations/000072_remove_min_autostart.up.sql @@ -3,3 +3,4 @@ ALTER TABLE "templates" DROP COLUMN "min_autostart_interval"; -- rename "max_ttl" to "default_ttl" on "templates" table ALTER TABLE "templates" RENAME COLUMN "max_ttl" TO "default_ttl"; +COMMENT ON COLUMN templates.default_ttl IS 'The default duration for auto-stop for workspaces created from this template.'; diff --git a/coderd/database/models.go b/coderd/database/models.go index 3d2b809b04d82..f457dede07eec 100644 --- a/coderd/database/models.go +++ b/coderd/database/models.go @@ -583,11 +583,12 @@ type Template struct { Provisioner ProvisionerType `db:"provisioner" json:"provisioner"` ActiveVersionID uuid.UUID `db:"active_version_id" json:"active_version_id"` Description string `db:"description" json:"description"` - DefaultTtl int64 `db:"default_ttl" json:"default_ttl"` - CreatedBy uuid.UUID `db:"created_by" json:"created_by"` - Icon string `db:"icon" json:"icon"` - UserACL TemplateACL `db:"user_acl" json:"user_acl"` - GroupACL TemplateACL `db:"group_acl" json:"group_acl"` + // The default duration for auto-stop for workspaces created from this template. + DefaultTtl int64 `db:"default_ttl" json:"default_ttl"` + CreatedBy uuid.UUID `db:"created_by" json:"created_by"` + Icon string `db:"icon" json:"icon"` + UserACL TemplateACL `db:"user_acl" json:"user_acl"` + GroupACL TemplateACL `db:"group_acl" json:"group_acl"` } type TemplateVersion struct { From f243da42c40092afb3e9f1e92d111fd2a70ba4c8 Mon Sep 17 00:00:00 2001 From: Garrett Date: Wed, 9 Nov 2022 19:13:17 +0000 Subject: [PATCH 18/18] fix migration --- ...in_autostart.down.sql => 000073_remove_min_autostart.down.sql} | 0 ...ve_min_autostart.up.sql => 000073_remove_min_autostart.up.sql} | 0 2 files changed, 0 insertions(+), 0 deletions(-) rename coderd/database/migrations/{000072_remove_min_autostart.down.sql => 000073_remove_min_autostart.down.sql} (100%) rename coderd/database/migrations/{000072_remove_min_autostart.up.sql => 000073_remove_min_autostart.up.sql} (100%) diff --git a/coderd/database/migrations/000072_remove_min_autostart.down.sql b/coderd/database/migrations/000073_remove_min_autostart.down.sql similarity index 100% rename from coderd/database/migrations/000072_remove_min_autostart.down.sql rename to coderd/database/migrations/000073_remove_min_autostart.down.sql diff --git a/coderd/database/migrations/000072_remove_min_autostart.up.sql b/coderd/database/migrations/000073_remove_min_autostart.up.sql similarity index 100% rename from coderd/database/migrations/000072_remove_min_autostart.up.sql rename to coderd/database/migrations/000073_remove_min_autostart.up.sql