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:
Steven Masley
2026-09-11 18:13:16 -05:00
committed by GitHub
parent 132a17c5f1
commit 940f988cfb
25 changed files with 515 additions and 85 deletions
+5
View File
@@ -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
View File
@@ -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.
+10 -2
View File
@@ -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": {
+10 -2
View File
@@ -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
View File
@@ -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,
}
}
+11 -1
View File
@@ -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,
+41 -3
View File
@@ -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)
}
})
}
}
+28
View File
@@ -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()
+14
View File
@@ -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
View File
@@ -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.
+62
View File
@@ -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.
+1
View File
@@ -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,
+56 -49
View File
@@ -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 | | |
+1
View File
@@ -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",
+10 -2
View File
@@ -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",
+11
View File
@@ -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
| | |
+5
View File
@@ -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.
+16 -2
View File
@@ -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;
/**
@@ -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();
});
});
});
@@ -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>
@@ -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({
+1
View File
@@ -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,
};