mirror of
https://github.com/coder/coder.git
synced 2026-09-24 15:04:27 +08:00
feat: enforce template-level constraints for TTL and autostart (#2018)
This PR adds fields to templates that constrain values for workspaces derived from that template. - Autostop: Adds a field max_ttl on the template which limits the maximum value of ttl on all workspaces derived from that template. Defaulting to 168 hours, enforced on edits to workspace metadata. New workspaces will default to the templates's `max_ttl` if not specified. - Autostart: Adds a field min_autostart_duration which limits the minimum duration between successive autostarts of a template, measured from a single reference time. Defaulting to 1 hour, enforced on edits to workspace metadata.
This commit is contained in:
@@ -1277,16 +1277,26 @@ func (q *fakeQuerier) InsertTemplate(_ context.Context, arg database.InsertTempl
|
||||
q.mutex.Lock()
|
||||
defer q.mutex.Unlock()
|
||||
|
||||
// default values
|
||||
if arg.MaxTtl == 0 {
|
||||
arg.MaxTtl = int64(168 * time.Hour)
|
||||
}
|
||||
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,
|
||||
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,
|
||||
}
|
||||
q.templates = append(q.templates, template)
|
||||
return template, nil
|
||||
|
||||
Generated
+3
-1
@@ -246,7 +246,9 @@ CREATE TABLE templates (
|
||||
name character varying(64) NOT NULL,
|
||||
provisioner provisioner_type NOT NULL,
|
||||
active_version_id uuid NOT NULL,
|
||||
description character varying(128) DEFAULT ''::character varying 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
|
||||
);
|
||||
|
||||
CREATE TABLE users (
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
ALTER TABLE ONLY templates DROP COLUMN IF EXISTS max_ttl;
|
||||
ALTER TABLE ONLY templates DROP COLUMN IF EXISTS min_autostart_interval;
|
||||
@@ -0,0 +1,2 @@
|
||||
ALTER TABLE ONLY templates ADD COLUMN IF NOT EXISTS max_ttl BIGINT NOT NULL DEFAULT 604800000000000; -- 168 hours
|
||||
ALTER TABLE ONLY templates ADD COLUMN IF NOT EXISTS min_autostart_interval BIGINT NOT NULL DEFAULT 3600000000000; -- 1 hour
|
||||
@@ -427,15 +427,17 @@ type ProvisionerJobLog 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"`
|
||||
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"`
|
||||
}
|
||||
|
||||
type TemplateVersion struct {
|
||||
|
||||
@@ -1603,7 +1603,7 @@ func (q *sqlQuerier) UpdateProvisionerJobWithCompleteByID(ctx context.Context, a
|
||||
|
||||
const getTemplateByID = `-- name: GetTemplateByID :one
|
||||
SELECT
|
||||
id, created_at, updated_at, organization_id, deleted, name, provisioner, active_version_id, description
|
||||
id, created_at, updated_at, organization_id, deleted, name, provisioner, active_version_id, description, max_ttl, min_autostart_interval
|
||||
FROM
|
||||
templates
|
||||
WHERE
|
||||
@@ -1625,13 +1625,15 @@ func (q *sqlQuerier) GetTemplateByID(ctx context.Context, id uuid.UUID) (Templat
|
||||
&i.Provisioner,
|
||||
&i.ActiveVersionID,
|
||||
&i.Description,
|
||||
&i.MaxTtl,
|
||||
&i.MinAutostartInterval,
|
||||
)
|
||||
return i, err
|
||||
}
|
||||
|
||||
const getTemplateByOrganizationAndName = `-- name: GetTemplateByOrganizationAndName :one
|
||||
SELECT
|
||||
id, created_at, updated_at, organization_id, deleted, name, provisioner, active_version_id, description
|
||||
id, created_at, updated_at, organization_id, deleted, name, provisioner, active_version_id, description, max_ttl, min_autostart_interval
|
||||
FROM
|
||||
templates
|
||||
WHERE
|
||||
@@ -1661,13 +1663,15 @@ func (q *sqlQuerier) GetTemplateByOrganizationAndName(ctx context.Context, arg G
|
||||
&i.Provisioner,
|
||||
&i.ActiveVersionID,
|
||||
&i.Description,
|
||||
&i.MaxTtl,
|
||||
&i.MinAutostartInterval,
|
||||
)
|
||||
return i, err
|
||||
}
|
||||
|
||||
const getTemplatesByIDs = `-- name: GetTemplatesByIDs :many
|
||||
SELECT
|
||||
id, created_at, updated_at, organization_id, deleted, name, provisioner, active_version_id, description
|
||||
id, created_at, updated_at, organization_id, deleted, name, provisioner, active_version_id, description, max_ttl, min_autostart_interval
|
||||
FROM
|
||||
templates
|
||||
WHERE
|
||||
@@ -1693,6 +1697,8 @@ func (q *sqlQuerier) GetTemplatesByIDs(ctx context.Context, ids []uuid.UUID) ([]
|
||||
&i.Provisioner,
|
||||
&i.ActiveVersionID,
|
||||
&i.Description,
|
||||
&i.MaxTtl,
|
||||
&i.MinAutostartInterval,
|
||||
); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -1709,7 +1715,7 @@ func (q *sqlQuerier) GetTemplatesByIDs(ctx context.Context, ids []uuid.UUID) ([]
|
||||
|
||||
const getTemplatesByOrganization = `-- name: GetTemplatesByOrganization :many
|
||||
SELECT
|
||||
id, created_at, updated_at, organization_id, deleted, name, provisioner, active_version_id, description
|
||||
id, created_at, updated_at, organization_id, deleted, name, provisioner, active_version_id, description, max_ttl, min_autostart_interval
|
||||
FROM
|
||||
templates
|
||||
WHERE
|
||||
@@ -1741,6 +1747,8 @@ func (q *sqlQuerier) GetTemplatesByOrganization(ctx context.Context, arg GetTemp
|
||||
&i.Provisioner,
|
||||
&i.ActiveVersionID,
|
||||
&i.Description,
|
||||
&i.MaxTtl,
|
||||
&i.MinAutostartInterval,
|
||||
); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -1765,21 +1773,25 @@ INSERT INTO
|
||||
"name",
|
||||
provisioner,
|
||||
active_version_id,
|
||||
description
|
||||
description,
|
||||
max_ttl,
|
||||
min_autostart_interval
|
||||
)
|
||||
VALUES
|
||||
($1, $2, $3, $4, $5, $6, $7, $8) RETURNING id, created_at, updated_at, organization_id, deleted, name, provisioner, active_version_id, description
|
||||
($1, $2, $3, $4, $5, $6, $7, $8, $9, $10) RETURNING id, created_at, updated_at, organization_id, deleted, name, provisioner, active_version_id, description, max_ttl, min_autostart_interval
|
||||
`
|
||||
|
||||
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"`
|
||||
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"`
|
||||
}
|
||||
|
||||
func (q *sqlQuerier) InsertTemplate(ctx context.Context, arg InsertTemplateParams) (Template, error) {
|
||||
@@ -1792,6 +1804,8 @@ func (q *sqlQuerier) InsertTemplate(ctx context.Context, arg InsertTemplateParam
|
||||
arg.Provisioner,
|
||||
arg.ActiveVersionID,
|
||||
arg.Description,
|
||||
arg.MaxTtl,
|
||||
arg.MinAutostartInterval,
|
||||
)
|
||||
var i Template
|
||||
err := row.Scan(
|
||||
@@ -1804,6 +1818,8 @@ func (q *sqlQuerier) InsertTemplate(ctx context.Context, arg InsertTemplateParam
|
||||
&i.Provisioner,
|
||||
&i.ActiveVersionID,
|
||||
&i.Description,
|
||||
&i.MaxTtl,
|
||||
&i.MinAutostartInterval,
|
||||
)
|
||||
return i, err
|
||||
}
|
||||
|
||||
@@ -47,10 +47,12 @@ INSERT INTO
|
||||
"name",
|
||||
provisioner,
|
||||
active_version_id,
|
||||
description
|
||||
description,
|
||||
max_ttl,
|
||||
min_autostart_interval
|
||||
)
|
||||
VALUES
|
||||
($1, $2, $3, $4, $5, $6, $7, $8) RETURNING *;
|
||||
($1, $2, $3, $4, $5, $6, $7, $8, $9, $10) RETURNING *;
|
||||
|
||||
-- name: UpdateTemplateActiveVersionByID :exec
|
||||
UPDATE
|
||||
|
||||
Reference in New Issue
Block a user