diff --git a/coderd/apidoc/docs.go b/coderd/apidoc/docs.go index dbf906bea4..09f59f39bb 100644 --- a/coderd/apidoc/docs.go +++ b/coderd/apidoc/docs.go @@ -18813,6 +18813,10 @@ const docTemplate = `{ "description": { "type": "string" }, + "disable_module_cache": { + "description": "DisableModuleCache disables the use of cached Terraform modules during\nprovisioning.", + "type": "boolean" + }, "display_name": { "type": "string" }, @@ -19769,6 +19773,10 @@ const docTemplate = `{ "description": "DisableEveryoneGroupAccess allows optionally disabling the default\nbehavior of granting the 'everyone' group access to use the template.\nIf this is set to true, the template will not be available to all users,\nand must be explicitly granted to users or groups in the permissions settings\nof the template.", "type": "boolean" }, + "disable_module_cache": { + "description": "DisableModuleCache disables the using of cached Terraform modules during\nprovisioning. It is recommended not to disable this.", + "type": "boolean" + }, "display_name": { "type": "string" }, diff --git a/coderd/apidoc/swagger.json b/coderd/apidoc/swagger.json index 4e462beb26..7ae1addf1a 100644 --- a/coderd/apidoc/swagger.json +++ b/coderd/apidoc/swagger.json @@ -17208,6 +17208,10 @@ "description": { "type": "string" }, + "disable_module_cache": { + "description": "DisableModuleCache disables the use of cached Terraform modules during\nprovisioning.", + "type": "boolean" + }, "display_name": { "type": "string" }, @@ -18118,6 +18122,10 @@ "description": "DisableEveryoneGroupAccess allows optionally disabling the default\nbehavior of granting the 'everyone' group access to use the template.\nIf this is set to true, the template will not be available to all users,\nand must be explicitly granted to users or groups in the permissions settings\nof the template.", "type": "boolean" }, + "disable_module_cache": { + "description": "DisableModuleCache disables the using of cached Terraform modules during\nprovisioning. It is recommended not to disable this.", + "type": "boolean" + }, "display_name": { "type": "string" }, diff --git a/coderd/database/dump.sql b/coderd/database/dump.sql index 9e5fff626c..868a8dc26c 100644 --- a/coderd/database/dump.sql +++ b/coderd/database/dump.sql @@ -2290,7 +2290,8 @@ CREATE TABLE templates ( activity_bump bigint DEFAULT '3600000000000'::bigint NOT NULL, max_port_sharing_level app_sharing_level DEFAULT 'owner'::app_sharing_level NOT NULL, use_classic_parameter_flow boolean DEFAULT false NOT NULL, - cors_behavior cors_behavior DEFAULT 'simple'::cors_behavior NOT NULL + cors_behavior cors_behavior DEFAULT 'simple'::cors_behavior NOT NULL, + disable_module_cache boolean DEFAULT false NOT NULL ); COMMENT ON COLUMN templates.default_ttl IS 'The default duration for autostop for workspaces created from this template.'; @@ -2344,6 +2345,7 @@ CREATE VIEW template_with_names AS templates.max_port_sharing_level, templates.use_classic_parameter_flow, templates.cors_behavior, + templates.disable_module_cache, 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, diff --git a/coderd/database/migrations/000416_workspace_module_reuse_toggle.down.sql b/coderd/database/migrations/000416_workspace_module_reuse_toggle.down.sql new file mode 100644 index 0000000000..d265d5a5b5 --- /dev/null +++ b/coderd/database/migrations/000416_workspace_module_reuse_toggle.down.sql @@ -0,0 +1,16 @@ +DROP VIEW template_with_names; +ALTER TABLE templates DROP COLUMN disable_module_cache; + +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.'; diff --git a/coderd/database/migrations/000416_workspace_module_reuse_toggle.up.sql b/coderd/database/migrations/000416_workspace_module_reuse_toggle.up.sql new file mode 100644 index 0000000000..5217bef0c6 --- /dev/null +++ b/coderd/database/migrations/000416_workspace_module_reuse_toggle.up.sql @@ -0,0 +1,16 @@ +DROP VIEW template_with_names; +ALTER TABLE templates ADD COLUMN disable_module_cache BOOL NOT NULL DEFAULT false; + +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.'; diff --git a/coderd/database/modelqueries.go b/coderd/database/modelqueries.go index 761218e15c..05b2b49657 100644 --- a/coderd/database/modelqueries.go +++ b/coderd/database/modelqueries.go @@ -127,6 +127,7 @@ func (q *sqlQuerier) GetAuthorizedTemplates(ctx context.Context, arg GetTemplate &i.MaxPortSharingLevel, &i.UseClassicParameterFlow, &i.CorsBehavior, + &i.DisableModuleCache, &i.CreatedByAvatarURL, &i.CreatedByUsername, &i.CreatedByName, diff --git a/coderd/database/models.go b/coderd/database/models.go index 752114575e..7398b2ef6b 100644 --- a/coderd/database/models.go +++ b/coderd/database/models.go @@ -4338,6 +4338,7 @@ type Template struct { MaxPortSharingLevel AppSharingLevel `db:"max_port_sharing_level" json:"max_port_sharing_level"` UseClassicParameterFlow bool `db:"use_classic_parameter_flow" json:"use_classic_parameter_flow"` CorsBehavior CorsBehavior `db:"cors_behavior" json:"cors_behavior"` + DisableModuleCache bool `db:"disable_module_cache" json:"disable_module_cache"` 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"` @@ -4387,6 +4388,7 @@ type TemplateTable struct { // Determines whether to default to the dynamic parameter creation flow for this template or continue using the legacy classic parameter creation flow.This is a template wide setting, the template admin can revert to the classic flow if there are any issues. An escape hatch is required, as workspace creation is a core workflow and cannot break. This column will be removed when the dynamic parameter creation flow is stable. UseClassicParameterFlow bool `db:"use_classic_parameter_flow" json:"use_classic_parameter_flow"` CorsBehavior CorsBehavior `db:"cors_behavior" json:"cors_behavior"` + DisableModuleCache bool `db:"disable_module_cache" json:"disable_module_cache"` } // Records aggregated usage statistics for templates/users. All usage is rounded up to the nearest minute. diff --git a/coderd/database/queries.sql.go b/coderd/database/queries.sql.go index 61d42e34b2..68c7473370 100644 --- a/coderd/database/queries.sql.go +++ b/coderd/database/queries.sql.go @@ -13729,7 +13729,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, 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, created_by_avatar_url, created_by_username, created_by_name, organization_name, organization_display_name, organization_icon FROM template_with_names WHERE @@ -13772,6 +13772,7 @@ func (q *sqlQuerier) GetTemplateByID(ctx context.Context, id uuid.UUID) (Templat &i.MaxPortSharingLevel, &i.UseClassicParameterFlow, &i.CorsBehavior, + &i.DisableModuleCache, &i.CreatedByAvatarURL, &i.CreatedByUsername, &i.CreatedByName, @@ -13784,7 +13785,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, 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, created_by_avatar_url, created_by_username, created_by_name, organization_name, organization_display_name, organization_icon FROM template_with_names AS templates WHERE @@ -13835,6 +13836,7 @@ func (q *sqlQuerier) GetTemplateByOrganizationAndName(ctx context.Context, arg G &i.MaxPortSharingLevel, &i.UseClassicParameterFlow, &i.CorsBehavior, + &i.DisableModuleCache, &i.CreatedByAvatarURL, &i.CreatedByUsername, &i.CreatedByName, @@ -13846,7 +13848,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, 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, 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 ` @@ -13890,6 +13892,7 @@ func (q *sqlQuerier) GetTemplates(ctx context.Context) ([]Template, error) { &i.MaxPortSharingLevel, &i.UseClassicParameterFlow, &i.CorsBehavior, + &i.DisableModuleCache, &i.CreatedByAvatarURL, &i.CreatedByUsername, &i.CreatedByName, @@ -13912,7 +13915,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.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.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 @@ -14071,6 +14074,7 @@ func (q *sqlQuerier) GetTemplatesWithFilter(ctx context.Context, arg GetTemplate &i.MaxPortSharingLevel, &i.UseClassicParameterFlow, &i.CorsBehavior, + &i.DisableModuleCache, &i.CreatedByAvatarURL, &i.CreatedByUsername, &i.CreatedByName, @@ -14256,7 +14260,8 @@ SET group_acl = $8, max_port_sharing_level = $9, use_classic_parameter_flow = $10, - cors_behavior = $11 + cors_behavior = $11, + disable_module_cache = $12 WHERE id = $1 ` @@ -14273,6 +14278,7 @@ type UpdateTemplateMetaByIDParams struct { MaxPortSharingLevel AppSharingLevel `db:"max_port_sharing_level" json:"max_port_sharing_level"` UseClassicParameterFlow bool `db:"use_classic_parameter_flow" json:"use_classic_parameter_flow"` CorsBehavior CorsBehavior `db:"cors_behavior" json:"cors_behavior"` + DisableModuleCache bool `db:"disable_module_cache" json:"disable_module_cache"` } func (q *sqlQuerier) UpdateTemplateMetaByID(ctx context.Context, arg UpdateTemplateMetaByIDParams) error { @@ -14288,6 +14294,7 @@ func (q *sqlQuerier) UpdateTemplateMetaByID(ctx context.Context, arg UpdateTempl arg.MaxPortSharingLevel, arg.UseClassicParameterFlow, arg.CorsBehavior, + arg.DisableModuleCache, ) return err } @@ -22843,7 +22850,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 + 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 FROM templates WHERE diff --git a/coderd/database/queries/templates.sql b/coderd/database/queries/templates.sql index 43f1aea6c5..eb6ada1972 100644 --- a/coderd/database/queries/templates.sql +++ b/coderd/database/queries/templates.sql @@ -173,7 +173,8 @@ SET group_acl = $8, max_port_sharing_level = $9, use_classic_parameter_flow = $10, - cors_behavior = $11 + cors_behavior = $11, + disable_module_cache = $12 WHERE id = $1 ; diff --git a/coderd/provisionerdserver/provisionerdserver.go b/coderd/provisionerdserver/provisionerdserver.go index 8ea3a708d1..6b47ecb7bf 100644 --- a/coderd/provisionerdserver/provisionerdserver.go +++ b/coderd/provisionerdserver/provisionerdserver.go @@ -512,13 +512,15 @@ func (s *server) acquireProtoJob(ctx context.Context, job database.ProvisionerJo // Fetch the file id of the cached module files if it exists. versionModulesFile := "" - tfvals, err := s.Database.GetTemplateVersionTerraformValues(ctx, templateVersion.ID) - if err != nil && !xerrors.Is(err, sql.ErrNoRows) { - // Older templates (before dynamic parameters) will not have cached module files. - return nil, failJob(fmt.Sprintf("get template version terraform values: %s", err)) - } - if err == nil && tfvals.CachedModuleFiles.Valid { - versionModulesFile = tfvals.CachedModuleFiles.UUID.String() + if !template.DisableModuleCache { + tfvals, err := s.Database.GetTemplateVersionTerraformValues(ctx, templateVersion.ID) + if err != nil && !xerrors.Is(err, sql.ErrNoRows) { + // Older templates (before dynamic parameters) will not have cached module files. + return nil, failJob(fmt.Sprintf("get template version terraform values: %s", err)) + } + if err == nil && tfvals.CachedModuleFiles.Valid { + versionModulesFile = tfvals.CachedModuleFiles.UUID.String() + } } var ownerSSHPublicKey, ownerSSHPrivateKey string diff --git a/coderd/templates.go b/coderd/templates.go index e038620ab4..adb32ce065 100644 --- a/coderd/templates.go +++ b/coderd/templates.go @@ -775,6 +775,10 @@ func (api *API) patchTemplateMeta(rw http.ResponseWriter, r *http.Request) { if req.UseClassicParameterFlow != nil { classicTemplateFlow = *req.UseClassicParameterFlow } + disableModuleCache := template.DisableModuleCache + if req.DisableModuleCache != nil { + disableModuleCache = *req.DisableModuleCache + } displayName := ptr.NilToDefault(req.DisplayName, template.DisplayName) description := ptr.NilToDefault(req.Description, template.Description) @@ -800,6 +804,7 @@ func (api *API) patchTemplateMeta(rw http.ResponseWriter, r *http.Request) { req.RequireActiveVersion == template.RequireActiveVersion && (deprecationMessage == template.Deprecated) && (classicTemplateFlow == template.UseClassicParameterFlow) && + (disableModuleCache == template.DisableModuleCache) && maxPortShareLevel == template.MaxPortSharingLevel && corsBehavior == template.CorsBehavior { return nil @@ -844,6 +849,7 @@ func (api *API) patchTemplateMeta(rw http.ResponseWriter, r *http.Request) { MaxPortSharingLevel: maxPortShareLevel, UseClassicParameterFlow: classicTemplateFlow, CorsBehavior: corsBehavior, + DisableModuleCache: disableModuleCache, }) if err != nil { return xerrors.Errorf("update template metadata: %w", err) @@ -1128,6 +1134,7 @@ func (api *API) convertTemplate( MaxPortShareLevel: maxPortShareLevel, UseClassicParameterFlow: template.UseClassicParameterFlow, CORSBehavior: codersdk.CORSBehavior(template.CorsBehavior), + DisableModuleCache: template.DisableModuleCache, } } diff --git a/coderd/templates_test.go b/coderd/templates_test.go index b47c1e9d65..511d6e1dcc 100644 --- a/coderd/templates_test.go +++ b/coderd/templates_test.go @@ -1616,6 +1616,39 @@ func TestPatchTemplateMeta(t *testing.T) { assert.False(t, updated.UseClassicParameterFlow, "expected false") }) + t.Run("DisableModuleCache", 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.DisableModuleCache, "default is false") + + req := codersdk.UpdateTemplateMeta{ + DisableModuleCache: ptr.Ref(true), + } + + ctx := testutil.Context(t, testutil.WaitLong) + + // set to true + updated, err := client.UpdateTemplateMeta(ctx, template.ID, req) + require.NoError(t, err) + assert.True(t, updated.DisableModuleCache, "expected true") + + // noop - should stay true when not specified + req.DisableModuleCache = nil + updated, err = client.UpdateTemplateMeta(ctx, template.ID, req) + require.NoError(t, err) + assert.True(t, updated.DisableModuleCache, "expected true") + + // back to false + req.DisableModuleCache = ptr.Ref(false) + updated, err = client.UpdateTemplateMeta(ctx, template.ID, req) + require.NoError(t, err) + assert.False(t, updated.DisableModuleCache, "expected false") + }) + t.Run("SupportEmptyOrDefaultFields", func(t *testing.T) { t.Parallel() diff --git a/codersdk/templates.go b/codersdk/templates.go index 27c29d93d3..d94dbbeed6 100644 --- a/codersdk/templates.go +++ b/codersdk/templates.go @@ -64,6 +64,10 @@ type Template struct { CORSBehavior CORSBehavior `json:"cors_behavior"` UseClassicParameterFlow bool `json:"use_classic_parameter_flow"` + + // DisableModuleCache disables the use of cached Terraform modules during + // provisioning. + DisableModuleCache bool `json:"disable_module_cache"` } // WeekdaysToBitmap converts a list of weekdays to a bitmap in accordance with @@ -263,6 +267,9 @@ type UpdateTemplateMeta struct { // made the default. // An "opt-out" is present in case the new feature breaks some existing templates. UseClassicParameterFlow *bool `json:"use_classic_parameter_flow,omitempty"` + // DisableModuleCache disables the using of cached Terraform modules during + // provisioning. It is recommended not to disable this. + DisableModuleCache *bool `json:"disable_module_cache,omitempty"` } type TemplateExample struct { diff --git a/docs/admin/security/audit-logs.md b/docs/admin/security/audit-logs.md index 31c6c45d64..9c4fb5dfc6 100644 --- a/docs/admin/security/audit-logs.md +++ b/docs/admin/security/audit-logs.md @@ -13,32 +13,32 @@ We track the following resources: -| Resource | | | -|----------------------------------------------------------|----------------------------------------------------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| -| APIKey
login, logout, register, create, delete | |
FieldTracked
allow_listfalse
created_attrue
expires_attrue
hashed_secretfalse
idfalse
ip_addressfalse
last_usedtrue
lifetime_secondsfalse
login_typefalse
scopesfalse
token_namefalse
updated_atfalse
user_idtrue
| -| AuditOAuthConvertState
| |
FieldTracked
created_attrue
expires_attrue
from_login_typetrue
to_login_typetrue
user_idtrue
| -| Group
create, write, delete | |
FieldTracked
avatar_urltrue
display_nametrue
idtrue
memberstrue
nametrue
organization_idfalse
quota_allowancetrue
sourcefalse
| -| AuditableOrganizationMember
| |
FieldTracked
created_attrue
organization_idfalse
rolestrue
updated_attrue
user_idtrue
usernametrue
| -| CustomRole
| |
FieldTracked
created_atfalse
display_nametrue
idfalse
is_systemfalse
member_permissionstrue
nametrue
org_permissionstrue
organization_idfalse
site_permissionstrue
updated_atfalse
user_permissionstrue
| -| GitSSHKey
create | |
FieldTracked
created_atfalse
private_keytrue
public_keytrue
updated_atfalse
user_idtrue
| -| GroupSyncSettings
| |
FieldTracked
auto_create_missing_groupstrue
fieldtrue
legacy_group_name_mappingfalse
mappingtrue
regex_filtertrue
| -| HealthSettings
| |
FieldTracked
dismissed_healthcheckstrue
idfalse
| -| License
create, delete | |
FieldTracked
exptrue
idfalse
jwtfalse
uploaded_attrue
uuidtrue
| -| NotificationTemplate
| |
FieldTracked
actionstrue
body_templatetrue
enabled_by_defaulttrue
grouptrue
idfalse
kindtrue
methodtrue
nametrue
title_templatetrue
| -| NotificationsSettings
| |
FieldTracked
idfalse
notifier_pausedtrue
| -| OAuth2ProviderApp
| |
FieldTracked
callback_urltrue
client_id_issued_atfalse
client_secret_expires_attrue
client_typetrue
client_uritrue
contactstrue
created_atfalse
dynamically_registeredtrue
grant_typestrue
icontrue
idfalse
jwkstrue
jwks_uritrue
logo_uritrue
nametrue
policy_uritrue
redirect_uristrue
registration_access_tokentrue
registration_client_uritrue
response_typestrue
scopetrue
software_idtrue
software_versiontrue
token_endpoint_auth_methodtrue
tos_uritrue
updated_atfalse
| -| OAuth2ProviderAppSecret
| |
FieldTracked
app_idfalse
created_atfalse
display_secretfalse
hashed_secretfalse
idfalse
last_used_atfalse
secret_prefixfalse
| -| Organization
| |
FieldTracked
created_atfalse
deletedtrue
descriptiontrue
display_nametrue
icontrue
idfalse
is_defaulttrue
nametrue
updated_attrue
workspace_sharing_disabledtrue
| -| OrganizationSyncSettings
| |
FieldTracked
assign_defaulttrue
fieldtrue
mappingtrue
| -| PrebuildsSettings
| |
FieldTracked
idfalse
reconciliation_pausedtrue
| -| RoleSyncSettings
| |
FieldTracked
fieldtrue
mappingtrue
| -| TaskTable
| |
FieldTracked
created_atfalse
deleted_atfalse
display_nametrue
idtrue
nametrue
organization_idfalse
owner_idtrue
prompttrue
template_parameterstrue
template_version_idtrue
workspace_idtrue
| -| Template
write, delete | |
FieldTracked
active_version_idtrue
activity_bumptrue
allow_user_autostarttrue
allow_user_autostoptrue
allow_user_cancel_workspace_jobstrue
autostart_block_days_of_weektrue
autostop_requirement_days_of_weektrue
autostop_requirement_weekstrue
cors_behaviortrue
created_atfalse
created_bytrue
created_by_avatar_urlfalse
created_by_namefalse
created_by_usernamefalse
default_ttltrue
deletedfalse
deprecatedtrue
descriptiontrue
display_nametrue
failure_ttltrue
group_acltrue
icontrue
idtrue
max_port_sharing_leveltrue
nametrue
organization_display_namefalse
organization_iconfalse
organization_idfalse
organization_namefalse
provisionertrue
require_active_versiontrue
time_til_dormanttrue
time_til_dormant_autodeletetrue
updated_atfalse
use_classic_parameter_flowtrue
user_acltrue
| -| TemplateVersion
create, write | |
FieldTracked
archivedtrue
created_atfalse
created_bytrue
created_by_avatar_urlfalse
created_by_namefalse
created_by_usernamefalse
external_auth_providersfalse
has_ai_taskfalse
has_external_agentfalse
idtrue
job_idfalse
messagefalse
nametrue
organization_idfalse
readmetrue
source_example_idfalse
template_idtrue
updated_atfalse
| -| User
create, write, delete | |
FieldTracked
avatar_urlfalse
created_atfalse
deletedtrue
emailtrue
github_com_user_idfalse
hashed_one_time_passcodefalse
hashed_passwordtrue
idtrue
is_systemtrue
last_seen_atfalse
login_typetrue
nametrue
one_time_passcode_expires_attrue
quiet_hours_scheduletrue
rbac_rolestrue
statustrue
updated_atfalse
usernametrue
| -| WorkspaceBuild
start, stop | |
FieldTracked
build_numberfalse
created_atfalse
daily_costfalse
deadlinefalse
has_ai_taskfalse
has_external_agentfalse
idfalse
initiator_by_avatar_urlfalse
initiator_by_namefalse
initiator_by_usernamefalse
initiator_idfalse
job_idfalse
max_deadlinefalse
provisioner_statefalse
reasonfalse
template_version_idtrue
template_version_preset_idfalse
transitionfalse
updated_atfalse
workspace_idfalse
| -| WorkspaceProxy
| |
FieldTracked
created_attrue
deletedfalse
derp_enabledtrue
derp_onlytrue
display_nametrue
icontrue
idtrue
nametrue
region_idtrue
token_hashed_secrettrue
updated_atfalse
urltrue
versiontrue
wildcard_hostnametrue
| -| WorkspaceTable
| |
FieldTracked
automatic_updatestrue
autostart_scheduletrue
created_atfalse
deletedfalse
deleting_attrue
dormant_attrue
favoritetrue
group_acltrue
idtrue
last_used_atfalse
nametrue
next_start_attrue
organization_idfalse
owner_idtrue
template_idtrue
ttltrue
updated_atfalse
user_acltrue
| +| Resource | | | +|----------------------------------------------------------|----------------------------------------------------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| APIKey
login, logout, register, create, delete | |
FieldTracked
allow_listfalse
created_attrue
expires_attrue
hashed_secretfalse
idfalse
ip_addressfalse
last_usedtrue
lifetime_secondsfalse
login_typefalse
scopesfalse
token_namefalse
updated_atfalse
user_idtrue
| +| AuditOAuthConvertState
| |
FieldTracked
created_attrue
expires_attrue
from_login_typetrue
to_login_typetrue
user_idtrue
| +| Group
create, write, delete | |
FieldTracked
avatar_urltrue
display_nametrue
idtrue
memberstrue
nametrue
organization_idfalse
quota_allowancetrue
sourcefalse
| +| AuditableOrganizationMember
| |
FieldTracked
created_attrue
organization_idfalse
rolestrue
updated_attrue
user_idtrue
usernametrue
| +| CustomRole
| |
FieldTracked
created_atfalse
display_nametrue
idfalse
is_systemfalse
member_permissionstrue
nametrue
org_permissionstrue
organization_idfalse
site_permissionstrue
updated_atfalse
user_permissionstrue
| +| GitSSHKey
create | |
FieldTracked
created_atfalse
private_keytrue
public_keytrue
updated_atfalse
user_idtrue
| +| GroupSyncSettings
| |
FieldTracked
auto_create_missing_groupstrue
fieldtrue
legacy_group_name_mappingfalse
mappingtrue
regex_filtertrue
| +| HealthSettings
| |
FieldTracked
dismissed_healthcheckstrue
idfalse
| +| License
create, delete | |
FieldTracked
exptrue
idfalse
jwtfalse
uploaded_attrue
uuidtrue
| +| NotificationTemplate
| |
FieldTracked
actionstrue
body_templatetrue
enabled_by_defaulttrue
grouptrue
idfalse
kindtrue
methodtrue
nametrue
title_templatetrue
| +| NotificationsSettings
| |
FieldTracked
idfalse
notifier_pausedtrue
| +| OAuth2ProviderApp
| |
FieldTracked
callback_urltrue
client_id_issued_atfalse
client_secret_expires_attrue
client_typetrue
client_uritrue
contactstrue
created_atfalse
dynamically_registeredtrue
grant_typestrue
icontrue
idfalse
jwkstrue
jwks_uritrue
logo_uritrue
nametrue
policy_uritrue
redirect_uristrue
registration_access_tokentrue
registration_client_uritrue
response_typestrue
scopetrue
software_idtrue
software_versiontrue
token_endpoint_auth_methodtrue
tos_uritrue
updated_atfalse
| +| OAuth2ProviderAppSecret
| |
FieldTracked
app_idfalse
created_atfalse
display_secretfalse
hashed_secretfalse
idfalse
last_used_atfalse
secret_prefixfalse
| +| Organization
| |
FieldTracked
created_atfalse
deletedtrue
descriptiontrue
display_nametrue
icontrue
idfalse
is_defaulttrue
nametrue
updated_attrue
workspace_sharing_disabledtrue
| +| OrganizationSyncSettings
| |
FieldTracked
assign_defaulttrue
fieldtrue
mappingtrue
| +| PrebuildsSettings
| |
FieldTracked
idfalse
reconciliation_pausedtrue
| +| RoleSyncSettings
| |
FieldTracked
fieldtrue
mappingtrue
| +| TaskTable
| |
FieldTracked
created_atfalse
deleted_atfalse
display_nametrue
idtrue
nametrue
organization_idfalse
owner_idtrue
prompttrue
template_parameterstrue
template_version_idtrue
workspace_idtrue
| +| Template
write, delete | |
FieldTracked
active_version_idtrue
activity_bumptrue
allow_user_autostarttrue
allow_user_autostoptrue
allow_user_cancel_workspace_jobstrue
autostart_block_days_of_weektrue
autostop_requirement_days_of_weektrue
autostop_requirement_weekstrue
cors_behaviortrue
created_atfalse
created_bytrue
created_by_avatar_urlfalse
created_by_namefalse
created_by_usernamefalse
default_ttltrue
deletedfalse
deprecatedtrue
descriptiontrue
disable_module_cachetrue
display_nametrue
failure_ttltrue
group_acltrue
icontrue
idtrue
max_port_sharing_leveltrue
nametrue
organization_display_namefalse
organization_iconfalse
organization_idfalse
organization_namefalse
provisionertrue
require_active_versiontrue
time_til_dormanttrue
time_til_dormant_autodeletetrue
updated_atfalse
use_classic_parameter_flowtrue
user_acltrue
| +| TemplateVersion
create, write | |
FieldTracked
archivedtrue
created_atfalse
created_bytrue
created_by_avatar_urlfalse
created_by_namefalse
created_by_usernamefalse
external_auth_providersfalse
has_ai_taskfalse
has_external_agentfalse
idtrue
job_idfalse
messagefalse
nametrue
organization_idfalse
readmetrue
source_example_idfalse
template_idtrue
updated_atfalse
| +| User
create, write, delete | |
FieldTracked
avatar_urlfalse
created_atfalse
deletedtrue
emailtrue
github_com_user_idfalse
hashed_one_time_passcodefalse
hashed_passwordtrue
idtrue
is_systemtrue
last_seen_atfalse
login_typetrue
nametrue
one_time_passcode_expires_attrue
quiet_hours_scheduletrue
rbac_rolestrue
statustrue
updated_atfalse
usernametrue
| +| WorkspaceBuild
start, stop | |
FieldTracked
build_numberfalse
created_atfalse
daily_costfalse
deadlinefalse
has_ai_taskfalse
has_external_agentfalse
idfalse
initiator_by_avatar_urlfalse
initiator_by_namefalse
initiator_by_usernamefalse
initiator_idfalse
job_idfalse
max_deadlinefalse
provisioner_statefalse
reasonfalse
template_version_idtrue
template_version_preset_idfalse
transitionfalse
updated_atfalse
workspace_idfalse
| +| WorkspaceProxy
| |
FieldTracked
created_attrue
deletedfalse
derp_enabledtrue
derp_onlytrue
display_nametrue
icontrue
idtrue
nametrue
region_idtrue
token_hashed_secrettrue
updated_atfalse
urltrue
versiontrue
wildcard_hostnametrue
| +| WorkspaceTable
| |
FieldTracked
automatic_updatestrue
autostart_scheduletrue
created_atfalse
deletedfalse
deleting_attrue
dormant_attrue
favoritetrue
group_acltrue
idtrue
last_used_atfalse
nametrue
next_start_attrue
organization_idfalse
owner_idtrue
template_idtrue
ttltrue
updated_atfalse
user_acltrue
| diff --git a/docs/reference/api/schemas.md b/docs/reference/api/schemas.md index 02b0efba24..d4d6fa4f16 100644 --- a/docs/reference/api/schemas.md +++ b/docs/reference/api/schemas.md @@ -8008,6 +8008,7 @@ Only certain features set these fields: - FeatureManagedAgentLimit| "deprecated": true, "deprecation_message": "string", "description": "string", + "disable_module_cache": true, "display_name": "string", "failure_ttl_ms": 0, "icon": "string", @@ -8048,6 +8049,7 @@ Only certain features set these fields: - FeatureManagedAgentLimit| | `deprecated` | boolean | false | | | | `deprecation_message` | string | false | | | | `description` | string | false | | | +| `disable_module_cache` | boolean | false | | Disable module cache disables the use of cached Terraform modules during provisioning. | | `display_name` | string | false | | | | `failure_ttl_ms` | integer | false | | Failure ttl ms TimeTilDormantMillis, and TimeTilDormantAutoDeleteMillis are enterprise-only. Their values are used if your license is entitled to use the advanced template scheduling feature. | | `icon` | string | false | | | @@ -9086,6 +9088,7 @@ Restarts will only happen on weekdays in this list on weeks which line up with W "deprecation_message": "string", "description": "string", "disable_everyone_group_access": true, + "disable_module_cache": true, "display_name": "string", "failure_ttl_ms": 0, "icon": "string", @@ -9115,6 +9118,7 @@ Restarts will only happen on weekdays in this list on weeks which line up with W | `deprecation_message` | string | false | | Deprecation message if set, will mark the template as deprecated and block any new workspaces from using this template. If passed an empty string, will remove the deprecated message, making the template usable for new workspaces again. | | `description` | string | false | | | | `disable_everyone_group_access` | boolean | false | | Disable everyone group access allows optionally disabling the default behavior of granting the 'everyone' group access to use the template. If this is set to true, the template will not be available to all users, and must be explicitly granted to users or groups in the permissions settings of the template. | +| `disable_module_cache` | boolean | false | | Disable module cache disables the using of cached Terraform modules during provisioning. It is recommended not to disable this. | | `display_name` | string | false | | | | `failure_ttl_ms` | integer | false | | | | `icon` | string | false | | | diff --git a/docs/reference/api/templates.md b/docs/reference/api/templates.md index b59efa8c90..cfc51f8fbf 100644 --- a/docs/reference/api/templates.md +++ b/docs/reference/api/templates.md @@ -65,6 +65,7 @@ To include deprecated templates, specify `deprecated:true` in the search query. "deprecated": true, "deprecation_message": "string", "description": "string", + "disable_module_cache": true, "display_name": "string", "failure_ttl_ms": 0, "icon": "string", @@ -122,6 +123,7 @@ Restarts will only happen on weekdays in this list on weeks which line up with W |`» deprecated`|boolean|false||| |`» deprecation_message`|string|false||| |`» description`|string|false||| +|`» disable_module_cache`|boolean|false||Disable module cache disables the use of cached Terraform modules during provisioning.| |`» display_name`|string|false||| |`» failure_ttl_ms`|integer|false||Failure ttl ms TimeTilDormantMillis, and TimeTilDormantAutoDeleteMillis are enterprise-only. Their values are used if your license is entitled to use the advanced template scheduling feature.| |`» icon`|string|false||| @@ -247,6 +249,7 @@ curl -X POST http://coder-server:8080/api/v2/organizations/{organization}/templa "deprecated": true, "deprecation_message": "string", "description": "string", + "disable_module_cache": true, "display_name": "string", "failure_ttl_ms": 0, "icon": "string", @@ -397,6 +400,7 @@ curl -X GET http://coder-server:8080/api/v2/organizations/{organization}/templat "deprecated": true, "deprecation_message": "string", "description": "string", + "disable_module_cache": true, "display_name": "string", "failure_ttl_ms": 0, "icon": "string", @@ -813,6 +817,7 @@ To include deprecated templates, specify `deprecated:true` in the search query. "deprecated": true, "deprecation_message": "string", "description": "string", + "disable_module_cache": true, "display_name": "string", "failure_ttl_ms": 0, "icon": "string", @@ -870,6 +875,7 @@ Restarts will only happen on weekdays in this list on weeks which line up with W |`» deprecated`|boolean|false||| |`» deprecation_message`|string|false||| |`» description`|string|false||| +|`» disable_module_cache`|boolean|false||Disable module cache disables the use of cached Terraform modules during provisioning.| |`» display_name`|string|false||| |`» failure_ttl_ms`|integer|false||Failure ttl ms TimeTilDormantMillis, and TimeTilDormantAutoDeleteMillis are enterprise-only. Their values are used if your license is entitled to use the advanced template scheduling feature.| |`» icon`|string|false||| @@ -1013,6 +1019,7 @@ curl -X GET http://coder-server:8080/api/v2/templates/{template} \ "deprecated": true, "deprecation_message": "string", "description": "string", + "disable_module_cache": true, "display_name": "string", "failure_ttl_ms": 0, "icon": "string", @@ -1122,6 +1129,7 @@ curl -X PATCH http://coder-server:8080/api/v2/templates/{template} \ "deprecation_message": "string", "description": "string", "disable_everyone_group_access": true, + "disable_module_cache": true, "display_name": "string", "failure_ttl_ms": 0, "icon": "string", @@ -1184,6 +1192,7 @@ curl -X PATCH http://coder-server:8080/api/v2/templates/{template} \ "deprecated": true, "deprecation_message": "string", "description": "string", + "disable_module_cache": true, "display_name": "string", "failure_ttl_ms": 0, "icon": "string", diff --git a/enterprise/audit/table.go b/enterprise/audit/table.go index 6565d5e49c..3c7adb7e64 100644 --- a/enterprise/audit/table.go +++ b/enterprise/audit/table.go @@ -119,6 +119,7 @@ var auditableResourcesTypes = map[any]map[string]Action{ "activity_bump": ActionTrack, "use_classic_parameter_flow": ActionTrack, "cors_behavior": ActionTrack, + "disable_module_cache": ActionTrack, }, &database.TemplateVersion{}: { "id": ActionTrack, diff --git a/site/src/api/typesGenerated.ts b/site/src/api/typesGenerated.ts index 57ec4d285e..cceedca129 100644 --- a/site/src/api/typesGenerated.ts +++ b/site/src/api/typesGenerated.ts @@ -5171,6 +5171,11 @@ export interface Template { readonly max_port_share_level: WorkspaceAgentPortShareLevel; readonly cors_behavior: CORSBehavior; readonly use_classic_parameter_flow: boolean; + /** + * DisableModuleCache disables the use of cached Terraform modules during + * provisioning. + */ + readonly disable_module_cache: boolean; } // From codersdk/templates.go @@ -5700,6 +5705,11 @@ export interface UpdateTemplateMeta { * An "opt-out" is present in case the new feature breaks some existing templates. */ readonly use_classic_parameter_flow?: boolean; + /** + * DisableModuleCache disables the using of cached Terraform modules during + * provisioning. It is recommended not to disable this. + */ + readonly disable_module_cache?: boolean; } // From codersdk/users.go diff --git a/site/src/pages/TemplateSettingsPage/TemplateGeneralSettingsPage/TemplateSettingsForm.tsx b/site/src/pages/TemplateSettingsPage/TemplateGeneralSettingsPage/TemplateSettingsForm.tsx index 5b35b5ba26..8db0167e11 100644 --- a/site/src/pages/TemplateSettingsPage/TemplateGeneralSettingsPage/TemplateSettingsForm.tsx +++ b/site/src/pages/TemplateSettingsPage/TemplateGeneralSettingsPage/TemplateSettingsForm.tsx @@ -51,6 +51,7 @@ export const validationSchema = Yup.object({ icon: iconValidator, require_active_version: Yup.boolean(), use_classic_parameter_flow: Yup.boolean(), + disable_module_cache: Yup.boolean(), deprecation_message: Yup.string(), max_port_sharing_level: Yup.string().oneOf(WorkspaceAppSharingLevels), cors_behavior: Yup.string().oneOf(Object.values(CORSBehaviors)), @@ -96,6 +97,7 @@ export const TemplateSettingsForm: FC = ({ max_port_share_level: template.max_port_share_level, use_classic_parameter_flow: template.use_classic_parameter_flow, cors_behavior: template.cors_behavior, + disable_module_cache: template.disable_module_cache, }, validationSchema, onSubmit, @@ -270,6 +272,31 @@ export const TemplateSettingsForm: FC = ({ } /> + + } + label={ + + Disable Terraform module caching + + When checked, Terraform modules are re-downloaded for each + workspace build instead of using cached versions.{" "} + + Warning: This makes workspace builds less predictable and is + not recommended for production templates. + + + + } + /> diff --git a/site/src/pages/TemplateSettingsPage/TemplateGeneralSettingsPage/TemplateSettingsPage.jest.tsx b/site/src/pages/TemplateSettingsPage/TemplateGeneralSettingsPage/TemplateSettingsPage.jest.tsx index 2ed5305966..e5e032cccb 100644 --- a/site/src/pages/TemplateSettingsPage/TemplateGeneralSettingsPage/TemplateSettingsPage.jest.tsx +++ b/site/src/pages/TemplateSettingsPage/TemplateGeneralSettingsPage/TemplateSettingsPage.jest.tsx @@ -56,6 +56,7 @@ const validFormValues: FormValues = { max_port_share_level: "owner", use_classic_parameter_flow: true, cors_behavior: "simple", + disable_module_cache: false, }; const renderTemplateSettingsPage = async () => { diff --git a/site/src/testHelpers/entities.ts b/site/src/testHelpers/entities.ts index 3ad401ca42..e28e904e04 100644 --- a/site/src/testHelpers/entities.ts +++ b/site/src/testHelpers/entities.ts @@ -855,6 +855,7 @@ export const MockTemplate: TypesGen.Template = { max_port_share_level: "public", use_classic_parameter_flow: false, cors_behavior: "simple", + disable_module_cache: false, }; const _MockTemplateVersionFiles: TemplateVersionFiles = {