mirror of
https://github.com/coder/coder.git
synced 2026-10-16 14:48:56 +08:00
feat: add deployment-level option to disable Terraform module caching (#29187)
<!-- Generated by Coder Agents on behalf of @Emyrk. --> <img width="762" height="239" alt="Screenshot From 2026-09-11 17-09-41" src="https://github.com/user-attachments/assets/9f31fd1e-4cb2-47ab-8d37-4073df9f0a7c" /> Closes #22834. Adds `CODER_PROVISIONER_DISABLE_MODULE_CACHE` so an admin can disable Terraform module caching for every template at once instead of toggling ~100 templates by hand. - The deployment setting wins. Templates cannot opt back in, and the per-template toggle becomes read-only while the deployment setting is on. - Modules stay cached at template import, so dynamic parameter rendering still resolves module-defined parameters. Only the workspace build is denied the cached files. - Templates report `module_cache_disabled_by_deployment`, and template settings shows why the checkbox is locked. <details><summary>Agent context</summary> Decisions taken with @Emyrk before implementing: 1. **Precedence.** `deployment || template`, no per-template override, so no migration and no nullable column. The rule lives in one place, `codersdk.ModuleCacheDisabled`. 2. **Import-time caching untouched.** `dynamicparameters/render.go` overlays `cached_module_files` at `.terraform/modules` to render module-defined parameters. Skipping the cache there would reproduce the `Module not loaded. Did you run terraform init?` failure reported by a customer, so the deployment setting only affects what `AcquireJob` hands the provisioner. 3. **Write path.** A PATCH carrying `disable_module_cache` is discarded while the deployment setting is on, rather than persisted. Persisting it would silently keep templates opted out after the deployment setting is removed. 4. **API shape.** `disable_module_cache` keeps its meaning (this template's stored value) and the new read-only `module_cache_disabled_by_deployment` reports the deployment state. Reading the effective value from `/deployment/config` was rejected because template admins may lack deployment-config read permission. 5. **Naming.** `CODER_PROVISIONER_DISABLE_MODULE_CACHE` over the issue's `CODER_DISABLE_TERRAFORM_MODULE_CACHE`, to match the provisioning option group. Note: `InsertTemplateParams` has no `disable_module_cache` binding, so new templates always store `false`. With deployment precedence they still behave as opted out, so the create path needed no change. Out of scope and tracked elsewhere: the `coderd_template` Terraform provider attribute (separate repo) and backporting the per-template toggle to `release/2.30`. </details> --- <sub>Coder Agents on behalf of @Emyrk.</sub>
This commit is contained in:
+5
@@ -803,6 +803,11 @@ PROVISIONING OPTIONS:
|
||||
Tune the behavior of the provisioner, which is responsible for creating,
|
||||
updating, and deleting workspace resources.
|
||||
|
||||
--provisioner-disable-module-cache bool, $CODER_PROVISIONER_DISABLE_MODULE_CACHE (default: false)
|
||||
Disable the reuse of Terraform modules cached at template import for
|
||||
all templates. Modules are re-downloaded on every workspace build.
|
||||
Individual templates cannot opt back in.
|
||||
|
||||
--provisioner-force-cancel-interval duration, $CODER_PROVISIONER_FORCE_CANCEL_INTERVAL (default: 10m0s)
|
||||
Time to force cancel provisioning tasks that are stuck.
|
||||
|
||||
|
||||
+5
@@ -494,6 +494,11 @@ provisioning:
|
||||
# Time to force cancel provisioning tasks that are stuck.
|
||||
# (default: 10m0s, type: duration)
|
||||
forceCancelInterval: 10m0s
|
||||
# Disable the reuse of Terraform modules cached at template import for all
|
||||
# templates. Modules are re-downloaded on every workspace build. Individual
|
||||
# templates cannot opt back in.
|
||||
# (default: false, type: bool)
|
||||
disableModuleCache: false
|
||||
# Enable one or more experiments. These are not ready for production. Separate
|
||||
# multiple experiments with commas, or enter '*' to opt-in to all available
|
||||
# experiments.
|
||||
|
||||
Generated
+10
-2
@@ -26530,6 +26530,10 @@ const docTemplate = `{
|
||||
"description": "Daemons is the number of built-in terraform provisioners.",
|
||||
"type": "integer"
|
||||
},
|
||||
"disable_module_cache": {
|
||||
"description": "DisableModuleCache disables the reuse of Terraform modules cached at\ntemplate import for every template in the deployment. Templates cannot\nopt back in.",
|
||||
"type": "boolean"
|
||||
},
|
||||
"force_cancel_interval": {
|
||||
"type": "integer"
|
||||
}
|
||||
@@ -27908,7 +27912,7 @@ const docTemplate = `{
|
||||
"type": "string"
|
||||
},
|
||||
"disable_module_cache": {
|
||||
"description": "DisableModuleCache disables the use of cached Terraform modules during\nprovisioning.",
|
||||
"description": "DisableModuleCache disables the use of cached Terraform modules during\nprovisioning for this template. It is read-only while\nModuleCacheDisabledByDeployment is true.",
|
||||
"type": "boolean"
|
||||
},
|
||||
"display_name": {
|
||||
@@ -27928,6 +27932,10 @@ const docTemplate = `{
|
||||
"max_port_share_level": {
|
||||
"$ref": "#/definitions/codersdk.WorkspaceAgentPortShareLevel"
|
||||
},
|
||||
"module_cache_disabled_by_deployment": {
|
||||
"description": "ModuleCacheDisabledByDeployment reports that the deployment disables the\nTerraform module cache for every template. Templates cannot opt back in,\nso the effective state is disabled regardless of DisableModuleCache.",
|
||||
"type": "boolean"
|
||||
},
|
||||
"name": {
|
||||
"type": "string"
|
||||
},
|
||||
@@ -29566,7 +29574,7 @@ const docTemplate = `{
|
||||
"type": "boolean"
|
||||
},
|
||||
"disable_module_cache": {
|
||||
"description": "DisableModuleCache disables the using of cached Terraform modules during\nprovisioning. It is recommended not to disable this.",
|
||||
"description": "DisableModuleCache disables the using of cached Terraform modules during\nprovisioning. It is ignored while the deployment disables the module\ncache for all templates. It is recommended not to disable this.",
|
||||
"type": "boolean"
|
||||
},
|
||||
"display_name": {
|
||||
|
||||
Generated
+10
-2
@@ -24317,6 +24317,10 @@
|
||||
"description": "Daemons is the number of built-in terraform provisioners.",
|
||||
"type": "integer"
|
||||
},
|
||||
"disable_module_cache": {
|
||||
"description": "DisableModuleCache disables the reuse of Terraform modules cached at\ntemplate import for every template in the deployment. Templates cannot\nopt back in.",
|
||||
"type": "boolean"
|
||||
},
|
||||
"force_cancel_interval": {
|
||||
"type": "integer"
|
||||
}
|
||||
@@ -25632,7 +25636,7 @@
|
||||
"type": "string"
|
||||
},
|
||||
"disable_module_cache": {
|
||||
"description": "DisableModuleCache disables the use of cached Terraform modules during\nprovisioning.",
|
||||
"description": "DisableModuleCache disables the use of cached Terraform modules during\nprovisioning for this template. It is read-only while\nModuleCacheDisabledByDeployment is true.",
|
||||
"type": "boolean"
|
||||
},
|
||||
"display_name": {
|
||||
@@ -25652,6 +25656,10 @@
|
||||
"max_port_share_level": {
|
||||
"$ref": "#/definitions/codersdk.WorkspaceAgentPortShareLevel"
|
||||
},
|
||||
"module_cache_disabled_by_deployment": {
|
||||
"description": "ModuleCacheDisabledByDeployment reports that the deployment disables the\nTerraform module cache for every template. Templates cannot opt back in,\nso the effective state is disabled regardless of DisableModuleCache.",
|
||||
"type": "boolean"
|
||||
},
|
||||
"name": {
|
||||
"type": "string"
|
||||
},
|
||||
@@ -27202,7 +27210,7 @@
|
||||
"type": "boolean"
|
||||
},
|
||||
"disable_module_cache": {
|
||||
"description": "DisableModuleCache disables the using of cached Terraform modules during\nprovisioning. It is recommended not to disable this.",
|
||||
"description": "DisableModuleCache disables the using of cached Terraform modules during\nprovisioning. It is ignored while the deployment disables the module\ncache for all templates. It is recommended not to disable this.",
|
||||
"type": "boolean"
|
||||
},
|
||||
"display_name": {
|
||||
|
||||
@@ -684,9 +684,11 @@ func (s *server) acquireProtoJob(ctx context.Context, job database.ProvisionerJo
|
||||
return nil, failJob(fmt.Sprintf("get owner: %s", err))
|
||||
}
|
||||
|
||||
// Fetch the file id of the cached module files if it exists.
|
||||
// Fetch the file id of the cached module files if it exists. Modules
|
||||
// stay cached for parameter rendering even when the cache is
|
||||
// disabled; they are only withheld from the build.
|
||||
versionModulesFile := ""
|
||||
if !template.DisableModuleCache {
|
||||
if !codersdk.ModuleCacheDisabled(s.DeploymentValues, 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.
|
||||
|
||||
@@ -954,6 +954,97 @@ func TestAcquireJob(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestAcquireJob_ModuleCache(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
cases := []struct {
|
||||
name string
|
||||
deploymentDisablesCache bool
|
||||
expectModulesFile bool
|
||||
}{
|
||||
{name: "Enabled", expectModulesFile: true},
|
||||
// The deployment setting wins even though the template does not opt out.
|
||||
{name: "DeploymentDisabled", deploymentDisablesCache: true},
|
||||
}
|
||||
for _, tc := range cases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
dv := coderdtest.DeploymentValues(t)
|
||||
dv.Provisioner.DisableModuleCache = serpent.Bool(tc.deploymentDisablesCache)
|
||||
srv, db, ps, pd := setup(t, false, &overrides{deploymentValues: dv})
|
||||
ctx := testutil.Context(t, testutil.WaitShort)
|
||||
|
||||
user := dbgen.User(t, db, database.User{})
|
||||
template := dbgen.Template(t, db, database.Template{
|
||||
Provisioner: database.ProvisionerTypeEcho,
|
||||
OrganizationID: pd.OrganizationID,
|
||||
CreatedBy: user.ID,
|
||||
})
|
||||
version := dbgen.TemplateVersion(t, db, database.TemplateVersion{
|
||||
CreatedBy: user.ID,
|
||||
OrganizationID: pd.OrganizationID,
|
||||
TemplateID: uuid.NullUUID{UUID: template.ID, Valid: true},
|
||||
JobID: uuid.New(),
|
||||
})
|
||||
_ = dbgen.ProvisionerJob(t, db, ps, database.ProvisionerJob{
|
||||
OrganizationID: pd.OrganizationID,
|
||||
ID: version.JobID,
|
||||
InitiatorID: user.ID,
|
||||
Provisioner: database.ProvisionerTypeEcho,
|
||||
StorageMethod: database.ProvisionerStorageMethodFile,
|
||||
Type: database.ProvisionerJobTypeTemplateVersionImport,
|
||||
Input: must(json.Marshal(provisionerdserver.TemplateVersionImportJob{TemplateVersionID: version.ID})),
|
||||
})
|
||||
moduleFile := dbgen.File(t, db, database.File{CreatedBy: user.ID, Hash: "modules"})
|
||||
err := db.InsertTemplateVersionTerraformValuesByJobID(ctx, database.InsertTemplateVersionTerraformValuesByJobIDParams{
|
||||
JobID: version.JobID,
|
||||
CachedPlan: []byte("{}"),
|
||||
CachedModuleFiles: uuid.NullUUID{UUID: moduleFile.ID, Valid: true},
|
||||
UpdatedAt: dbtime.Now(),
|
||||
})
|
||||
require.NoError(t, err)
|
||||
|
||||
workspace := dbgen.Workspace(t, db, database.WorkspaceTable{
|
||||
TemplateID: template.ID,
|
||||
OwnerID: user.ID,
|
||||
OrganizationID: pd.OrganizationID,
|
||||
})
|
||||
buildID := uuid.New()
|
||||
buildJob := dbgen.ProvisionerJob(t, db, ps, database.ProvisionerJob{
|
||||
OrganizationID: pd.OrganizationID,
|
||||
InitiatorID: user.ID,
|
||||
Provisioner: database.ProvisionerTypeEcho,
|
||||
StorageMethod: database.ProvisionerStorageMethodFile,
|
||||
FileID: dbgen.File(t, db, database.File{CreatedBy: user.ID, Hash: "build"}).ID,
|
||||
Type: database.ProvisionerJobTypeWorkspaceBuild,
|
||||
Input: must(json.Marshal(provisionerdserver.WorkspaceProvisionJob{WorkspaceBuildID: buildID})),
|
||||
Tags: pd.Tags,
|
||||
})
|
||||
_ = dbgen.WorkspaceBuild(t, db, database.WorkspaceBuild{
|
||||
ID: buildID,
|
||||
WorkspaceID: workspace.ID,
|
||||
BuildNumber: 1,
|
||||
JobID: buildJob.ID,
|
||||
TemplateVersionID: version.ID,
|
||||
Transition: database.WorkspaceTransitionStart,
|
||||
Reason: database.BuildReasonInitiator,
|
||||
InitiatorID: user.ID,
|
||||
})
|
||||
|
||||
job, err := srv.AcquireJob(ctx, nil)
|
||||
require.NoError(t, err)
|
||||
|
||||
got := job.GetWorkspaceBuild().GetMetadata().GetTemplateVersionModulesFile()
|
||||
if tc.expectModulesFile {
|
||||
require.Equal(t, moduleFile.ID.String(), got)
|
||||
return
|
||||
}
|
||||
require.Empty(t, got, "cached modules must not be shipped to the build")
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestUpdateJob(t *testing.T) {
|
||||
t.Parallel()
|
||||
ctx := context.Background()
|
||||
|
||||
+12
-11
@@ -692,7 +692,7 @@ func (api *API) patchTemplateMeta(rw http.ResponseWriter, r *http.Request) {
|
||||
// values for any pointer field that is nil in the request, so that
|
||||
// omitted fields are preserved instead of being overwritten with
|
||||
// Go zero values.
|
||||
resolved, validErrs := resolveTemplateMetaUpdate(template, scheduleOpts, req)
|
||||
resolved, validErrs := resolveTemplateMetaUpdate(template, scheduleOpts, req, api.DeploymentValues)
|
||||
|
||||
if resolved.defaultTTLMillis < 0 {
|
||||
validErrs = append(validErrs, codersdk.ValidationError{Field: "default_ttl_ms", Detail: "Must be a positive integer."})
|
||||
@@ -1053,16 +1053,17 @@ func (api *API) convertTemplate(
|
||||
DaysOfWeek: codersdk.BitmapToWeekdays(template.AutostartAllowedDays()),
|
||||
},
|
||||
// These values depend on entitlements and come from the templateAccessControl
|
||||
RequireActiveVersion: templateAccessControl.RequireActiveVersion,
|
||||
Deprecated: templateAccessControl.IsDeprecated(),
|
||||
DeprecationMessage: templateAccessControl.Deprecated,
|
||||
Deleted: template.Deleted,
|
||||
MaxPortShareLevel: maxPortShareLevel,
|
||||
UseClassicParameterFlow: template.UseClassicParameterFlow,
|
||||
CORSBehavior: codersdk.CORSBehavior(template.CorsBehavior),
|
||||
DisableModuleCache: template.DisableModuleCache,
|
||||
AgentsAllowed: template.AgentsAllowed,
|
||||
AllowWorkspaceRenames: template.AllowWorkspaceRenames,
|
||||
RequireActiveVersion: templateAccessControl.RequireActiveVersion,
|
||||
Deprecated: templateAccessControl.IsDeprecated(),
|
||||
DeprecationMessage: templateAccessControl.Deprecated,
|
||||
Deleted: template.Deleted,
|
||||
MaxPortShareLevel: maxPortShareLevel,
|
||||
UseClassicParameterFlow: template.UseClassicParameterFlow,
|
||||
CORSBehavior: codersdk.CORSBehavior(template.CorsBehavior),
|
||||
DisableModuleCache: template.DisableModuleCache,
|
||||
ModuleCacheDisabledByDeployment: codersdk.ModuleCacheDisabledByDeployment(api.DeploymentValues),
|
||||
AgentsAllowed: template.AgentsAllowed,
|
||||
AllowWorkspaceRenames: template.AllowWorkspaceRenames,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -62,13 +62,23 @@ type templateMetaUpdate struct {
|
||||
// failureTTLMillis >= 1 minute, max port share level) and validation
|
||||
// that depends on external interfaces (such as port-sharing licensure)
|
||||
// are the caller's responsibility.
|
||||
//
|
||||
// The deployment values make disableModuleCache read-only when the deployment
|
||||
// disables the Terraform module cache for every template: the request value is
|
||||
// discarded rather than persisted.
|
||||
func resolveTemplateMetaUpdate(
|
||||
template database.Template,
|
||||
scheduleOpts schedule.TemplateScheduleOptions,
|
||||
req codersdk.UpdateTemplateMeta,
|
||||
deploymentValues *codersdk.DeploymentValues,
|
||||
) (templateMetaUpdate, []codersdk.ValidationError) {
|
||||
var validErrs []codersdk.ValidationError
|
||||
|
||||
disableModuleCache := ptr.NilToDefault(req.DisableModuleCache, template.DisableModuleCache)
|
||||
if codersdk.ModuleCacheDisabledByDeployment(deploymentValues) {
|
||||
disableModuleCache = template.DisableModuleCache
|
||||
}
|
||||
|
||||
out := templateMetaUpdate{
|
||||
name: ptr.NilToDefault(req.Name, template.Name),
|
||||
displayName: ptr.NilToDefault(req.DisplayName, template.DisplayName),
|
||||
@@ -87,7 +97,7 @@ func resolveTemplateMetaUpdate(
|
||||
requireActiveVersion: ptr.NilToDefault(req.RequireActiveVersion, template.RequireActiveVersion),
|
||||
deprecationMessage: ptr.NilToDefault(req.DeprecationMessage, template.Deprecated),
|
||||
useClassicTemplateFlow: ptr.NilToDefault(req.UseClassicParameterFlow, template.UseClassicParameterFlow),
|
||||
disableModuleCache: ptr.NilToDefault(req.DisableModuleCache, template.DisableModuleCache),
|
||||
disableModuleCache: disableModuleCache,
|
||||
allowWorkspaceRenames: ptr.NilToDefault(req.AllowWorkspaceRenames, template.AllowWorkspaceRenames),
|
||||
groupACL: template.GroupACL,
|
||||
|
||||
|
||||
@@ -9,6 +9,7 @@ import (
|
||||
"github.com/coder/coder/v2/coderd/database"
|
||||
"github.com/coder/coder/v2/coderd/schedule"
|
||||
"github.com/coder/coder/v2/codersdk"
|
||||
"github.com/coder/serpent"
|
||||
)
|
||||
|
||||
// baselineTemplate returns a database.Template populated with non-default
|
||||
@@ -451,7 +452,7 @@ func TestResolveTemplateMetaUpdate(t *testing.T) {
|
||||
tc.expected.base(&tpl)
|
||||
}
|
||||
schedOpts := baselineScheduleOpts()
|
||||
got, validErrs := resolveTemplateMetaUpdate(tpl, schedOpts, tc.req)
|
||||
got, validErrs := resolveTemplateMetaUpdate(tpl, schedOpts, tc.req, nil)
|
||||
|
||||
want := baselineResolved()
|
||||
tc.expected.override(&want)
|
||||
@@ -485,7 +486,7 @@ func TestResolveTemplateMetaUpdate_NameClearedFallsBackToTemplateName(t *testing
|
||||
|
||||
got, _ := resolveTemplateMetaUpdate(tpl, schedOpts, codersdk.UpdateTemplateMeta{
|
||||
Name: new(""),
|
||||
})
|
||||
}, nil)
|
||||
if got.name != tpl.Name {
|
||||
t.Fatalf("got name = %q, want %q (preserved)", got.name, tpl.Name)
|
||||
}
|
||||
@@ -508,7 +509,7 @@ func TestResolveTemplateMetaUpdate_NilRequestUsesScheduleOptsForRequirements(t *
|
||||
},
|
||||
}
|
||||
|
||||
got, validErrs := resolveTemplateMetaUpdate(tpl, schedOpts, codersdk.UpdateTemplateMeta{})
|
||||
got, validErrs := resolveTemplateMetaUpdate(tpl, schedOpts, codersdk.UpdateTemplateMeta{}, nil)
|
||||
if len(validErrs) != 0 {
|
||||
t.Fatalf("unexpected validation errors: %+v", validErrs)
|
||||
}
|
||||
@@ -527,3 +528,40 @@ func TestResolveTemplateMetaUpdate_NilRequestUsesScheduleOptsForRequirements(t *
|
||||
got.autostopRequirementWeeks, schedOpts.AutostopRequirement.Weeks)
|
||||
}
|
||||
}
|
||||
|
||||
// TestResolveTemplateMetaUpdate_ModuleCacheDisabledByDeployment verifies that the
|
||||
// per-template toggle is read-only while the deployment disables the module
|
||||
// cache: the request value is discarded in both directions.
|
||||
func TestResolveTemplateMetaUpdate_ModuleCacheDisabledByDeployment(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
cases := []struct {
|
||||
name string
|
||||
stored bool
|
||||
request *bool
|
||||
expected bool
|
||||
}{
|
||||
{name: "IgnoresOptIn", stored: true, request: new(false), expected: true},
|
||||
{name: "IgnoresOptOut", stored: false, request: new(true), expected: false},
|
||||
{name: "PreservesStored", stored: true, request: nil, expected: true},
|
||||
}
|
||||
for _, tc := range cases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
tpl := baselineTemplate()
|
||||
tpl.DisableModuleCache = tc.stored
|
||||
dv := &codersdk.DeploymentValues{}
|
||||
dv.Provisioner.DisableModuleCache = serpent.Bool(true)
|
||||
got, validErrs := resolveTemplateMetaUpdate(tpl, baselineScheduleOpts(), codersdk.UpdateTemplateMeta{
|
||||
DisableModuleCache: tc.request,
|
||||
}, dv)
|
||||
if len(validErrs) != 0 {
|
||||
t.Fatalf("unexpected validation errors: %+v", validErrs)
|
||||
}
|
||||
if got.disableModuleCache != tc.expected {
|
||||
t.Errorf("disableModuleCache = %t, want %t", got.disableModuleCache, tc.expected)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -31,6 +31,7 @@ import (
|
||||
"github.com/coder/coder/v2/codersdk/workspacesdk"
|
||||
"github.com/coder/coder/v2/provisioner/echo"
|
||||
"github.com/coder/coder/v2/testutil"
|
||||
"github.com/coder/serpent"
|
||||
)
|
||||
|
||||
// TestTemplatesListSingleAuthorizePrepare guards against reintroducing the
|
||||
@@ -1843,6 +1844,33 @@ func TestPatchTemplateMeta(t *testing.T) {
|
||||
assert.False(t, updated.DisableModuleCache, "expected false")
|
||||
})
|
||||
|
||||
t.Run("DisableModuleCacheDeploymentWide", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
dv := coderdtest.DeploymentValues(t)
|
||||
dv.Provisioner.DisableModuleCache = serpent.Bool(true)
|
||||
client := coderdtest.New(t, &coderdtest.Options{DeploymentValues: dv})
|
||||
user := coderdtest.CreateFirstUser(t, client)
|
||||
version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, nil)
|
||||
template := coderdtest.CreateTemplate(t, client, user.OrganizationID, version.ID)
|
||||
require.True(t, template.ModuleCacheDisabledByDeployment, "the deployment disables the module cache")
|
||||
require.False(t, template.DisableModuleCache, "the template itself does not opt out")
|
||||
|
||||
ctx := testutil.Context(t, testutil.WaitLong)
|
||||
|
||||
// The per-template toggle is read-only while the deployment disables the
|
||||
// cache, so this request does not change anything.
|
||||
_, err := client.UpdateTemplateMeta(ctx, template.ID, codersdk.UpdateTemplateMeta{
|
||||
DisableModuleCache: new(true),
|
||||
})
|
||||
require.NoError(t, err)
|
||||
|
||||
updated, err := client.Template(ctx, template.ID)
|
||||
require.NoError(t, err)
|
||||
assert.False(t, updated.DisableModuleCache, "expected the stored value to be untouched")
|
||||
assert.True(t, updated.ModuleCacheDisabledByDeployment, "expected true")
|
||||
})
|
||||
|
||||
t.Run("AllowWorkspaceRenames", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
|
||||
@@ -1239,6 +1239,10 @@ type ProvisionerConfig struct {
|
||||
DaemonPollJitter serpent.Duration `json:"daemon_poll_jitter" typescript:",notnull"`
|
||||
ForceCancelInterval serpent.Duration `json:"force_cancel_interval" typescript:",notnull"`
|
||||
DaemonPSK serpent.String `json:"daemon_psk" typescript:",notnull"`
|
||||
// DisableModuleCache disables the reuse of Terraform modules cached at
|
||||
// template import for every template in the deployment. Templates cannot
|
||||
// opt back in.
|
||||
DisableModuleCache serpent.Bool `json:"disable_module_cache" typescript:",notnull"`
|
||||
}
|
||||
|
||||
type RateLimitConfig struct {
|
||||
@@ -3215,6 +3219,16 @@ communicating directly.`,
|
||||
YAML: "forceCancelInterval",
|
||||
Annotations: serpent.Annotations{}.Mark(annotationFormatDuration, "true"),
|
||||
},
|
||||
{
|
||||
Name: "Disable Terraform Module Cache",
|
||||
Description: "Disable the reuse of Terraform modules cached at template import for all templates. Modules are re-downloaded on every workspace build. Individual templates cannot opt back in.",
|
||||
Flag: "provisioner-disable-module-cache",
|
||||
Env: "CODER_PROVISIONER_DISABLE_MODULE_CACHE",
|
||||
Default: "false",
|
||||
Value: &c.Provisioner.DisableModuleCache,
|
||||
Group: &deploymentGroupProvisioning,
|
||||
YAML: "disableModuleCache",
|
||||
},
|
||||
{
|
||||
Name: "Provisioner Daemon Pre-shared Key (PSK)",
|
||||
Description: "Pre-shared key to authenticate external provisioner daemons to Coder server.",
|
||||
|
||||
+22
-2
@@ -71,9 +71,15 @@ type Template struct {
|
||||
AgentsAllowed bool `json:"agents_allowed"`
|
||||
|
||||
// DisableModuleCache disables the use of cached Terraform modules during
|
||||
// provisioning.
|
||||
// provisioning for this template. It is read-only while
|
||||
// ModuleCacheDisabledByDeployment is true.
|
||||
DisableModuleCache bool `json:"disable_module_cache"`
|
||||
|
||||
// ModuleCacheDisabledByDeployment reports that the deployment disables the
|
||||
// Terraform module cache for every template. Templates cannot opt back in,
|
||||
// so the effective state is disabled regardless of DisableModuleCache.
|
||||
ModuleCacheDisabledByDeployment bool `json:"module_cache_disabled_by_deployment"`
|
||||
|
||||
// AllowWorkspaceRenames permits users to rename workspaces built from this
|
||||
// template. Renaming can be destructive for templates whose Terraform
|
||||
// references the workspace name.
|
||||
@@ -183,6 +189,19 @@ type ArchiveTemplateVersionsResponse struct {
|
||||
ArchivedIDs []uuid.UUID `json:"archived_ids"`
|
||||
}
|
||||
|
||||
// ModuleCacheDisabled reports whether cached Terraform modules must be withheld
|
||||
// from provisioner jobs. The deployment-wide setting wins: a template cannot opt
|
||||
// back into the cache once the deployment disables it.
|
||||
func ModuleCacheDisabled(dv *DeploymentValues, templateDisableModuleCache bool) bool {
|
||||
return templateDisableModuleCache || ModuleCacheDisabledByDeployment(dv)
|
||||
}
|
||||
|
||||
// ModuleCacheDisabledByDeployment reports whether the deployment disables the
|
||||
// Terraform module cache for every template.
|
||||
func ModuleCacheDisabledByDeployment(dv *DeploymentValues) bool {
|
||||
return dv != nil && dv.Provisioner.DisableModuleCache.Value()
|
||||
}
|
||||
|
||||
type TemplateRole string
|
||||
|
||||
const (
|
||||
@@ -286,7 +305,8 @@ type UpdateTemplateMeta struct {
|
||||
// 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.
|
||||
// provisioning. It is ignored while the deployment disables the module
|
||||
// cache for all templates. It is recommended not to disable this.
|
||||
DisableModuleCache *bool `json:"disable_module_cache,omitempty"`
|
||||
// AgentsAllowed controls whether Coder Agents can create workspaces using
|
||||
// this template. If omitted, the current value is preserved.
|
||||
|
||||
@@ -0,0 +1,62 @@
|
||||
package codersdk_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/coder/coder/v2/codersdk"
|
||||
"github.com/coder/serpent"
|
||||
)
|
||||
|
||||
func TestModuleCacheDisabled(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
cases := []struct {
|
||||
name string
|
||||
deploymentValues *codersdk.DeploymentValues
|
||||
templateDisabled bool
|
||||
expected bool
|
||||
}{
|
||||
{
|
||||
name: "NoDeploymentValues",
|
||||
deploymentValues: nil,
|
||||
expected: false,
|
||||
},
|
||||
{
|
||||
name: "NoDeploymentValuesTemplateDisabled",
|
||||
deploymentValues: nil,
|
||||
templateDisabled: true,
|
||||
expected: true,
|
||||
},
|
||||
{
|
||||
name: "BothEnabled",
|
||||
deploymentValues: deploymentValuesWithModuleCacheDisabled(false),
|
||||
expected: false,
|
||||
},
|
||||
{
|
||||
name: "TemplateDisabled",
|
||||
deploymentValues: deploymentValuesWithModuleCacheDisabled(false),
|
||||
templateDisabled: true,
|
||||
expected: true,
|
||||
},
|
||||
{
|
||||
// The deployment cannot be overridden by a template.
|
||||
name: "DeploymentDisabled",
|
||||
deploymentValues: deploymentValuesWithModuleCacheDisabled(true),
|
||||
expected: true,
|
||||
},
|
||||
}
|
||||
for _, tc := range cases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
require.Equal(t, tc.expected, codersdk.ModuleCacheDisabled(tc.deploymentValues, tc.templateDisabled))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func deploymentValuesWithModuleCacheDisabled(disabled bool) *codersdk.DeploymentValues {
|
||||
dv := &codersdk.DeploymentValues{}
|
||||
dv.Provisioner.DisableModuleCache = serpent.Bool(disabled)
|
||||
return dv
|
||||
}
|
||||
@@ -1618,6 +1618,15 @@ OIDC issuer urls must match in the request, the id_token 'iss' claim, and in the
|
||||
|
||||
Tune the behavior of the provisioner, which is responsible for creating, updating, and deleting workspace resources.
|
||||
|
||||
### Disable Terraform module cache
|
||||
|
||||
Disable the reuse of Terraform modules cached at template import for all templates. Modules are re-downloaded on every workspace build. Individual templates cannot opt back in.
|
||||
|
||||
- Environment variable: `CODER_PROVISIONER_DISABLE_MODULE_CACHE`
|
||||
- CLI flag: [`--provisioner-disable-module-cache`](../../reference/cli/server.md#--provisioner-disable-module-cache)
|
||||
- YAML key: `provisioning.disableModuleCache`
|
||||
- Default value: `false`
|
||||
|
||||
### Force cancel interval
|
||||
|
||||
Time to force cancel provisioning tasks that are stuck.
|
||||
|
||||
Generated
+1
@@ -533,6 +533,7 @@ curl -X GET http://coder-server:8080/api/v2/deployment/config \
|
||||
"string"
|
||||
],
|
||||
"daemons": 0,
|
||||
"disable_module_cache": true,
|
||||
"force_cancel_interval": 0
|
||||
},
|
||||
"proxy_health_status_interval": 0,
|
||||
|
||||
Generated
+56
-49
@@ -7589,6 +7589,7 @@ CreateWorkspaceRequest provides options for creating a new workspace. Only one o
|
||||
"string"
|
||||
],
|
||||
"daemons": 0,
|
||||
"disable_module_cache": true,
|
||||
"force_cancel_interval": 0
|
||||
},
|
||||
"proxy_health_status_interval": 0,
|
||||
@@ -8195,6 +8196,7 @@ CreateWorkspaceRequest provides options for creating a new workspace. Only one o
|
||||
"string"
|
||||
],
|
||||
"daemons": 0,
|
||||
"disable_module_cache": true,
|
||||
"force_cancel_interval": 0
|
||||
},
|
||||
"proxy_health_status_interval": 0,
|
||||
@@ -12346,20 +12348,22 @@ Git clone makes use of this by parsing the URL from: 'Username for "https://gith
|
||||
"string"
|
||||
],
|
||||
"daemons": 0,
|
||||
"disable_module_cache": true,
|
||||
"force_cancel_interval": 0
|
||||
}
|
||||
```
|
||||
|
||||
### Properties
|
||||
|
||||
| Name | Type | Required | Restrictions | Description |
|
||||
|-------------------------|-----------------|----------|--------------|-----------------------------------------------------------|
|
||||
| `daemon_poll_interval` | integer | false | | |
|
||||
| `daemon_poll_jitter` | integer | false | | |
|
||||
| `daemon_psk` | string | false | | |
|
||||
| `daemon_types` | array of string | false | | |
|
||||
| `daemons` | integer | false | | Daemons is the number of built-in terraform provisioners. |
|
||||
| `force_cancel_interval` | integer | false | | |
|
||||
| Name | Type | Required | Restrictions | Description |
|
||||
|-------------------------|-----------------|----------|--------------|------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| `daemon_poll_interval` | integer | false | | |
|
||||
| `daemon_poll_jitter` | integer | false | | |
|
||||
| `daemon_psk` | string | false | | |
|
||||
| `daemon_types` | array of string | false | | |
|
||||
| `daemons` | integer | false | | Daemons is the number of built-in terraform provisioners. |
|
||||
| `disable_module_cache` | boolean | false | | Disable module cache disables the reuse of Terraform modules cached at template import for every template in the deployment. Templates cannot opt back in. |
|
||||
| `force_cancel_interval` | integer | false | | |
|
||||
|
||||
## codersdk.ProvisionerDaemon
|
||||
|
||||
@@ -13637,6 +13641,7 @@ Git clone makes use of this by parsing the URL from: 'Username for "https://gith
|
||||
"icon": "string",
|
||||
"id": "497f6eca-6276-4993-bfeb-53cbbbba6f08",
|
||||
"max_port_share_level": "owner",
|
||||
"module_cache_disabled_by_deployment": true,
|
||||
"name": "string",
|
||||
"organization_display_name": "string",
|
||||
"organization_icon": "string",
|
||||
@@ -13654,46 +13659,47 @@ Git clone makes use of this by parsing the URL from: 'Username for "https://gith
|
||||
|
||||
### Properties
|
||||
|
||||
| Name | Type | Required | Restrictions | Description |
|
||||
|------------------------------------|--------------------------------------------------------------------------------|----------|--------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| `active_user_count` | integer | false | | Active user count is set to -1 when loading. |
|
||||
| `active_version_id` | string | 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](#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 | | |
|
||||
| `cors_behavior` | [codersdk.CORSBehavior](#codersdkcorsbehavior) | false | | |
|
||||
| `created_at` | string | false | | |
|
||||
| `created_by_id` | string | false | | |
|
||||
| `created_by_name` | string | false | | |
|
||||
| `default_ttl_ms` | integer | false | | |
|
||||
| `deleted` | boolean | false | | |
|
||||
| `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 | | |
|
||||
| `id` | string | false | | |
|
||||
| `max_port_share_level` | [codersdk.WorkspaceAgentPortShareLevel](#codersdkworkspaceagentportsharelevel) | false | | |
|
||||
| `name` | string | false | | |
|
||||
| `organization_display_name` | string | false | | |
|
||||
| `organization_icon` | string | false | | |
|
||||
| `organization_id` | string | false | | |
|
||||
| `organization_name` | string | false | | |
|
||||
| `provisioner` | string | false | | |
|
||||
| `require_active_version` | boolean | false | | Require active version mandates that workspaces are built with the active template version. |
|
||||
| `time_til_autostop_notify_ms` | integer | false | | Time til autostop notify ms is the duration before the workspace's autostop deadline at which a reminder notification is sent. 0 disables the notification. |
|
||||
| `time_til_dormant_autodelete_ms` | integer | false | | |
|
||||
| `time_til_dormant_ms` | integer | false | | |
|
||||
| `updated_at` | string | false | | |
|
||||
| `use_classic_parameter_flow` | boolean | false | | |
|
||||
| Name | Type | Required | Restrictions | Description |
|
||||
|---------------------------------------|--------------------------------------------------------------------------------|----------|--------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| `active_user_count` | integer | false | | Active user count is set to -1 when loading. |
|
||||
| `active_version_id` | string | 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](#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 | | |
|
||||
| `cors_behavior` | [codersdk.CORSBehavior](#codersdkcorsbehavior) | false | | |
|
||||
| `created_at` | string | false | | |
|
||||
| `created_by_id` | string | false | | |
|
||||
| `created_by_name` | string | false | | |
|
||||
| `default_ttl_ms` | integer | false | | |
|
||||
| `deleted` | boolean | false | | |
|
||||
| `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 for this template. It is read-only while ModuleCacheDisabledByDeployment is true. |
|
||||
| `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 | | |
|
||||
| `id` | string | false | | |
|
||||
| `max_port_share_level` | [codersdk.WorkspaceAgentPortShareLevel](#codersdkworkspaceagentportsharelevel) | false | | |
|
||||
| `module_cache_disabled_by_deployment` | boolean | false | | Module cache disabled by deployment reports that the deployment disables the Terraform module cache for every template. Templates cannot opt back in, so the effective state is disabled regardless of DisableModuleCache. |
|
||||
| `name` | string | false | | |
|
||||
| `organization_display_name` | string | false | | |
|
||||
| `organization_icon` | string | false | | |
|
||||
| `organization_id` | string | false | | |
|
||||
| `organization_name` | string | false | | |
|
||||
| `provisioner` | string | false | | |
|
||||
| `require_active_version` | boolean | false | | Require active version mandates that workspaces are built with the active template version. |
|
||||
| `time_til_autostop_notify_ms` | integer | false | | Time til autostop notify ms is the duration before the workspace's autostop deadline at which a reminder notification is sent. 0 disables the notification. |
|
||||
| `time_til_dormant_autodelete_ms` | integer | false | | |
|
||||
| `time_til_dormant_ms` | integer | false | | |
|
||||
| `updated_at` | string | false | | |
|
||||
| `use_classic_parameter_flow` | boolean | false | | |
|
||||
|
||||
#### Enumerated Values
|
||||
|
||||
@@ -14144,6 +14150,7 @@ Restarts will only happen on weekdays in this list on weeks which line up with W
|
||||
"icon": "string",
|
||||
"id": "497f6eca-6276-4993-bfeb-53cbbbba6f08",
|
||||
"max_port_share_level": "owner",
|
||||
"module_cache_disabled_by_deployment": true,
|
||||
"name": "string",
|
||||
"organization_display_name": "string",
|
||||
"organization_icon": "string",
|
||||
@@ -15779,7 +15786,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. |
|
||||
| `disable_module_cache` | boolean | false | | Disable module cache disables the using of cached Terraform modules during provisioning. It is ignored while the deployment disables the module cache for all templates. It is recommended not to disable this. |
|
||||
| `display_name` | string | false | | |
|
||||
| `failure_ttl_ms` | integer | false | | |
|
||||
| `icon` | string | false | | |
|
||||
|
||||
Generated
+1
@@ -216,6 +216,7 @@ curl -X POST http://coder-server:8080/api/v2/templatebuilder/compose/template \
|
||||
"icon": "string",
|
||||
"id": "497f6eca-6276-4993-bfeb-53cbbbba6f08",
|
||||
"max_port_share_level": "owner",
|
||||
"module_cache_disabled_by_deployment": true,
|
||||
"name": "string",
|
||||
"organization_display_name": "string",
|
||||
"organization_icon": "string",
|
||||
|
||||
Generated
+10
-2
@@ -79,6 +79,7 @@ To include deprecated templates, specify `deprecated:true` in the search query.
|
||||
"icon": "string",
|
||||
"id": "497f6eca-6276-4993-bfeb-53cbbbba6f08",
|
||||
"max_port_share_level": "owner",
|
||||
"module_cache_disabled_by_deployment": true,
|
||||
"name": "string",
|
||||
"organization_display_name": "string",
|
||||
"organization_icon": "string",
|
||||
@@ -135,12 +136,13 @@ 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.|
|
||||
|`» disable_module_cache`|boolean|false||Disable module cache disables the use of cached Terraform modules during provisioning for this template. It is read-only while ModuleCacheDisabledByDeployment is true.|
|
||||
|`» 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|||
|
||||
|`» id`|string(uuid)|false|||
|
||||
|`» max_port_share_level`|[codersdk.WorkspaceAgentPortShareLevel](schemas.md#codersdkworkspaceagentportsharelevel)|false|||
|
||||
|`» module_cache_disabled_by_deployment`|boolean|false||Module cache disabled by deployment reports that the deployment disables the Terraform module cache for every template. Templates cannot opt back in, so the effective state is disabled regardless of DisableModuleCache.|
|
||||
|`» name`|string|false|||
|
||||
|`» organization_display_name`|string|false|||
|
||||
|`» organization_icon`|string|false|||
|
||||
@@ -274,6 +276,7 @@ curl -X POST http://coder-server:8080/api/v2/organizations/{organization}/templa
|
||||
"icon": "string",
|
||||
"id": "497f6eca-6276-4993-bfeb-53cbbbba6f08",
|
||||
"max_port_share_level": "owner",
|
||||
"module_cache_disabled_by_deployment": true,
|
||||
"name": "string",
|
||||
"organization_display_name": "string",
|
||||
"organization_icon": "string",
|
||||
@@ -429,6 +432,7 @@ curl -X GET http://coder-server:8080/api/v2/organizations/{organization}/templat
|
||||
"icon": "string",
|
||||
"id": "497f6eca-6276-4993-bfeb-53cbbbba6f08",
|
||||
"max_port_share_level": "owner",
|
||||
"module_cache_disabled_by_deployment": true,
|
||||
"name": "string",
|
||||
"organization_display_name": "string",
|
||||
"organization_icon": "string",
|
||||
@@ -854,6 +858,7 @@ To include deprecated templates, specify `deprecated:true` in the search query.
|
||||
"icon": "string",
|
||||
"id": "497f6eca-6276-4993-bfeb-53cbbbba6f08",
|
||||
"max_port_share_level": "owner",
|
||||
"module_cache_disabled_by_deployment": true,
|
||||
"name": "string",
|
||||
"organization_display_name": "string",
|
||||
"organization_icon": "string",
|
||||
@@ -910,12 +915,13 @@ 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.|
|
||||
|`» disable_module_cache`|boolean|false||Disable module cache disables the use of cached Terraform modules during provisioning for this template. It is read-only while ModuleCacheDisabledByDeployment is true.|
|
||||
|`» 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|||
|
||||
|`» id`|string(uuid)|false|||
|
||||
|`» max_port_share_level`|[codersdk.WorkspaceAgentPortShareLevel](schemas.md#codersdkworkspaceagentportsharelevel)|false|||
|
||||
|`» module_cache_disabled_by_deployment`|boolean|false||Module cache disabled by deployment reports that the deployment disables the Terraform module cache for every template. Templates cannot opt back in, so the effective state is disabled regardless of DisableModuleCache.|
|
||||
|`» name`|string|false|||
|
||||
|`» organization_display_name`|string|false|||
|
||||
|`» organization_icon`|string|false|||
|
||||
@@ -1064,6 +1070,7 @@ curl -X GET http://coder-server:8080/api/v2/templates/{template} \
|
||||
"icon": "string",
|
||||
"id": "497f6eca-6276-4993-bfeb-53cbbbba6f08",
|
||||
"max_port_share_level": "owner",
|
||||
"module_cache_disabled_by_deployment": true,
|
||||
"name": "string",
|
||||
"organization_display_name": "string",
|
||||
"organization_icon": "string",
|
||||
@@ -1244,6 +1251,7 @@ curl -X PATCH http://coder-server:8080/api/v2/templates/{template} \
|
||||
"icon": "string",
|
||||
"id": "497f6eca-6276-4993-bfeb-53cbbbba6f08",
|
||||
"max_port_share_level": "owner",
|
||||
"module_cache_disabled_by_deployment": true,
|
||||
"name": "string",
|
||||
"organization_display_name": "string",
|
||||
"organization_icon": "string",
|
||||
|
||||
Generated
+11
@@ -826,6 +826,17 @@ Deprecated and ignored.
|
||||
|
||||
Time to force cancel provisioning tasks that are stuck.
|
||||
|
||||
### --provisioner-disable-module-cache
|
||||
|
||||
| | |
|
||||
|-------------|------------------------------------------------------|
|
||||
| Type | <code>bool</code> |
|
||||
| Environment | <code>$CODER_PROVISIONER_DISABLE_MODULE_CACHE</code> |
|
||||
| YAML | <code>provisioning.disableModuleCache</code> |
|
||||
| Default | <code>false</code> |
|
||||
|
||||
Disable the reuse of Terraform modules cached at template import for all templates. Modules are re-downloaded on every workspace build. Individual templates cannot opt back in.
|
||||
|
||||
### --provisioner-daemon-psk
|
||||
|
||||
| | |
|
||||
|
||||
@@ -804,6 +804,11 @@ PROVISIONING OPTIONS:
|
||||
Tune the behavior of the provisioner, which is responsible for creating,
|
||||
updating, and deleting workspace resources.
|
||||
|
||||
--provisioner-disable-module-cache bool, $CODER_PROVISIONER_DISABLE_MODULE_CACHE (default: false)
|
||||
Disable the reuse of Terraform modules cached at template import for
|
||||
all templates. Modules are re-downloaded on every workspace build.
|
||||
Individual templates cannot opt back in.
|
||||
|
||||
--provisioner-force-cancel-interval duration, $CODER_PROVISIONER_FORCE_CANCEL_INTERVAL (default: 10m0s)
|
||||
Time to force cancel provisioning tasks that are stuck.
|
||||
|
||||
|
||||
Generated
+16
-2
@@ -7533,6 +7533,12 @@ export interface ProvisionerConfig {
|
||||
readonly daemon_poll_jitter: number;
|
||||
readonly force_cancel_interval: number;
|
||||
readonly daemon_psk: string;
|
||||
/**
|
||||
* DisableModuleCache disables the reuse of Terraform modules cached at
|
||||
* template import for every template in the deployment. Templates cannot
|
||||
* opt back in.
|
||||
*/
|
||||
readonly disable_module_cache: boolean;
|
||||
}
|
||||
|
||||
// From codersdk/provisionerdaemons.go
|
||||
@@ -8936,9 +8942,16 @@ export interface Template {
|
||||
readonly agents_allowed: boolean;
|
||||
/**
|
||||
* DisableModuleCache disables the use of cached Terraform modules during
|
||||
* provisioning.
|
||||
* provisioning for this template. It is read-only while
|
||||
* ModuleCacheDisabledByDeployment is true.
|
||||
*/
|
||||
readonly disable_module_cache: boolean;
|
||||
/**
|
||||
* ModuleCacheDisabledByDeployment reports that the deployment disables the
|
||||
* Terraform module cache for every template. Templates cannot opt back in,
|
||||
* so the effective state is disabled regardless of DisableModuleCache.
|
||||
*/
|
||||
readonly module_cache_disabled_by_deployment: boolean;
|
||||
/**
|
||||
* AllowWorkspaceRenames permits users to rename workspaces built from this
|
||||
* template. Renaming can be destructive for templates whose Terraform
|
||||
@@ -9945,7 +9958,8 @@ export interface UpdateTemplateMeta {
|
||||
readonly use_classic_parameter_flow?: boolean;
|
||||
/**
|
||||
* DisableModuleCache disables the using of cached Terraform modules during
|
||||
* provisioning. It is recommended not to disable this.
|
||||
* provisioning. It is ignored while the deployment disables the module
|
||||
* cache for all templates. It is recommended not to disable this.
|
||||
*/
|
||||
readonly disable_module_cache?: boolean;
|
||||
/**
|
||||
|
||||
+39
@@ -9,6 +9,11 @@ const renameCheckbox = () =>
|
||||
name: /allow users to rename their workspaces/i,
|
||||
});
|
||||
|
||||
const moduleCacheCheckbox = () =>
|
||||
screen.getByRole("checkbox", {
|
||||
name: /disable terraform module caching/i,
|
||||
});
|
||||
|
||||
const renderForm = (
|
||||
overrides: Partial<Parameters<typeof TemplateSettingsForm>[0]> = {},
|
||||
) => {
|
||||
@@ -79,4 +84,38 @@ describe("TemplateSettingsForm", () => {
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe("disable_module_cache", () => {
|
||||
it("submits the new value when toggled on", async () => {
|
||||
const user = userEvent.setup();
|
||||
const { onSubmit } = renderForm();
|
||||
|
||||
await user.click(moduleCacheCheckbox());
|
||||
await user.click(screen.getByRole("button", { name: /save/i }));
|
||||
|
||||
await waitFor(() => {
|
||||
expect(onSubmit).toHaveBeenCalledWith(
|
||||
expect.objectContaining({ disable_module_cache: true }),
|
||||
expect.anything(),
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
it("is checked and read-only when the deployment disables caching", async () => {
|
||||
const user = userEvent.setup();
|
||||
renderForm({
|
||||
template: {
|
||||
...MockTemplate,
|
||||
module_cache_disabled_by_deployment: true,
|
||||
},
|
||||
});
|
||||
|
||||
const checkbox = moduleCacheCheckbox();
|
||||
expect(checkbox).toBeChecked();
|
||||
expect(checkbox).toBeDisabled();
|
||||
|
||||
await user.click(checkbox);
|
||||
expect(checkbox).toBeChecked();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
+42
-9
@@ -1,5 +1,6 @@
|
||||
import { cn } from "cn";
|
||||
import { type FormikTouched, useFormik } from "formik";
|
||||
import { LockIcon, TriangleAlertIcon } from "lucide-react";
|
||||
import type { FC } from "react";
|
||||
import * as Yup from "yup";
|
||||
import {
|
||||
@@ -8,6 +9,7 @@ import {
|
||||
type UpdateTemplateMeta,
|
||||
WorkspaceAppSharingLevels,
|
||||
} from "#/api/typesGenerated";
|
||||
import { Badge } from "#/components/Badge/Badge";
|
||||
import { PremiumBadge } from "#/components/Badge/PresetBadges";
|
||||
import { Button } from "#/components/Button/Button";
|
||||
import { Checkbox } from "#/components/Checkbox/Checkbox";
|
||||
@@ -124,6 +126,8 @@ export const TemplateSettingsForm: FC<TemplateSettingsForm> = ({
|
||||
helperText: "Use Passthru to bypass Coder's built-in CORS protection.",
|
||||
});
|
||||
const corsBehaviorHelperId = `${corsBehaviorField.id}-helper`;
|
||||
const moduleCacheDisabledByDeployment =
|
||||
template.module_cache_disabled_by_deployment;
|
||||
|
||||
return (
|
||||
<HorizontalForm
|
||||
@@ -291,22 +295,51 @@ export const TemplateSettingsForm: FC<TemplateSettingsForm> = ({
|
||||
<Checkbox
|
||||
id="disable_module_cache"
|
||||
name="disable_module_cache"
|
||||
checked={form.values.disable_module_cache}
|
||||
checked={
|
||||
moduleCacheDisabledByDeployment ||
|
||||
form.values.disable_module_cache
|
||||
}
|
||||
onCheckedChange={(checked) => {
|
||||
form.setFieldValue("disable_module_cache", checked === true);
|
||||
}}
|
||||
disabled={isSubmitting}
|
||||
disabled={isSubmitting || moduleCacheDisabledByDeployment}
|
||||
/>
|
||||
<Label htmlFor="disable_module_cache">
|
||||
<StackLabel>
|
||||
Disable Terraform module caching
|
||||
<span className="flex flex-row gap-2 items-center">
|
||||
Disable Terraform module caching
|
||||
{moduleCacheDisabledByDeployment && (
|
||||
<Badge variant="warning" size="sm" svgSize="sm">
|
||||
<LockIcon />
|
||||
Set deployment wide
|
||||
</Badge>
|
||||
)}
|
||||
</span>
|
||||
<StackLabelHelperText>
|
||||
When checked, Terraform modules are re-downloaded for each
|
||||
workspace build instead of using cached versions.{" "}
|
||||
<strong>
|
||||
Warning: This makes workspace builds less predictable and is
|
||||
not recommended for production templates.
|
||||
</strong>
|
||||
<span>
|
||||
When checked, Terraform modules are re-downloaded for each
|
||||
workspace build instead of using cached versions.{" "}
|
||||
<strong>
|
||||
Warning: This makes workspace builds less predictable and
|
||||
is not recommended for production templates.
|
||||
</strong>
|
||||
</span>
|
||||
|
||||
{moduleCacheDisabledByDeployment && (
|
||||
<span className="flex flex-row gap-2 items-start mt-4">
|
||||
<TriangleAlertIcon className="size-icon-sm shrink-0 mt-0.5 text-content-warning" />
|
||||
<span>
|
||||
<strong>
|
||||
This setting is locked because module caching is
|
||||
disabled for every template by the deployment option{" "}
|
||||
<code>CODER_PROVISIONER_DISABLE_MODULE_CACHE</code>.
|
||||
</strong>{" "}
|
||||
This template cannot turn caching back on, so modules
|
||||
are re-downloaded on each workspace build. Ask a
|
||||
deployment admin to clear that option to change it here.
|
||||
</span>
|
||||
</span>
|
||||
)}
|
||||
</StackLabelHelperText>
|
||||
</StackLabel>
|
||||
</Label>
|
||||
|
||||
+9
@@ -30,6 +30,15 @@ export const AgentsNotAllowed: Story = {
|
||||
},
|
||||
};
|
||||
|
||||
export const ModuleCacheDisabledByDeployment: Story = {
|
||||
args: {
|
||||
template: {
|
||||
...MockTemplate,
|
||||
module_cache_disabled_by_deployment: true,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
export const SaveTemplateSettingsError: Story = {
|
||||
args: {
|
||||
submitError: mockApiError({
|
||||
|
||||
@@ -976,6 +976,7 @@ export const MockTemplate: TypesGen.Template = {
|
||||
use_classic_parameter_flow: false,
|
||||
cors_behavior: "simple",
|
||||
disable_module_cache: false,
|
||||
module_cache_disabled_by_deployment: false,
|
||||
allow_workspace_renames: false,
|
||||
};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user