diff --git a/cli/templatecreate.go b/cli/templatecreate.go index 458cb1c2015b0..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 - maxTTL time.Duration - minAutostartInterval time.Duration + directory string + provisioner string + parameterFile string + defaultTTL time.Duration ) cmd := &cobra.Command{ Use: "create [name]", @@ -108,10 +107,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,8 +131,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(&minAutostartInterval, "min-autostart-interval", "", time.Hour, "Specify a minimum autostart interval 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.") // This is for testing! err := cmd.Flags().MarkHidden("test.provisioner") if err != nil { diff --git a/cli/templatecreate_test.go b/cli/templatecreate_test.go index ccffb45fc7114..98af8158241d3 100644 --- a/cli/templatecreate_test.go +++ b/cli/templatecreate_test.go @@ -52,8 +52,7 @@ func TestTemplateCreate(t *testing.T) { "my-template", "--directory", source, "--test.provisioner", string(database.ProvisionerTypeEcho), - "--max-ttl", "24h", - "--min-autostart-interval", "2h", + "--default-ttl", "24h", } cmd, root := clitest.New(t, args...) clitest.SetupConfig(t, client, root) diff --git a/cli/templateedit.go b/cli/templateedit.go index e0e4cf57a7196..867cb41d208a7 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 + defaultTTL 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, + DefaultTTLMillis: defaultTTL.Milliseconds(), } _, err = client.UpdateTemplateMeta(cmd.Context(), template.ID, req) @@ -59,8 +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(&minAutostartInterval, "min-autostart-interval", "", 0, "Edit the template minimum autostart interval - workspaces created from this template must wait at least this long between autostarts.") + 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..fbfc77d26fdf5 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,6 @@ 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.MinAutostartIntervalMillis, updated.MinAutostartIntervalMillis) + assert.Equal(t, template.DefaultTTLMillis, updated.DefaultTTLMillis) }) } diff --git a/cli/templates.go b/cli/templates.go index 4c85606d70c6f..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.MaxTTLMillis) * 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/database/databasefake/databasefake.go b/coderd/database/databasefake/databasefake.go index e2b9f09f3c37f..91525b5eb09af 100644 --- a/coderd/database/databasefake/databasefake.go +++ b/coderd/database/databasefake/databasefake.go @@ -1455,8 +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.MinAutostartInterval = arg.MinAutostartInterval + tpl.DefaultTtl = arg.DefaultTtl 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, + DefaultTtl: arg.DefaultTtl, + CreatedBy: arg.CreatedBy, + UserACL: arg.UserACL, + GroupACL: arg.GroupACL, } q.templates = append(q.templates, template) return template, nil diff --git a/coderd/database/dump.sql b/coderd/database/dump.sql index a32ea8af471f1..2b369cf167fe3 100644 --- a/coderd/database/dump.sql +++ b/coderd/database/dump.sql @@ -349,14 +349,15 @@ 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, - min_autostart_interval bigint DEFAULT '3600000000000'::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, 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/000073_remove_min_autostart.down.sql b/coderd/database/migrations/000073_remove_min_autostart.down.sql new file mode 100644 index 0000000000000..0b7b72c9fc974 --- /dev/null +++ b/coderd/database/migrations/000073_remove_min_autostart.down.sql @@ -0,0 +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/000073_remove_min_autostart.up.sql b/coderd/database/migrations/000073_remove_min_autostart.up.sql new file mode 100644 index 0000000000000..cc13b5f1746e6 --- /dev/null +++ b/coderd/database/migrations/000073_remove_min_autostart.up.sql @@ -0,0 +1,6 @@ +-- 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"; +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 b3da222afa827..f457dede07eec 100644 --- a/coderd/database/models.go +++ b/coderd/database/models.go @@ -574,21 +574,21 @@ 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"` + // 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 { diff --git a/coderd/database/queries.sql.go b/coderd/database/queries.sql.go index 7c1bf4bfcd34d..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, min_autostart_interval, 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,8 +3041,7 @@ func (q *sqlQuerier) GetTemplateByID(ctx context.Context, id uuid.UUID) (Templat &i.Provisioner, &i.ActiveVersionID, &i.Description, - &i.MaxTtl, - &i.MinAutostartInterval, + &i.DefaultTtl, &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, default_ttl, created_by, icon, user_acl, group_acl FROM templates WHERE @@ -3083,8 +3082,7 @@ func (q *sqlQuerier) GetTemplateByOrganizationAndName(ctx context.Context, arg G &i.Provisioner, &i.ActiveVersionID, &i.Description, - &i.MaxTtl, - &i.MinAutostartInterval, + &i.DefaultTtl, &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, default_ttl, created_by, icon, user_acl, group_acl FROM templates ORDER BY (name, id) ASC ` @@ -3117,8 +3115,7 @@ func (q *sqlQuerier) GetTemplates(ctx context.Context) ([]Template, error) { &i.Provisioner, &i.ActiveVersionID, &i.Description, - &i.MaxTtl, - &i.MinAutostartInterval, + &i.DefaultTtl, &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, default_ttl, created_by, icon, user_acl, group_acl FROM templates WHERE @@ -3197,8 +3194,7 @@ func (q *sqlQuerier) GetTemplatesWithFilter(ctx context.Context, arg GetTemplate &i.Provisioner, &i.ActiveVersionID, &i.Description, - &i.MaxTtl, - &i.MinAutostartInterval, + &i.DefaultTtl, &i.CreatedBy, &i.Icon, &i.UserACL, @@ -3228,32 +3224,30 @@ INSERT INTO provisioner, active_version_id, description, - max_ttl, - min_autostart_interval, + default_ttl, 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, default_ttl, 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"` + 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"` } func (q *sqlQuerier) InsertTemplate(ctx context.Context, arg InsertTemplateParams) (Template, error) { @@ -3266,8 +3260,7 @@ func (q *sqlQuerier) InsertTemplate(ctx context.Context, arg InsertTemplateParam arg.Provisioner, arg.ActiveVersionID, arg.Description, - arg.MaxTtl, - arg.MinAutostartInterval, + arg.DefaultTtl, arg.CreatedBy, arg.Icon, arg.UserACL, @@ -3284,8 +3277,7 @@ func (q *sqlQuerier) InsertTemplate(ctx context.Context, arg InsertTemplateParam &i.Provisioner, &i.ActiveVersionID, &i.Description, - &i.MaxTtl, - &i.MinAutostartInterval, + &i.DefaultTtl, &i.CreatedBy, &i.Icon, &i.UserACL, @@ -3303,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, default_ttl, created_by, icon, user_acl, group_acl ` type UpdateTemplateACLByIDParams struct { @@ -3325,8 +3317,7 @@ func (q *sqlQuerier) UpdateTemplateACLByID(ctx context.Context, arg UpdateTempla &i.Provisioner, &i.ActiveVersionID, &i.Description, - &i.MaxTtl, - &i.MinAutostartInterval, + &i.DefaultTtl, &i.CreatedBy, &i.Icon, &i.UserACL, @@ -3383,24 +3374,22 @@ UPDATE SET updated_at = $2, description = $3, - max_ttl = $4, - min_autostart_interval = $5, - name = $6, - icon = $7 + 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, min_autostart_interval, 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"` - 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"` + DefaultTtl int64 `db:"default_ttl" json:"default_ttl"` + Name string `db:"name" json:"name"` + Icon string `db:"icon" json:"icon"` } func (q *sqlQuerier) UpdateTemplateMetaByID(ctx context.Context, arg UpdateTemplateMetaByIDParams) (Template, error) { @@ -3408,8 +3397,7 @@ func (q *sqlQuerier) UpdateTemplateMetaByID(ctx context.Context, arg UpdateTempl arg.ID, arg.UpdatedAt, arg.Description, - arg.MaxTtl, - arg.MinAutostartInterval, + arg.DefaultTtl, arg.Name, arg.Icon, ) @@ -3424,8 +3412,7 @@ func (q *sqlQuerier) UpdateTemplateMetaByID(ctx context.Context, arg UpdateTempl &i.Provisioner, &i.ActiveVersionID, &i.Description, - &i.MaxTtl, - &i.MinAutostartInterval, + &i.DefaultTtl, &i.CreatedBy, &i.Icon, &i.UserACL, diff --git a/coderd/database/queries/templates.sql b/coderd/database/queries/templates.sql index 6eb73af288b81..7063b87075c6f 100644 --- a/coderd/database/queries/templates.sql +++ b/coderd/database/queries/templates.sql @@ -65,15 +65,14 @@ INSERT INTO provisioner, active_version_id, description, - max_ttl, - min_autostart_interval, + default_ttl, 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 @@ -99,10 +98,9 @@ UPDATE SET updated_at = $2, description = $3, - max_ttl = $4, - min_autostart_interval = $5, - name = $6, - icon = $7 + default_ttl = $4, + name = $5, + icon = $6 WHERE id = $1 RETURNING diff --git a/coderd/templates.go b/coderd/templates.go index 163464db39b12..8becfd31c04df 100644 --- a/coderd/templates.go +++ b/coderd/templates.go @@ -22,15 +22,9 @@ 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" ) -var ( - maxTTLDefault = 24 * 7 * time.Hour - minAutostartIntervalDefault = time.Hour -) - // Auto-importable templates. These can be auto-imported after the first user // has been created. type AutoImportTemplate string @@ -212,52 +206,36 @@ func (api *API) postTemplateByOrganization(rw http.ResponseWriter, r *http.Reque return } - maxTTL := maxTTLDefault - if createTemplate.MaxTTLMillis != nil { - maxTTL = time.Duration(*createTemplate.MaxTTLMillis) * time.Millisecond + var ttl time.Duration + if createTemplate.DefaultTTLMillis != nil { + 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."}, + {Field: "default_ttl_ms", Detail: "Must be a positive integer."}, }, }) return } - if maxTTL > maxTTLDefault { - 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()}, - }, - }) - 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, + DefaultTtl: int64(ttl), + CreatedBy: apiKey.UserID, + UserACL: database.TemplateACL{}, GroupACL: database.TemplateACL{ organization.ID.String(): []rbac.Action{rbac.ActionRead}, }, @@ -464,14 +442,8 @@ 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.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()}) + if req.DefaultTTLMillis < 0 { + validErrs = append(validErrs, codersdk.ValidationError{Field: "default_ttl_ms", Detail: "Must be a positive integer."}) } if len(validErrs) > 0 { @@ -501,8 +473,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.DefaultTTLMillis == time.Duration(template.DefaultTtl).Milliseconds() { return nil } @@ -510,8 +481,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 - minAutostartInterval := time.Duration(req.MinAutostartIntervalMillis) * time.Millisecond + maxTTL := time.Duration(req.DefaultTTLMillis) * time.Millisecond if name == "" { name = template.Name @@ -519,18 +489,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, + DefaultTtl: int64(maxTTL), }) if err != nil { return err @@ -666,18 +632,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.", + DefaultTtl: 0, + CreatedBy: opts.userID, + UserACL: database.TemplateACL{}, GroupACL: database.TemplateACL{ opts.orgID.String(): []rbac.Action{rbac.ActionRead}, }, @@ -768,21 +733,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, + DefaultTTLMillis: time.Duration(template.DefaultTtl).Milliseconds(), + CreatedByID: template.CreatedBy, + CreatedByName: createdByName, } } diff --git a/coderd/templates_test.go b/coderd/templates_test.go index bb602d0b05406..df047993b5061 100644 --- a/coderd/templates_test.go +++ b/coderd/templates_test.go @@ -131,34 +131,14 @@ 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) 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, - MaxTTLMillis: 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) { @@ -171,12 +151,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,15 +286,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(), - MinAutostartIntervalMillis: time.Minute.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. @@ -329,8 +307,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.MinAutostartIntervalMillis, updated.MinAutostartIntervalMillis) + assert.Equal(t, req.DefaultTTLMillis, updated.DefaultTTLMillis) // Extra paranoid: did it _really_ happen? updated, err = client.Template(ctx, template.ID) @@ -339,8 +316,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.MinAutostartIntervalMillis, updated.MinAutostartIntervalMillis) + assert.Equal(t, req.DefaultTTLMillis, updated.DefaultTTLMillis) require.Len(t, auditor.AuditLogs, 4) assert.Equal(t, database.AuditActionWrite, auditor.AuditLogs[3].Action) @@ -353,10 +329,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 @@ -372,7 +348,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) { @@ -382,49 +358,23 @@ 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()) - }) - req := codersdk.UpdateTemplateMeta{ - MaxTTLMillis: -1, - } - - ctx, cancel := context.WithTimeout(context.Background(), testutil.WaitLong) - 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.MaxTTLMillis, template.MaxTTLMillis) - }) - - 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.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: -1, } 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) 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) { @@ -436,19 +386,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, - MinAutostartIntervalMillis: template.MinAutostartIntervalMillis, + 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") @@ -458,8 +406,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.MinAutostartIntervalMillis, updated.MinAutostartIntervalMillis) + assert.Equal(t, template.DefaultTTLMillis, updated.DefaultTTLMillis) }) t.Run("Invalid", func(t *testing.T) { @@ -470,24 +417,21 @@ 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), - MinAutostartIntervalMillis: -int64(time.Hour), + DefaultTTLMillis: -int64(time.Hour), } _, err := client.UpdateTemplateMeta(ctx, template.ID, req) var apiErr *codersdk.Error require.ErrorAs(t, err, &apiErr) 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") + require.Len(t, apiErr.Validations, 1) + assert.Equal(t, apiErr.Validations[0].Field, "default_ttl_ms") updated, err := client.Template(ctx, template.ID) require.NoError(t, err) @@ -495,8 +439,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.MinAutostartIntervalMillis, updated.MinAutostartIntervalMillis) + assert.Equal(t, template.DefaultTTLMillis, updated.DefaultTTLMillis) }) t.Run("RemoveIcon", func(t *testing.T) { diff --git a/coderd/workspaces.go b/coderd/workspaces.go index 38d53107363dd..0002c65178cc6 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) { @@ -333,7 +332,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.", @@ -342,7 +341,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, template.DefaultTtl) if err != nil { httpapi.Write(ctx, rw, http.StatusBadRequest, codersdk.Response{ Message: "Invalid Workspace Time to Shutdown.", @@ -666,16 +665,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.", @@ -739,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.MaxTtl)) + dbTTL, err = validWorkspaceTTLMillis(req.TTLMillis, template.DefaultTtl) if err != nil { return codersdk.ValidationError{Field: "ttl_ms", Detail: err.Error()} } @@ -793,13 +783,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 @@ -833,7 +816,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); 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. @@ -1104,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 @@ -1119,18 +1109,13 @@ 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), }, 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 @@ -1141,28 +1126,19 @@ func validWorkspaceDeadline(startedAt, newDeadline time.Time, max time.Duration) return errDeadlineBeforeStart } - delta := newDeadline.Sub(startedAt) - if delta > max { - return errDeadlineOverTemplateMax - } - 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/coderd/workspaces_test.go b/coderd/workspaces_test.go index ca92537da0e27..bc8a6b05664cb 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) { @@ -298,58 +298,38 @@ 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.MaxTTLMillis + 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) - }) }) - t.Run("InvalidAutostart", func(t *testing.T) { + 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) - template := coderdtest.CreateTemplate(t, client, user.OrganizationID, version.ID) + 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", - AutostartSchedule: ptr.Ref("CRON_TZ=US/Central * * * * *"), + TemplateID: template.ID, + Name: "testing", } - _, 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") + 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) }) } @@ -1187,23 +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.MaxTTLMillis = 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)) }, - }, - { - 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)) }, - }, } for _, testCase := range testCases { @@ -1322,14 +1285,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.MaxTTLMillis) * 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/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 22e707050aed3..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"` - MaxTTLMillis int64 `json:"max_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 { @@ -72,11 +71,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"` + 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 d5ee1f0e369c5..226fc44b4131c 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}, + DefaultTtl: 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()}, + "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, diff --git a/site/src/api/typesGenerated.ts b/site/src/api/typesGenerated.ts index b38ed2ea6c040..bb8d4fa38c16f 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,8 +619,7 @@ export interface Template { readonly build_time_stats: TemplateBuildTimeStats readonly description: string readonly icon: string - readonly max_ttl_ms: number - readonly min_autostart_interval_ms: number + readonly default_ttl_ms: number readonly created_by_id: string readonly created_by_name: string } @@ -700,8 +698,7 @@ export interface UpdateTemplateMeta { readonly name?: string readonly description?: string readonly icon?: string - readonly max_ttl_ms?: number - readonly min_autostart_interval_ms?: number + readonly default_ttl_ms?: number } // From codersdk/users.go 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..95aed343c0d01 100644 --- a/site/src/pages/TemplateSettingsPage/TemplateSettingsPage.test.tsx +++ b/site/src/pages/TemplateSettingsPage/TemplateSettingsPage.test.tsx @@ -26,15 +26,15 @@ 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">) => { +}: Required) => { const nameField = await screen.findByLabelText(FormLanguage.nameLabel) await userEvent.clear(nameField) await userEvent.type(nameField, name) @@ -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..69361d68b9222 100644 --- a/site/src/testHelpers/entities.ts +++ b/site/src/testHelpers/entities.ts @@ -198,8 +198,7 @@ export const MockTemplate: TypesGen.Template = { delete_ms: 3000, }, description: "This is a test description.", - max_ttl_ms: 24 * 60 * 60 * 1000, - min_autostart_interval_ms: 60 * 60 * 1000, + default_ttl_ms: 24 * 60 * 60 * 1000, created_by_id: "test-creator-id", created_by_name: "test_creator", icon: "/icon/code.svg", 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) pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy