From 744e476bc85c1f5c71509f2fb7dddbcdde7a5288 Mon Sep 17 00:00:00 2001 From: Kira Pilot Date: Mon, 1 May 2023 19:30:40 +0000 Subject: [PATCH 01/21] added workspace actions entitlement --- codersdk/deployment.go | 2 ++ enterprise/coderd/coderd.go | 1 + enterprise/coderd/coderd_test.go | 1 + site/src/api/typesGenerated.ts | 2 ++ 4 files changed, 6 insertions(+) diff --git a/codersdk/deployment.go b/codersdk/deployment.go index 61ab6658f3732..123e1477d80ca 100644 --- a/codersdk/deployment.go +++ b/codersdk/deployment.go @@ -46,6 +46,7 @@ const ( FeatureAppearance FeatureName = "appearance" FeatureAdvancedTemplateScheduling FeatureName = "advanced_template_scheduling" FeatureWorkspaceProxy FeatureName = "workspace_proxy" + FeatureWorkspaceActions FeatureName = "workspace_actions" ) // FeatureNames must be kept in-sync with the Feature enum above. @@ -61,6 +62,7 @@ var FeatureNames = []FeatureName{ FeatureAppearance, FeatureAdvancedTemplateScheduling, FeatureWorkspaceProxy, + FeatureWorkspaceActions, } // Humanize returns the feature name in a human-readable format. diff --git a/enterprise/coderd/coderd.go b/enterprise/coderd/coderd.go index 990faae898cb0..6fe25261ab9ad 100644 --- a/enterprise/coderd/coderd.go +++ b/enterprise/coderd/coderd.go @@ -322,6 +322,7 @@ func (api *API) updateEntitlements(ctx context.Context) error { codersdk.FeatureExternalProvisionerDaemons: true, codersdk.FeatureAdvancedTemplateScheduling: true, codersdk.FeatureWorkspaceProxy: true, + codersdk.FeatureWorkspaceActions: true, }) if err != nil { return err diff --git a/enterprise/coderd/coderd_test.go b/enterprise/coderd/coderd_test.go index 27aa2cb4c33eb..26526721f1f8c 100644 --- a/enterprise/coderd/coderd_test.go +++ b/enterprise/coderd/coderd_test.go @@ -54,6 +54,7 @@ func TestEntitlements(t *testing.T) { codersdk.FeatureExternalProvisionerDaemons: 1, codersdk.FeatureAdvancedTemplateScheduling: 1, codersdk.FeatureWorkspaceProxy: 1, + codersdk.FeatureWorkspaceActions: 1, }, GraceAt: time.Now().Add(59 * 24 * time.Hour), }) diff --git a/site/src/api/typesGenerated.ts b/site/src/api/typesGenerated.ts index 6c3e7f0cea6bf..236904897b380 100644 --- a/site/src/api/typesGenerated.ts +++ b/site/src/api/typesGenerated.ts @@ -1376,6 +1376,7 @@ export type FeatureName = | "scim" | "template_rbac" | "user_limit" + | "workspace_actions" | "workspace_proxy" export const FeatureNames: FeatureName[] = [ "advanced_template_scheduling", @@ -1388,6 +1389,7 @@ export const FeatureNames: FeatureName[] = [ "scim", "template_rbac", "user_limit", + "workspace_actions", "workspace_proxy", ] From e6e1ec679a7282b38019a17ff6303d485f2ad238 Mon Sep 17 00:00:00 2001 From: Kira Pilot Date: Mon, 1 May 2023 19:50:23 +0000 Subject: [PATCH 02/21] added workspace actions experiment --- coderd/apidoc/docs.go | 6 ++++-- coderd/apidoc/swagger.json | 4 ++-- codersdk/deployment.go | 3 +++ docs/api/schemas.md | 7 ++++--- site/src/api/typesGenerated.ts | 4 ++-- 5 files changed, 15 insertions(+), 9 deletions(-) diff --git a/coderd/apidoc/docs.go b/coderd/apidoc/docs.go index 91bae7945e422..4483ba6b802d7 100644 --- a/coderd/apidoc/docs.go +++ b/coderd/apidoc/docs.go @@ -7397,10 +7397,12 @@ const docTemplate = `{ "codersdk.Experiment": { "type": "string", "enum": [ - "moons" + "moons", + "workspace_actions" ], "x-enum-varnames": [ - "ExperimentMoons" + "ExperimentMoons", + "ExperimentWorkspaceActions" ] }, "codersdk.Feature": { diff --git a/coderd/apidoc/swagger.json b/coderd/apidoc/swagger.json index 7e279b3643e56..1fb14f664d45e 100644 --- a/coderd/apidoc/swagger.json +++ b/coderd/apidoc/swagger.json @@ -6613,8 +6613,8 @@ }, "codersdk.Experiment": { "type": "string", - "enum": ["moons"], - "x-enum-varnames": ["ExperimentMoons"] + "enum": ["moons", "workspace_actions"], + "x-enum-varnames": ["ExperimentMoons", "ExperimentWorkspaceActions"] }, "codersdk.Feature": { "type": "object", diff --git a/codersdk/deployment.go b/codersdk/deployment.go index 123e1477d80ca..aaee164d5a2b3 100644 --- a/codersdk/deployment.go +++ b/codersdk/deployment.go @@ -1670,6 +1670,9 @@ const ( // feature is not yet complete in functionality. ExperimentMoons Experiment = "moons" + // https://github.com/coder/coder/milestone/19 + ExperimentWorkspaceActions Experiment = "workspace_actions" + // Add new experiments here! // ExperimentExample Experiment = "example" ) diff --git a/docs/api/schemas.md b/docs/api/schemas.md index ee8e52e07a4a4..6c6dfcd692ccd 100644 --- a/docs/api/schemas.md +++ b/docs/api/schemas.md @@ -2502,9 +2502,10 @@ CreateParameterRequest is a structure used to create a new parameter value for a #### Enumerated Values -| Value | -| ------- | -| `moons` | +| Value | +| ------------------- | +| `moons` | +| `workspace_actions` | ## codersdk.Feature diff --git a/site/src/api/typesGenerated.ts b/site/src/api/typesGenerated.ts index 236904897b380..aa72e51f3f687 100644 --- a/site/src/api/typesGenerated.ts +++ b/site/src/api/typesGenerated.ts @@ -1361,8 +1361,8 @@ export const Entitlements: Entitlement[] = [ ] // From codersdk/deployment.go -export type Experiment = "moons" -export const Experiments: Experiment[] = ["moons"] +export type Experiment = "moons" | "workspace_actions" +export const Experiments: Experiment[] = ["moons", "workspace_actions"] // From codersdk/deployment.go export type FeatureName = From c049e9e4eee9ed2785e401ea55433987ba326835 Mon Sep 17 00:00:00 2001 From: Kira Pilot Date: Wed, 3 May 2023 13:17:58 +0000 Subject: [PATCH 03/21] added new route for template enterprise meta --- .vscode/settings.json | 1 + coderd/apidoc/docs.go | 58 +++++++++++++++++++ coderd/apidoc/swagger.json | 52 +++++++++++++++++ coderd/database/dump.sql | 4 +- .../000121_add_template_cleanup_ttls.down.sql | 2 + .../000121_add_template_cleanup_ttls.up.sql | 2 + coderd/database/models.go | 4 +- coderd/database/queries.sql.go | 40 ++++++++++--- coderd/database/queries/templates.sql | 4 +- coderd/database/sqlc.yaml | 2 + codersdk/templates.go | 5 ++ docs/admin/audit-logs.md | 24 ++++---- docs/api/enterprise.md | 55 ++++++++++++++++++ docs/api/schemas.md | 16 +++++ enterprise/audit/table.go | 2 + enterprise/coderd/coderd.go | 7 ++- enterprise/coderd/templates.go | 57 ++++++++++++++++++ site/src/api/typesGenerated.ts | 6 ++ 18 files changed, 314 insertions(+), 27 deletions(-) create mode 100644 coderd/database/migrations/000121_add_template_cleanup_ttls.down.sql create mode 100644 coderd/database/migrations/000121_add_template_cleanup_ttls.up.sql diff --git a/.vscode/settings.json b/.vscode/settings.json index a14ed55e9df97..fd36a7aac52e7 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -34,6 +34,7 @@ "Dsts", "embeddedpostgres", "enablements", + "enterprisemeta", "errgroup", "eventsourcemock", "Failf", diff --git a/coderd/apidoc/docs.go b/coderd/apidoc/docs.go index 4483ba6b802d7..d4c47d897a66d 100644 --- a/coderd/apidoc/docs.go +++ b/coderd/apidoc/docs.go @@ -2129,6 +2129,53 @@ const docTemplate = `{ } } }, + "/templates/{template}/enterprisemeta": { + "patch": { + "security": [ + { + "CoderSessionToken": [] + } + ], + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Enterprise" + ], + "summary": "Update template enterprise meta", + "operationId": "update-template-enterprise-meta", + "parameters": [ + { + "type": "string", + "format": "uuid", + "description": "Template ID", + "name": "template", + "in": "path", + "required": true + }, + { + "description": "Update template enterprise meta", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/codersdk.UpdateTemplateEnterpriseMeta" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/codersdk.Response" + } + } + } + } + }, "/templates/{template}/versions": { "get": { "security": [ @@ -9129,6 +9176,17 @@ const docTemplate = `{ } } }, + "codersdk.UpdateTemplateEnterpriseMeta": { + "type": "object", + "properties": { + "failure_ttl": { + "type": "integer" + }, + "inactivity_ttl": { + "type": "integer" + } + } + }, "codersdk.UpdateUserPasswordRequest": { "type": "object", "required": [ diff --git a/coderd/apidoc/swagger.json b/coderd/apidoc/swagger.json index 1fb14f664d45e..657eeaa2541e5 100644 --- a/coderd/apidoc/swagger.json +++ b/coderd/apidoc/swagger.json @@ -1853,6 +1853,47 @@ } } }, + "/templates/{template}/enterprisemeta": { + "patch": { + "security": [ + { + "CoderSessionToken": [] + } + ], + "consumes": ["application/json"], + "produces": ["application/json"], + "tags": ["Enterprise"], + "summary": "Update template enterprise meta", + "operationId": "update-template-enterprise-meta", + "parameters": [ + { + "type": "string", + "format": "uuid", + "description": "Template ID", + "name": "template", + "in": "path", + "required": true + }, + { + "description": "Update template enterprise meta", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/codersdk.UpdateTemplateEnterpriseMeta" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/codersdk.Response" + } + } + } + } + }, "/templates/{template}/versions": { "get": { "security": [ @@ -8218,6 +8259,17 @@ } } }, + "codersdk.UpdateTemplateEnterpriseMeta": { + "type": "object", + "properties": { + "failure_ttl": { + "type": "integer" + }, + "inactivity_ttl": { + "type": "integer" + } + } + }, "codersdk.UpdateUserPasswordRequest": { "type": "object", "required": ["password"], diff --git a/coderd/database/dump.sql b/coderd/database/dump.sql index bb8c12d2bc654..64d490ed14671 100644 --- a/coderd/database/dump.sql +++ b/coderd/database/dump.sql @@ -477,7 +477,9 @@ CREATE TABLE templates ( allow_user_cancel_workspace_jobs boolean DEFAULT true NOT NULL, max_ttl bigint DEFAULT '0'::bigint NOT NULL, allow_user_autostart boolean DEFAULT true NOT NULL, - allow_user_autostop boolean DEFAULT true NOT NULL + allow_user_autostop boolean DEFAULT true NOT NULL, + failure_ttl bigint DEFAULT 0 NOT NULL, + inactivity_ttl bigint DEFAULT 0 NOT NULL ); COMMENT ON COLUMN templates.default_ttl IS 'The default duration for autostop for workspaces created from this template.'; diff --git a/coderd/database/migrations/000121_add_template_cleanup_ttls.down.sql b/coderd/database/migrations/000121_add_template_cleanup_ttls.down.sql new file mode 100644 index 0000000000000..70fd50d21a66d --- /dev/null +++ b/coderd/database/migrations/000121_add_template_cleanup_ttls.down.sql @@ -0,0 +1,2 @@ +ALTER TABLE ONLY templates DROP COLUMN IF EXISTS failure_ttl; +ALTER TABLE ONLY templates DROP COLUMN IF EXISTS inactivity_ttl; diff --git a/coderd/database/migrations/000121_add_template_cleanup_ttls.up.sql b/coderd/database/migrations/000121_add_template_cleanup_ttls.up.sql new file mode 100644 index 0000000000000..980588e269d45 --- /dev/null +++ b/coderd/database/migrations/000121_add_template_cleanup_ttls.up.sql @@ -0,0 +1,2 @@ +ALTER TABLE ONLY templates ADD COLUMN IF NOT EXISTS failure_ttl BIGINT NOT NULL DEFAULT 0; +ALTER TABLE ONLY templates ADD COLUMN IF NOT EXISTS inactivity_ttl BIGINT NOT NULL DEFAULT 0; diff --git a/coderd/database/models.go b/coderd/database/models.go index bda061b89448d..71b15b093161d 100644 --- a/coderd/database/models.go +++ b/coderd/database/models.go @@ -1435,7 +1435,9 @@ type Template struct { // Allow users to specify an autostart schedule for workspaces (enterprise). AllowUserAutostart bool `db:"allow_user_autostart" json:"allow_user_autostart"` // Allow users to specify custom autostop values for workspaces (enterprise). - AllowUserAutostop bool `db:"allow_user_autostop" json:"allow_user_autostop"` + AllowUserAutostop bool `db:"allow_user_autostop" json:"allow_user_autostop"` + FailureTTL int64 `db:"failure_ttl" json:"failure_ttl"` + InactivityTTL int64 `db:"inactivity_ttl" json:"inactivity_ttl"` } type TemplateVersion struct { diff --git a/coderd/database/queries.sql.go b/coderd/database/queries.sql.go index e78e08632d835..ddfd6a8231a14 100644 --- a/coderd/database/queries.sql.go +++ b/coderd/database/queries.sql.go @@ -3482,7 +3482,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, default_ttl, created_by, icon, user_acl, group_acl, display_name, allow_user_cancel_workspace_jobs, max_ttl, allow_user_autostart, allow_user_autostop + id, created_at, updated_at, organization_id, deleted, name, provisioner, active_version_id, description, default_ttl, created_by, icon, user_acl, group_acl, display_name, allow_user_cancel_workspace_jobs, max_ttl, allow_user_autostart, allow_user_autostop, failure_ttl, inactivity_ttl FROM templates WHERE @@ -3514,13 +3514,15 @@ func (q *sqlQuerier) GetTemplateByID(ctx context.Context, id uuid.UUID) (Templat &i.MaxTTL, &i.AllowUserAutostart, &i.AllowUserAutostop, + &i.FailureTTL, + &i.InactivityTTL, ) return i, err } const getTemplateByOrganizationAndName = `-- name: GetTemplateByOrganizationAndName :one SELECT - id, created_at, updated_at, organization_id, deleted, name, provisioner, active_version_id, description, default_ttl, created_by, icon, user_acl, group_acl, display_name, allow_user_cancel_workspace_jobs, max_ttl, allow_user_autostart, allow_user_autostop + id, created_at, updated_at, organization_id, deleted, name, provisioner, active_version_id, description, default_ttl, created_by, icon, user_acl, group_acl, display_name, allow_user_cancel_workspace_jobs, max_ttl, allow_user_autostart, allow_user_autostop, failure_ttl, inactivity_ttl FROM templates WHERE @@ -3560,12 +3562,14 @@ func (q *sqlQuerier) GetTemplateByOrganizationAndName(ctx context.Context, arg G &i.MaxTTL, &i.AllowUserAutostart, &i.AllowUserAutostop, + &i.FailureTTL, + &i.InactivityTTL, ) return i, err } const getTemplates = `-- name: GetTemplates :many -SELECT id, created_at, updated_at, organization_id, deleted, name, provisioner, active_version_id, description, default_ttl, created_by, icon, user_acl, group_acl, display_name, allow_user_cancel_workspace_jobs, max_ttl, allow_user_autostart, allow_user_autostop 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, display_name, allow_user_cancel_workspace_jobs, max_ttl, allow_user_autostart, allow_user_autostop, failure_ttl, inactivity_ttl FROM templates ORDER BY (name, id) ASC ` @@ -3598,6 +3602,8 @@ func (q *sqlQuerier) GetTemplates(ctx context.Context) ([]Template, error) { &i.MaxTTL, &i.AllowUserAutostart, &i.AllowUserAutostop, + &i.FailureTTL, + &i.InactivityTTL, ); err != nil { return nil, err } @@ -3614,7 +3620,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, default_ttl, created_by, icon, user_acl, group_acl, display_name, allow_user_cancel_workspace_jobs, max_ttl, allow_user_autostart, allow_user_autostop + id, created_at, updated_at, organization_id, deleted, name, provisioner, active_version_id, description, default_ttl, created_by, icon, user_acl, group_acl, display_name, allow_user_cancel_workspace_jobs, max_ttl, allow_user_autostart, allow_user_autostop, failure_ttl, inactivity_ttl FROM templates WHERE @@ -3684,6 +3690,8 @@ func (q *sqlQuerier) GetTemplatesWithFilter(ctx context.Context, arg GetTemplate &i.MaxTTL, &i.AllowUserAutostart, &i.AllowUserAutostop, + &i.FailureTTL, + &i.InactivityTTL, ); err != nil { return nil, err } @@ -3717,7 +3725,7 @@ INSERT INTO allow_user_cancel_workspace_jobs ) 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, default_ttl, created_by, icon, user_acl, group_acl, display_name, allow_user_cancel_workspace_jobs, max_ttl, allow_user_autostart, allow_user_autostop + ($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, default_ttl, created_by, icon, user_acl, group_acl, display_name, allow_user_cancel_workspace_jobs, max_ttl, allow_user_autostart, allow_user_autostop, failure_ttl, inactivity_ttl ` type InsertTemplateParams struct { @@ -3775,6 +3783,8 @@ func (q *sqlQuerier) InsertTemplate(ctx context.Context, arg InsertTemplateParam &i.MaxTTL, &i.AllowUserAutostart, &i.AllowUserAutostop, + &i.FailureTTL, + &i.InactivityTTL, ) return i, err } @@ -3788,7 +3798,7 @@ SET WHERE id = $3 RETURNING - id, created_at, updated_at, organization_id, deleted, name, provisioner, active_version_id, description, default_ttl, created_by, icon, user_acl, group_acl, display_name, allow_user_cancel_workspace_jobs, max_ttl, allow_user_autostart, allow_user_autostop + id, created_at, updated_at, organization_id, deleted, name, provisioner, active_version_id, description, default_ttl, created_by, icon, user_acl, group_acl, display_name, allow_user_cancel_workspace_jobs, max_ttl, allow_user_autostart, allow_user_autostop, failure_ttl, inactivity_ttl ` type UpdateTemplateACLByIDParams struct { @@ -3820,6 +3830,8 @@ func (q *sqlQuerier) UpdateTemplateACLByID(ctx context.Context, arg UpdateTempla &i.MaxTTL, &i.AllowUserAutostart, &i.AllowUserAutostop, + &i.FailureTTL, + &i.InactivityTTL, ) return i, err } @@ -3875,11 +3887,13 @@ SET name = $4, icon = $5, display_name = $6, - allow_user_cancel_workspace_jobs = $7 + allow_user_cancel_workspace_jobs = $7, + failure_ttl = $8, + inactivity_ttl = $9 WHERE id = $1 RETURNING - id, created_at, updated_at, organization_id, deleted, name, provisioner, active_version_id, description, default_ttl, created_by, icon, user_acl, group_acl, display_name, allow_user_cancel_workspace_jobs, max_ttl, allow_user_autostart, allow_user_autostop + id, created_at, updated_at, organization_id, deleted, name, provisioner, active_version_id, description, default_ttl, created_by, icon, user_acl, group_acl, display_name, allow_user_cancel_workspace_jobs, max_ttl, allow_user_autostart, allow_user_autostop, failure_ttl, inactivity_ttl ` type UpdateTemplateMetaByIDParams struct { @@ -3890,6 +3904,8 @@ type UpdateTemplateMetaByIDParams struct { Icon string `db:"icon" json:"icon"` DisplayName string `db:"display_name" json:"display_name"` AllowUserCancelWorkspaceJobs bool `db:"allow_user_cancel_workspace_jobs" json:"allow_user_cancel_workspace_jobs"` + FailureTTL int64 `db:"failure_ttl" json:"failure_ttl"` + InactivityTTL int64 `db:"inactivity_ttl" json:"inactivity_ttl"` } func (q *sqlQuerier) UpdateTemplateMetaByID(ctx context.Context, arg UpdateTemplateMetaByIDParams) (Template, error) { @@ -3901,6 +3917,8 @@ func (q *sqlQuerier) UpdateTemplateMetaByID(ctx context.Context, arg UpdateTempl arg.Icon, arg.DisplayName, arg.AllowUserCancelWorkspaceJobs, + arg.FailureTTL, + arg.InactivityTTL, ) var i Template err := row.Scan( @@ -3923,6 +3941,8 @@ func (q *sqlQuerier) UpdateTemplateMetaByID(ctx context.Context, arg UpdateTempl &i.MaxTTL, &i.AllowUserAutostart, &i.AllowUserAutostop, + &i.FailureTTL, + &i.InactivityTTL, ) return i, err } @@ -3939,7 +3959,7 @@ SET WHERE id = $1 RETURNING - id, created_at, updated_at, organization_id, deleted, name, provisioner, active_version_id, description, default_ttl, created_by, icon, user_acl, group_acl, display_name, allow_user_cancel_workspace_jobs, max_ttl, allow_user_autostart, allow_user_autostop + id, created_at, updated_at, organization_id, deleted, name, provisioner, active_version_id, description, default_ttl, created_by, icon, user_acl, group_acl, display_name, allow_user_cancel_workspace_jobs, max_ttl, allow_user_autostart, allow_user_autostop, failure_ttl, inactivity_ttl ` type UpdateTemplateScheduleByIDParams struct { @@ -3981,6 +4001,8 @@ func (q *sqlQuerier) UpdateTemplateScheduleByID(ctx context.Context, arg UpdateT &i.MaxTTL, &i.AllowUserAutostart, &i.AllowUserAutostop, + &i.FailureTTL, + &i.InactivityTTL, ) return i, err } diff --git a/coderd/database/queries/templates.sql b/coderd/database/queries/templates.sql index 309cb1ce6b5b4..5fe3139df8c0d 100644 --- a/coderd/database/queries/templates.sql +++ b/coderd/database/queries/templates.sql @@ -104,7 +104,9 @@ SET name = $4, icon = $5, display_name = $6, - allow_user_cancel_workspace_jobs = $7 + allow_user_cancel_workspace_jobs = $7, + failure_ttl = $8, + inactivity_ttl = $9 WHERE id = $1 RETURNING diff --git a/coderd/database/sqlc.yaml b/coderd/database/sqlc.yaml index fcc5d777003ca..cd3e846afb430 100644 --- a/coderd/database/sqlc.yaml +++ b/coderd/database/sqlc.yaml @@ -51,6 +51,8 @@ overrides: template_max_ttl: TemplateMaxTTL motd_file: MOTDFile uuid: UUID + failure_ttl: FailureTTL + inactivity_ttl: InactivityTTL sql: - schema: "./dump.sql" diff --git a/codersdk/templates.go b/codersdk/templates.go index 5a7bcec6831ec..223db20de7f0a 100644 --- a/codersdk/templates.go +++ b/codersdk/templates.go @@ -97,6 +97,11 @@ type UpdateTemplateMeta struct { AllowUserCancelWorkspaceJobs bool `json:"allow_user_cancel_workspace_jobs,omitempty"` } +type UpdateTemplateEnterpriseMeta struct { + FailureTTL int64 `json:"failure_ttl,omitempty"` + InactivityTTL int64 `json:"inactivity_ttl,omitempty"` +} + type TemplateExample struct { ID string `json:"id" format:"uuid"` URL string `json:"url"` diff --git a/docs/admin/audit-logs.md b/docs/admin/audit-logs.md index 643ae0d76e9c6..7bad515cdc341 100644 --- a/docs/admin/audit-logs.md +++ b/docs/admin/audit-logs.md @@ -9,18 +9,18 @@ We track the following resources: -| Resource | | -| -------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| APIKey
login, logout, register, create, delete |
FieldTracked
created_attrue
expires_attrue
hashed_secretfalse
idfalse
ip_addressfalse
last_usedtrue
lifetime_secondsfalse
login_typefalse
scopefalse
token_namefalse
updated_atfalse
user_idtrue
| -| Group
create, write, delete |
FieldTracked
avatar_urltrue
idtrue
memberstrue
nametrue
organization_idfalse
quota_allowancetrue
| -| GitSSHKey
create |
FieldTracked
created_atfalse
private_keytrue
public_keytrue
updated_atfalse
user_idtrue
| -| License
create, delete |
FieldTracked
exptrue
idfalse
jwtfalse
uploaded_attrue
uuidtrue
| -| Template
write, delete |
FieldTracked
active_version_idtrue
allow_user_autostarttrue
allow_user_autostoptrue
allow_user_cancel_workspace_jobstrue
created_atfalse
created_bytrue
default_ttltrue
deletedfalse
descriptiontrue
display_nametrue
group_acltrue
icontrue
idtrue
max_ttltrue
nametrue
organization_idfalse
provisionertrue
updated_atfalse
user_acltrue
| -| TemplateVersion
create, write |
FieldTracked
created_atfalse
created_bytrue
git_auth_providersfalse
idtrue
job_idfalse
nametrue
organization_idfalse
readmetrue
template_idtrue
updated_atfalse
| -| User
create, write, delete |
FieldTracked
avatar_urlfalse
created_atfalse
deletedtrue
emailtrue
hashed_passwordtrue
idtrue
last_seen_atfalse
login_typefalse
rbac_rolestrue
statustrue
updated_atfalse
usernametrue
| -| Workspace
create, write, delete |
FieldTracked
autostart_scheduletrue
created_atfalse
deletedfalse
idtrue
last_used_atfalse
nametrue
organization_idfalse
owner_idtrue
template_idtrue
ttltrue
updated_atfalse
| -| WorkspaceBuild
start, stop |
FieldTracked
build_numberfalse
created_atfalse
daily_costfalse
deadlinefalse
idfalse
initiator_idfalse
job_idfalse
max_deadlinefalse
provisioner_statefalse
reasonfalse
template_version_idtrue
transitionfalse
updated_atfalse
workspace_idfalse
| -| WorkspaceProxy
|
FieldTracked
created_attrue
deletedfalse
display_nametrue
icontrue
idtrue
nametrue
token_hashed_secrettrue
updated_atfalse
urltrue
wildcard_hostnametrue
| +| Resource | | +| -------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| APIKey
login, logout, register, create, delete |
FieldTracked
created_attrue
expires_attrue
hashed_secretfalse
idfalse
ip_addressfalse
last_usedtrue
lifetime_secondsfalse
login_typefalse
scopefalse
token_namefalse
updated_atfalse
user_idtrue
| +| Group
create, write, delete |
FieldTracked
avatar_urltrue
idtrue
memberstrue
nametrue
organization_idfalse
quota_allowancetrue
| +| GitSSHKey
create |
FieldTracked
created_atfalse
private_keytrue
public_keytrue
updated_atfalse
user_idtrue
| +| License
create, delete |
FieldTracked
exptrue
idfalse
jwtfalse
uploaded_attrue
uuidtrue
| +| Template
write, delete |
FieldTracked
active_version_idtrue
allow_user_autostarttrue
allow_user_autostoptrue
allow_user_cancel_workspace_jobstrue
created_atfalse
created_bytrue
default_ttltrue
deletedfalse
descriptiontrue
display_nametrue
failure_ttltrue
group_acltrue
icontrue
idtrue
inactivity_ttltrue
max_ttltrue
nametrue
organization_idfalse
provisionertrue
updated_atfalse
user_acltrue
| +| TemplateVersion
create, write |
FieldTracked
created_atfalse
created_bytrue
git_auth_providersfalse
idtrue
job_idfalse
nametrue
organization_idfalse
readmetrue
template_idtrue
updated_atfalse
| +| User
create, write, delete |
FieldTracked
avatar_urlfalse
created_atfalse
deletedtrue
emailtrue
hashed_passwordtrue
idtrue
last_seen_atfalse
login_typefalse
rbac_rolestrue
statustrue
updated_atfalse
usernametrue
| +| Workspace
create, write, delete |
FieldTracked
autostart_scheduletrue
created_atfalse
deletedfalse
idtrue
last_used_atfalse
nametrue
organization_idfalse
owner_idtrue
template_idtrue
ttltrue
updated_atfalse
| +| WorkspaceBuild
start, stop |
FieldTracked
build_numberfalse
created_atfalse
daily_costfalse
deadlinefalse
idfalse
initiator_idfalse
job_idfalse
max_deadlinefalse
provisioner_statefalse
reasonfalse
template_version_idtrue
transitionfalse
updated_atfalse
workspace_idfalse
| +| WorkspaceProxy
|
FieldTracked
created_attrue
deletedfalse
display_nametrue
icontrue
idtrue
nametrue
token_hashed_secrettrue
updated_atfalse
urltrue
wildcard_hostnametrue
| diff --git a/docs/api/enterprise.md b/docs/api/enterprise.md index fbee85b9970f1..f3dc94b9e952e 100644 --- a/docs/api/enterprise.md +++ b/docs/api/enterprise.md @@ -1122,6 +1122,61 @@ curl -X PATCH http://coder-server:8080/api/v2/templates/{template}/acl \ To perform this operation, you must be authenticated. [Learn more](authentication.md). +## Update template enterprise meta + +### Code samples + +```shell +# Example request using curl +curl -X PATCH http://coder-server:8080/api/v2/templates/{template}/enterprisemeta \ + -H 'Content-Type: application/json' \ + -H 'Accept: application/json' \ + -H 'Coder-Session-Token: API_KEY' +``` + +`PATCH /templates/{template}/enterprisemeta` + +> Body parameter + +```json +{ + "failure_ttl": 0, + "inactivity_ttl": 0 +} +``` + +### Parameters + +| Name | In | Type | Required | Description | +| ---------- | ---- | ---------------------------------------------------------------------------------------- | -------- | ------------------------------- | +| `template` | path | string(uuid) | true | Template ID | +| `body` | body | [codersdk.UpdateTemplateEnterpriseMeta](schemas.md#codersdkupdatetemplateenterprisemeta) | true | Update template enterprise meta | + +### Example responses + +> 200 Response + +```json +{ + "detail": "string", + "message": "string", + "validations": [ + { + "detail": "string", + "field": "string" + } + ] +} +``` + +### Responses + +| Status | Meaning | Description | Schema | +| ------ | ------------------------------------------------------- | ----------- | ------------------------------------------------ | +| 200 | [OK](https://tools.ietf.org/html/rfc7231#section-6.3.1) | OK | [codersdk.Response](schemas.md#codersdkresponse) | + +To perform this operation, you must be authenticated. [Learn more](authentication.md). + ## Get workspace quota by user ### Code samples diff --git a/docs/api/schemas.md b/docs/api/schemas.md index 6c6dfcd692ccd..7f1ade66d059b 100644 --- a/docs/api/schemas.md +++ b/docs/api/schemas.md @@ -4368,6 +4368,22 @@ Parameter represents a set value for the scope. | `user_perms` | object | false | | | | » `[any property]` | [codersdk.TemplateRole](#codersdktemplaterole) | false | | | +## codersdk.UpdateTemplateEnterpriseMeta + +```json +{ + "failure_ttl": 0, + "inactivity_ttl": 0 +} +``` + +### Properties + +| Name | Type | Required | Restrictions | Description | +| ---------------- | ------- | -------- | ------------ | ----------- | +| `failure_ttl` | integer | false | | | +| `inactivity_ttl` | integer | false | | | + ## codersdk.UpdateUserPasswordRequest ```json diff --git a/enterprise/audit/table.go b/enterprise/audit/table.go index 38378cf678be1..c8b90b8b23567 100644 --- a/enterprise/audit/table.go +++ b/enterprise/audit/table.go @@ -75,6 +75,8 @@ var auditableResourcesTypes = map[any]map[string]Action{ "allow_user_autostop": ActionTrack, "allow_user_cancel_workspace_jobs": ActionTrack, "max_ttl": ActionTrack, + "failure_ttl": ActionTrack, + "inactivity_ttl": ActionTrack, }, &database.TemplateVersion{}: { "id": ActionTrack, diff --git a/enterprise/coderd/coderd.go b/enterprise/coderd/coderd.go index 6fe25261ab9ad..5a0440a5e87bf 100644 --- a/enterprise/coderd/coderd.go +++ b/enterprise/coderd/coderd.go @@ -148,14 +148,15 @@ func New(ctx context.Context, options *Options) (*API, error) { r.Get("/", api.provisionerDaemons) r.Get("/serve", api.provisionerDaemonServe) }) - r.Route("/templates/{template}/acl", func(r chi.Router) { + r.Route("/templates/{template}", func(r chi.Router) { r.Use( api.templateRBACEnabledMW, apiKeyMiddleware, httpmw.ExtractTemplateParam(api.Database), ) - r.Get("/", api.templateACL) - r.Patch("/", api.patchTemplateACL) + r.Get("/acl", api.templateACL) + r.Patch("/acl", api.patchTemplateACL) + r.Patch("/enterprisemeta", api.patchEnterpriseMeta) }) r.Route("/groups/{group}", func(r chi.Router) { r.Use( diff --git a/enterprise/coderd/templates.go b/enterprise/coderd/templates.go index cbd45471547fc..efa1612d15fee 100644 --- a/enterprise/coderd/templates.go +++ b/enterprise/coderd/templates.go @@ -185,6 +185,63 @@ func (api *API) patchTemplateACL(rw http.ResponseWriter, r *http.Request) { }) } +// @Summary Update template enterprise meta +// @ID update-template-enterprise-meta +// @Security CoderSessionToken +// @Accept json +// @Produce json +// @Tags Enterprise +// @Param template path string true "Template ID" format(uuid) +// @Param request body codersdk.UpdateTemplateEnterpriseMeta true "Update template enterprise meta" +// @Success 200 {object} codersdk.Response +// @Router /templates/{template}/enterprisemeta [patch] +func (api *API) patchEnterpriseMeta(rw http.ResponseWriter, r *http.Request) { + var ( + ctx = r.Context() + template = httpmw.TemplateParam(r) + auditor = api.AGPL.Auditor.Load() + aReq, commitAudit = audit.InitRequest[database.Template](rw, &audit.RequestParams{ + Audit: *auditor, + Log: api.Logger, + Request: r, + Action: database.AuditActionWrite, + }) + ) + defer commitAudit() + aReq.Old = template + + var req codersdk.UpdateTemplateEnterpriseMeta + if !httpapi.Read(ctx, rw, r, &req) { + return + } + + var validErrs []codersdk.ValidationError + if req.FailureTTL < 0 { + validErrs = append(validErrs, codersdk.ValidationError{Field: "failure_ttl", Detail: "Must be a positive integer."}) + } + if req.InactivityTTL < 0 { + validErrs = append(validErrs, codersdk.ValidationError{Field: "inactivity_ttl", Detail: "Must be a positive integer."}) + } + if len(validErrs) > 0 { + httpapi.Write(ctx, rw, http.StatusBadRequest, codersdk.Response{ + Message: "Invalid request to update template enterprise metadata!", + Validations: validErrs, + }) + return + } + + // do we need a transaction + // updated, err := tx.UpdateTemplateMetaByID(ctx, database.UpdateTemplateMetaByIDParams{ + // ID: template.ID, + // UpdatedAt: database.Now(), + // FailureTTL: req.FailureTTL, + // InactivityTTL: req.InactivityTTL, + // }) + // if err != nil { + // return xerrors.Errorf("update template enterprise metadata: %w", err) + // } +} + // nolint TODO fix stupid flag. func validateTemplateACLPerms(ctx context.Context, db database.Store, perms map[string]codersdk.TemplateRole, field string, isUser bool) []codersdk.ValidationError { var validErrs []codersdk.ValidationError diff --git a/site/src/api/typesGenerated.ts b/site/src/api/typesGenerated.ts index aa72e51f3f687..ef7722fe8f8f8 100644 --- a/site/src/api/typesGenerated.ts +++ b/site/src/api/typesGenerated.ts @@ -999,6 +999,12 @@ export interface UpdateTemplateACL { readonly group_perms?: Record } +// From codersdk/templates.go +export interface UpdateTemplateEnterpriseMeta { + readonly failure_ttl?: number + readonly inactivity_ttl?: number +} + // From codersdk/templates.go export interface UpdateTemplateMeta { readonly name?: string From 537ced7def1deaf41ef6a279b220322294e2f52a Mon Sep 17 00:00:00 2001 From: Kira Pilot Date: Wed, 3 May 2023 19:06:29 +0000 Subject: [PATCH 04/21] removing new route; repurposing old --- coderd/apidoc/docs.go | 58 --------------------------- coderd/apidoc/swagger.json | 52 ------------------------ coderd/database/queries.sql.go | 16 ++++---- coderd/database/queries/templates.sql | 8 ++-- coderd/schedule/template.go | 14 +++++-- coderd/templates.go | 31 +++++++++----- codersdk/templates.go | 7 +--- docs/api/enterprise.md | 55 ------------------------- docs/api/schemas.md | 16 -------- enterprise/coderd/coderd.go | 8 ++-- enterprise/coderd/templates.go | 57 -------------------------- site/src/api/typesGenerated.ts | 8 +--- 12 files changed, 50 insertions(+), 280 deletions(-) diff --git a/coderd/apidoc/docs.go b/coderd/apidoc/docs.go index d4c47d897a66d..4483ba6b802d7 100644 --- a/coderd/apidoc/docs.go +++ b/coderd/apidoc/docs.go @@ -2129,53 +2129,6 @@ const docTemplate = `{ } } }, - "/templates/{template}/enterprisemeta": { - "patch": { - "security": [ - { - "CoderSessionToken": [] - } - ], - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "Enterprise" - ], - "summary": "Update template enterprise meta", - "operationId": "update-template-enterprise-meta", - "parameters": [ - { - "type": "string", - "format": "uuid", - "description": "Template ID", - "name": "template", - "in": "path", - "required": true - }, - { - "description": "Update template enterprise meta", - "name": "request", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/codersdk.UpdateTemplateEnterpriseMeta" - } - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/codersdk.Response" - } - } - } - } - }, "/templates/{template}/versions": { "get": { "security": [ @@ -9176,17 +9129,6 @@ const docTemplate = `{ } } }, - "codersdk.UpdateTemplateEnterpriseMeta": { - "type": "object", - "properties": { - "failure_ttl": { - "type": "integer" - }, - "inactivity_ttl": { - "type": "integer" - } - } - }, "codersdk.UpdateUserPasswordRequest": { "type": "object", "required": [ diff --git a/coderd/apidoc/swagger.json b/coderd/apidoc/swagger.json index 657eeaa2541e5..1fb14f664d45e 100644 --- a/coderd/apidoc/swagger.json +++ b/coderd/apidoc/swagger.json @@ -1853,47 +1853,6 @@ } } }, - "/templates/{template}/enterprisemeta": { - "patch": { - "security": [ - { - "CoderSessionToken": [] - } - ], - "consumes": ["application/json"], - "produces": ["application/json"], - "tags": ["Enterprise"], - "summary": "Update template enterprise meta", - "operationId": "update-template-enterprise-meta", - "parameters": [ - { - "type": "string", - "format": "uuid", - "description": "Template ID", - "name": "template", - "in": "path", - "required": true - }, - { - "description": "Update template enterprise meta", - "name": "request", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/codersdk.UpdateTemplateEnterpriseMeta" - } - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/codersdk.Response" - } - } - } - } - }, "/templates/{template}/versions": { "get": { "security": [ @@ -8259,17 +8218,6 @@ } } }, - "codersdk.UpdateTemplateEnterpriseMeta": { - "type": "object", - "properties": { - "failure_ttl": { - "type": "integer" - }, - "inactivity_ttl": { - "type": "integer" - } - } - }, "codersdk.UpdateUserPasswordRequest": { "type": "object", "required": ["password"], diff --git a/coderd/database/queries.sql.go b/coderd/database/queries.sql.go index ddfd6a8231a14..35f09d165ecc9 100644 --- a/coderd/database/queries.sql.go +++ b/coderd/database/queries.sql.go @@ -3887,9 +3887,7 @@ SET name = $4, icon = $5, display_name = $6, - allow_user_cancel_workspace_jobs = $7, - failure_ttl = $8, - inactivity_ttl = $9 + allow_user_cancel_workspace_jobs = $7 WHERE id = $1 RETURNING @@ -3904,8 +3902,6 @@ type UpdateTemplateMetaByIDParams struct { Icon string `db:"icon" json:"icon"` DisplayName string `db:"display_name" json:"display_name"` AllowUserCancelWorkspaceJobs bool `db:"allow_user_cancel_workspace_jobs" json:"allow_user_cancel_workspace_jobs"` - FailureTTL int64 `db:"failure_ttl" json:"failure_ttl"` - InactivityTTL int64 `db:"inactivity_ttl" json:"inactivity_ttl"` } func (q *sqlQuerier) UpdateTemplateMetaByID(ctx context.Context, arg UpdateTemplateMetaByIDParams) (Template, error) { @@ -3917,8 +3913,6 @@ func (q *sqlQuerier) UpdateTemplateMetaByID(ctx context.Context, arg UpdateTempl arg.Icon, arg.DisplayName, arg.AllowUserCancelWorkspaceJobs, - arg.FailureTTL, - arg.InactivityTTL, ) var i Template err := row.Scan( @@ -3955,7 +3949,9 @@ SET allow_user_autostart = $3, allow_user_autostop = $4, default_ttl = $5, - max_ttl = $6 + max_ttl = $6, + failure_ttl = $7, + inactivity_ttl = $8 WHERE id = $1 RETURNING @@ -3969,6 +3965,8 @@ type UpdateTemplateScheduleByIDParams struct { AllowUserAutostop bool `db:"allow_user_autostop" json:"allow_user_autostop"` DefaultTTL int64 `db:"default_ttl" json:"default_ttl"` MaxTTL int64 `db:"max_ttl" json:"max_ttl"` + FailureTTL int64 `db:"failure_ttl" json:"failure_ttl"` + InactivityTTL int64 `db:"inactivity_ttl" json:"inactivity_ttl"` } func (q *sqlQuerier) UpdateTemplateScheduleByID(ctx context.Context, arg UpdateTemplateScheduleByIDParams) (Template, error) { @@ -3979,6 +3977,8 @@ func (q *sqlQuerier) UpdateTemplateScheduleByID(ctx context.Context, arg UpdateT arg.AllowUserAutostop, arg.DefaultTTL, arg.MaxTTL, + arg.FailureTTL, + arg.InactivityTTL, ) var i Template err := row.Scan( diff --git a/coderd/database/queries/templates.sql b/coderd/database/queries/templates.sql index 5fe3139df8c0d..d9eb13b0f6ac3 100644 --- a/coderd/database/queries/templates.sql +++ b/coderd/database/queries/templates.sql @@ -104,9 +104,7 @@ SET name = $4, icon = $5, display_name = $6, - allow_user_cancel_workspace_jobs = $7, - failure_ttl = $8, - inactivity_ttl = $9 + allow_user_cancel_workspace_jobs = $7 WHERE id = $1 RETURNING @@ -120,7 +118,9 @@ SET allow_user_autostart = $3, allow_user_autostop = $4, default_ttl = $5, - max_ttl = $6 + max_ttl = $6, + failure_ttl = $7, + inactivity_ttl = $8 WHERE id = $1 RETURNING diff --git a/coderd/schedule/template.go b/coderd/schedule/template.go index c445edee7dfe0..084218cb56a42 100644 --- a/coderd/schedule/template.go +++ b/coderd/schedule/template.go @@ -18,6 +18,10 @@ type TemplateScheduleOptions struct { // // If set, users cannot disable automatic workspace shutdown. MaxTTL time.Duration `json:"max_ttl"` + // If FailureTTL is set, all failed workspaces will be stopped automatically after this time has elapsed. + FailureTTL time.Duration `json:"failure_ttl"` + // If InactivityTTL is set, all inactive workspaces will be deleted automatically after this time has elapsed. + InactivityTTL time.Duration `json:"inactivity_ttl"` } // TemplateScheduleStore provides an interface for retrieving template @@ -47,9 +51,11 @@ func (*agplTemplateScheduleStore) GetTemplateScheduleOptions(ctx context.Context UserAutostartEnabled: true, UserAutostopEnabled: true, DefaultTTL: time.Duration(tpl.DefaultTTL), - // Disregard the value in the database, since MaxTTL is an enterprise - // feature. - MaxTTL: 0, + // Disregard the values in the database, since MaxTTL, FailureTTL, and InactivityTTL are enterprise + // features. + MaxTTL: 0, + FailureTTL: 0, + InactivityTTL: 0, }, nil } @@ -68,5 +74,7 @@ func (*agplTemplateScheduleStore) SetTemplateScheduleOptions(ctx context.Context AllowUserAutostart: tpl.AllowUserAutostart, AllowUserAutostop: tpl.AllowUserAutostop, MaxTTL: tpl.MaxTTL, + FailureTTL: tpl.FailureTTL, + InactivityTTL: tpl.InactivityTTL, }) } diff --git a/coderd/templates.go b/coderd/templates.go index 2250d52698c78..615c2459ec458 100644 --- a/coderd/templates.go +++ b/coderd/templates.go @@ -476,6 +476,12 @@ func (api *API) patchTemplateMeta(rw http.ResponseWriter, r *http.Request) { if req.MaxTTLMillis != 0 && req.DefaultTTLMillis > req.MaxTTLMillis { validErrs = append(validErrs, codersdk.ValidationError{Field: "default_ttl_ms", Detail: "Must be less than or equal to max_ttl_ms if max_ttl_ms is set."}) } + if req.FailureTTLMillis < 0 { + validErrs = append(validErrs, codersdk.ValidationError{Field: "failure_ttl_ms", Detail: "Must be a positive integer."}) + } + if req.InactivityTTLMillis < 0 { + validErrs = append(validErrs, codersdk.ValidationError{Field: "inactivity_ttl_ms", Detail: "Must be a positive integer."}) + } if len(validErrs) > 0 { httpapi.Write(ctx, rw, http.StatusBadRequest, codersdk.Response{ @@ -495,18 +501,14 @@ func (api *API) patchTemplateMeta(rw http.ResponseWriter, r *http.Request) { req.AllowUserAutostop == template.AllowUserAutostop && req.AllowUserCancelWorkspaceJobs == template.AllowUserCancelWorkspaceJobs && req.DefaultTTLMillis == time.Duration(template.DefaultTTL).Milliseconds() && - req.MaxTTLMillis == time.Duration(template.MaxTTL).Milliseconds() { + req.MaxTTLMillis == time.Duration(template.MaxTTL).Milliseconds() && + req.FailureTTLMillis == time.Duration(template.FailureTTL).Milliseconds() && + req.InactivityTTLMillis == time.Duration(template.InactivityTTL).Milliseconds() { return nil } - // Update template metadata -- empty fields are not overwritten, - // except for display_name, description, icon, and default_ttl. - // These exceptions are required to clear content of these fields with UI. + // Users should not be able to clear the template name in the UI name := req.Name - displayName := req.DisplayName - desc := req.Description - icon := req.Icon - if name == "" { name = template.Name } @@ -516,9 +518,9 @@ func (api *API) patchTemplateMeta(rw http.ResponseWriter, r *http.Request) { ID: template.ID, UpdatedAt: database.Now(), Name: name, - DisplayName: displayName, - Description: desc, - Icon: icon, + DisplayName: req.DisplayName, + Description: req.Description, + Icon: req.Icon, AllowUserCancelWorkspaceJobs: req.AllowUserCancelWorkspaceJobs, }) if err != nil { @@ -527,8 +529,13 @@ func (api *API) patchTemplateMeta(rw http.ResponseWriter, r *http.Request) { defaultTTL := time.Duration(req.DefaultTTLMillis) * time.Millisecond maxTTL := time.Duration(req.MaxTTLMillis) * time.Millisecond + failureTTL := time.Duration(req.FailureTTLMillis) * time.Millisecond + inactivityTTL := time.Duration(req.InactivityTTLMillis) * time.Millisecond + if defaultTTL != time.Duration(template.DefaultTTL) || maxTTL != time.Duration(template.MaxTTL) || + failureTTL != time.Duration(template.FailureTTL) || + inactivityTTL != time.Duration(template.InactivityTTL) || req.AllowUserAutostart != template.AllowUserAutostart || req.AllowUserAutostop != template.AllowUserAutostop { updated, err = (*api.TemplateScheduleStore.Load()).SetTemplateScheduleOptions(ctx, tx, updated, schedule.TemplateScheduleOptions{ @@ -539,6 +546,8 @@ func (api *API) patchTemplateMeta(rw http.ResponseWriter, r *http.Request) { UserAutostopEnabled: req.AllowUserAutostop, DefaultTTL: defaultTTL, MaxTTL: maxTTL, + FailureTTL: failureTTL, + InactivityTTL: inactivityTTL, }) if err != nil { return xerrors.Errorf("set template schedule options: %w", err) diff --git a/codersdk/templates.go b/codersdk/templates.go index 223db20de7f0a..7b5457bec2f0b 100644 --- a/codersdk/templates.go +++ b/codersdk/templates.go @@ -95,11 +95,8 @@ type UpdateTemplateMeta struct { AllowUserAutostart bool `json:"allow_user_autostart,omitempty"` AllowUserAutostop bool `json:"allow_user_autostop,omitempty"` AllowUserCancelWorkspaceJobs bool `json:"allow_user_cancel_workspace_jobs,omitempty"` -} - -type UpdateTemplateEnterpriseMeta struct { - FailureTTL int64 `json:"failure_ttl,omitempty"` - InactivityTTL int64 `json:"inactivity_ttl,omitempty"` + FailureTTLMillis int64 `json:"failure_ttl_ms,omitempty"` + InactivityTTLMillis int64 `json:"inactivity_ttl_ms,omitempty"` } type TemplateExample struct { diff --git a/docs/api/enterprise.md b/docs/api/enterprise.md index f3dc94b9e952e..fbee85b9970f1 100644 --- a/docs/api/enterprise.md +++ b/docs/api/enterprise.md @@ -1122,61 +1122,6 @@ curl -X PATCH http://coder-server:8080/api/v2/templates/{template}/acl \ To perform this operation, you must be authenticated. [Learn more](authentication.md). -## Update template enterprise meta - -### Code samples - -```shell -# Example request using curl -curl -X PATCH http://coder-server:8080/api/v2/templates/{template}/enterprisemeta \ - -H 'Content-Type: application/json' \ - -H 'Accept: application/json' \ - -H 'Coder-Session-Token: API_KEY' -``` - -`PATCH /templates/{template}/enterprisemeta` - -> Body parameter - -```json -{ - "failure_ttl": 0, - "inactivity_ttl": 0 -} -``` - -### Parameters - -| Name | In | Type | Required | Description | -| ---------- | ---- | ---------------------------------------------------------------------------------------- | -------- | ------------------------------- | -| `template` | path | string(uuid) | true | Template ID | -| `body` | body | [codersdk.UpdateTemplateEnterpriseMeta](schemas.md#codersdkupdatetemplateenterprisemeta) | true | Update template enterprise meta | - -### Example responses - -> 200 Response - -```json -{ - "detail": "string", - "message": "string", - "validations": [ - { - "detail": "string", - "field": "string" - } - ] -} -``` - -### Responses - -| Status | Meaning | Description | Schema | -| ------ | ------------------------------------------------------- | ----------- | ------------------------------------------------ | -| 200 | [OK](https://tools.ietf.org/html/rfc7231#section-6.3.1) | OK | [codersdk.Response](schemas.md#codersdkresponse) | - -To perform this operation, you must be authenticated. [Learn more](authentication.md). - ## Get workspace quota by user ### Code samples diff --git a/docs/api/schemas.md b/docs/api/schemas.md index 7f1ade66d059b..6c6dfcd692ccd 100644 --- a/docs/api/schemas.md +++ b/docs/api/schemas.md @@ -4368,22 +4368,6 @@ Parameter represents a set value for the scope. | `user_perms` | object | false | | | | » `[any property]` | [codersdk.TemplateRole](#codersdktemplaterole) | false | | | -## codersdk.UpdateTemplateEnterpriseMeta - -```json -{ - "failure_ttl": 0, - "inactivity_ttl": 0 -} -``` - -### Properties - -| Name | Type | Required | Restrictions | Description | -| ---------------- | ------- | -------- | ------------ | ----------- | -| `failure_ttl` | integer | false | | | -| `inactivity_ttl` | integer | false | | | - ## codersdk.UpdateUserPasswordRequest ```json diff --git a/enterprise/coderd/coderd.go b/enterprise/coderd/coderd.go index 5a0440a5e87bf..990faae898cb0 100644 --- a/enterprise/coderd/coderd.go +++ b/enterprise/coderd/coderd.go @@ -148,15 +148,14 @@ func New(ctx context.Context, options *Options) (*API, error) { r.Get("/", api.provisionerDaemons) r.Get("/serve", api.provisionerDaemonServe) }) - r.Route("/templates/{template}", func(r chi.Router) { + r.Route("/templates/{template}/acl", func(r chi.Router) { r.Use( api.templateRBACEnabledMW, apiKeyMiddleware, httpmw.ExtractTemplateParam(api.Database), ) - r.Get("/acl", api.templateACL) - r.Patch("/acl", api.patchTemplateACL) - r.Patch("/enterprisemeta", api.patchEnterpriseMeta) + r.Get("/", api.templateACL) + r.Patch("/", api.patchTemplateACL) }) r.Route("/groups/{group}", func(r chi.Router) { r.Use( @@ -323,7 +322,6 @@ func (api *API) updateEntitlements(ctx context.Context) error { codersdk.FeatureExternalProvisionerDaemons: true, codersdk.FeatureAdvancedTemplateScheduling: true, codersdk.FeatureWorkspaceProxy: true, - codersdk.FeatureWorkspaceActions: true, }) if err != nil { return err diff --git a/enterprise/coderd/templates.go b/enterprise/coderd/templates.go index efa1612d15fee..cbd45471547fc 100644 --- a/enterprise/coderd/templates.go +++ b/enterprise/coderd/templates.go @@ -185,63 +185,6 @@ func (api *API) patchTemplateACL(rw http.ResponseWriter, r *http.Request) { }) } -// @Summary Update template enterprise meta -// @ID update-template-enterprise-meta -// @Security CoderSessionToken -// @Accept json -// @Produce json -// @Tags Enterprise -// @Param template path string true "Template ID" format(uuid) -// @Param request body codersdk.UpdateTemplateEnterpriseMeta true "Update template enterprise meta" -// @Success 200 {object} codersdk.Response -// @Router /templates/{template}/enterprisemeta [patch] -func (api *API) patchEnterpriseMeta(rw http.ResponseWriter, r *http.Request) { - var ( - ctx = r.Context() - template = httpmw.TemplateParam(r) - auditor = api.AGPL.Auditor.Load() - aReq, commitAudit = audit.InitRequest[database.Template](rw, &audit.RequestParams{ - Audit: *auditor, - Log: api.Logger, - Request: r, - Action: database.AuditActionWrite, - }) - ) - defer commitAudit() - aReq.Old = template - - var req codersdk.UpdateTemplateEnterpriseMeta - if !httpapi.Read(ctx, rw, r, &req) { - return - } - - var validErrs []codersdk.ValidationError - if req.FailureTTL < 0 { - validErrs = append(validErrs, codersdk.ValidationError{Field: "failure_ttl", Detail: "Must be a positive integer."}) - } - if req.InactivityTTL < 0 { - validErrs = append(validErrs, codersdk.ValidationError{Field: "inactivity_ttl", Detail: "Must be a positive integer."}) - } - if len(validErrs) > 0 { - httpapi.Write(ctx, rw, http.StatusBadRequest, codersdk.Response{ - Message: "Invalid request to update template enterprise metadata!", - Validations: validErrs, - }) - return - } - - // do we need a transaction - // updated, err := tx.UpdateTemplateMetaByID(ctx, database.UpdateTemplateMetaByIDParams{ - // ID: template.ID, - // UpdatedAt: database.Now(), - // FailureTTL: req.FailureTTL, - // InactivityTTL: req.InactivityTTL, - // }) - // if err != nil { - // return xerrors.Errorf("update template enterprise metadata: %w", err) - // } -} - // nolint TODO fix stupid flag. func validateTemplateACLPerms(ctx context.Context, db database.Store, perms map[string]codersdk.TemplateRole, field string, isUser bool) []codersdk.ValidationError { var validErrs []codersdk.ValidationError diff --git a/site/src/api/typesGenerated.ts b/site/src/api/typesGenerated.ts index ef7722fe8f8f8..95dab12f6edcd 100644 --- a/site/src/api/typesGenerated.ts +++ b/site/src/api/typesGenerated.ts @@ -999,12 +999,6 @@ export interface UpdateTemplateACL { readonly group_perms?: Record } -// From codersdk/templates.go -export interface UpdateTemplateEnterpriseMeta { - readonly failure_ttl?: number - readonly inactivity_ttl?: number -} - // From codersdk/templates.go export interface UpdateTemplateMeta { readonly name?: string @@ -1016,6 +1010,8 @@ export interface UpdateTemplateMeta { readonly allow_user_autostart?: boolean readonly allow_user_autostop?: boolean readonly allow_user_cancel_workspace_jobs?: boolean + readonly failure_ttl_ms?: number + readonly inactivity_ttl_ms?: number } // From codersdk/users.go From cd6a485602f6d3af07c8c87b9615b6f183a9c91a Mon Sep 17 00:00:00 2001 From: Kira Pilot Date: Wed, 3 May 2023 19:49:41 +0000 Subject: [PATCH 05/21] add new fields to get endpoints --- coderd/database/modelqueries.go | 2 ++ coderd/templates.go | 2 ++ codersdk/templates.go | 6 ++++++ site/src/api/typesGenerated.ts | 2 ++ 4 files changed, 12 insertions(+) diff --git a/coderd/database/modelqueries.go b/coderd/database/modelqueries.go index 162b91cf5d7a9..8a105ec52ca02 100644 --- a/coderd/database/modelqueries.go +++ b/coderd/database/modelqueries.go @@ -80,6 +80,8 @@ func (q *sqlQuerier) GetAuthorizedTemplates(ctx context.Context, arg GetTemplate &i.MaxTTL, &i.AllowUserAutostart, &i.AllowUserAutostop, + &i.FailureTTL, + &i.InactivityTTL, ); err != nil { return nil, err } diff --git a/coderd/templates.go b/coderd/templates.go index 615c2459ec458..c66c5875217bf 100644 --- a/coderd/templates.go +++ b/coderd/templates.go @@ -687,5 +687,7 @@ func (api *API) convertTemplate( AllowUserAutostart: template.AllowUserAutostart, AllowUserAutostop: template.AllowUserAutostop, AllowUserCancelWorkspaceJobs: template.AllowUserCancelWorkspaceJobs, + FailureTTLMillis: time.Duration(template.FailureTTL).Milliseconds(), + InactivityTTLMillis: time.Duration(template.InactivityTTL).Milliseconds(), } } diff --git a/codersdk/templates.go b/codersdk/templates.go index 7b5457bec2f0b..9f8b2e2033a36 100644 --- a/codersdk/templates.go +++ b/codersdk/templates.go @@ -40,6 +40,12 @@ type Template struct { AllowUserAutostart bool `json:"allow_user_autostart"` AllowUserAutostop bool `json:"allow_user_autostop"` AllowUserCancelWorkspaceJobs bool `json:"allow_user_cancel_workspace_jobs"` + + // FailureTTLMillis and InactivityTTLMillis are enterprise-only. Their + // values are used if your license is entitled to use the workspace actions + // feature + FailureTTLMillis int64 `json:"failure_ttl_ms"` + InactivityTTLMillis int64 `json:"inactivity_ttl_ms"` } type TransitionStats struct { diff --git a/site/src/api/typesGenerated.ts b/site/src/api/typesGenerated.ts index 95dab12f6edcd..985d43d727f25 100644 --- a/site/src/api/typesGenerated.ts +++ b/site/src/api/typesGenerated.ts @@ -841,6 +841,8 @@ export interface Template { readonly allow_user_autostart: boolean readonly allow_user_autostop: boolean readonly allow_user_cancel_workspace_jobs: boolean + readonly failure_ttl_ms: number + readonly inactivity_ttl_ms: number } // From codersdk/templates.go From 57a9de72c99788aec61d4b23be89938c2dca0de5 Mon Sep 17 00:00:00 2001 From: Kira Pilot Date: Wed, 3 May 2023 19:57:06 +0000 Subject: [PATCH 06/21] removed workspace actions experiment --- coderd/apidoc/docs.go | 13 +++++++++---- coderd/apidoc/swagger.json | 11 +++++++++-- codersdk/deployment.go | 5 ----- docs/api/schemas.md | 11 +++++++---- docs/api/templates.md | 12 ++++++++++++ enterprise/coderd/coderd.go | 1 - enterprise/coderd/coderd_test.go | 1 - site/src/api/typesGenerated.ts | 6 ++---- 8 files changed, 39 insertions(+), 21 deletions(-) diff --git a/coderd/apidoc/docs.go b/coderd/apidoc/docs.go index 65642323a2f36..0805a4904a50f 100644 --- a/coderd/apidoc/docs.go +++ b/coderd/apidoc/docs.go @@ -7400,12 +7400,10 @@ const docTemplate = `{ "codersdk.Experiment": { "type": "string", "enum": [ - "moons", - "workspace_actions" + "moons" ], "x-enum-varnames": [ - "ExperimentMoons", - "ExperimentWorkspaceActions" + "ExperimentMoons" ] }, "codersdk.Feature": { @@ -8698,6 +8696,10 @@ const docTemplate = `{ "display_name": { "type": "string" }, + "failure_ttl_ms": { + "description": "FailureTTLMillis and InactivityTTLMillis are enterprise-only. Their\nvalues are used if your license is entitled to use the workspace actions\nfeature", + "type": "integer" + }, "icon": { "type": "string" }, @@ -8705,6 +8707,9 @@ const docTemplate = `{ "type": "string", "format": "uuid" }, + "inactivity_ttl_ms": { + "type": "integer" + }, "max_ttl_ms": { "description": "MaxTTLMillis is an enterprise feature. It's value is only used if your\nlicense is entitled to use the advanced template scheduling feature.", "type": "integer" diff --git a/coderd/apidoc/swagger.json b/coderd/apidoc/swagger.json index f1d59dfa2cfc9..6e8082107c167 100644 --- a/coderd/apidoc/swagger.json +++ b/coderd/apidoc/swagger.json @@ -6614,8 +6614,8 @@ }, "codersdk.Experiment": { "type": "string", - "enum": ["moons", "workspace_actions"], - "x-enum-varnames": ["ExperimentMoons", "ExperimentWorkspaceActions"] + "enum": ["moons"], + "x-enum-varnames": ["ExperimentMoons"] }, "codersdk.Feature": { "type": "object", @@ -7816,6 +7816,10 @@ "display_name": { "type": "string" }, + "failure_ttl_ms": { + "description": "FailureTTLMillis and InactivityTTLMillis are enterprise-only. Their\nvalues are used if your license is entitled to use the workspace actions\nfeature", + "type": "integer" + }, "icon": { "type": "string" }, @@ -7823,6 +7827,9 @@ "type": "string", "format": "uuid" }, + "inactivity_ttl_ms": { + "type": "integer" + }, "max_ttl_ms": { "description": "MaxTTLMillis is an enterprise feature. It's value is only used if your\nlicense is entitled to use the advanced template scheduling feature.", "type": "integer" diff --git a/codersdk/deployment.go b/codersdk/deployment.go index aaee164d5a2b3..61ab6658f3732 100644 --- a/codersdk/deployment.go +++ b/codersdk/deployment.go @@ -46,7 +46,6 @@ const ( FeatureAppearance FeatureName = "appearance" FeatureAdvancedTemplateScheduling FeatureName = "advanced_template_scheduling" FeatureWorkspaceProxy FeatureName = "workspace_proxy" - FeatureWorkspaceActions FeatureName = "workspace_actions" ) // FeatureNames must be kept in-sync with the Feature enum above. @@ -62,7 +61,6 @@ var FeatureNames = []FeatureName{ FeatureAppearance, FeatureAdvancedTemplateScheduling, FeatureWorkspaceProxy, - FeatureWorkspaceActions, } // Humanize returns the feature name in a human-readable format. @@ -1670,9 +1668,6 @@ const ( // feature is not yet complete in functionality. ExperimentMoons Experiment = "moons" - // https://github.com/coder/coder/milestone/19 - ExperimentWorkspaceActions Experiment = "workspace_actions" - // Add new experiments here! // ExperimentExample Experiment = "example" ) diff --git a/docs/api/schemas.md b/docs/api/schemas.md index 0224980889bce..cd14e523ae93e 100644 --- a/docs/api/schemas.md +++ b/docs/api/schemas.md @@ -2502,10 +2502,9 @@ CreateParameterRequest is a structure used to create a new parameter value for a #### Enumerated Values -| Value | -| ------------------- | -| `moons` | -| `workspace_actions` | +| Value | +| ------- | +| `moons` | ## codersdk.Feature @@ -3866,8 +3865,10 @@ Parameter represents a set value for the scope. "default_ttl_ms": 0, "description": "string", "display_name": "string", + "failure_ttl_ms": 0, "icon": "string", "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", + "inactivity_ttl_ms": 0, "max_ttl_ms": 0, "name": "string", "organization_id": "7c60d51f-b44e-4682-87d6-449835ea4de6", @@ -3892,8 +3893,10 @@ Parameter represents a set value for the scope. | `default_ttl_ms` | integer | false | | | | `description` | string | false | | | | `display_name` | string | false | | | +| `failure_ttl_ms` | integer | false | | Failure ttl ms and InactivityTTLMillis are enterprise-only. Their values are used if your license is entitled to use the workspace actions feature | | `icon` | string | false | | | | `id` | string | false | | | +| `inactivity_ttl_ms` | integer | false | | | | `max_ttl_ms` | integer | false | | Max ttl ms is an enterprise feature. It's value is only used if your license is entitled to use the advanced template scheduling feature. | | `name` | string | false | | | | `organization_id` | string | false | | | diff --git a/docs/api/templates.md b/docs/api/templates.md index 5b97c47b7bf75..054d3fddea9d4 100644 --- a/docs/api/templates.md +++ b/docs/api/templates.md @@ -47,8 +47,10 @@ curl -X GET http://coder-server:8080/api/v2/organizations/{organization}/templat "default_ttl_ms": 0, "description": "string", "display_name": "string", + "failure_ttl_ms": 0, "icon": "string", "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", + "inactivity_ttl_ms": 0, "max_ttl_ms": 0, "name": "string", "organization_id": "7c60d51f-b44e-4682-87d6-449835ea4de6", @@ -86,8 +88,10 @@ Status Code **200** | `» default_ttl_ms` | integer | false | | | | `» description` | string | false | | | | `» display_name` | string | false | | | +| `» failure_ttl_ms` | integer | false | | Failure ttl ms and InactivityTTLMillis are enterprise-only. Their values are used if your license is entitled to use the workspace actions feature | | `» icon` | string | false | | | | `» id` | string(uuid) | false | | | +| `» inactivity_ttl_ms` | integer | false | | | | `» max_ttl_ms` | integer | false | | Max ttl ms is an enterprise feature. It's value is only used if your license is entitled to use the advanced template scheduling feature. | | `» name` | string | false | | | | `» organization_id` | string(uuid) | false | | | @@ -176,8 +180,10 @@ curl -X POST http://coder-server:8080/api/v2/organizations/{organization}/templa "default_ttl_ms": 0, "description": "string", "display_name": "string", + "failure_ttl_ms": 0, "icon": "string", "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", + "inactivity_ttl_ms": 0, "max_ttl_ms": 0, "name": "string", "organization_id": "7c60d51f-b44e-4682-87d6-449835ea4de6", @@ -301,8 +307,10 @@ curl -X GET http://coder-server:8080/api/v2/organizations/{organization}/templat "default_ttl_ms": 0, "description": "string", "display_name": "string", + "failure_ttl_ms": 0, "icon": "string", "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", + "inactivity_ttl_ms": 0, "max_ttl_ms": 0, "name": "string", "organization_id": "7c60d51f-b44e-4682-87d6-449835ea4de6", @@ -628,8 +636,10 @@ curl -X GET http://coder-server:8080/api/v2/templates/{template} \ "default_ttl_ms": 0, "description": "string", "display_name": "string", + "failure_ttl_ms": 0, "icon": "string", "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", + "inactivity_ttl_ms": 0, "max_ttl_ms": 0, "name": "string", "organization_id": "7c60d51f-b44e-4682-87d6-449835ea4de6", @@ -736,8 +746,10 @@ curl -X PATCH http://coder-server:8080/api/v2/templates/{template} \ "default_ttl_ms": 0, "description": "string", "display_name": "string", + "failure_ttl_ms": 0, "icon": "string", "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", + "inactivity_ttl_ms": 0, "max_ttl_ms": 0, "name": "string", "organization_id": "7c60d51f-b44e-4682-87d6-449835ea4de6", diff --git a/enterprise/coderd/coderd.go b/enterprise/coderd/coderd.go index 3d8ad12edea29..190a552a80c99 100644 --- a/enterprise/coderd/coderd.go +++ b/enterprise/coderd/coderd.go @@ -326,7 +326,6 @@ func (api *API) updateEntitlements(ctx context.Context) error { codersdk.FeatureExternalProvisionerDaemons: true, codersdk.FeatureAdvancedTemplateScheduling: true, codersdk.FeatureWorkspaceProxy: true, - codersdk.FeatureWorkspaceActions: true, }) if err != nil { return err diff --git a/enterprise/coderd/coderd_test.go b/enterprise/coderd/coderd_test.go index 26526721f1f8c..27aa2cb4c33eb 100644 --- a/enterprise/coderd/coderd_test.go +++ b/enterprise/coderd/coderd_test.go @@ -54,7 +54,6 @@ func TestEntitlements(t *testing.T) { codersdk.FeatureExternalProvisionerDaemons: 1, codersdk.FeatureAdvancedTemplateScheduling: 1, codersdk.FeatureWorkspaceProxy: 1, - codersdk.FeatureWorkspaceActions: 1, }, GraceAt: time.Now().Add(59 * 24 * time.Hour), }) diff --git a/site/src/api/typesGenerated.ts b/site/src/api/typesGenerated.ts index 952b98b6e2095..5cce0d50d8580 100644 --- a/site/src/api/typesGenerated.ts +++ b/site/src/api/typesGenerated.ts @@ -1365,8 +1365,8 @@ export const Entitlements: Entitlement[] = [ ] // From codersdk/deployment.go -export type Experiment = "moons" | "workspace_actions" -export const Experiments: Experiment[] = ["moons", "workspace_actions"] +export type Experiment = "moons" +export const Experiments: Experiment[] = ["moons"] // From codersdk/deployment.go export type FeatureName = @@ -1380,7 +1380,6 @@ export type FeatureName = | "scim" | "template_rbac" | "user_limit" - | "workspace_actions" | "workspace_proxy" export const FeatureNames: FeatureName[] = [ "advanced_template_scheduling", @@ -1393,7 +1392,6 @@ export const FeatureNames: FeatureName[] = [ "scim", "template_rbac", "user_limit", - "workspace_actions", "workspace_proxy", ] From 07cb07c506e570a83f012d60a935837efb00a0d4 Mon Sep 17 00:00:00 2001 From: Kira Pilot Date: Wed, 3 May 2023 21:42:47 +0000 Subject: [PATCH 07/21] added logic to enterprise template store --- coderd/database/dbfake/databasefake.go | 2 + enterprise/coderd/provisionerdaemons.go | 6 +++ .../TemplateScheduleForm.tsx | 50 ++++++++++++++++++- 3 files changed, 56 insertions(+), 2 deletions(-) diff --git a/coderd/database/dbfake/databasefake.go b/coderd/database/dbfake/databasefake.go index 7f80385af2dfb..f254ee2668f12 100644 --- a/coderd/database/dbfake/databasefake.go +++ b/coderd/database/dbfake/databasefake.go @@ -1945,6 +1945,8 @@ func (q *fakeQuerier) UpdateTemplateScheduleByID(_ context.Context, arg database tpl.UpdatedAt = database.Now() tpl.DefaultTTL = arg.DefaultTTL tpl.MaxTTL = arg.MaxTTL + tpl.FailureTTL = arg.FailureTTL + tpl.InactivityTTL = arg.InactivityTTL q.templates[idx] = tpl return tpl.DeepCopy(), nil } diff --git a/enterprise/coderd/provisionerdaemons.go b/enterprise/coderd/provisionerdaemons.go index 27573014edf88..d6f9f4f089c80 100644 --- a/enterprise/coderd/provisionerdaemons.go +++ b/enterprise/coderd/provisionerdaemons.go @@ -322,12 +322,16 @@ func (*enterpriseTemplateScheduleStore) GetTemplateScheduleOptions(ctx context.C UserAutostopEnabled: tpl.AllowUserAutostop, DefaultTTL: time.Duration(tpl.DefaultTTL), MaxTTL: time.Duration(tpl.MaxTTL), + FailureTTL: time.Duration(tpl.FailureTTL), + InactivityTTL: time.Duration(tpl.InactivityTTL), }, nil } func (*enterpriseTemplateScheduleStore) SetTemplateScheduleOptions(ctx context.Context, db database.Store, tpl database.Template, opts schedule.TemplateScheduleOptions) (database.Template, error) { if int64(opts.DefaultTTL) == tpl.DefaultTTL && int64(opts.MaxTTL) == tpl.MaxTTL && + int64(opts.FailureTTL) == tpl.FailureTTL && + int64(opts.InactivityTTL) == tpl.InactivityTTL && opts.UserAutostartEnabled == tpl.AllowUserAutostart && opts.UserAutostopEnabled == tpl.AllowUserAutostop { // Avoid updating the UpdatedAt timestamp if nothing will be changed. @@ -341,6 +345,8 @@ func (*enterpriseTemplateScheduleStore) SetTemplateScheduleOptions(ctx context.C AllowUserAutostop: opts.UserAutostopEnabled, DefaultTTL: int64(opts.DefaultTTL), MaxTTL: int64(opts.MaxTTL), + FailureTTL: int64(opts.FailureTTL), + InactivityTTL: int64(opts.InactivityTTL), }) if err != nil { return database.Template{}, xerrors.Errorf("update template schedule: %w", err) diff --git a/site/src/pages/TemplateSettingsPage/TemplateSchedulePage/TemplateScheduleForm.tsx b/site/src/pages/TemplateSettingsPage/TemplateSchedulePage/TemplateScheduleForm.tsx index 21fbca3ed646c..3ecbf68944daa 100644 --- a/site/src/pages/TemplateSettingsPage/TemplateSchedulePage/TemplateScheduleForm.tsx +++ b/site/src/pages/TemplateSettingsPage/TemplateSchedulePage/TemplateScheduleForm.tsx @@ -32,6 +32,7 @@ const TTLHelperText = ({ const MAX_TTL_DAYS = 7 const MS_HOUR_CONVERSION = 3600000 +const MS_DAY_CONVERSION = 86400000 export const getValidationSchema = (): Yup.AnyObjectSchema => Yup.object({ @@ -79,11 +80,18 @@ export const TemplateScheduleForm: FC = ({ initialValues: { // on display, convert from ms => hours default_ttl_ms: template.default_ttl_ms / MS_HOUR_CONVERSION, - // the API ignores this value, but to avoid tripping up validation set + // the API ignores these values, but to avoid tripping up validation set // it to zero if the user can't set the field. max_ttl_ms: allowAdvancedScheduling ? template.max_ttl_ms / MS_HOUR_CONVERSION : 0, + failure_ttl_ms: allowAdvancedScheduling + ? template.failure_ttl_ms / MS_DAY_CONVERSION + : 0, + inactivity_ttl_ms: allowAdvancedScheduling + ? template.inactivity_ttl_ms / MS_DAY_CONVERSION + : 0, + allow_user_autostart: template.allow_user_autostart, allow_user_autostop: template.allow_user_autostop, }, @@ -97,6 +105,13 @@ export const TemplateScheduleForm: FC = ({ max_ttl_ms: formData.max_ttl_ms ? formData.max_ttl_ms * MS_HOUR_CONVERSION : undefined, + failure_ttl_ms: formData.failure_ttl_ms + ? formData.failure_ttl_ms * MS_DAY_CONVERSION + : undefined, + inactivity_ttl_ms: formData.inactivity_ttl_ms + ? formData.inactivity_ttl_ms * MS_DAY_CONVERSION + : undefined, + allow_user_autostart: formData.allow_user_autostart, allow_user_autostop: formData.allow_user_autostop, }) @@ -215,7 +230,38 @@ export const TemplateScheduleForm: FC = ({ - + + + + + + + + + + ) From 7091fc16410ca9591647ffe062d876f280fcdd33 Mon Sep 17 00:00:00 2001 From: Kira Pilot Date: Wed, 3 May 2023 23:16:44 +0000 Subject: [PATCH 08/21] added new form fields --- site/src/i18n/en/templateSettingsPage.json | 6 + .../TemplateScheduleForm.tsx | 143 ++++++++++++++++-- 2 files changed, 138 insertions(+), 11 deletions(-) diff --git a/site/src/i18n/en/templateSettingsPage.json b/site/src/i18n/en/templateSettingsPage.json index 0b918be773e9d..e60ccb9fac7d7 100644 --- a/site/src/i18n/en/templateSettingsPage.json +++ b/site/src/i18n/en/templateSettingsPage.json @@ -19,6 +19,12 @@ "maxTTLHelperText_zero": "Workspaces may run indefinitely.", "maxTTLHelperText_one": "Workspaces must stop within 1 hour of starting.", "maxTTLHelperText_other": "Workspaces must stop within {{count}} hours of starting.", + "failureTTLHelperText_zero": "Coder will not automatically stop failed workspaces", + "failureTTLHelperText_one": "Coder will automatically stop failed workspaces after {{count}} day.", + "failureTTLHelperText_other": "Coder will automatically stop failed workspaces after {{count}} days.", + "inactivityTTLHelperText_zero": "Coder will not automatically delete inactive workspaces", + "inactivityTTLHelperText_one": "Coder will automatically delete inactive workspaces after {{count}} day.", + "inactivityTTLHelperText_other": "Coder will automatically delete inactive workspaces after {{count}} days.", "allowUserCancelWorkspaceJobsLabel": "Allow users to cancel in-progress workspace jobs.", "allowUserCancelWorkspaceJobsNotice": "Depending on your template, canceling builds may leave workspaces in an unhealthy state. This option isn't recommended for most use cases.", "allowUsersCancelHelperText": "If checked, users may be able to corrupt their workspace.", diff --git a/site/src/pages/TemplateSettingsPage/TemplateSchedulePage/TemplateScheduleForm.tsx b/site/src/pages/TemplateSettingsPage/TemplateSchedulePage/TemplateScheduleForm.tsx index 3ecbf68944daa..c60e326fccfdc 100644 --- a/site/src/pages/TemplateSettingsPage/TemplateSchedulePage/TemplateScheduleForm.tsx +++ b/site/src/pages/TemplateSettingsPage/TemplateSchedulePage/TemplateScheduleForm.tsx @@ -1,17 +1,24 @@ import TextField from "@material-ui/core/TextField" import { Template, UpdateTemplateMeta } from "api/typesGenerated" import { FormikTouched, useFormik } from "formik" -import { FC } from "react" +import { FC, ChangeEvent } from "react" import { getFormHelpers } from "utils/formUtils" import * as Yup from "yup" import i18next from "i18next" import { useTranslation } from "react-i18next" import { Maybe } from "components/Conditionals/Maybe" -import { FormSection, HorizontalForm, FormFooter } from "components/Form/Form" +import { + FormSection, + HorizontalForm, + FormFooter, + FormFields, +} from "components/Form/Form" import { Stack } from "components/Stack/Stack" import { makeStyles } from "@material-ui/core/styles" import Link from "@material-ui/core/Link" import Checkbox from "@material-ui/core/Checkbox" +import FormControlLabel from "@material-ui/core/FormControlLabel" +import Switch from "@material-ui/core/Switch" const TTLHelperText = ({ ttl, @@ -34,6 +41,11 @@ const MAX_TTL_DAYS = 7 const MS_HOUR_CONVERSION = 3600000 const MS_DAY_CONVERSION = 86400000 +export interface TemplateScheduleFormValues extends UpdateTemplateMeta { + failure_cleanup_enabled: boolean + inactivity_cleanup_enabled: boolean +} + export const getValidationSchema = (): Yup.AnyObjectSchema => Yup.object({ default_ttl_ms: Yup.number() @@ -50,6 +62,36 @@ export const getValidationSchema = (): Yup.AnyObjectSchema => 24 * MAX_TTL_DAYS /* 7 days in hours */, i18next.t("maxTTLMaxError", { ns: "templateSettingsPage" }), ), + failure_ttl_ms: Yup.number() + .integer() + .min(0, "Failure cleanup days must not be less than 0.") + .test( + "positive-if-enabled", + "Failure cleanup days must be greater than zero when enabled.", + function (value) { + const parent = this.parent as TemplateScheduleFormValues + if (parent.failure_cleanup_enabled) { + return Boolean(value) + } else { + return true + } + }, + ), + inactivity_ttl_ms: Yup.number() + .integer() + .min(0, "Inactivity cleanup days must not be less than 0.") + .test( + "positive-if-enabled", + "Inactivity cleanup days must be greater than zero when enabled.", + function (value) { + const parent = this.parent as TemplateScheduleFormValues + if (parent.failure_cleanup_enabled) { + return Boolean(value) + } else { + return true + } + }, + ), allow_user_autostart: Yup.boolean(), allow_user_autostop: Yup.boolean(), }) @@ -76,7 +118,7 @@ export const TemplateScheduleForm: FC = ({ }) => { const { t: commonT } = useTranslation("common") const validationSchema = getValidationSchema() - const form = useFormik({ + const form = useFormik({ initialValues: { // on display, convert from ms => hours default_ttl_ms: template.default_ttl_ms / MS_HOUR_CONVERSION, @@ -94,6 +136,10 @@ export const TemplateScheduleForm: FC = ({ allow_user_autostart: template.allow_user_autostart, allow_user_autostop: template.allow_user_autostop, + failure_cleanup_enabled: + allowAdvancedScheduling && Boolean(template.failure_ttl_ms), + inactivity_cleanup_enabled: + allowAdvancedScheduling && Boolean(template.inactivity_ttl_ms), }, validationSchema, onSubmit: (formData) => { @@ -118,10 +164,51 @@ export const TemplateScheduleForm: FC = ({ }, initialTouched, }) - const getFieldHelpers = getFormHelpers(form, error) + const getFieldHelpers = getFormHelpers( + form, + error, + ) const { t } = useTranslation("templateSettingsPage") const styles = useStyles() + const handleToggleFailureCleanup = async (e: ChangeEvent) => { + form.handleChange(e) + if (!form.values.failure_cleanup_enabled) { + // fill failure_ttl_ms with defaults + await form.setValues({ + ...form.values, + failure_cleanup_enabled: true, + failure_ttl_ms: 14, + }) + } else { + // clear failure_ttl_ms + await form.setValues({ + ...form.values, + failure_cleanup_enabled: false, + failure_ttl_ms: 0, + }) + } + } + + const handleToggleInactivityCleanup = async (e: ChangeEvent) => { + form.handleChange(e) + if (!form.values.inactivity_cleanup_enabled) { + // fill inactivity_ttl_ms with defaults + await form.setValues({ + ...form.values, + inactivity_cleanup_enabled: true, + inactivity_ttl_ms: 14, + }) + } else { + // clear inactivity_ttl_ms + await form.setValues({ + ...form.values, + inactivity_cleanup_enabled: false, + inactivity_ttl_ms: 0, + }) + } + } + return ( = ({ title="Failure Cleanup" description="When enabled, Coder will automatically stop workspaces that are in a failed state after a specified number of days." > - + + + } + label="Enable Failure Cleanup" + /> , + )} + disabled={isSubmitting || !form.values.failure_cleanup_enabled} fullWidth inputProps={{ min: 0, step: 1 }} label="Time until cleanup (days)" variant="outlined" type="number" /> - + - + + + } + label="Enable Inactivity Cleanup" + /> , + )} disabled={isSubmitting} fullWidth inputProps={{ min: 0, step: 1 }} @@ -260,7 +381,7 @@ export const TemplateScheduleForm: FC = ({ variant="outlined" type="number" /> - + From e56522584a60d81ce82ef9adc0c68bcd8a34ab53 Mon Sep 17 00:00:00 2001 From: Kira Pilot Date: Wed, 3 May 2023 23:44:03 +0000 Subject: [PATCH 09/21] feature flagged new fields --- coderd/apidoc/docs.go | 6 +- coderd/apidoc/swagger.json | 4 +- codersdk/deployment.go | 3 + docs/api/schemas.md | 7 +- site/src/api/typesGenerated.ts | 4 +- .../TemplateScheduleForm.tsx | 136 +++++++++--------- .../TemplateSchedulePage.tsx | 7 +- .../TemplateSchedulePageView.tsx | 3 + 8 files changed, 96 insertions(+), 74 deletions(-) diff --git a/coderd/apidoc/docs.go b/coderd/apidoc/docs.go index 0805a4904a50f..14421645d78f7 100644 --- a/coderd/apidoc/docs.go +++ b/coderd/apidoc/docs.go @@ -7400,10 +7400,12 @@ const docTemplate = `{ "codersdk.Experiment": { "type": "string", "enum": [ - "moons" + "moons", + "workspace_actions" ], "x-enum-varnames": [ - "ExperimentMoons" + "ExperimentMoons", + "ExperimentWorkspaceActions" ] }, "codersdk.Feature": { diff --git a/coderd/apidoc/swagger.json b/coderd/apidoc/swagger.json index 6e8082107c167..b95dfbe5f68c4 100644 --- a/coderd/apidoc/swagger.json +++ b/coderd/apidoc/swagger.json @@ -6614,8 +6614,8 @@ }, "codersdk.Experiment": { "type": "string", - "enum": ["moons"], - "x-enum-varnames": ["ExperimentMoons"] + "enum": ["moons", "workspace_actions"], + "x-enum-varnames": ["ExperimentMoons", "ExperimentWorkspaceActions"] }, "codersdk.Feature": { "type": "object", diff --git a/codersdk/deployment.go b/codersdk/deployment.go index 61ab6658f3732..979f9ee36e3fc 100644 --- a/codersdk/deployment.go +++ b/codersdk/deployment.go @@ -1668,6 +1668,9 @@ const ( // feature is not yet complete in functionality. ExperimentMoons Experiment = "moons" + // https://github.com/coder/coder/milestone/19 + ExperimentWorkspaceActions Experiment = "workspace_actions" + // Add new experiments here! // ExperimentExample Experiment = "example" ) diff --git a/docs/api/schemas.md b/docs/api/schemas.md index cd14e523ae93e..1241a8a4c68cb 100644 --- a/docs/api/schemas.md +++ b/docs/api/schemas.md @@ -2502,9 +2502,10 @@ CreateParameterRequest is a structure used to create a new parameter value for a #### Enumerated Values -| Value | -| ------- | -| `moons` | +| Value | +| ------------------- | +| `moons` | +| `workspace_actions` | ## codersdk.Feature diff --git a/site/src/api/typesGenerated.ts b/site/src/api/typesGenerated.ts index 5cce0d50d8580..dcc90b4c40778 100644 --- a/site/src/api/typesGenerated.ts +++ b/site/src/api/typesGenerated.ts @@ -1365,8 +1365,8 @@ export const Entitlements: Entitlement[] = [ ] // From codersdk/deployment.go -export type Experiment = "moons" -export const Experiments: Experiment[] = ["moons"] +export type Experiment = "moons" | "workspace_actions" +export const Experiments: Experiment[] = ["moons", "workspace_actions"] // From codersdk/deployment.go export type FeatureName = diff --git a/site/src/pages/TemplateSettingsPage/TemplateSchedulePage/TemplateScheduleForm.tsx b/site/src/pages/TemplateSettingsPage/TemplateSchedulePage/TemplateScheduleForm.tsx index c60e326fccfdc..21b80e92b0a14 100644 --- a/site/src/pages/TemplateSettingsPage/TemplateSchedulePage/TemplateScheduleForm.tsx +++ b/site/src/pages/TemplateSettingsPage/TemplateSchedulePage/TemplateScheduleForm.tsx @@ -103,6 +103,7 @@ export interface TemplateScheduleForm { isSubmitting: boolean error?: unknown allowAdvancedScheduling: boolean + allowWorkspaceActions: boolean // Helpful to show field errors on Storybook initialTouched?: FormikTouched } @@ -113,6 +114,7 @@ export const TemplateScheduleForm: FC = ({ onCancel, error, allowAdvancedScheduling, + allowWorkspaceActions, isSubmitting, initialTouched, }) => { @@ -317,72 +319,78 @@ export const TemplateScheduleForm: FC = ({ - - - + + + + } + label="Enable Failure Cleanup" /> - } - label="Enable Failure Cleanup" - /> - , - )} - disabled={isSubmitting || !form.values.failure_cleanup_enabled} - fullWidth - inputProps={{ min: 0, step: 1 }} - label="Time until cleanup (days)" - variant="outlined" - type="number" - /> - - - - - , + )} + disabled={isSubmitting || !form.values.failure_cleanup_enabled} + fullWidth + inputProps={{ min: 0, step: 1 }} + label="Time until cleanup (days)" + variant="outlined" + type="number" /> - } - label="Enable Inactivity Cleanup" - /> - , - )} - disabled={isSubmitting} - fullWidth - inputProps={{ min: 0, step: 1 }} - label="Time until cleanup (days)" - variant="outlined" - type="number" - /> - - + + + + + + } + label="Enable Inactivity Cleanup" + /> + , + )} + disabled={ + isSubmitting || !form.values.inactivity_cleanup_enabled + } + fullWidth + inputProps={{ min: 0, step: 1 }} + label="Time until cleanup (days)" + variant="outlined" + type="number" + /> + + + + )} ) diff --git a/site/src/pages/TemplateSettingsPage/TemplateSchedulePage/TemplateSchedulePage.tsx b/site/src/pages/TemplateSettingsPage/TemplateSchedulePage/TemplateSchedulePage.tsx index f861ffc3c116d..af5424ab8cbf4 100644 --- a/site/src/pages/TemplateSettingsPage/TemplateSchedulePage/TemplateSchedulePage.tsx +++ b/site/src/pages/TemplateSettingsPage/TemplateSchedulePage/TemplateSchedulePage.tsx @@ -14,9 +14,13 @@ const TemplateSchedulePage: FC = () => { const { template: templateName } = useParams() as { template: string } const navigate = useNavigate() const { template } = useTemplateSettingsContext() - const { entitlements } = useDashboard() + const { entitlements, experiments } = useDashboard() const allowAdvancedScheduling = entitlements.features["advanced_template_scheduling"].enabled + // This check can be removed when https://github.com/coder/coder/milestone/19 + // is merged up + const allowWorkspaceActions = experiments.includes("workspace_actions") + const { mutate: updateTemplate, isLoading: isSubmitting, @@ -37,6 +41,7 @@ const TemplateSchedulePage: FC = () => { ["initialTouched"] allowAdvancedScheduling: boolean + allowWorkspaceActions: boolean } export const TemplateSchedulePageView: FC = ({ @@ -20,6 +21,7 @@ export const TemplateSchedulePageView: FC = ({ onSubmit, isSubmitting, allowAdvancedScheduling, + allowWorkspaceActions, submitError, initialTouched, }) => { @@ -33,6 +35,7 @@ export const TemplateSchedulePageView: FC = ({ Date: Thu, 4 May 2023 00:06:43 +0000 Subject: [PATCH 10/21] fix validation --- .../TemplateSchedulePage/TemplateScheduleForm.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/site/src/pages/TemplateSettingsPage/TemplateSchedulePage/TemplateScheduleForm.tsx b/site/src/pages/TemplateSettingsPage/TemplateSchedulePage/TemplateScheduleForm.tsx index 21b80e92b0a14..5068ba44ee635 100644 --- a/site/src/pages/TemplateSettingsPage/TemplateSchedulePage/TemplateScheduleForm.tsx +++ b/site/src/pages/TemplateSettingsPage/TemplateSchedulePage/TemplateScheduleForm.tsx @@ -85,7 +85,7 @@ export const getValidationSchema = (): Yup.AnyObjectSchema => "Inactivity cleanup days must be greater than zero when enabled.", function (value) { const parent = this.parent as TemplateScheduleFormValues - if (parent.failure_cleanup_enabled) { + if (parent.inactivity_cleanup_enabled) { return Boolean(value) } else { return true From abab4c8f792f811f4c5bc166bc51568b5b9c2733 Mon Sep 17 00:00:00 2001 From: Kira Pilot Date: Thu, 4 May 2023 00:23:21 +0000 Subject: [PATCH 11/21] fixed submit btn --- .../TemplateSchedulePage/TemplateScheduleForm.tsx | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/site/src/pages/TemplateSettingsPage/TemplateSchedulePage/TemplateScheduleForm.tsx b/site/src/pages/TemplateSettingsPage/TemplateSchedulePage/TemplateScheduleForm.tsx index 5068ba44ee635..a4e3a3082acbd 100644 --- a/site/src/pages/TemplateSettingsPage/TemplateSchedulePage/TemplateScheduleForm.tsx +++ b/site/src/pages/TemplateSettingsPage/TemplateSchedulePage/TemplateScheduleForm.tsx @@ -391,7 +391,11 @@ export const TemplateScheduleForm: FC = ({ )} - + ) } From 240f2979decb919ebf4f9cfc62602ec218ff98b4 Mon Sep 17 00:00:00 2001 From: Kira Pilot Date: Thu, 4 May 2023 12:35:42 +0000 Subject: [PATCH 12/21] fix tests --- ...tls.down.sql => 000122_add_template_cleanup_ttls.down.sql} | 0 ...up_ttls.up.sql => 000122_add_template_cleanup_ttls.up.sql} | 0 site/src/testHelpers/entities.ts | 4 ++++ 3 files changed, 4 insertions(+) rename coderd/database/migrations/{000121_add_template_cleanup_ttls.down.sql => 000122_add_template_cleanup_ttls.down.sql} (100%) rename coderd/database/migrations/{000121_add_template_cleanup_ttls.up.sql => 000122_add_template_cleanup_ttls.up.sql} (100%) diff --git a/coderd/database/migrations/000121_add_template_cleanup_ttls.down.sql b/coderd/database/migrations/000122_add_template_cleanup_ttls.down.sql similarity index 100% rename from coderd/database/migrations/000121_add_template_cleanup_ttls.down.sql rename to coderd/database/migrations/000122_add_template_cleanup_ttls.down.sql diff --git a/coderd/database/migrations/000121_add_template_cleanup_ttls.up.sql b/coderd/database/migrations/000122_add_template_cleanup_ttls.up.sql similarity index 100% rename from coderd/database/migrations/000121_add_template_cleanup_ttls.up.sql rename to coderd/database/migrations/000122_add_template_cleanup_ttls.up.sql diff --git a/site/src/testHelpers/entities.ts b/site/src/testHelpers/entities.ts index 91dc24132f009..28f00c52f458d 100644 --- a/site/src/testHelpers/entities.ts +++ b/site/src/testHelpers/entities.ts @@ -341,6 +341,10 @@ export const MockTemplate: TypesGen.Template = { created_by_name: "test_creator", icon: "/icon/code.svg", allow_user_cancel_workspace_jobs: true, + failure_ttl_ms: 0, + inactivity_ttl_ms: 0, + allow_user_autostart: false, + allow_user_autostop: false, } export const MockTemplateVersionFiles: TemplateVersionFiles = { From be512eebf22cada0b1e0951e1da5cc154d3db11e Mon Sep 17 00:00:00 2001 From: Kira Pilot Date: Thu, 4 May 2023 12:48:59 +0000 Subject: [PATCH 13/21] changed ttl defaults --- .../TemplateSchedulePage/TemplateScheduleForm.tsx | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/site/src/pages/TemplateSettingsPage/TemplateSchedulePage/TemplateScheduleForm.tsx b/site/src/pages/TemplateSettingsPage/TemplateSchedulePage/TemplateScheduleForm.tsx index a4e3a3082acbd..3967bdcde73ec 100644 --- a/site/src/pages/TemplateSettingsPage/TemplateSchedulePage/TemplateScheduleForm.tsx +++ b/site/src/pages/TemplateSettingsPage/TemplateSchedulePage/TemplateScheduleForm.tsx @@ -40,6 +40,8 @@ const TTLHelperText = ({ const MAX_TTL_DAYS = 7 const MS_HOUR_CONVERSION = 3600000 const MS_DAY_CONVERSION = 86400000 +const FAILURE_CLEANUP_DEFAULT = 7 +const INACTIVITY_CLEANUP_DEFAULT = 180 export interface TemplateScheduleFormValues extends UpdateTemplateMeta { failure_cleanup_enabled: boolean @@ -180,7 +182,7 @@ export const TemplateScheduleForm: FC = ({ await form.setValues({ ...form.values, failure_cleanup_enabled: true, - failure_ttl_ms: 14, + failure_ttl_ms: FAILURE_CLEANUP_DEFAULT, }) } else { // clear failure_ttl_ms @@ -199,7 +201,7 @@ export const TemplateScheduleForm: FC = ({ await form.setValues({ ...form.values, inactivity_cleanup_enabled: true, - inactivity_ttl_ms: 14, + inactivity_ttl_ms: INACTIVITY_CLEANUP_DEFAULT, }) } else { // clear inactivity_ttl_ms From 8536b75b806d8f7d05c6f58002160aa6bbcc1b75 Mon Sep 17 00:00:00 2001 From: Kira Pilot Date: Thu, 4 May 2023 14:36:37 +0000 Subject: [PATCH 14/21] added FE tests --- .../TemplateScheduleForm.tsx | 2 + .../TemplateSchedulePage.test.tsx | 110 +++++++++++++++++- .../TemplateSchedulePageView.stories.tsx | 1 + site/src/testHelpers/entities.ts | 2 +- 4 files changed, 108 insertions(+), 7 deletions(-) diff --git a/site/src/pages/TemplateSettingsPage/TemplateSchedulePage/TemplateScheduleForm.tsx b/site/src/pages/TemplateSettingsPage/TemplateSchedulePage/TemplateScheduleForm.tsx index 3967bdcde73ec..5e15afad4a941 100644 --- a/site/src/pages/TemplateSettingsPage/TemplateSchedulePage/TemplateScheduleForm.tsx +++ b/site/src/pages/TemplateSettingsPage/TemplateSchedulePage/TemplateScheduleForm.tsx @@ -353,6 +353,7 @@ export const TemplateScheduleForm: FC = ({ label="Time until cleanup (days)" variant="outlined" type="number" + aria-label="Failure Cleanup" /> @@ -388,6 +389,7 @@ export const TemplateScheduleForm: FC = ({ label="Time until cleanup (days)" variant="outlined" type="number" + aria-label="Inactivity Cleanup" /> diff --git a/site/src/pages/TemplateSettingsPage/TemplateSchedulePage/TemplateSchedulePage.test.tsx b/site/src/pages/TemplateSettingsPage/TemplateSchedulePage/TemplateSchedulePage.test.tsx index b902002a159da..c767c4c71398c 100644 --- a/site/src/pages/TemplateSettingsPage/TemplateSchedulePage/TemplateSchedulePage.test.tsx +++ b/site/src/pages/TemplateSettingsPage/TemplateSchedulePage/TemplateSchedulePage.test.tsx @@ -6,11 +6,11 @@ import { Language as FooterFormLanguage } from "components/FormFooter/FormFooter import { MockEntitlementsWithScheduling, MockTemplate, -} from "../../../testHelpers/entities" +} from "testHelpers/entities" import { renderWithTemplateSettingsLayout, waitForLoaderToBeRemoved, -} from "../../../testHelpers/renderHelpers" +} from "testHelpers/renderHelpers" import { getValidationSchema } from "./TemplateScheduleForm" import TemplateSchedulePage from "./TemplateSchedulePage" import i18next from "i18next" @@ -20,6 +20,8 @@ const { t } = i18next const validFormValues = { default_ttl_ms: 1, max_ttl_ms: 2, + failure_ttl_ms: 7, + inactivity_ttl_ms: 180, } const renderTemplateSchedulePage = async () => { @@ -33,9 +35,13 @@ const renderTemplateSchedulePage = async () => { const fillAndSubmitForm = async ({ default_ttl_ms, max_ttl_ms, + failure_ttl_ms, + inactivity_ttl_ms, }: { default_ttl_ms: number max_ttl_ms: number + failure_ttl_ms: number + inactivity_ttl_ms: number }) => { const user = userEvent.setup() const defaultTtlLabel = t("defaultTtlLabel", { ns: "templateSettingsPage" }) @@ -48,6 +54,16 @@ const fillAndSubmitForm = async ({ await user.clear(maxTtlField) await user.type(maxTtlField, max_ttl_ms.toString()) + const failureTtlField = screen.getByRole("checkbox", { + name: /Failure Cleanup/i, + }) + await user.type(failureTtlField, failure_ttl_ms.toString()) + + const inactivityTtlField = screen.getByRole("checkbox", { + name: /Inactivity Cleanup/i, + }) + await user.type(inactivityTtlField, inactivity_ttl_ms.toString()) + const submitButton = await screen.findByText( FooterFormLanguage.defaultSubmitLabel, ) @@ -59,6 +75,9 @@ describe("TemplateSchedulePage", () => { jest .spyOn(API, "getEntitlements") .mockResolvedValue(MockEntitlementsWithScheduling) + + // remove when https://github.com/coder/coder/milestone/19 is completed. + jest.spyOn(API, "getExperiments").mockResolvedValue(["workspace_actions"]) }) it("succeeds", async () => { @@ -71,7 +90,7 @@ describe("TemplateSchedulePage", () => { await waitFor(() => expect(API.updateTemplateMeta).toBeCalledTimes(1)) }) - test("ttl is converted to and from hours", async () => { + test("default and max ttl is converted to and from hours", async () => { await renderTemplateSchedulePage() jest.spyOn(API, "updateTemplateMeta").mockResolvedValueOnce({ @@ -92,7 +111,28 @@ describe("TemplateSchedulePage", () => { ) }) - it("allows a ttl of 7 days", () => { + test("failure and inactivity ttl converted to and from days", async () => { + await renderTemplateSchedulePage() + + jest.spyOn(API, "updateTemplateMeta").mockResolvedValueOnce({ + ...MockTemplate, + ...validFormValues, + }) + + await fillAndSubmitForm(validFormValues) + await waitFor(() => expect(API.updateTemplateMeta).toBeCalledTimes(1)) + await waitFor(() => + expect(API.updateTemplateMeta).toBeCalledWith( + "test-template", + expect.objectContaining({ + failure_ttl_ms: validFormValues.failure_ttl_ms * 86400000, + inactivity_ttl_ms: validFormValues.inactivity_ttl_ms * 86400000, + }), + ), + ) + }) + + it("allows a default ttl of 7 days", () => { const values: UpdateTemplateMeta = { ...validFormValues, default_ttl_ms: 24 * 7, @@ -101,7 +141,7 @@ describe("TemplateSchedulePage", () => { expect(validate).not.toThrowError() }) - it("allows ttl of 0", () => { + it("allows default ttl of 0", () => { const values: UpdateTemplateMeta = { ...validFormValues, default_ttl_ms: 0, @@ -110,7 +150,7 @@ describe("TemplateSchedulePage", () => { expect(validate).not.toThrowError() }) - it("disallows a ttl of 7 days + 1 hour", () => { + it("disallows a default ttl of 7 days + 1 hour", () => { const values: UpdateTemplateMeta = { ...validFormValues, default_ttl_ms: 24 * 7 + 1, @@ -120,4 +160,62 @@ describe("TemplateSchedulePage", () => { t("defaultTTLMaxError", { ns: "templateSettingsPage" }), ) }) + + it("allows a failure ttl of 7 days", () => { + const values: UpdateTemplateMeta = { + ...validFormValues, + failure_ttl_ms: 86400000 * 7, + } + const validate = () => getValidationSchema().validateSync(values) + expect(validate).not.toThrowError() + }) + + it("allows failure ttl of 0", () => { + const values: UpdateTemplateMeta = { + ...validFormValues, + failure_ttl_ms: 0, + } + const validate = () => getValidationSchema().validateSync(values) + expect(validate).not.toThrowError() + }) + + it("disallows a negative failure ttl", () => { + const values: UpdateTemplateMeta = { + ...validFormValues, + failure_ttl_ms: -1, + } + const validate = () => getValidationSchema().validateSync(values) + expect(validate).toThrowError( + "Failure cleanup days must not be less than 0.", + ) + }) + + it("allows an inactivity ttl of 7 days", () => { + const values: UpdateTemplateMeta = { + ...validFormValues, + inactivity_ttl_ms: 86400000 * 7, + } + const validate = () => getValidationSchema().validateSync(values) + expect(validate).not.toThrowError() + }) + + it("allows an inactivity ttl of 0", () => { + const values: UpdateTemplateMeta = { + ...validFormValues, + inactivity_ttl_ms: 0, + } + const validate = () => getValidationSchema().validateSync(values) + expect(validate).not.toThrowError() + }) + + it("disallows a negative inactivity ttl", () => { + const values: UpdateTemplateMeta = { + ...validFormValues, + inactivity_ttl_ms: -1, + } + const validate = () => getValidationSchema().validateSync(values) + expect(validate).toThrowError( + "Inactivity cleanup days must not be less than 0.", + ) + }) }) diff --git a/site/src/pages/TemplateSettingsPage/TemplateSchedulePage/TemplateSchedulePageView.stories.tsx b/site/src/pages/TemplateSettingsPage/TemplateSchedulePage/TemplateSchedulePageView.stories.tsx index 64a28a12ff38c..5c5e4689429e9 100644 --- a/site/src/pages/TemplateSettingsPage/TemplateSchedulePage/TemplateSchedulePageView.stories.tsx +++ b/site/src/pages/TemplateSettingsPage/TemplateSchedulePage/TemplateSchedulePageView.stories.tsx @@ -11,6 +11,7 @@ export default { component: TemplateSchedulePageView, args: { allowAdvancedScheduling: true, + allowWorkspaceActions: true, template: MockTemplate, onSubmit: action("onSubmit"), onCancel: action("cancel"), diff --git a/site/src/testHelpers/entities.ts b/site/src/testHelpers/entities.ts index 28f00c52f458d..d662e5f040580 100644 --- a/site/src/testHelpers/entities.ts +++ b/site/src/testHelpers/entities.ts @@ -1343,7 +1343,7 @@ export const MockEntitlementsWithScheduling: TypesGen.Entitlements = { }), } -export const MockExperiments: TypesGen.Experiment[] = [] +export const MockExperiments: TypesGen.Experiment[] = ["workspace_actions"] export const MockAuditLog: TypesGen.AuditLog = { id: "fbd2116a-8961-4954-87ae-e4575bd29ce0", From a05eb6c10f9707b349a9b2c96690e876a08367f9 Mon Sep 17 00:00:00 2001 From: Kira Pilot Date: Thu, 4 May 2023 15:18:41 +0000 Subject: [PATCH 15/21] added BE tests --- coderd/apidoc/docs.go | 8 ++++ coderd/apidoc/swagger.json | 8 ++++ coderd/templates_test.go | 86 ++++++++++++++++++++++++++++++++++ codersdk/organizations.go | 7 +++ docs/api/schemas.md | 4 ++ docs/api/templates.md | 2 + site/src/api/typesGenerated.ts | 2 + 7 files changed, 117 insertions(+) diff --git a/coderd/apidoc/docs.go b/coderd/apidoc/docs.go index 14421645d78f7..97709d11d4af6 100644 --- a/coderd/apidoc/docs.go +++ b/coderd/apidoc/docs.go @@ -6719,10 +6719,18 @@ const docTemplate = `{ "description": "DisplayName is the displayed name of the template.", "type": "string" }, + "failure_ttl_ms": { + "description": "FailureTTLMillis allows optionally specifying the max lifetime before Coder\nstops all resources for failed workspaces created from this template.", + "type": "integer" + }, "icon": { "description": "Icon is a relative path or external URL that specifies\nan icon to be displayed in the dashboard.", "type": "string" }, + "inactivity_ttl_ms": { + "description": "InactivityTTLMillis allows optionally specifying the max lifetime before Coder\ndeletes inactive workspaces created from this template.", + "type": "integer" + }, "max_ttl_ms": { "description": "MaxTTLMillis allows optionally specifying the max lifetime for\nworkspaces created from this template.", "type": "integer" diff --git a/coderd/apidoc/swagger.json b/coderd/apidoc/swagger.json index b95dfbe5f68c4..f8827ccb16092 100644 --- a/coderd/apidoc/swagger.json +++ b/coderd/apidoc/swagger.json @@ -5978,10 +5978,18 @@ "description": "DisplayName is the displayed name of the template.", "type": "string" }, + "failure_ttl_ms": { + "description": "FailureTTLMillis allows optionally specifying the max lifetime before Coder\nstops all resources for failed workspaces created from this template.", + "type": "integer" + }, "icon": { "description": "Icon is a relative path or external URL that specifies\nan icon to be displayed in the dashboard.", "type": "string" }, + "inactivity_ttl_ms": { + "description": "InactivityTTLMillis allows optionally specifying the max lifetime before Coder\ndeletes inactive workspaces created from this template.", + "type": "integer" + }, "max_ttl_ms": { "description": "MaxTTLMillis allows optionally specifying the max lifetime for\nworkspaces created from this template.", "type": "integer" diff --git a/coderd/templates_test.go b/coderd/templates_test.go index 75ef12ede2bb0..9b8b2ffaaa1a3 100644 --- a/coderd/templates_test.go +++ b/coderd/templates_test.go @@ -604,6 +604,92 @@ func TestPatchTemplateMeta(t *testing.T) { }) }) + t.Run("CleanupTTLs", func(t *testing.T) { + t.Parallel() + + const ( + failureTTL = 7 * 24 * time.Hour + inactivityTTL = 180 * 24 * time.Hour + ) + + t.Run("OK", func(t *testing.T) { + t.Parallel() + + var setCalled int64 + client := coderdtest.New(t, &coderdtest.Options{ + TemplateScheduleStore: schedule.MockTemplateScheduleStore{ + SetFn: func(ctx context.Context, db database.Store, template database.Template, options schedule.TemplateScheduleOptions) (database.Template, error) { + if atomic.AddInt64(&setCalled, 1) == 2 { + require.Equal(t, failureTTL, options.FailureTTL) + require.Equal(t, inactivityTTL, options.InactivityTTL) + + } + template.FailureTTL = int64(options.FailureTTL) + template.InactivityTTL = int64(options.InactivityTTL) + return template, 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.FailureTTLMillis = ptr.Ref(0 * time.Hour.Milliseconds()) + ctr.InactivityTTLMillis = ptr.Ref(0 * time.Hour.Milliseconds()) + }) + + ctx, cancel := context.WithTimeout(context.Background(), testutil.WaitLong) + defer cancel() + + got, err := client.UpdateTemplateMeta(ctx, template.ID, codersdk.UpdateTemplateMeta{ + Name: template.Name, + DisplayName: template.DisplayName, + Description: template.Description, + Icon: template.Icon, + DefaultTTLMillis: 0, + MaxTTLMillis: 0, + AllowUserCancelWorkspaceJobs: template.AllowUserCancelWorkspaceJobs, + FailureTTLMillis: failureTTL.Milliseconds(), + InactivityTTLMillis: inactivityTTL.Milliseconds(), + }) + require.NoError(t, err) + + require.EqualValues(t, 2, atomic.LoadInt64(&setCalled)) + require.EqualValues(t, 0, got.DefaultTTLMillis) + require.Equal(t, failureTTL.Milliseconds(), got.FailureTTLMillis) + require.Equal(t, inactivityTTL.Milliseconds(), got.InactivityTTLMillis) + }) + + t.Run("IgnoredUnlicensed", 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.FailureTTLMillis = ptr.Ref(0 * time.Hour.Milliseconds()) + ctr.InactivityTTLMillis = ptr.Ref(0 * time.Hour.Milliseconds()) + }) + + ctx, cancel := context.WithTimeout(context.Background(), testutil.WaitLong) + defer cancel() + + got, err := client.UpdateTemplateMeta(ctx, template.ID, codersdk.UpdateTemplateMeta{ + Name: template.Name, + DisplayName: template.DisplayName, + Description: template.Description, + Icon: template.Icon, + DefaultTTLMillis: template.DefaultTTLMillis, + MaxTTLMillis: template.MaxTTLMillis, + AllowUserCancelWorkspaceJobs: template.AllowUserCancelWorkspaceJobs, + FailureTTLMillis: failureTTL.Milliseconds(), + InactivityTTLMillis: inactivityTTL.Milliseconds(), + }) + require.NoError(t, err) + require.Zero(t, got.FailureTTLMillis) + require.Zero(t, got.InactivityTTLMillis) + }) + }) + t.Run("AllowUserScheduling", func(t *testing.T) { t.Parallel() diff --git a/codersdk/organizations.go b/codersdk/organizations.go index f82315e6f02ae..2b23d2e464c3e 100644 --- a/codersdk/organizations.go +++ b/codersdk/organizations.go @@ -106,6 +106,13 @@ type CreateTemplateRequest struct { // false, the DefaultTTL will always be used. This can only be disabled when // using an enterprise license. AllowUserAutostop *bool `json:"allow_user_autostop"` + + // FailureTTLMillis allows optionally specifying the max lifetime before Coder + // stops all resources for failed workspaces created from this template. + FailureTTLMillis *int64 `json:"failure_ttl_ms,omitempty"` + // InactivityTTLMillis allows optionally specifying the max lifetime before Coder + // deletes inactive workspaces created from this template. + InactivityTTLMillis *int64 `json:"inactivity_ttl_ms,omitempty"` } // CreateWorkspaceRequest provides options for creating a new workspace. diff --git a/docs/api/schemas.md b/docs/api/schemas.md index 1241a8a4c68cb..a7398e1e7c4fb 100644 --- a/docs/api/schemas.md +++ b/docs/api/schemas.md @@ -1333,7 +1333,9 @@ CreateParameterRequest is a structure used to create a new parameter value for a "default_ttl_ms": 0, "description": "string", "display_name": "string", + "failure_ttl_ms": 0, "icon": "string", + "inactivity_ttl_ms": 0, "max_ttl_ms": 0, "name": "string", "parameter_values": [ @@ -1359,7 +1361,9 @@ CreateParameterRequest is a structure used to create a new parameter value for a | `default_ttl_ms` | integer | false | | Default ttl ms allows optionally specifying the default TTL for all workspaces created from this template. | | `description` | string | false | | Description is a description of what the template contains. It must be less than 128 bytes. | | `display_name` | string | false | | Display name is the displayed name of the template. | +| `failure_ttl_ms` | integer | false | | Failure ttl ms allows optionally specifying the max lifetime before Coder stops all resources for failed workspaces created from this template. | | `icon` | string | false | | Icon is a relative path or external URL that specifies an icon to be displayed in the dashboard. | +| `inactivity_ttl_ms` | integer | false | | Inactivity ttl ms allows optionally specifying the max lifetime before Coder deletes inactive workspaces created from this template. | | `max_ttl_ms` | integer | false | | Max ttl ms allows optionally specifying the max lifetime for workspaces created from this template. | | `name` | string | true | | Name is the name of the template. | | `parameter_values` | array of [codersdk.CreateParameterRequest](#codersdkcreateparameterrequest) | false | | Parameter values is a structure used to create a new parameter value for a scope.] | diff --git a/docs/api/templates.md b/docs/api/templates.md index 054d3fddea9d4..bd56159b8eaf9 100644 --- a/docs/api/templates.md +++ b/docs/api/templates.md @@ -130,7 +130,9 @@ curl -X POST http://coder-server:8080/api/v2/organizations/{organization}/templa "default_ttl_ms": 0, "description": "string", "display_name": "string", + "failure_ttl_ms": 0, "icon": "string", + "inactivity_ttl_ms": 0, "max_ttl_ms": 0, "name": "string", "parameter_values": [ diff --git a/site/src/api/typesGenerated.ts b/site/src/api/typesGenerated.ts index dcc90b4c40778..8bc4b1e6aedc7 100644 --- a/site/src/api/typesGenerated.ts +++ b/site/src/api/typesGenerated.ts @@ -194,6 +194,8 @@ export interface CreateTemplateRequest { readonly allow_user_cancel_workspace_jobs?: boolean readonly allow_user_autostart?: boolean readonly allow_user_autostop?: boolean + readonly failure_ttl_ms?: number + readonly inactivity_ttl_ms?: number } // From codersdk/templateversions.go From 0478e070b9850bb044302bdaa08bc24352050c6e Mon Sep 17 00:00:00 2001 From: Kira Pilot Date: Thu, 4 May 2023 15:28:42 +0000 Subject: [PATCH 16/21] fixed lint --- coderd/templates_test.go | 1 - 1 file changed, 1 deletion(-) diff --git a/coderd/templates_test.go b/coderd/templates_test.go index 9b8b2ffaaa1a3..6c9e2bdfcbaed 100644 --- a/coderd/templates_test.go +++ b/coderd/templates_test.go @@ -622,7 +622,6 @@ func TestPatchTemplateMeta(t *testing.T) { if atomic.AddInt64(&setCalled, 1) == 2 { require.Equal(t, failureTTL, options.FailureTTL) require.Equal(t, inactivityTTL, options.InactivityTTL) - } template.FailureTTL = int64(options.FailureTTL) template.InactivityTTL = int64(options.InactivityTTL) From bba4722686d233b4b196d248cda76e490e5a4303 Mon Sep 17 00:00:00 2001 From: Kira Pilot Date: Thu, 4 May 2023 15:34:42 +0000 Subject: [PATCH 17/21] adjusted comment language --- codersdk/templates.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/codersdk/templates.go b/codersdk/templates.go index 9f8b2e2033a36..b1f258d9daa5b 100644 --- a/codersdk/templates.go +++ b/codersdk/templates.go @@ -42,8 +42,8 @@ type Template struct { AllowUserCancelWorkspaceJobs bool `json:"allow_user_cancel_workspace_jobs"` // FailureTTLMillis and InactivityTTLMillis are enterprise-only. Their - // values are used if your license is entitled to use the workspace actions - // feature + // values are used if your license is entitled to use the advanced + // template scheduling feature. FailureTTLMillis int64 `json:"failure_ttl_ms"` InactivityTTLMillis int64 `json:"inactivity_ttl_ms"` } From c76a0f46b940c940678c621b25fea2a20298a752 Mon Sep 17 00:00:00 2001 From: Kira Pilot Date: Thu, 4 May 2023 15:43:58 +0000 Subject: [PATCH 18/21] fixing unstaged changes check --- coderd/apidoc/docs.go | 2 +- coderd/apidoc/swagger.json | 2 +- docs/api/schemas.md | 2 +- docs/api/templates.md | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/coderd/apidoc/docs.go b/coderd/apidoc/docs.go index 97709d11d4af6..2ad130318dcbb 100644 --- a/coderd/apidoc/docs.go +++ b/coderd/apidoc/docs.go @@ -8707,7 +8707,7 @@ const docTemplate = `{ "type": "string" }, "failure_ttl_ms": { - "description": "FailureTTLMillis and InactivityTTLMillis are enterprise-only. Their\nvalues are used if your license is entitled to use the workspace actions\nfeature", + "description": "FailureTTLMillis and InactivityTTLMillis are enterprise-only. Their\nvalues are used if your license is entitled to use the advanced\ntemplate scheduling feature.", "type": "integer" }, "icon": { diff --git a/coderd/apidoc/swagger.json b/coderd/apidoc/swagger.json index f8827ccb16092..e2a593c1a415c 100644 --- a/coderd/apidoc/swagger.json +++ b/coderd/apidoc/swagger.json @@ -7825,7 +7825,7 @@ "type": "string" }, "failure_ttl_ms": { - "description": "FailureTTLMillis and InactivityTTLMillis are enterprise-only. Their\nvalues are used if your license is entitled to use the workspace actions\nfeature", + "description": "FailureTTLMillis and InactivityTTLMillis are enterprise-only. Their\nvalues are used if your license is entitled to use the advanced\ntemplate scheduling feature.", "type": "integer" }, "icon": { diff --git a/docs/api/schemas.md b/docs/api/schemas.md index a7398e1e7c4fb..6a82b9a1c98ec 100644 --- a/docs/api/schemas.md +++ b/docs/api/schemas.md @@ -3898,7 +3898,7 @@ Parameter represents a set value for the scope. | `default_ttl_ms` | integer | false | | | | `description` | string | false | | | | `display_name` | string | false | | | -| `failure_ttl_ms` | integer | false | | Failure ttl ms and InactivityTTLMillis are enterprise-only. Their values are used if your license is entitled to use the workspace actions feature | +| `failure_ttl_ms` | integer | false | | Failure ttl ms and InactivityTTLMillis are enterprise-only. Their values are used if your license is entitled to use the advanced template scheduling feature. | | `icon` | string | false | | | | `id` | string | false | | | | `inactivity_ttl_ms` | integer | false | | | diff --git a/docs/api/templates.md b/docs/api/templates.md index bd56159b8eaf9..7df57183b54e3 100644 --- a/docs/api/templates.md +++ b/docs/api/templates.md @@ -88,7 +88,7 @@ Status Code **200** | `» default_ttl_ms` | integer | false | | | | `» description` | string | false | | | | `» display_name` | string | false | | | -| `» failure_ttl_ms` | integer | false | | Failure ttl ms and InactivityTTLMillis are enterprise-only. Their values are used if your license is entitled to use the workspace actions feature | +| `» failure_ttl_ms` | integer | false | | Failure ttl ms and InactivityTTLMillis are enterprise-only. Their values are used if your license is entitled to use the advanced template scheduling feature. | | `» icon` | string | false | | | | `» id` | string(uuid) | false | | | | `» inactivity_ttl_ms` | integer | false | | | From 9db6b130c8e828d8e3916b47ebca5ac5d6352bb8 Mon Sep 17 00:00:00 2001 From: Kira Pilot Date: Thu, 4 May 2023 15:55:39 +0000 Subject: [PATCH 19/21] fix test --- coderd/templates_test.go | 1 - 1 file changed, 1 deletion(-) diff --git a/coderd/templates_test.go b/coderd/templates_test.go index 6c9e2bdfcbaed..f0db7a12cadde 100644 --- a/coderd/templates_test.go +++ b/coderd/templates_test.go @@ -653,7 +653,6 @@ func TestPatchTemplateMeta(t *testing.T) { require.NoError(t, err) require.EqualValues(t, 2, atomic.LoadInt64(&setCalled)) - require.EqualValues(t, 0, got.DefaultTTLMillis) require.Equal(t, failureTTL.Milliseconds(), got.FailureTTLMillis) require.Equal(t, inactivityTTL.Milliseconds(), got.InactivityTTLMillis) }) From 9fa9c5b676a9267ea2702d275f286720b1521636 Mon Sep 17 00:00:00 2001 From: Kira Pilot Date: Fri, 5 May 2023 07:19:23 -0700 Subject: [PATCH 20/21] Update coderd/database/migrations/000122_add_template_cleanup_ttls.down.sql Co-authored-by: Dean Sheather --- .../migrations/000122_add_template_cleanup_ttls.down.sql | 2 ++ 1 file changed, 2 insertions(+) diff --git a/coderd/database/migrations/000122_add_template_cleanup_ttls.down.sql b/coderd/database/migrations/000122_add_template_cleanup_ttls.down.sql index 70fd50d21a66d..78a04e961eea5 100644 --- a/coderd/database/migrations/000122_add_template_cleanup_ttls.down.sql +++ b/coderd/database/migrations/000122_add_template_cleanup_ttls.down.sql @@ -1,2 +1,4 @@ +BEGIN; ALTER TABLE ONLY templates DROP COLUMN IF EXISTS failure_ttl; ALTER TABLE ONLY templates DROP COLUMN IF EXISTS inactivity_ttl; +COMMIT; From 43a2267e316b27a3d814a3affbbe5ba1792f671d Mon Sep 17 00:00:00 2001 From: Kira Pilot Date: Fri, 5 May 2023 07:20:06 -0700 Subject: [PATCH 21/21] Update coderd/database/migrations/000122_add_template_cleanup_ttls.up.sql Co-authored-by: Dean Sheather --- .../database/migrations/000122_add_template_cleanup_ttls.up.sql | 2 ++ 1 file changed, 2 insertions(+) diff --git a/coderd/database/migrations/000122_add_template_cleanup_ttls.up.sql b/coderd/database/migrations/000122_add_template_cleanup_ttls.up.sql index 980588e269d45..f043356375c99 100644 --- a/coderd/database/migrations/000122_add_template_cleanup_ttls.up.sql +++ b/coderd/database/migrations/000122_add_template_cleanup_ttls.up.sql @@ -1,2 +1,4 @@ +BEGIN; ALTER TABLE ONLY templates ADD COLUMN IF NOT EXISTS failure_ttl BIGINT NOT NULL DEFAULT 0; ALTER TABLE ONLY templates ADD COLUMN IF NOT EXISTS inactivity_ttl BIGINT NOT NULL DEFAULT 0; +COMMIT;