feat: add a per-template workspace rename setting (#27558)

This commit is contained in:
McKayla はな
2026-08-25 11:05:42 -06:00
committed by GitHub
parent 827c11f43a
commit 29995c9ef8
39 changed files with 574 additions and 191 deletions
-7
View File
@@ -17,13 +17,6 @@ SUBCOMMANDS:
PostgreSQL deployment.
OPTIONS:
--allow-workspace-renames bool, $CODER_ALLOW_WORKSPACE_RENAMES (default: false)
Allow users to rename their workspaces. WARNING: Renaming a workspace
can cause Terraform resources that depend on the workspace name to be
destroyed and recreated, potentially causing data loss. Only enable
this if your templates do not use workspace names in resource
identifiers, or if you understand the risks.
--cache-dir string, $CODER_CACHE_DIRECTORY (default: [cache dir])
The directory to cache temporary files. If unspecified and
$CACHE_DIRECTORY is set, it will be used for compatibility with
+4 -4
View File
@@ -635,10 +635,10 @@ userQuietHoursSchedule:
# change their quiet hours schedule and the site default is always used.
# (default: true, type: bool)
allowCustomQuietHours: true
# Allow users to rename their workspaces. WARNING: Renaming a workspace can cause
# Terraform resources that depend on the workspace name to be destroyed and
# recreated, potentially causing data loss. Only enable this if your templates do
# not use workspace names in resource identifiers, or if you understand the risks.
# Deprecated: use the per-template "Allow workspace renames" setting instead.
# While set, it force-enables renames for every template in the deployment.
# WARNING: Renaming a workspace can cause Terraform resources that depend on the
# workspace name to be destroyed and recreated, potentially causing data loss.
# (default: false, type: bool)
allowWorkspaceRenames: false
# Configure how emails are sent.
+14
View File
@@ -20753,6 +20753,10 @@ const docTemplate = `{
"description": "Allow users to cancel in-progress workspace jobs.\n*bool as the default value is \"true\".",
"type": "boolean"
},
"allow_workspace_renames": {
"description": "AllowWorkspaceRenames permits users to rename workspaces built from this\ntemplate. Renaming can be destructive for templates whose Terraform\nreferences the workspace name, so this defaults to false.",
"type": "boolean"
},
"autostart_requirement": {
"description": "AutostartRequirement allows optionally specifying the autostart allowed days\nfor workspaces created from this template. This is an enterprise feature.",
"allOf": [
@@ -21678,6 +21682,7 @@ const docTemplate = `{
"$ref": "#/definitions/codersdk.AIConfig"
},
"allow_workspace_renames": {
"description": "Deprecated: Use the per-template allow_workspace_renames setting instead.",
"type": "boolean"
},
"autobuild_poll_interval": {
@@ -26543,6 +26548,10 @@ const docTemplate = `{
"allow_user_cancel_workspace_jobs": {
"type": "boolean"
},
"allow_workspace_renames": {
"description": "AllowWorkspaceRenames permits users to rename workspaces built from this\ntemplate. Renaming can be destructive for templates whose Terraform\nreferences the workspace name.",
"type": "boolean"
},
"autostart_requirement": {
"$ref": "#/definitions/codersdk.TemplateAutostartRequirement"
},
@@ -28102,6 +28111,10 @@ const docTemplate = `{
"allow_user_cancel_workspace_jobs": {
"type": "boolean"
},
"allow_workspace_renames": {
"description": "AllowWorkspaceRenames permits users to rename workspaces built from this\ntemplate. Renaming can be destructive for templates whose Terraform\nreferences the workspace name.",
"type": "boolean"
},
"autostart_requirement": {
"$ref": "#/definitions/codersdk.TemplateAutostartRequirement"
},
@@ -29139,6 +29152,7 @@ const docTemplate = `{
"type": "object",
"properties": {
"allow_renames": {
"description": "AllowRenames is the effective rename permission for this workspace,\nderived from the template's allow_workspace_renames setting and the\ndeprecated deployment-wide flag.",
"type": "boolean"
},
"automatic_updates": {
+14
View File
@@ -18828,6 +18828,10 @@
"description": "Allow users to cancel in-progress workspace jobs.\n*bool as the default value is \"true\".",
"type": "boolean"
},
"allow_workspace_renames": {
"description": "AllowWorkspaceRenames permits users to rename workspaces built from this\ntemplate. Renaming can be destructive for templates whose Terraform\nreferences the workspace name, so this defaults to false.",
"type": "boolean"
},
"autostart_requirement": {
"description": "AutostartRequirement allows optionally specifying the autostart allowed days\nfor workspaces created from this template. This is an enterprise feature.",
"allOf": [
@@ -19717,6 +19721,7 @@
"$ref": "#/definitions/codersdk.AIConfig"
},
"allow_workspace_renames": {
"description": "Deprecated: Use the per-template allow_workspace_renames setting instead.",
"type": "boolean"
},
"autobuild_poll_interval": {
@@ -24405,6 +24410,10 @@
"allow_user_cancel_workspace_jobs": {
"type": "boolean"
},
"allow_workspace_renames": {
"description": "AllowWorkspaceRenames permits users to rename workspaces built from this\ntemplate. Renaming can be destructive for templates whose Terraform\nreferences the workspace name.",
"type": "boolean"
},
"autostart_requirement": {
"$ref": "#/definitions/codersdk.TemplateAutostartRequirement"
},
@@ -25876,6 +25885,10 @@
"allow_user_cancel_workspace_jobs": {
"type": "boolean"
},
"allow_workspace_renames": {
"description": "AllowWorkspaceRenames permits users to rename workspaces built from this\ntemplate. Renaming can be destructive for templates whose Terraform\nreferences the workspace name.",
"type": "boolean"
},
"autostart_requirement": {
"$ref": "#/definitions/codersdk.TemplateAutostartRequirement"
},
@@ -26862,6 +26875,7 @@
"type": "object",
"properties": {
"allow_renames": {
"description": "AllowRenames is the effective rename permission for this workspace,\nderived from the template's allow_workspace_renames setting and the\ndeprecated deployment-wide flag.",
"type": "boolean"
},
"automatic_updates": {
+1
View File
@@ -596,6 +596,7 @@ func Template(t testing.TB, db database.Store, seed database.Template) database.
UseClassicParameterFlow: takeFirst(seed.UseClassicParameterFlow, false),
CorsBehavior: takeFirst(seed.CorsBehavior, database.CorsBehaviorSimple),
AgentsAllowed: seed.AgentsAllowed,
AllowWorkspaceRenames: seed.AllowWorkspaceRenames,
})
require.NoError(t, err, "insert template")
+5 -1
View File
@@ -3571,7 +3571,8 @@ CREATE TABLE templates (
cors_behavior cors_behavior DEFAULT 'simple'::cors_behavior NOT NULL,
disable_module_cache boolean DEFAULT false NOT NULL,
time_til_autostop_notify bigint DEFAULT 0 NOT NULL,
agents_allowed boolean DEFAULT true NOT NULL
agents_allowed boolean DEFAULT true NOT NULL,
allow_workspace_renames boolean DEFAULT false NOT NULL
);
COMMENT ON COLUMN templates.default_ttl IS 'The default duration for autostop for workspaces created from this template.';
@@ -3598,6 +3599,8 @@ COMMENT ON COLUMN templates.time_til_autostop_notify IS 'How long before the wor
COMMENT ON COLUMN templates.agents_allowed IS 'Whether Coder Agents can create workspaces using this template.';
COMMENT ON COLUMN templates.allow_workspace_renames IS 'Whether workspaces built from this template may be renamed. Renaming can be destructive for templates whose Terraform references the workspace name.';
CREATE VIEW template_with_names AS
SELECT templates.id,
templates.created_at,
@@ -3632,6 +3635,7 @@ CREATE VIEW template_with_names AS
templates.disable_module_cache,
templates.time_til_autostop_notify,
templates.agents_allowed,
templates.allow_workspace_renames,
COALESCE(visible_users.avatar_url, ''::text) AS created_by_avatar_url,
COALESCE(visible_users.username, ''::text) AS created_by_username,
COALESCE(visible_users.name, ''::text) AS created_by_name,
@@ -0,0 +1,17 @@
DROP VIEW template_with_names;
ALTER TABLE templates DROP COLUMN allow_workspace_renames;
CREATE VIEW template_with_names AS
SELECT templates.*,
COALESCE(visible_users.avatar_url, ''::text) AS created_by_avatar_url,
COALESCE(visible_users.username, ''::text) AS created_by_username,
COALESCE(visible_users.name, ''::text) AS created_by_name,
COALESCE(organizations.name, ''::text) AS organization_name,
COALESCE(organizations.display_name, ''::text) AS organization_display_name,
COALESCE(organizations.icon, ''::text) AS organization_icon
FROM ((templates
LEFT JOIN visible_users ON ((templates.created_by = visible_users.id)))
LEFT JOIN organizations ON ((templates.organization_id = organizations.id)));
COMMENT ON VIEW template_with_names IS 'Joins in the display name information such as username, avatar, and organization name.';
@@ -0,0 +1,19 @@
ALTER TABLE templates ADD COLUMN allow_workspace_renames boolean DEFAULT false NOT NULL;
COMMENT ON COLUMN templates.allow_workspace_renames IS 'Whether workspaces built from this template may be renamed. Renaming can be destructive for templates whose Terraform references the workspace name.';
DROP VIEW template_with_names;
CREATE VIEW template_with_names AS
SELECT templates.*,
COALESCE(visible_users.avatar_url, ''::text) AS created_by_avatar_url,
COALESCE(visible_users.username, ''::text) AS created_by_username,
COALESCE(visible_users.name, ''::text) AS created_by_name,
COALESCE(organizations.name, ''::text) AS organization_name,
COALESCE(organizations.display_name, ''::text) AS organization_display_name,
COALESCE(organizations.icon, ''::text) AS organization_icon
FROM ((templates
LEFT JOIN visible_users ON ((templates.created_by = visible_users.id)))
LEFT JOIN organizations ON ((templates.organization_id = organizations.id)));
COMMENT ON VIEW template_with_names IS 'Joins in the display name information such as username, avatar, and organization name.';
+1
View File
@@ -192,6 +192,7 @@ func (q *sqlQuerier) GetAuthorizedTemplates(ctx context.Context, arg GetTemplate
&i.DisableModuleCache,
&i.TimeTilAutostopNotify,
&i.AgentsAllowed,
&i.AllowWorkspaceRenames,
&i.CreatedByAvatarURL,
&i.CreatedByUsername,
&i.CreatedByName,
+3
View File
@@ -6112,6 +6112,7 @@ type Template struct {
DisableModuleCache bool `db:"disable_module_cache" json:"disable_module_cache"`
TimeTilAutostopNotify int64 `db:"time_til_autostop_notify" json:"time_til_autostop_notify"`
AgentsAllowed bool `db:"agents_allowed" json:"agents_allowed"`
AllowWorkspaceRenames bool `db:"allow_workspace_renames" json:"allow_workspace_renames"`
CreatedByAvatarURL string `db:"created_by_avatar_url" json:"created_by_avatar_url"`
CreatedByUsername string `db:"created_by_username" json:"created_by_username"`
CreatedByName string `db:"created_by_name" json:"created_by_name"`
@@ -6166,6 +6167,8 @@ type TemplateTable struct {
TimeTilAutostopNotify int64 `db:"time_til_autostop_notify" json:"time_til_autostop_notify"`
// Whether Coder Agents can create workspaces using this template.
AgentsAllowed bool `db:"agents_allowed" json:"agents_allowed"`
// Whether workspaces built from this template may be renamed. Renaming can be destructive for templates whose Terraform references the workspace name.
AllowWorkspaceRenames bool `db:"allow_workspace_renames" json:"allow_workspace_renames"`
}
// Records aggregated usage statistics for templates/users. All usage is rounded up to the nearest minute.
+18 -8
View File
@@ -27674,7 +27674,7 @@ func (q *sqlQuerier) GetTemplateAverageBuildTime(ctx context.Context, templateID
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, allow_user_autostart, allow_user_autostop, failure_ttl, time_til_dormant, time_til_dormant_autodelete, autostop_requirement_days_of_week, autostop_requirement_weeks, autostart_block_days_of_week, require_active_version, deprecated, activity_bump, max_port_sharing_level, use_classic_parameter_flow, cors_behavior, disable_module_cache, time_til_autostop_notify, agents_allowed, created_by_avatar_url, created_by_username, created_by_name, organization_name, organization_display_name, organization_icon
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, allow_user_autostart, allow_user_autostop, failure_ttl, time_til_dormant, time_til_dormant_autodelete, autostop_requirement_days_of_week, autostop_requirement_weeks, autostart_block_days_of_week, require_active_version, deprecated, activity_bump, max_port_sharing_level, use_classic_parameter_flow, cors_behavior, disable_module_cache, time_til_autostop_notify, agents_allowed, allow_workspace_renames, created_by_avatar_url, created_by_username, created_by_name, organization_name, organization_display_name, organization_icon
FROM
template_with_names
WHERE
@@ -27720,6 +27720,7 @@ func (q *sqlQuerier) GetTemplateByID(ctx context.Context, id uuid.UUID) (Templat
&i.DisableModuleCache,
&i.TimeTilAutostopNotify,
&i.AgentsAllowed,
&i.AllowWorkspaceRenames,
&i.CreatedByAvatarURL,
&i.CreatedByUsername,
&i.CreatedByName,
@@ -27732,7 +27733,7 @@ func (q *sqlQuerier) GetTemplateByID(ctx context.Context, id uuid.UUID) (Templat
const getTemplateByOrganizationAndName = `-- name: GetTemplateByOrganizationAndName :one
SELECT
id, created_at, updated_at, organization_id, deleted, name, provisioner, active_version_id, description, default_ttl, created_by, icon, user_acl, group_acl, display_name, allow_user_cancel_workspace_jobs, allow_user_autostart, allow_user_autostop, failure_ttl, time_til_dormant, time_til_dormant_autodelete, autostop_requirement_days_of_week, autostop_requirement_weeks, autostart_block_days_of_week, require_active_version, deprecated, activity_bump, max_port_sharing_level, use_classic_parameter_flow, cors_behavior, disable_module_cache, time_til_autostop_notify, agents_allowed, created_by_avatar_url, created_by_username, created_by_name, organization_name, organization_display_name, organization_icon
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, allow_user_autostart, allow_user_autostop, failure_ttl, time_til_dormant, time_til_dormant_autodelete, autostop_requirement_days_of_week, autostop_requirement_weeks, autostart_block_days_of_week, require_active_version, deprecated, activity_bump, max_port_sharing_level, use_classic_parameter_flow, cors_behavior, disable_module_cache, time_til_autostop_notify, agents_allowed, allow_workspace_renames, created_by_avatar_url, created_by_username, created_by_name, organization_name, organization_display_name, organization_icon
FROM
template_with_names AS templates
WHERE
@@ -27786,6 +27787,7 @@ func (q *sqlQuerier) GetTemplateByOrganizationAndName(ctx context.Context, arg G
&i.DisableModuleCache,
&i.TimeTilAutostopNotify,
&i.AgentsAllowed,
&i.AllowWorkspaceRenames,
&i.CreatedByAvatarURL,
&i.CreatedByUsername,
&i.CreatedByName,
@@ -27797,7 +27799,7 @@ func (q *sqlQuerier) GetTemplateByOrganizationAndName(ctx context.Context, arg G
}
const getTemplates = `-- name: GetTemplates :many
SELECT id, created_at, updated_at, organization_id, deleted, name, provisioner, active_version_id, description, default_ttl, created_by, icon, user_acl, group_acl, display_name, allow_user_cancel_workspace_jobs, allow_user_autostart, allow_user_autostop, failure_ttl, time_til_dormant, time_til_dormant_autodelete, autostop_requirement_days_of_week, autostop_requirement_weeks, autostart_block_days_of_week, require_active_version, deprecated, activity_bump, max_port_sharing_level, use_classic_parameter_flow, cors_behavior, disable_module_cache, time_til_autostop_notify, agents_allowed, created_by_avatar_url, created_by_username, created_by_name, organization_name, organization_display_name, organization_icon FROM template_with_names AS 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, allow_user_autostart, allow_user_autostop, failure_ttl, time_til_dormant, time_til_dormant_autodelete, autostop_requirement_days_of_week, autostop_requirement_weeks, autostart_block_days_of_week, require_active_version, deprecated, activity_bump, max_port_sharing_level, use_classic_parameter_flow, cors_behavior, disable_module_cache, time_til_autostop_notify, agents_allowed, allow_workspace_renames, created_by_avatar_url, created_by_username, created_by_name, organization_name, organization_display_name, organization_icon FROM template_with_names AS templates
ORDER BY (name, id) ASC
`
@@ -27844,6 +27846,7 @@ func (q *sqlQuerier) GetTemplates(ctx context.Context) ([]Template, error) {
&i.DisableModuleCache,
&i.TimeTilAutostopNotify,
&i.AgentsAllowed,
&i.AllowWorkspaceRenames,
&i.CreatedByAvatarURL,
&i.CreatedByUsername,
&i.CreatedByName,
@@ -27866,7 +27869,7 @@ func (q *sqlQuerier) GetTemplates(ctx context.Context) ([]Template, error) {
const getTemplatesWithFilter = `-- name: GetTemplatesWithFilter :many
SELECT
t.id, t.created_at, t.updated_at, t.organization_id, t.deleted, t.name, t.provisioner, t.active_version_id, t.description, t.default_ttl, t.created_by, t.icon, t.user_acl, t.group_acl, t.display_name, t.allow_user_cancel_workspace_jobs, t.allow_user_autostart, t.allow_user_autostop, t.failure_ttl, t.time_til_dormant, t.time_til_dormant_autodelete, t.autostop_requirement_days_of_week, t.autostop_requirement_weeks, t.autostart_block_days_of_week, t.require_active_version, t.deprecated, t.activity_bump, t.max_port_sharing_level, t.use_classic_parameter_flow, t.cors_behavior, t.disable_module_cache, t.time_til_autostop_notify, t.agents_allowed, t.created_by_avatar_url, t.created_by_username, t.created_by_name, t.organization_name, t.organization_display_name, t.organization_icon
t.id, t.created_at, t.updated_at, t.organization_id, t.deleted, t.name, t.provisioner, t.active_version_id, t.description, t.default_ttl, t.created_by, t.icon, t.user_acl, t.group_acl, t.display_name, t.allow_user_cancel_workspace_jobs, t.allow_user_autostart, t.allow_user_autostop, t.failure_ttl, t.time_til_dormant, t.time_til_dormant_autodelete, t.autostop_requirement_days_of_week, t.autostop_requirement_weeks, t.autostart_block_days_of_week, t.require_active_version, t.deprecated, t.activity_bump, t.max_port_sharing_level, t.use_classic_parameter_flow, t.cors_behavior, t.disable_module_cache, t.time_til_autostop_notify, t.agents_allowed, t.allow_workspace_renames, t.created_by_avatar_url, t.created_by_username, t.created_by_name, t.organization_name, t.organization_display_name, t.organization_icon
FROM
template_with_names AS t
LEFT JOIN
@@ -28036,6 +28039,7 @@ func (q *sqlQuerier) GetTemplatesWithFilter(ctx context.Context, arg GetTemplate
&i.DisableModuleCache,
&i.TimeTilAutostopNotify,
&i.AgentsAllowed,
&i.AllowWorkspaceRenames,
&i.CreatedByAvatarURL,
&i.CreatedByUsername,
&i.CreatedByName,
@@ -28076,10 +28080,11 @@ INSERT INTO
max_port_sharing_level,
use_classic_parameter_flow,
cors_behavior,
agents_allowed
agents_allowed,
allow_workspace_renames
)
VALUES
($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16, $17, $18)
($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16, $17, $18, $19)
`
type InsertTemplateParams struct {
@@ -28101,6 +28106,7 @@ type InsertTemplateParams struct {
UseClassicParameterFlow bool `db:"use_classic_parameter_flow" json:"use_classic_parameter_flow"`
CorsBehavior CorsBehavior `db:"cors_behavior" json:"cors_behavior"`
AgentsAllowed bool `db:"agents_allowed" json:"agents_allowed"`
AllowWorkspaceRenames bool `db:"allow_workspace_renames" json:"allow_workspace_renames"`
}
func (q *sqlQuerier) InsertTemplate(ctx context.Context, arg InsertTemplateParams) error {
@@ -28123,6 +28129,7 @@ func (q *sqlQuerier) InsertTemplate(ctx context.Context, arg InsertTemplateParam
arg.UseClassicParameterFlow,
arg.CorsBehavior,
arg.AgentsAllowed,
arg.AllowWorkspaceRenames,
)
return err
}
@@ -28226,7 +28233,8 @@ SET
use_classic_parameter_flow = $10,
cors_behavior = $11,
disable_module_cache = $12,
agents_allowed = $13
agents_allowed = $13,
allow_workspace_renames = $14
WHERE
id = $1
`
@@ -28245,6 +28253,7 @@ type UpdateTemplateMetaByIDParams struct {
CorsBehavior CorsBehavior `db:"cors_behavior" json:"cors_behavior"`
DisableModuleCache bool `db:"disable_module_cache" json:"disable_module_cache"`
AgentsAllowed bool `db:"agents_allowed" json:"agents_allowed"`
AllowWorkspaceRenames bool `db:"allow_workspace_renames" json:"allow_workspace_renames"`
}
func (q *sqlQuerier) UpdateTemplateMetaByID(ctx context.Context, arg UpdateTemplateMetaByIDParams) error {
@@ -28262,6 +28271,7 @@ func (q *sqlQuerier) UpdateTemplateMetaByID(ctx context.Context, arg UpdateTempl
arg.CorsBehavior,
arg.DisableModuleCache,
arg.AgentsAllowed,
arg.AllowWorkspaceRenames,
)
return err
}
@@ -39515,7 +39525,7 @@ LEFT JOIN LATERAL (
) latest_build ON TRUE
LEFT JOIN LATERAL (
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, allow_user_autostart, allow_user_autostop, failure_ttl, time_til_dormant, time_til_dormant_autodelete, autostop_requirement_days_of_week, autostop_requirement_weeks, autostart_block_days_of_week, require_active_version, deprecated, activity_bump, max_port_sharing_level, use_classic_parameter_flow, cors_behavior, disable_module_cache, time_til_autostop_notify, agents_allowed
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, allow_user_autostart, allow_user_autostop, failure_ttl, time_til_dormant, time_til_dormant_autodelete, autostop_requirement_days_of_week, autostop_requirement_weeks, autostart_block_days_of_week, require_active_version, deprecated, activity_bump, max_port_sharing_level, use_classic_parameter_flow, cors_behavior, disable_module_cache, time_til_autostop_notify, agents_allowed, allow_workspace_renames
FROM
templates
WHERE
+5 -3
View File
@@ -144,10 +144,11 @@ INSERT INTO
max_port_sharing_level,
use_classic_parameter_flow,
cors_behavior,
agents_allowed
agents_allowed,
allow_workspace_renames
)
VALUES
($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16, $17, $18);
($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16, $17, $18, $19);
-- name: UpdateTemplateActiveVersionByID :exec
UPDATE
@@ -182,7 +183,8 @@ SET
use_classic_parameter_flow = $10,
cors_behavior = $11,
disable_module_cache = $12,
agents_allowed = $13
agents_allowed = $13,
allow_workspace_renames = $14
WHERE
id = $1
;
+1
View File
@@ -556,6 +556,7 @@ func (api *API) templateBuilderCreateTemplate(rw http.ResponseWriter, r *http.Re
UseClassicParameterFlow: false,
CorsBehavior: database.CorsBehaviorSimple,
AgentsAllowed: true,
AllowWorkspaceRenames: false,
})
if err != nil {
if database.IsUniqueViolation(err, database.UniqueTemplatesOrganizationIDNameIndex) {
+4
View File
@@ -420,6 +420,7 @@ func (api *API) postTemplateByOrganization(rw http.ResponseWriter, r *http.Reque
allowUserCancelWorkspaceJobs = ptr.NilToDefault(createTemplate.AllowUserCancelWorkspaceJobs, false)
allowUserAutostart = ptr.NilToDefault(createTemplate.AllowUserAutostart, true)
allowUserAutostop = ptr.NilToDefault(createTemplate.AllowUserAutostop, true)
allowWorkspaceRenames = ptr.NilToDefault(createTemplate.AllowWorkspaceRenames, false)
)
defaultsGroups := database.TemplateACL{}
@@ -450,6 +451,7 @@ func (api *API) postTemplateByOrganization(rw http.ResponseWriter, r *http.Reque
UseClassicParameterFlow: useClassicParameterFlow,
CorsBehavior: corsBehavior,
AgentsAllowed: agentsAllowed,
AllowWorkspaceRenames: allowWorkspaceRenames,
})
if err != nil {
return xerrors.Errorf("insert template: %s", err)
@@ -782,6 +784,7 @@ func (api *API) patchTemplateMeta(rw http.ResponseWriter, r *http.Request) {
CorsBehavior: resolved.corsBehavior,
DisableModuleCache: resolved.disableModuleCache,
AgentsAllowed: resolved.agentsAllowed,
AllowWorkspaceRenames: resolved.allowWorkspaceRenames,
})
if err != nil {
return xerrors.Errorf("update template metadata: %w", err)
@@ -1059,6 +1062,7 @@ func (api *API) convertTemplate(
CORSBehavior: codersdk.CORSBehavior(template.CorsBehavior),
DisableModuleCache: template.DisableModuleCache,
AgentsAllowed: template.AgentsAllowed,
AllowWorkspaceRenames: template.AllowWorkspaceRenames,
}
}
+2
View File
@@ -34,6 +34,7 @@ type templateMetaUpdate struct {
deprecationMessage string
useClassicTemplateFlow bool
disableModuleCache bool
allowWorkspaceRenames bool
corsBehavior database.CorsBehavior
autostopRequirementDaysOfWeekParsed uint8
autostartRequirementDaysOfWeekParsed uint8
@@ -87,6 +88,7 @@ func resolveTemplateMetaUpdate(
deprecationMessage: ptr.NilToDefault(req.DeprecationMessage, template.Deprecated),
useClassicTemplateFlow: ptr.NilToDefault(req.UseClassicParameterFlow, template.UseClassicParameterFlow),
disableModuleCache: ptr.NilToDefault(req.DisableModuleCache, template.DisableModuleCache),
allowWorkspaceRenames: ptr.NilToDefault(req.AllowWorkspaceRenames, template.AllowWorkspaceRenames),
groupACL: template.GroupACL,
// Default to the original values
@@ -44,6 +44,7 @@ func baselineTemplate() database.Template {
UseClassicParameterFlow: true,
CorsBehavior: database.CorsBehaviorPassthru,
DisableModuleCache: true,
AllowWorkspaceRenames: true,
GroupACL: database.TemplateACL{
orgID.String(): {"read"},
},
@@ -87,6 +88,7 @@ func baselineResolved() templateMetaUpdate {
deprecationMessage: tpl.Deprecated,
useClassicTemplateFlow: tpl.UseClassicParameterFlow,
disableModuleCache: tpl.DisableModuleCache,
allowWorkspaceRenames: tpl.AllowWorkspaceRenames,
corsBehavior: tpl.CorsBehavior,
autostopRequirementDaysOfWeekParsed: 0b0000001,
autostartRequirementDaysOfWeekParsed: 0b1000000,
@@ -290,6 +292,13 @@ func TestResolveTemplateMetaUpdate(t *testing.T) {
r.disableModuleCache = false
}},
},
{
name: "AllowWorkspaceRenames",
req: codersdk.UpdateTemplateMeta{AllowWorkspaceRenames: ptr.Ref(false)},
expected: expected{override: func(r *templateMetaUpdate) {
r.allowWorkspaceRenames = false
}},
},
// CORS behavior.
{
+32
View File
@@ -1844,6 +1844,38 @@ func TestPatchTemplateMeta(t *testing.T) {
assert.False(t, updated.DisableModuleCache, "expected false")
})
t.Run("AllowWorkspaceRenames", 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)
require.False(t, template.AllowWorkspaceRenames, "default is false")
ctx := testutil.Context(t, testutil.WaitLong)
req := codersdk.UpdateTemplateMeta{
AllowWorkspaceRenames: ptr.Ref(true),
}
updated, err := client.UpdateTemplateMeta(ctx, template.ID, req)
require.NoError(t, err)
assert.True(t, updated.AllowWorkspaceRenames, "expected true")
// Omitting the field preserves the existing value.
req.AllowWorkspaceRenames = nil
_, err = client.UpdateTemplateMeta(ctx, template.ID, req)
require.NoError(t, err)
updated, err = client.Template(ctx, template.ID)
require.NoError(t, err)
assert.True(t, updated.AllowWorkspaceRenames, "expected true")
req.AllowWorkspaceRenames = ptr.Ref(false)
updated, err = client.UpdateTemplateMeta(ctx, template.ID, req)
require.NoError(t, err)
assert.False(t, updated.AllowWorkspaceRenames, "expected false")
})
t.Run("SupportEmptyOrDefaultFields", func(t *testing.T) {
t.Parallel()
+22 -13
View File
@@ -1154,9 +1154,17 @@ func (api *API) patchWorkspace(rw http.ResponseWriter, r *http.Request) {
// patched in the future, it's enough if one changes.
name := workspace.Name
if req.Name != "" || req.Name != workspace.Name {
if !api.AllowWorkspaceRenames {
template, err := api.Database.GetTemplateByID(ctx, workspace.TemplateID)
if err != nil {
httpapi.Write(ctx, rw, http.StatusInternalServerError, codersdk.Response{
Message: "Internal error fetching workspace template.",
Detail: err.Error(),
})
return
}
if !api.AllowWorkspaceRenames && !template.AllowWorkspaceRenames {
httpapi.Write(ctx, rw, http.StatusBadRequest, codersdk.Response{
Message: "Workspace renames are not allowed.",
Message: "Workspace renames are not enabled for this template.",
})
return
}
@@ -2596,10 +2604,11 @@ func (api *API) patchWorkspaceACL(rw http.ResponseWriter, r *http.Request) {
}
type workspaceData struct {
templates []database.Template
builds []codersdk.WorkspaceBuild
appStatuses []codersdk.WorkspaceAppStatus
allowRenames bool
templates []database.Template
builds []codersdk.WorkspaceBuild
appStatuses []codersdk.WorkspaceAppStatus
// deploymentAllowsRenames is the deprecated deployment-wide rename flag.
deploymentAllowsRenames bool
}
// @Summary Completely clears the workspace's user and group ACLs.
@@ -2757,10 +2766,10 @@ func (api *API) workspaceData(ctx context.Context, workspaces []database.Workspa
}
return workspaceData{
templates: templates,
appStatuses: db2sdk.WorkspaceAppStatuses(appStatuses),
builds: apiBuilds,
allowRenames: api.AllowWorkspaceRenames,
templates: templates,
appStatuses: db2sdk.WorkspaceAppStatuses(appStatuses),
builds: apiBuilds,
deploymentAllowsRenames: api.AllowWorkspaceRenames,
}, nil
}
@@ -2846,7 +2855,7 @@ func convertWorkspaces(
workspace,
build,
template,
data.allowRenames,
data.deploymentAllowsRenames,
appStatus,
)
if err != nil {
@@ -2865,7 +2874,7 @@ func convertWorkspace(
workspace database.Workspace,
workspaceBuild codersdk.WorkspaceBuild,
template database.Template,
allowRenames bool,
deploymentAllowsRenames bool,
latestAppStatus codersdk.WorkspaceAppStatus,
) (codersdk.Workspace, error) {
if requesterID == uuid.Nil {
@@ -2953,7 +2962,7 @@ func convertWorkspace(
FailingAgents: failingAgents,
},
AutomaticUpdates: codersdk.AutomaticUpdates(workspace.AutomaticUpdates),
AllowRenames: allowRenames,
AllowRenames: deploymentAllowsRenames || template.AllowWorkspaceRenames,
Favorite: requesterFavorite,
NextStartAt: nextStartAt,
IsPrebuild: workspace.IsPrebuild(),
+38 -1
View File
@@ -220,7 +220,44 @@ func TestWorkspace(t *testing.T) {
err := client.UpdateWorkspace(ctx, ws1.ID, codersdk.UpdateWorkspaceRequest{
Name: want,
})
require.ErrorContains(t, err, "Workspace renames are not allowed")
require.ErrorContains(t, err, "Workspace renames are not enabled for this template")
})
t.Run("RenameAllowedByTemplate", func(t *testing.T) {
t.Parallel()
// The deployment-wide flag is off. The template setting alone must be
// enough to permit renames.
client := coderdtest.New(t, &coderdtest.Options{
IncludeProvisionerDaemon: true,
AllowWorkspaceRenames: false,
})
user := coderdtest.CreateFirstUser(t, client)
version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, nil)
coderdtest.AwaitTemplateVersionJobCompleted(t, client, version.ID)
template := coderdtest.CreateTemplate(t, client, user.OrganizationID, version.ID, func(req *codersdk.CreateTemplateRequest) {
req.AllowWorkspaceRenames = ptr.Ref(true)
})
ws1 := coderdtest.CreateWorkspace(t, client, template.ID)
coderdtest.AwaitWorkspaceBuildJobCompleted(t, client, ws1.LatestBuild.ID)
ctx, cancel := context.WithTimeout(context.Background(), testutil.WaitMedium)
defer cancel()
require.True(t, template.AllowWorkspaceRenames)
fetched, err := client.Workspace(ctx, ws1.ID)
require.NoError(t, err)
require.True(t, fetched.AllowRenames)
const want = "new-name"
err = client.UpdateWorkspace(ctx, ws1.ID, codersdk.UpdateWorkspaceRequest{
Name: want,
})
require.NoError(t, err, "workspace rename failed")
ws, err := client.Workspace(ctx, ws1.ID)
require.NoError(t, err)
require.Equal(t, want, ws.Name)
})
t.Run("TemplateProperties", func(t *testing.T) {
+18 -16
View File
@@ -741,19 +741,20 @@ type DeploymentValues struct {
EnableTerraformDebugMode serpent.Bool `json:"enable_terraform_debug_mode,omitempty" typescript:",notnull"`
UserQuietHoursSchedule UserQuietHoursScheduleConfig `json:"user_quiet_hours_schedule,omitempty" typescript:",notnull"`
WebTerminalRenderer serpent.String `json:"web_terminal_renderer,omitempty" typescript:",notnull"`
AllowWorkspaceRenames serpent.Bool `json:"allow_workspace_renames,omitempty" typescript:",notnull"`
Healthcheck HealthcheckConfig `json:"healthcheck,omitempty" typescript:",notnull"`
Retention RetentionConfig `json:"retention,omitempty" typescript:",notnull"`
CLIUpgradeMessage serpent.String `json:"cli_upgrade_message,omitempty" typescript:",notnull"`
TermsOfServiceURL serpent.String `json:"terms_of_service_url,omitempty" typescript:",notnull"`
Notifications NotificationsConfig `json:"notifications,omitempty" typescript:",notnull"`
AdditionalCSPPolicy serpent.StringArray `json:"additional_csp_policy,omitempty" typescript:",notnull"`
WorkspaceHostnameSuffix serpent.String `json:"workspace_hostname_suffix,omitempty" typescript:",notnull"`
Prebuilds PrebuildsConfig `json:"workspace_prebuilds,omitempty" typescript:",notnull"`
EnableAITasks serpent.Bool `json:"enable_ai_tasks,omitempty" typescript:",notnull"`
AI AIConfig `json:"ai,omitempty"`
StatsCollection StatsCollectionConfig `json:"stats_collection,omitempty" typescript:",notnull"`
TemplateBuilder TemplateBuilderConfig `json:"template_builder,omitempty"`
// Deprecated: Use the per-template allow_workspace_renames setting instead.
AllowWorkspaceRenames serpent.Bool `json:"allow_workspace_renames,omitempty" typescript:",notnull"`
Healthcheck HealthcheckConfig `json:"healthcheck,omitempty" typescript:",notnull"`
Retention RetentionConfig `json:"retention,omitempty" typescript:",notnull"`
CLIUpgradeMessage serpent.String `json:"cli_upgrade_message,omitempty" typescript:",notnull"`
TermsOfServiceURL serpent.String `json:"terms_of_service_url,omitempty" typescript:",notnull"`
Notifications NotificationsConfig `json:"notifications,omitempty" typescript:",notnull"`
AdditionalCSPPolicy serpent.StringArray `json:"additional_csp_policy,omitempty" typescript:",notnull"`
WorkspaceHostnameSuffix serpent.String `json:"workspace_hostname_suffix,omitempty" typescript:",notnull"`
Prebuilds PrebuildsConfig `json:"workspace_prebuilds,omitempty" typescript:",notnull"`
EnableAITasks serpent.Bool `json:"enable_ai_tasks,omitempty" typescript:",notnull"`
AI AIConfig `json:"ai,omitempty"`
StatsCollection StatsCollectionConfig `json:"stats_collection,omitempty" typescript:",notnull"`
TemplateBuilder TemplateBuilderConfig `json:"template_builder,omitempty"`
Config serpent.YAMLConfigPath `json:"config,omitempty" typescript:",notnull"`
WriteConfig serpent.Bool `json:"write_config,omitempty" typescript:",notnull"`
@@ -3962,13 +3963,14 @@ Write out the current server config as YAML to stdout.`,
},
{
Name: "Allow Workspace Renames",
Description: "Allow users to rename their workspaces. " +
Description: "Deprecated: use the per-template \"Allow workspace renames\" setting instead. " +
"While set, it force-enables renames for every template in the deployment. " +
"WARNING: Renaming a workspace can cause Terraform resources that depend on the " +
"workspace name to be destroyed and recreated, potentially causing data loss. " +
"Only enable this if your templates do not use workspace names in resource identifiers, or if you understand the risks.",
"workspace name to be destroyed and recreated, potentially causing data loss.",
Flag: "allow-workspace-renames",
Env: "CODER_ALLOW_WORKSPACE_RENAMES",
Default: "false",
Hidden: true,
Value: &c.AllowWorkspaceRenames,
YAML: "allowWorkspaceRenames",
},
+5
View File
@@ -233,6 +233,11 @@ type CreateTemplateRequest struct {
// AgentsAllowed controls whether Coder Agents can create workspaces using
// this template. Defaults to true.
AgentsAllowed *bool `json:"agents_allowed,omitempty"`
// AllowWorkspaceRenames permits users to rename workspaces built from this
// template. Renaming can be destructive for templates whose Terraform
// references the workspace name, so this defaults to false.
AllowWorkspaceRenames *bool `json:"allow_workspace_renames,omitempty"`
}
// CreateWorkspaceRequest provides options for creating a new workspace.
+9
View File
@@ -73,6 +73,11 @@ type Template struct {
// DisableModuleCache disables the use of cached Terraform modules during
// provisioning.
DisableModuleCache bool `json:"disable_module_cache"`
// AllowWorkspaceRenames permits users to rename workspaces built from this
// template. Renaming can be destructive for templates whose Terraform
// references the workspace name.
AllowWorkspaceRenames bool `json:"allow_workspace_renames"`
}
// WeekdaysToBitmap converts a list of weekdays to a bitmap in accordance with
@@ -286,6 +291,10 @@ type UpdateTemplateMeta struct {
// AgentsAllowed controls whether Coder Agents can create workspaces using
// this template. If omitted, the current value is preserved.
AgentsAllowed *bool `json:"agents_allowed,omitempty"`
// AllowWorkspaceRenames permits users to rename workspaces built from this
// template. Renaming can be destructive for templates whose Terraform
// references the workspace name.
AllowWorkspaceRenames *bool `json:"allow_workspace_renames,omitempty"`
}
type TemplateExample struct {
+6 -3
View File
@@ -66,9 +66,12 @@ type Workspace struct {
// what is causing an unhealthy status.
Health WorkspaceHealth `json:"health"`
AutomaticUpdates AutomaticUpdates `json:"automatic_updates" enums:"always,never"`
AllowRenames bool `json:"allow_renames"`
Favorite bool `json:"favorite"`
NextStartAt *time.Time `json:"next_start_at" format:"date-time"`
// AllowRenames is the effective rename permission for this workspace,
// derived from the template's allow_workspace_renames setting and the
// deprecated deployment-wide flag.
AllowRenames bool `json:"allow_renames"`
Favorite bool `json:"favorite"`
NextStartAt *time.Time `json:"next_start_at" format:"date-time"`
// IsPrebuild indicates whether the workspace is a prebuilt workspace.
// Prebuilt workspaces are owned by the prebuilds system user and have specific behavior,
// such as being managed differently from regular workspaces.
+1 -1
View File
@@ -45,7 +45,7 @@ We track the following resources:
| PrebuildsSettings<br><i></i> | <table><thead><tr><th>Field</th><th>Tracked</th></tr></thead><tbody> | <tr><td>id</td><td>false</td></tr><tr><td>reconciliation_paused</td><td>true</td></tr></tbody></table> |
| RoleSyncSettings<br><i></i> | <table><thead><tr><th>Field</th><th>Tracked</th></tr></thead><tbody> | <tr><td>field</td><td>true</td></tr><tr><td>mapping</td><td>true</td></tr></tbody></table> |
| TaskTable<br><i></i> | <table><thead><tr><th>Field</th><th>Tracked</th></tr></thead><tbody> | <tr><td>created_at</td><td>false</td></tr><tr><td>deleted_at</td><td>false</td></tr><tr><td>display_name</td><td>true</td></tr><tr><td>id</td><td>true</td></tr><tr><td>name</td><td>true</td></tr><tr><td>organization_id</td><td>false</td></tr><tr><td>owner_id</td><td>true</td></tr><tr><td>prompt</td><td>true</td></tr><tr><td>template_parameters</td><td>true</td></tr><tr><td>template_version_id</td><td>true</td></tr><tr><td>workspace_id</td><td>true</td></tr></tbody></table> |
| Template<br><i>write, delete</i> | <table><thead><tr><th>Field</th><th>Tracked</th></tr></thead><tbody> | <tr><td>active_version_id</td><td>true</td></tr><tr><td>activity_bump</td><td>true</td></tr><tr><td>agents_allowed</td><td>true</td></tr><tr><td>allow_user_autostart</td><td>true</td></tr><tr><td>allow_user_autostop</td><td>true</td></tr><tr><td>allow_user_cancel_workspace_jobs</td><td>true</td></tr><tr><td>autostart_block_days_of_week</td><td>true</td></tr><tr><td>autostop_requirement_days_of_week</td><td>true</td></tr><tr><td>autostop_requirement_weeks</td><td>true</td></tr><tr><td>cors_behavior</td><td>true</td></tr><tr><td>created_at</td><td>false</td></tr><tr><td>created_by</td><td>true</td></tr><tr><td>created_by_avatar_url</td><td>false</td></tr><tr><td>created_by_name</td><td>false</td></tr><tr><td>created_by_username</td><td>false</td></tr><tr><td>default_ttl</td><td>true</td></tr><tr><td>deleted</td><td>false</td></tr><tr><td>deprecated</td><td>true</td></tr><tr><td>description</td><td>true</td></tr><tr><td>disable_module_cache</td><td>true</td></tr><tr><td>display_name</td><td>true</td></tr><tr><td>failure_ttl</td><td>true</td></tr><tr><td>group_acl</td><td>true</td></tr><tr><td>icon</td><td>true</td></tr><tr><td>id</td><td>true</td></tr><tr><td>max_port_sharing_level</td><td>true</td></tr><tr><td>name</td><td>true</td></tr><tr><td>organization_display_name</td><td>false</td></tr><tr><td>organization_icon</td><td>false</td></tr><tr><td>organization_id</td><td>false</td></tr><tr><td>organization_name</td><td>false</td></tr><tr><td>provisioner</td><td>true</td></tr><tr><td>require_active_version</td><td>true</td></tr><tr><td>time_til_autostop_notify</td><td>true</td></tr><tr><td>time_til_dormant</td><td>true</td></tr><tr><td>time_til_dormant_autodelete</td><td>true</td></tr><tr><td>updated_at</td><td>false</td></tr><tr><td>use_classic_parameter_flow</td><td>true</td></tr><tr><td>user_acl</td><td>true</td></tr></tbody></table> |
| Template<br><i>write, delete</i> | <table><thead><tr><th>Field</th><th>Tracked</th></tr></thead><tbody> | <tr><td>active_version_id</td><td>true</td></tr><tr><td>activity_bump</td><td>true</td></tr><tr><td>agents_allowed</td><td>true</td></tr><tr><td>allow_user_autostart</td><td>true</td></tr><tr><td>allow_user_autostop</td><td>true</td></tr><tr><td>allow_user_cancel_workspace_jobs</td><td>true</td></tr><tr><td>allow_workspace_renames</td><td>true</td></tr><tr><td>autostart_block_days_of_week</td><td>true</td></tr><tr><td>autostop_requirement_days_of_week</td><td>true</td></tr><tr><td>autostop_requirement_weeks</td><td>true</td></tr><tr><td>cors_behavior</td><td>true</td></tr><tr><td>created_at</td><td>false</td></tr><tr><td>created_by</td><td>true</td></tr><tr><td>created_by_avatar_url</td><td>false</td></tr><tr><td>created_by_name</td><td>false</td></tr><tr><td>created_by_username</td><td>false</td></tr><tr><td>default_ttl</td><td>true</td></tr><tr><td>deleted</td><td>false</td></tr><tr><td>deprecated</td><td>true</td></tr><tr><td>description</td><td>true</td></tr><tr><td>disable_module_cache</td><td>true</td></tr><tr><td>display_name</td><td>true</td></tr><tr><td>failure_ttl</td><td>true</td></tr><tr><td>group_acl</td><td>true</td></tr><tr><td>icon</td><td>true</td></tr><tr><td>id</td><td>true</td></tr><tr><td>max_port_sharing_level</td><td>true</td></tr><tr><td>name</td><td>true</td></tr><tr><td>organization_display_name</td><td>false</td></tr><tr><td>organization_icon</td><td>false</td></tr><tr><td>organization_id</td><td>false</td></tr><tr><td>organization_name</td><td>false</td></tr><tr><td>provisioner</td><td>true</td></tr><tr><td>require_active_version</td><td>true</td></tr><tr><td>time_til_autostop_notify</td><td>true</td></tr><tr><td>time_til_dormant</td><td>true</td></tr><tr><td>time_til_dormant_autodelete</td><td>true</td></tr><tr><td>updated_at</td><td>false</td></tr><tr><td>use_classic_parameter_flow</td><td>true</td></tr><tr><td>user_acl</td><td>true</td></tr></tbody></table> |
| TemplateVersion<br><i>create, write</i> | <table><thead><tr><th>Field</th><th>Tracked</th></tr></thead><tbody> | <tr><td>archived</td><td>true</td></tr><tr><td>created_at</td><td>false</td></tr><tr><td>created_by</td><td>true</td></tr><tr><td>created_by_avatar_url</td><td>false</td></tr><tr><td>created_by_name</td><td>false</td></tr><tr><td>created_by_username</td><td>false</td></tr><tr><td>external_auth_providers</td><td>false</td></tr><tr><td>has_ai_task</td><td>false</td></tr><tr><td>has_external_agent</td><td>false</td></tr><tr><td>id</td><td>true</td></tr><tr><td>job_id</td><td>false</td></tr><tr><td>message</td><td>false</td></tr><tr><td>name</td><td>true</td></tr><tr><td>organization_id</td><td>false</td></tr><tr><td>readme</td><td>true</td></tr><tr><td>source_example_id</td><td>false</td></tr><tr><td>template_id</td><td>true</td></tr><tr><td>updated_at</td><td>false</td></tr></tbody></table> |
| User<br><i>create, write, delete</i> | <table><thead><tr><th>Field</th><th>Tracked</th></tr></thead><tbody> | <tr><td>avatar_url</td><td>false</td></tr><tr><td>chat_spend_limit_micros</td><td>true</td></tr><tr><td>created_at</td><td>false</td></tr><tr><td>deleted</td><td>true</td></tr><tr><td>email</td><td>true</td></tr><tr><td>github_com_user_id</td><td>false</td></tr><tr><td>hashed_one_time_passcode</td><td>false</td></tr><tr><td>hashed_password</td><td>true</td></tr><tr><td>id</td><td>true</td></tr><tr><td>is_service_account</td><td>true</td></tr><tr><td>is_system</td><td>true</td></tr><tr><td>last_seen_at</td><td>false</td></tr><tr><td>login_type</td><td>true</td></tr><tr><td>name</td><td>true</td></tr><tr><td>one_time_passcode_expires_at</td><td>true</td></tr><tr><td>quiet_hours_schedule</td><td>true</td></tr><tr><td>rbac_roles</td><td>true</td></tr><tr><td>status</td><td>true</td></tr><tr><td>updated_at</td><td>false</td></tr><tr><td>username</td><td>true</td></tr></tbody></table> |
| UserSecret<br><i>create, write, delete</i> | <table><thead><tr><th>Field</th><th>Tracked</th></tr></thead><tbody> | <tr><td>created_at</td><td>false</td></tr><tr><td>description</td><td>true</td></tr><tr><td>enabled</td><td>true</td></tr><tr><td>env_name</td><td>true</td></tr><tr><td>file_path</td><td>true</td></tr><tr><td>id</td><td>true</td></tr><tr><td>name</td><td>true</td></tr><tr><td>updated_at</td><td>false</td></tr><tr><td>user_id</td><td>true</td></tr><tr><td>value</td><td>true</td></tr><tr><td>value_key_id</td><td>false</td></tr></tbody></table> |
@@ -22,15 +22,6 @@ Deprecated options are listed at the end of each section.
## General
### Allow workspace renames
Allow users to rename their workspaces. WARNING: Renaming a workspace can cause Terraform resources that depend on the workspace name to be destroyed and recreated, potentially causing data loss. Only enable this if your templates do not use workspace names in resource identifiers, or if you understand the risks.
- Environment variable: `CODER_ALLOW_WORKSPACE_RENAMES`
- CLI flag: [`--allow-workspace-renames`](../../reference/cli/server.md#--allow-workspace-renames)
- YAML key: `allowWorkspaceRenames`
- Default value: `false`
### Cache directory
The directory to cache temporary files. If unspecified and $CACHE_DIRECTORY is set, it will be used for compatibility with systemd. This directory is NOT safe to be configured as a shared directory across coderd/provisionerd replicas.
@@ -52,12 +52,19 @@ Because we depend on `coder_workspace.me.owner`, if the owner changes their
username, Terraform will recreate the volume (wiping its data!) the next time
that Coder starts the workspace.
To prevent this, use immutable IDs:
✅ To prevent this, use immutable IDs:
- `coder_workspace.me.owner_id`
- `coder_workspace.me.id`
You should also avoid using `coder_workspace.me.name` if your deployment allows workspace renaming via `CODER_ALLOW_WORKSPACE_RENAMES` or `--allow-workspace-renames`.
⚠️ Avoid mutable IDs:
- `coder_workspace.me.name`
- `coder_workspace.me.owner`
Workspace renaming is disabled by default to help prevent this kind of
destruction, but a template admin can enable it. Refer to
[Workspace renaming](../managing-templates/index.md#workspace-renaming).
```tf
data "coder_workspace" "me" {
@@ -77,6 +77,49 @@ automatically updated on the next startup.
![Template update policies](../../../images/templates/update-policies.png)
## Workspace renaming
Workspace renaming is disabled by default on every template.
Enable it per template, once you have confirmed that a rename won't destroy anything.
Terraform exposes the workspace name as `data.coder_workspace.me.name`.
If a template uses that value in an attribute Terraform can't change in place, renaming the workspace makes Terraform destroy the resource and create a new one on the next build.
When that resource is the home volume, the developer loses their data.
Other uses of the name are harmless.
A Kubernetes label or an environment variable changes without replacing anything.
Coder can't tell the two cases apart, so the decision stays with the template admin.
### Check whether your template is safe to rename
Search the template for uses of the workspace name:
```sh
grep -rn 'coder_workspace\.[a-z_]*\.name' .
```
For each result, ask whether Terraform would replace the resource if that value changed.
Attributes such as a volume name, a disk name, or an instance name usually force replacement.
Labels, tags, and environment variables usually don't.
If a resource would be replaced, reference an immutable identifier instead, such as `data.coder_workspace.me.id`.
Refer to [Resource persistence](../extending-templates/resource-persistence.md) for the full set of practices.
### Enable renaming for a template
1. Go to the template, then select **Settings**.
1. On the **General** page, under **Operations**, select **Allow users to rename their workspaces**.
1. Select **Save**.
Developers can then rename a workspace from the workspace's **Settings** page, or with `coder rename`.
While the setting is off, the workspace name field is disabled and the API rejects renames.
> [!WARNING]
> The deployment-wide `CODER_ALLOW_WORKSPACE_RENAMES` option is deprecated.
> While it is set, renaming is enabled for every template in the deployment, and the per-template setting can't turn it off.
> Unset it, then enable renaming on the templates that need it.
## Delete templates
You can delete a template using both the coder CLI and UI. Only
+86 -79
View File
@@ -6449,6 +6449,7 @@ AuthorizationObject can represent a "set" of objects, such as: all workspaces in
"allow_user_autostart": true,
"allow_user_autostop": true,
"allow_user_cancel_workspace_jobs": true,
"allow_workspace_renames": true,
"autostart_requirement": {
"days_of_week": [
"monday"
@@ -6487,6 +6488,7 @@ AuthorizationObject can represent a "set" of objects, such as: all workspaces in
| `allow_user_autostart` | boolean | false | | Allow user autostart allows users to set a schedule for autostarting their workspace. By default this is true. This can only be disabled when using an enterprise license. |
| `allow_user_autostop` | boolean | false | | Allow user autostop allows users to set a custom workspace TTL to use in place of the template's DefaultTTL field. By default this is true. If false, the DefaultTTL will always be used. This can only be disabled when using an enterprise license. |
| `allow_user_cancel_workspace_jobs` | boolean | false | | Allow users to cancel in-progress workspace jobs. *bool as the default value is "true". |
| `allow_workspace_renames` | boolean | false | | Allow workspace renames permits users to rename workspaces built from this template. Renaming can be destructive for templates whose Terraform references the workspace name, so this defaults to false. |
| `autostart_requirement` | [codersdk.TemplateAutostartRequirement](#codersdktemplateautostartrequirement) | false | | Autostart requirement allows optionally specifying the autostart allowed days for workspaces created from this template. This is an enterprise feature. |
| `autostop_requirement` | [codersdk.TemplateAutostopRequirement](#codersdktemplateautostoprequirement) | false | | Autostop requirement allows optionally specifying the autostop requirement for workspaces created from this template. This is an enterprise feature. |
| `cors_behavior` | [codersdk.CORSBehavior](#codersdkcorsbehavior) | false | | Cors behavior allows optionally specifying the CORS behavior for all shared ports. |
@@ -8421,84 +8423,84 @@ CreateWorkspaceRequest provides options for creating a new workspace. Only one o
### Properties
| Name | Type | Required | Restrictions | Description |
|------------------------------------------------|------------------------------------------------------------------------------------------------------|----------|--------------|--------------------------------------------------------------------|
| `access_url` | [serpent.URL](#serpenturl) | false | | |
| `additional_csp_policy` | array of string | false | | |
| `address` | [serpent.HostPort](#serpenthostport) | false | | Deprecated: Use HTTPAddress or TLS.Address instead. |
| `agent_fallback_troubleshooting_url` | [serpent.URL](#serpenturl) | false | | |
| `agent_stat_refresh_interval` | integer | false | | |
| `ai` | [codersdk.AIConfig](#codersdkaiconfig) | false | | |
| `allow_workspace_renames` | boolean | false | | |
| `autobuild_poll_interval` | integer | false | | |
| `browser_only` | boolean | false | | |
| `cache_directory` | string | false | | |
| `cli_upgrade_message` | string | false | | |
| `cluster` | [codersdk.ClusterConfig](#codersdkclusterconfig) | false | | |
| `config` | string | false | | |
| `config_ssh` | [codersdk.SSHConfig](#codersdksshconfig) | false | | |
| `dangerous` | [codersdk.DangerousConfig](#codersdkdangerousconfig) | false | | |
| `derp` | [codersdk.DERP](#codersdkderp) | false | | |
| `disable_chat_sharing` | boolean | false | | |
| `disable_owner_workspace_exec` | boolean | false | | |
| `disable_password_auth` | boolean | false | | |
| `disable_path_apps` | boolean | false | | |
| `disable_workspace_agent_context_sync` | boolean | false | | |
| `disable_workspace_sharing` | boolean | false | | |
| `docs_url` | [serpent.URL](#serpenturl) | false | | |
| `enable_ai_tasks` | boolean | false | | |
| `enable_authz_recording` | boolean | false | | |
| `enable_terraform_debug_mode` | boolean | false | | |
| `ephemeral_deployment` | boolean | false | | |
| `experiments` | array of string | false | | |
| `external_auth` | [serpent.Struct-array_codersdk_ExternalAuthConfig](#serpentstruct-array_codersdk_externalauthconfig) | false | | |
| `external_auth_github_default_provider_enable` | boolean | false | | |
| `external_token_encryption_keys` | array of string | false | | |
| `healthcheck` | [codersdk.HealthcheckConfig](#codersdkhealthcheckconfig) | false | | |
| `http_address` | string | false | | Http address is a string because it may be set to zero to disable. |
| `http_cookies` | [codersdk.HTTPCookieConfig](#codersdkhttpcookieconfig) | false | | |
| `job_hang_detector_interval` | integer | false | | |
| `logging` | [codersdk.LoggingConfig](#codersdkloggingconfig) | false | | |
| `metrics_cache_refresh_interval` | integer | false | | |
| `notifications` | [codersdk.NotificationsConfig](#codersdknotificationsconfig) | false | | |
| `oauth2` | [codersdk.OAuth2Config](#codersdkoauth2config) | false | | |
| `oidc` | [codersdk.OIDCConfig](#codersdkoidcconfig) | false | | |
| `pg_auth` | string | false | | |
| `pg_conn_max_idle` | string | false | | |
| `pg_conn_max_open` | integer | false | | |
| `pg_connection_url` | string | false | | |
| `pprof` | [codersdk.PprofConfig](#codersdkpprofconfig) | false | | |
| `prometheus` | [codersdk.PrometheusConfig](#codersdkprometheusconfig) | false | | |
| `provisioner` | [codersdk.ProvisionerConfig](#codersdkprovisionerconfig) | false | | |
| `proxy_health_status_interval` | integer | false | | |
| `proxy_trusted_headers` | array of string | false | | |
| `proxy_trusted_origins` | array of string | false | | |
| `rate_limit` | [codersdk.RateLimitConfig](#codersdkratelimitconfig) | false | | |
| `redirect_to_access_url` | boolean | false | | |
| `retention` | [codersdk.RetentionConfig](#codersdkretentionconfig) | false | | |
| `scim_api_key` | string | false | | |
| `scim_use_legacy` | boolean | false | | |
| `session_lifetime` | [codersdk.SessionLifetime](#codersdksessionlifetime) | false | | |
| `ssh_keygen_algorithm` | string | false | | |
| `stats_collection` | [codersdk.StatsCollectionConfig](#codersdkstatscollectionconfig) | false | | |
| `strict_transport_security` | integer | false | | |
| `strict_transport_security_options` | array of string | false | | |
| `support` | [codersdk.SupportConfig](#codersdksupportconfig) | false | | |
| `swagger` | [codersdk.SwaggerConfig](#codersdkswaggerconfig) | false | | |
| `telemetry` | [codersdk.TelemetryConfig](#codersdktelemetryconfig) | false | | |
| `template_builder` | [codersdk.TemplateBuilderConfig](#codersdktemplatebuilderconfig) | false | | |
| `terms_of_service_url` | string | false | | |
| `tls` | [codersdk.TLSConfig](#codersdktlsconfig) | false | | |
| `trace` | [codersdk.TraceConfig](#codersdktraceconfig) | false | | |
| `update_check` | boolean | false | | |
| `user_quiet_hours_schedule` | [codersdk.UserQuietHoursScheduleConfig](#codersdkuserquiethoursscheduleconfig) | false | | |
| `verbose` | boolean | false | | |
| `web_terminal_renderer` | string | false | | |
| `wgtunnel_host` | string | false | | |
| `wildcard_access_url` | string | false | | |
| `workspace_hostname_suffix` | string | false | | |
| `workspace_prebuilds` | [codersdk.PrebuildsConfig](#codersdkprebuildsconfig) | false | | |
| `write_config` | boolean | false | | |
| Name | Type | Required | Restrictions | Description |
|------------------------------------------------|------------------------------------------------------------------------------------------------------|----------|--------------|---------------------------------------------------------------------------|
| `access_url` | [serpent.URL](#serpenturl) | false | | |
| `additional_csp_policy` | array of string | false | | |
| `address` | [serpent.HostPort](#serpenthostport) | false | | Deprecated: Use HTTPAddress or TLS.Address instead. |
| `agent_fallback_troubleshooting_url` | [serpent.URL](#serpenturl) | false | | |
| `agent_stat_refresh_interval` | integer | false | | |
| `ai` | [codersdk.AIConfig](#codersdkaiconfig) | false | | |
| `allow_workspace_renames` | boolean | false | | Deprecated: Use the per-template allow_workspace_renames setting instead. |
| `autobuild_poll_interval` | integer | false | | |
| `browser_only` | boolean | false | | |
| `cache_directory` | string | false | | |
| `cli_upgrade_message` | string | false | | |
| `cluster` | [codersdk.ClusterConfig](#codersdkclusterconfig) | false | | |
| `config` | string | false | | |
| `config_ssh` | [codersdk.SSHConfig](#codersdksshconfig) | false | | |
| `dangerous` | [codersdk.DangerousConfig](#codersdkdangerousconfig) | false | | |
| `derp` | [codersdk.DERP](#codersdkderp) | false | | |
| `disable_chat_sharing` | boolean | false | | |
| `disable_owner_workspace_exec` | boolean | false | | |
| `disable_password_auth` | boolean | false | | |
| `disable_path_apps` | boolean | false | | |
| `disable_workspace_agent_context_sync` | boolean | false | | |
| `disable_workspace_sharing` | boolean | false | | |
| `docs_url` | [serpent.URL](#serpenturl) | false | | |
| `enable_ai_tasks` | boolean | false | | |
| `enable_authz_recording` | boolean | false | | |
| `enable_terraform_debug_mode` | boolean | false | | |
| `ephemeral_deployment` | boolean | false | | |
| `experiments` | array of string | false | | |
| `external_auth` | [serpent.Struct-array_codersdk_ExternalAuthConfig](#serpentstruct-array_codersdk_externalauthconfig) | false | | |
| `external_auth_github_default_provider_enable` | boolean | false | | |
| `external_token_encryption_keys` | array of string | false | | |
| `healthcheck` | [codersdk.HealthcheckConfig](#codersdkhealthcheckconfig) | false | | |
| `http_address` | string | false | | Http address is a string because it may be set to zero to disable. |
| `http_cookies` | [codersdk.HTTPCookieConfig](#codersdkhttpcookieconfig) | false | | |
| `job_hang_detector_interval` | integer | false | | |
| `logging` | [codersdk.LoggingConfig](#codersdkloggingconfig) | false | | |
| `metrics_cache_refresh_interval` | integer | false | | |
| `notifications` | [codersdk.NotificationsConfig](#codersdknotificationsconfig) | false | | |
| `oauth2` | [codersdk.OAuth2Config](#codersdkoauth2config) | false | | |
| `oidc` | [codersdk.OIDCConfig](#codersdkoidcconfig) | false | | |
| `pg_auth` | string | false | | |
| `pg_conn_max_idle` | string | false | | |
| `pg_conn_max_open` | integer | false | | |
| `pg_connection_url` | string | false | | |
| `pprof` | [codersdk.PprofConfig](#codersdkpprofconfig) | false | | |
| `prometheus` | [codersdk.PrometheusConfig](#codersdkprometheusconfig) | false | | |
| `provisioner` | [codersdk.ProvisionerConfig](#codersdkprovisionerconfig) | false | | |
| `proxy_health_status_interval` | integer | false | | |
| `proxy_trusted_headers` | array of string | false | | |
| `proxy_trusted_origins` | array of string | false | | |
| `rate_limit` | [codersdk.RateLimitConfig](#codersdkratelimitconfig) | false | | |
| `redirect_to_access_url` | boolean | false | | |
| `retention` | [codersdk.RetentionConfig](#codersdkretentionconfig) | false | | |
| `scim_api_key` | string | false | | |
| `scim_use_legacy` | boolean | false | | |
| `session_lifetime` | [codersdk.SessionLifetime](#codersdksessionlifetime) | false | | |
| `ssh_keygen_algorithm` | string | false | | |
| `stats_collection` | [codersdk.StatsCollectionConfig](#codersdkstatscollectionconfig) | false | | |
| `strict_transport_security` | integer | false | | |
| `strict_transport_security_options` | array of string | false | | |
| `support` | [codersdk.SupportConfig](#codersdksupportconfig) | false | | |
| `swagger` | [codersdk.SwaggerConfig](#codersdkswaggerconfig) | false | | |
| `telemetry` | [codersdk.TelemetryConfig](#codersdktelemetryconfig) | false | | |
| `template_builder` | [codersdk.TemplateBuilderConfig](#codersdktemplatebuilderconfig) | false | | |
| `terms_of_service_url` | string | false | | |
| `tls` | [codersdk.TLSConfig](#codersdktlsconfig) | false | | |
| `trace` | [codersdk.TraceConfig](#codersdktraceconfig) | false | | |
| `update_check` | boolean | false | | |
| `user_quiet_hours_schedule` | [codersdk.UserQuietHoursScheduleConfig](#codersdkuserquiethoursscheduleconfig) | false | | |
| `verbose` | boolean | false | | |
| `web_terminal_renderer` | string | false | | |
| `wgtunnel_host` | string | false | | |
| `wildcard_access_url` | string | false | | |
| `workspace_hostname_suffix` | string | false | | |
| `workspace_prebuilds` | [codersdk.PrebuildsConfig](#codersdkprebuildsconfig) | false | | |
| `write_config` | boolean | false | | |
## codersdk.DiagnosticExtra
@@ -13613,6 +13615,7 @@ Git clone makes use of this by parsing the URL from: 'Username for "https://gith
"allow_user_autostart": true,
"allow_user_autostop": true,
"allow_user_cancel_workspace_jobs": true,
"allow_workspace_renames": true,
"autostart_requirement": {
"days_of_week": [
"monday"
@@ -13675,6 +13678,7 @@ Git clone makes use of this by parsing the URL from: 'Username for "https://gith
| `allow_user_autostart` | boolean | false | | Allow user autostart and AllowUserAutostop are enterprise-only. Their values are only used if your license is entitled to use the advanced template scheduling feature. |
| `allow_user_autostop` | boolean | false | | |
| `allow_user_cancel_workspace_jobs` | boolean | false | | |
| `allow_workspace_renames` | boolean | false | | Allow workspace renames permits users to rename workspaces built from this template. Renaming can be destructive for templates whose Terraform references the workspace name. |
| `autostart_requirement` | [codersdk.TemplateAutostartRequirement](#codersdktemplateautostartrequirement) | false | | |
| `autostop_requirement` | [codersdk.TemplateAutostopRequirement](#codersdktemplateautostoprequirement) | false | | Autostop requirement and AutostartRequirement are enterprise features. Its value is only used if your license is entitled to use the advanced template scheduling feature. |
| `build_time_stats` | [codersdk.TemplateBuildTimeStats](#codersdktemplatebuildtimestats) | false | | |
@@ -14079,6 +14083,7 @@ Restarts will only happen on weekdays in this list on weeks which line up with W
"allow_user_autostart": true,
"allow_user_autostop": true,
"allow_user_cancel_workspace_jobs": true,
"allow_workspace_renames": true,
"autostart_requirement": {
"days_of_week": [
"monday"
@@ -15578,6 +15583,7 @@ Restarts will only happen on weekdays in this list on weeks which line up with W
"allow_user_autostart": true,
"allow_user_autostop": true,
"allow_user_cancel_workspace_jobs": true,
"allow_workspace_renames": true,
"autostart_requirement": {
"days_of_week": [
"monday"
@@ -15619,6 +15625,7 @@ Restarts will only happen on weekdays in this list on weeks which line up with W
| `allow_user_autostart` | boolean | false | | |
| `allow_user_autostop` | boolean | false | | |
| `allow_user_cancel_workspace_jobs` | boolean | false | | |
| `allow_workspace_renames` | boolean | false | | Allow workspace renames permits users to rename workspaces built from this template. Renaming can be destructive for templates whose Terraform references the workspace name. |
| `autostart_requirement` | [codersdk.TemplateAutostartRequirement](#codersdktemplateautostartrequirement) | false | | |
| `autostop_requirement` | [codersdk.TemplateAutostopRequirement](#codersdktemplateautostoprequirement) | false | | Autostop requirement and AutostartRequirement can only be set if your license includes the advanced template scheduling feature. If you attempt to set this value while unlicensed, it will be ignored. |
| `cors_behavior` | [codersdk.CORSBehavior](#codersdkcorsbehavior) | false | | |
@@ -17049,7 +17056,7 @@ If the schedule is empty, the user will be updated to use the default schedule.|
| Name | Type | Required | Restrictions | Description |
|---------------------------------------------|-------------------------------------------------------------------------|----------|--------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `allow_renames` | boolean | false | | |
| `allow_renames` | boolean | false | | Allow renames is the effective rename permission for this workspace, derived from the template's allow_workspace_renames setting and the deprecated deployment-wide flag. |
| `automatic_updates` | [codersdk.AutomaticUpdates](#codersdkautomaticupdates) | false | | |
| `autostart_schedule` | string | false | | |
| `created_at` | string | false | | |
+1
View File
@@ -169,6 +169,7 @@ curl -X POST http://coder-server:8080/api/v2/templatebuilder/compose/template \
"allow_user_autostart": true,
"allow_user_autostop": true,
"allow_user_cancel_workspace_jobs": true,
"allow_workspace_renames": true,
"autostart_requirement": {
"days_of_week": [
"monday"
+36 -26
View File
@@ -42,6 +42,7 @@ To include deprecated templates, specify `deprecated:true` in the search query.
"allow_user_autostart": true,
"allow_user_autostop": true,
"allow_user_cancel_workspace_jobs": true,
"allow_workspace_renames": true,
"autostart_requirement": {
"days_of_week": [
"monday"
@@ -104,19 +105,20 @@ To include deprecated templates, specify `deprecated:true` in the search query.
Status Code **200**
| Name | Type | Required | Restrictions | Description |
|--------------------------------------|------------------------------------------------------------------------------------------|----------|--------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `[array item]` | array | false | | |
| `» active_user_count` | integer | false | | Active user count is set to -1 when loading. |
| `» active_version_id` | string(uuid) | false | | |
| `» activity_bump_ms` | integer | false | | |
| `» agents_allowed` | boolean | false | | |
| `» allow_user_autostart` | boolean | false | | Allow user autostart and AllowUserAutostop are enterprise-only. Their values are only used if your license is entitled to use the advanced template scheduling feature. |
| `» allow_user_autostop` | boolean | false | | |
| `» allow_user_cancel_workspace_jobs` | boolean | false | | |
| `» autostart_requirement` | [codersdk.TemplateAutostartRequirement](schemas.md#codersdktemplateautostartrequirement) | false | | |
| `»» days_of_week` | array | false | | Days of week is a list of days of the week in which autostart is allowed to happen. If no days are specified, autostart is not allowed. |
| `» autostop_requirement` | [codersdk.TemplateAutostopRequirement](schemas.md#codersdktemplateautostoprequirement) | false | | Autostop requirement and AutostartRequirement are enterprise features. Its value is only used if your license is entitled to use the advanced template scheduling feature. |
| Name | Type | Required | Restrictions | Description |
|--------------------------------------|------------------------------------------------------------------------------------------|----------|--------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `[array item]` | array | false | | |
| `» active_user_count` | integer | false | | Active user count is set to -1 when loading. |
| `» active_version_id` | string(uuid) | false | | |
| `» activity_bump_ms` | integer | false | | |
| `» agents_allowed` | boolean | false | | |
| `» allow_user_autostart` | boolean | false | | Allow user autostart and AllowUserAutostop are enterprise-only. Their values are only used if your license is entitled to use the advanced template scheduling feature. |
| `» allow_user_autostop` | boolean | false | | |
| `» allow_user_cancel_workspace_jobs` | boolean | false | | |
| `» allow_workspace_renames` | boolean | false | | Allow workspace renames permits users to rename workspaces built from this template. Renaming can be destructive for templates whose Terraform references the workspace name. |
| `» autostart_requirement` | [codersdk.TemplateAutostartRequirement](schemas.md#codersdktemplateautostartrequirement) | false | | |
| `»» days_of_week` | array | false | | Days of week is a list of days of the week in which autostart is allowed to happen. If no days are specified, autostart is not allowed. |
| `» autostop_requirement` | [codersdk.TemplateAutostopRequirement](schemas.md#codersdktemplateautostoprequirement) | false | | Autostop requirement and AutostartRequirement are enterprise features. Its value is only used if your license is entitled to use the advanced template scheduling feature. |
|`»» days_of_week`|array|false||Days of week is a list of days of the week on which restarts are required. Restarts happen within the user's quiet hours (in their configured timezone). If no days are specified, restarts are not required. Weekdays cannot be specified twice.
Restarts will only happen on weekdays in this list on weeks which line up with Weeks.|
|`»» weeks`|integer|false||Weeks is the number of weeks between required restarts. Weeks are synced across all workspaces (and Coder deployments) using modulo math on a hardcoded epoch week of January 2nd, 2023 (the first Monday of 2023). Values of 0 or 1 indicate weekly restarts. Values of 2 indicate fortnightly restarts, etc.|
@@ -185,6 +187,7 @@ curl -X POST http://coder-server:8080/api/v2/organizations/{organization}/templa
"allow_user_autostart": true,
"allow_user_autostop": true,
"allow_user_cancel_workspace_jobs": true,
"allow_workspace_renames": true,
"autostart_requirement": {
"days_of_week": [
"monday"
@@ -234,6 +237,7 @@ curl -X POST http://coder-server:8080/api/v2/organizations/{organization}/templa
"allow_user_autostart": true,
"allow_user_autostop": true,
"allow_user_cancel_workspace_jobs": true,
"allow_workspace_renames": true,
"autostart_requirement": {
"days_of_week": [
"monday"
@@ -388,6 +392,7 @@ curl -X GET http://coder-server:8080/api/v2/organizations/{organization}/templat
"allow_user_autostart": true,
"allow_user_autostop": true,
"allow_user_cancel_workspace_jobs": true,
"allow_workspace_renames": true,
"autostart_requirement": {
"days_of_week": [
"monday"
@@ -812,6 +817,7 @@ To include deprecated templates, specify `deprecated:true` in the search query.
"allow_user_autostart": true,
"allow_user_autostop": true,
"allow_user_cancel_workspace_jobs": true,
"allow_workspace_renames": true,
"autostart_requirement": {
"days_of_week": [
"monday"
@@ -874,19 +880,20 @@ To include deprecated templates, specify `deprecated:true` in the search query.
Status Code **200**
| Name | Type | Required | Restrictions | Description |
|--------------------------------------|------------------------------------------------------------------------------------------|----------|--------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `[array item]` | array | false | | |
| `» active_user_count` | integer | false | | Active user count is set to -1 when loading. |
| `» active_version_id` | string(uuid) | false | | |
| `» activity_bump_ms` | integer | false | | |
| `» agents_allowed` | boolean | false | | |
| `» allow_user_autostart` | boolean | false | | Allow user autostart and AllowUserAutostop are enterprise-only. Their values are only used if your license is entitled to use the advanced template scheduling feature. |
| `» allow_user_autostop` | boolean | false | | |
| `» allow_user_cancel_workspace_jobs` | boolean | false | | |
| `» autostart_requirement` | [codersdk.TemplateAutostartRequirement](schemas.md#codersdktemplateautostartrequirement) | false | | |
| `»» days_of_week` | array | false | | Days of week is a list of days of the week in which autostart is allowed to happen. If no days are specified, autostart is not allowed. |
| `» autostop_requirement` | [codersdk.TemplateAutostopRequirement](schemas.md#codersdktemplateautostoprequirement) | false | | Autostop requirement and AutostartRequirement are enterprise features. Its value is only used if your license is entitled to use the advanced template scheduling feature. |
| Name | Type | Required | Restrictions | Description |
|--------------------------------------|------------------------------------------------------------------------------------------|----------|--------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `[array item]` | array | false | | |
| `» active_user_count` | integer | false | | Active user count is set to -1 when loading. |
| `» active_version_id` | string(uuid) | false | | |
| `» activity_bump_ms` | integer | false | | |
| `» agents_allowed` | boolean | false | | |
| `» allow_user_autostart` | boolean | false | | Allow user autostart and AllowUserAutostop are enterprise-only. Their values are only used if your license is entitled to use the advanced template scheduling feature. |
| `» allow_user_autostop` | boolean | false | | |
| `» allow_user_cancel_workspace_jobs` | boolean | false | | |
| `» allow_workspace_renames` | boolean | false | | Allow workspace renames permits users to rename workspaces built from this template. Renaming can be destructive for templates whose Terraform references the workspace name. |
| `» autostart_requirement` | [codersdk.TemplateAutostartRequirement](schemas.md#codersdktemplateautostartrequirement) | false | | |
| `»» days_of_week` | array | false | | Days of week is a list of days of the week in which autostart is allowed to happen. If no days are specified, autostart is not allowed. |
| `» autostop_requirement` | [codersdk.TemplateAutostopRequirement](schemas.md#codersdktemplateautostoprequirement) | false | | Autostop requirement and AutostartRequirement are enterprise features. Its value is only used if your license is entitled to use the advanced template scheduling feature. |
|`»» days_of_week`|array|false||Days of week is a list of days of the week on which restarts are required. Restarts happen within the user's quiet hours (in their configured timezone). If no days are specified, restarts are not required. Weekdays cannot be specified twice.
Restarts will only happen on weekdays in this list on weeks which line up with Weeks.|
|`»» weeks`|integer|false||Weeks is the number of weeks between required restarts. Weeks are synced across all workspaces (and Coder deployments) using modulo math on a hardcoded epoch week of January 2nd, 2023 (the first Monday of 2023). Values of 0 or 1 indicate weekly restarts. Values of 2 indicate fortnightly restarts, etc.|
@@ -1020,6 +1027,7 @@ curl -X GET http://coder-server:8080/api/v2/templates/{template} \
"allow_user_autostart": true,
"allow_user_autostop": true,
"allow_user_cancel_workspace_jobs": true,
"allow_workspace_renames": true,
"autostart_requirement": {
"days_of_week": [
"monday"
@@ -1146,6 +1154,7 @@ curl -X PATCH http://coder-server:8080/api/v2/templates/{template} \
"allow_user_autostart": true,
"allow_user_autostop": true,
"allow_user_cancel_workspace_jobs": true,
"allow_workspace_renames": true,
"autostart_requirement": {
"days_of_week": [
"monday"
@@ -1198,6 +1207,7 @@ curl -X PATCH http://coder-server:8080/api/v2/templates/{template} \
"allow_user_autostart": true,
"allow_user_autostop": true,
"allow_user_cancel_workspace_jobs": true,
"allow_workspace_renames": true,
"autostart_requirement": {
"days_of_week": [
"monday"
-11
View File
@@ -1370,17 +1370,6 @@ Allow users to set their own quiet hours schedule for workspaces to stop in (dep
The renderer to use when opening a web terminal. Valid values are 'canvas', 'webgl', or 'dom'.
### --allow-workspace-renames
| | |
|-------------|---------------------------------------------|
| Type | <code>bool</code> |
| Environment | <code>$CODER_ALLOW_WORKSPACE_RENAMES</code> |
| YAML | <code>allowWorkspaceRenames</code> |
| Default | <code>false</code> |
Allow users to rename their workspaces. WARNING: Renaming a workspace can cause Terraform resources that depend on the workspace name to be destroyed and recreated, potentially causing data loss. Only enable this if your templates do not use workspace names in resource identifiers, or if you understand the risks.
### --health-check-refresh
| | |
+4
View File
@@ -45,6 +45,10 @@ coder show <workspace-name>
| Reserved names | Cannot use `new` or `create` |
| Uniqueness | Must be unique within your workspaces |
Renaming a workspace after you create it is off by default. A template admin can
allow it per template. Refer to
[Workspace renaming](../admin/templates/managing-templates/index.md#workspace-renaming).
## Workspace filtering
In the Coder UI, you can filter your workspaces using pre-defined filters or
+1
View File
@@ -136,6 +136,7 @@ var auditableResourcesTypes = map[any]map[string]Action{
"disable_module_cache": ActionTrack,
"time_til_autostop_notify": ActionTrack,
"agents_allowed": ActionTrack,
"allow_workspace_renames": ActionTrack,
},
&database.TemplateVersion{}: {
"id": ActionTrack,
-7
View File
@@ -18,13 +18,6 @@ SUBCOMMANDS:
PostgreSQL deployment.
OPTIONS:
--allow-workspace-renames bool, $CODER_ALLOW_WORKSPACE_RENAMES (default: false)
Allow users to rename their workspaces. WARNING: Renaming a workspace
can cause Terraform resources that depend on the workspace name to be
destroyed and recreated, potentially causing data loss. Only enable
this if your templates do not use workspace names in resource
identifiers, or if you understand the risks.
--cache-dir string, $CODER_CACHE_DIRECTORY (default: [cache dir])
The directory to cache temporary files. If unspecified and
$CACHE_DIRECTORY is set, it will be used for compatibility with
+26
View File
@@ -4198,6 +4198,12 @@ export interface CreateTemplateRequest {
* this template. Defaults to true.
*/
readonly agents_allowed?: boolean;
/**
* AllowWorkspaceRenames permits users to rename workspaces built from this
* template. Renaming can be destructive for templates whose Terraform
* references the workspace name, so this defaults to false.
*/
readonly allow_workspace_renames?: boolean;
}
// From codersdk/templateversions.go
@@ -4835,6 +4841,9 @@ export interface DeploymentValues {
readonly enable_terraform_debug_mode?: boolean;
readonly user_quiet_hours_schedule?: UserQuietHoursScheduleConfig;
readonly web_terminal_renderer?: string;
/**
* @deprecated Use the per-template allow_workspace_renames setting instead.
*/
readonly allow_workspace_renames?: boolean;
readonly healthcheck?: HealthcheckConfig;
readonly retention?: RetentionConfig;
@@ -9146,6 +9155,12 @@ export interface Template {
* provisioning.
*/
readonly disable_module_cache: boolean;
/**
* AllowWorkspaceRenames permits users to rename workspaces built from this
* template. Renaming can be destructive for templates whose Terraform
* references the workspace name.
*/
readonly allow_workspace_renames: boolean;
}
// From codersdk/templates.go
@@ -10135,6 +10150,12 @@ export interface UpdateTemplateMeta {
* this template. If omitted, the current value is preserved.
*/
readonly agents_allowed?: boolean;
/**
* AllowWorkspaceRenames permits users to rename workspaces built from this
* template. Renaming can be destructive for templates whose Terraform
* references the workspace name.
*/
readonly allow_workspace_renames?: boolean;
}
// From codersdk/users.go
@@ -10965,6 +10986,11 @@ export interface Workspace {
*/
readonly health: WorkspaceHealth;
readonly automatic_updates: AutomaticUpdates;
/**
* AllowRenames is the effective rename permission for this workspace,
* derived from the template's allow_workspace_renames setting and the
* deprecated deployment-wide flag.
*/
readonly allow_renames: boolean;
readonly favorite: boolean;
readonly next_start_at: string | null;
@@ -0,0 +1,82 @@
import { screen, waitFor } from "@testing-library/react";
import userEvent from "@testing-library/user-event";
import { MockTemplate } from "#/testHelpers/entities";
import { renderComponent } from "#/testHelpers/renderHelpers";
import { TemplateSettingsForm } from "./TemplateSettingsForm";
const renameCheckbox = () =>
screen.getByRole("checkbox", {
name: /allow users to rename their workspaces/i,
});
const renderForm = (
overrides: Partial<Parameters<typeof TemplateSettingsForm>[0]> = {},
) => {
const onSubmit = vi.fn();
renderComponent(
<TemplateSettingsForm
template={MockTemplate}
onSubmit={onSubmit}
onCancel={vi.fn()}
isSubmitting={false}
accessControlEnabled
advancedSchedulingEnabled
portSharingControlsEnabled
{...overrides}
/>,
);
return { onSubmit };
};
describe("TemplateSettingsForm", () => {
describe("allow_workspace_renames", () => {
it("reflects the template's current value", () => {
renderForm({
template: { ...MockTemplate, allow_workspace_renames: true },
});
expect(renameCheckbox()).toBeChecked();
});
it("is unchecked when the template disallows renames", () => {
renderForm({
template: { ...MockTemplate, allow_workspace_renames: false },
});
expect(renameCheckbox()).not.toBeChecked();
});
it("submits the new value when toggled on", async () => {
const user = userEvent.setup();
const { onSubmit } = renderForm({
template: { ...MockTemplate, allow_workspace_renames: false },
});
await user.click(renameCheckbox());
await user.click(screen.getByRole("button", { name: /save/i }));
await waitFor(() => {
expect(onSubmit).toHaveBeenCalledWith(
expect.objectContaining({ allow_workspace_renames: true }),
// Formik passes its helpers as a second argument.
expect.anything(),
);
});
});
it("submits the new value when toggled off", async () => {
const user = userEvent.setup();
const { onSubmit } = renderForm({
template: { ...MockTemplate, allow_workspace_renames: true },
});
await user.click(renameCheckbox());
await user.click(screen.getByRole("button", { name: /save/i }));
await waitFor(() => {
expect(onSubmit).toHaveBeenCalledWith(
expect.objectContaining({ allow_workspace_renames: false }),
expect.anything(),
);
});
});
});
});
@@ -19,6 +19,7 @@ import {
import { FormField } from "#/components/FormField/FormField";
import { IconField } from "#/components/IconField/IconField";
import { Label } from "#/components/Label/Label";
import { Link } from "#/components/Link/Link";
import {
Select,
SelectContent,
@@ -33,6 +34,7 @@ import {
} from "#/components/StackLabel/StackLabel";
import { Textarea } from "#/components/Textarea/Textarea";
import { cn } from "#/utils/cn";
import { docs } from "#/utils/docs";
import {
displayNameValidator,
getFormHelpers,
@@ -53,6 +55,7 @@ export const validationSchema = Yup.object({
),
allow_user_cancel_workspace_jobs: Yup.boolean(),
agents_allowed: Yup.boolean(),
allow_workspace_renames: Yup.boolean(),
icon: iconValidator,
require_active_version: Yup.boolean(),
disable_module_cache: Yup.boolean(),
@@ -102,6 +105,7 @@ export const TemplateSettingsForm: FC<TemplateSettingsForm> = ({
max_port_share_level: template.max_port_share_level,
cors_behavior: template.cors_behavior,
disable_module_cache: template.disable_module_cache,
allow_workspace_renames: template.allow_workspace_renames,
},
validationSchema,
onSubmit,
@@ -307,6 +311,38 @@ export const TemplateSettingsForm: FC<TemplateSettingsForm> = ({
</StackLabel>
</Label>
</div>
<div className="flex items-start">
<Checkbox
id="allow_workspace_renames"
name="allow_workspace_renames"
checked={form.values.allow_workspace_renames}
onCheckedChange={(checked) => {
form.setFieldValue("allow_workspace_renames", checked === true);
}}
disabled={isSubmitting}
/>
<Label htmlFor="allow_workspace_renames">
<StackLabel>
Allow users to rename their workspaces.
<StackLabelHelperText>
<div>
Only enable this if your template does not use the workspace
name in such a way that changing it may destroy/recreate a
resource.
</div>
<Link
className="text-xs"
href={docs(
"/admin/templates/extending-templates/resource-persistence",
)}
>
Learn more
</Link>
</StackLabelHelperText>
</StackLabel>
</Label>
</div>
</FormFields>
</FormSection>
@@ -46,6 +46,7 @@ const validFormValues: FormValues = {
use_classic_parameter_flow: true,
cors_behavior: "simple",
disable_module_cache: false,
allow_workspace_renames: false,
};
describe("TemplateSettingsPage", () => {
+1
View File
@@ -963,6 +963,7 @@ export const MockTemplate: TypesGen.Template = {
use_classic_parameter_flow: false,
cors_behavior: "simple",
disable_module_cache: false,
allow_workspace_renames: false,
};
const _MockTemplateVersionFiles: TemplateVersionFiles = {