mirror of
https://github.com/coder/coder.git
synced 2026-10-16 06:37:58 +08:00
feat!: add a hidden flag to disable dynamic parameter closure evaluation (#29356)
**What changed** Dynamic parameter rendering now evaluates only the coder parameter blocks and what they reference. Other root-module resources are skipped. Parameter, preset, and tag values are unchanged; optimizing rendering on large templates. **How to revert** Set `CODER_DYNAMIC_PARAMETERS_FULL_EVALUATION=true` (or `--dynamic-parameters-full-evaluation`) on `coder server` to restore full evaluation. The flag is hidden and needs no restart of workspaces. If you need it, please [open an issue](https://github.com/coder/coder/issues/new) with the template so we can fix the root cause. **Troubleshooting** Symptoms would be a parameter default, option list, preset value, or workspace tag that shows as unknown or empty after upgrading, when it was populated before. Confirm by setting the flag above; if the value comes back, report it. Templates written in `.tf.json` are not affected (they always use full evaluation). **Why** Rendering re-ran the entire template graph on every parameter change in the workspace form, which dominated latency on large templates. Resources a workspace would create cannot feed a parameter, so evaluating them is wasted work. The optimization lives in coder/preview#226 (coder/trivy#74); this PR adds the deployment-level escape hatch. --- - `CODER_DYNAMIC_PARAMETERS_FULL_EVALUATION` / `--dynamic-parameters-full-evaluation` (hidden, default `false`) on `DeploymentValues`. - `dynamicparameters.PreviewOptions(dv)` maps it to `preview.OptionFullEvaluation()`; `dynamicparameters.WithPreviewOptions` threads it through the loader to `preview.Preview`. - Wired at the three render paths that hold deployment values: the template version tags extractor, the dynamic parameters websocket, and `wsbuilder` builds created from `coderd` (which call `.DeploymentValues(...)`). Not covered: the autobuild lifecycle executor and the enterprise prebuilds reconciler construct `wsbuilder` without deployment values, so builds started from those paths use preview's default regardless of the flag. Those only re-render for builds, not the keystroke websocket path the optimization targets. Prepared with Coder Agents assistance.
This commit is contained in:
+5
@@ -505,6 +505,11 @@ provisioning:
|
||||
# templates cannot opt back in.
|
||||
# (default: false, type: bool)
|
||||
disableModuleCache: false
|
||||
# Evaluate every resource in a template when rendering dynamic parameters, instead
|
||||
# of only the parameter, preset, and tag blocks and what they reference. Slower,
|
||||
# and only needed if a template renders incorrectly with the default.
|
||||
# (default: false, type: bool)
|
||||
dynamicParametersFullEvaluation: 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
+3
@@ -23154,6 +23154,9 @@ const docTemplate = `{
|
||||
"docs_url": {
|
||||
"$ref": "#/definitions/serpent.URL"
|
||||
},
|
||||
"dynamic_parameters_full_evaluation": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"enable_authz_recording": {
|
||||
"type": "boolean"
|
||||
},
|
||||
|
||||
Generated
+3
@@ -21047,6 +21047,9 @@
|
||||
"docs_url": {
|
||||
"$ref": "#/definitions/serpent.URL"
|
||||
},
|
||||
"dynamic_parameters_full_evaluation": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"enable_authz_recording": {
|
||||
"type": "boolean"
|
||||
},
|
||||
|
||||
@@ -17,6 +17,7 @@ import (
|
||||
"github.com/coder/coder/v2/coderd/database"
|
||||
"github.com/coder/coder/v2/coderd/database/dbauthz"
|
||||
"github.com/coder/coder/v2/coderd/files"
|
||||
"github.com/coder/coder/v2/codersdk"
|
||||
"github.com/coder/preview"
|
||||
previewtypes "github.com/coder/preview/types"
|
||||
)
|
||||
@@ -45,6 +46,9 @@ type loader struct {
|
||||
job *database.ProvisionerJob
|
||||
terraformValues *database.TemplateVersionTerraformValue
|
||||
templateVariableValues *[]database.TemplateVersionVariable
|
||||
|
||||
// previewOptions are passed through to every preview.Preview call.
|
||||
previewOptions []preview.Option
|
||||
}
|
||||
|
||||
// Prepare is the entrypoint for this package. It loads the necessary objects &
|
||||
@@ -90,6 +94,30 @@ func WithTerraformValues(values database.TemplateVersionTerraformValue) func(r *
|
||||
}
|
||||
}
|
||||
|
||||
// WithPreviewOptions passes options through to preview.Preview. See
|
||||
// PreviewOptions for the options implied by the deployment configuration.
|
||||
func WithPreviewOptions(opts ...preview.Option) func(r *loader) {
|
||||
return func(r *loader) {
|
||||
r.previewOptions = append(r.previewOptions, opts...)
|
||||
}
|
||||
}
|
||||
|
||||
// PreviewOptions returns the preview options implied by the deployment
|
||||
// configuration. A nil configuration yields preview's defaults.
|
||||
func PreviewOptions(dv *codersdk.DeploymentValues) []preview.Option {
|
||||
if dv == nil {
|
||||
return nil
|
||||
}
|
||||
var opts []preview.Option
|
||||
if dv.DynamicParametersFullEvaluation.Value() {
|
||||
// Escape hatch for preview's resource closure optimization: evaluate
|
||||
// every resource rather than only what parameters, presets, and tags
|
||||
// reference.
|
||||
opts = append(opts, preview.OptionFullEvaluation())
|
||||
}
|
||||
return opts
|
||||
}
|
||||
|
||||
func (r *loader) loadData(ctx context.Context, db database.Store) error {
|
||||
if r.templateVersion == nil {
|
||||
tv, err := db.GetTemplateVersionByID(ctx, r.templateVersionID)
|
||||
@@ -257,7 +285,7 @@ func (r *dynamicRenderer) Render(ctx context.Context, ownerID uuid.UUID, values
|
||||
Logger: slog.New(slog.DiscardHandler),
|
||||
}
|
||||
|
||||
return preview.Preview(ctx, input, r.templateFS)
|
||||
return preview.Preview(ctx, input, r.templateFS, r.data.previewOptions...)
|
||||
}
|
||||
|
||||
func (r *dynamicRenderer) getWorkspaceOwnerData(ctx context.Context, ownerID uuid.UUID) error {
|
||||
|
||||
@@ -6,6 +6,7 @@ import (
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/coder/coder/v2/coderd/dynamicparameters"
|
||||
"github.com/coder/coder/v2/codersdk"
|
||||
)
|
||||
|
||||
func TestProvisionerVersionSupportsDynamicParameters(t *testing.T) {
|
||||
@@ -33,3 +34,25 @@ func TestProvisionerVersionSupportsDynamicParameters(t *testing.T) {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestPreviewOptions(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
t.Run("NilConfig", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
require.Empty(t, dynamicparameters.PreviewOptions(nil))
|
||||
})
|
||||
|
||||
t.Run("Default", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
dv := &codersdk.DeploymentValues{}
|
||||
require.Empty(t, dynamicparameters.PreviewOptions(dv))
|
||||
})
|
||||
|
||||
t.Run("FullEvaluation", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
dv := &codersdk.DeploymentValues{}
|
||||
require.NoError(t, dv.DynamicParametersFullEvaluation.Set("true"))
|
||||
require.Len(t, dynamicparameters.PreviewOptions(dv), 1)
|
||||
})
|
||||
}
|
||||
|
||||
@@ -82,6 +82,7 @@ func (api *API) templateVersionDynamicParameters(listen bool, initial codersdk.D
|
||||
|
||||
renderer, err := dynamicparameters.Prepare(ctx, api.Database, api.FileCache, templateVersion.ID,
|
||||
dynamicparameters.WithTemplateVersion(templateVersion),
|
||||
dynamicparameters.WithPreviewOptions(dynamicparameters.PreviewOptions(api.DeploymentValues)...),
|
||||
)
|
||||
if err != nil {
|
||||
if httpapi.Is404Error(err) {
|
||||
|
||||
@@ -1858,7 +1858,7 @@ func (api *API) dynamicTemplateVersionTags(ctx context.Context, rw http.Response
|
||||
Logger: stdslog.New(stdslog.DiscardHandler),
|
||||
TFVars: tfVarValues,
|
||||
}
|
||||
output, diags := preview.Preview(ctx, input, files)
|
||||
output, diags := preview.Preview(ctx, input, files, dynamicparameters.PreviewOptions(api.DeploymentValues)...)
|
||||
if output != nil {
|
||||
// ValidatePrebuilds iterates through the presets and validate their values. This
|
||||
// ensures the prebuild can actually succeed in a workspace build. The failure
|
||||
|
||||
@@ -728,6 +728,7 @@ func (b *Builder) getDynamicParameterRenderer() (dynamicparameters.Renderer, err
|
||||
dynamicparameters.WithProvisionerJob(*job),
|
||||
dynamicparameters.WithTerraformValues(*tfVals),
|
||||
dynamicparameters.WithTemplateVariableValues(variableValues),
|
||||
dynamicparameters.WithPreviewOptions(dynamicparameters.PreviewOptions(b.deploymentValues)...),
|
||||
)
|
||||
if err != nil {
|
||||
return nil, xerrors.Errorf("get template version renderer: %w", err)
|
||||
|
||||
@@ -738,6 +738,7 @@ type DeploymentValues struct {
|
||||
DisableUserSecretFilePath serpent.Bool `json:"disable_user_secret_file_path,omitempty" typescript:",notnull"`
|
||||
ProxyHealthStatusInterval serpent.Duration `json:"proxy_health_status_interval,omitempty" typescript:",notnull"`
|
||||
EnableTerraformDebugMode serpent.Bool `json:"enable_terraform_debug_mode,omitempty" typescript:",notnull"`
|
||||
DynamicParametersFullEvaluation serpent.Bool `json:"dynamic_parameters_full_evaluation,omitempty" typescript:",notnull"`
|
||||
UserQuietHoursSchedule UserQuietHoursScheduleConfig `json:"user_quiet_hours_schedule,omitempty" typescript:",notnull"`
|
||||
WebTerminalRenderer serpent.String `json:"web_terminal_renderer,omitempty" typescript:",notnull"`
|
||||
// Deprecated: Use the per-template allow_workspace_renames setting instead.
|
||||
@@ -3344,6 +3345,18 @@ communicating directly.`,
|
||||
Group: &deploymentGroupIntrospectionLogging,
|
||||
YAML: "enableTerraformDebugMode",
|
||||
},
|
||||
{
|
||||
Name: "Dynamic Parameters Full Evaluation",
|
||||
Description: "Evaluate every resource in a template when rendering dynamic parameters, " +
|
||||
"instead of only the parameter, preset, and tag blocks and what they reference. " +
|
||||
"Slower, and only needed if a template renders incorrectly with the default.",
|
||||
Flag: "dynamic-parameters-full-evaluation",
|
||||
Env: "CODER_DYNAMIC_PARAMETERS_FULL_EVALUATION",
|
||||
Default: "false",
|
||||
Value: &c.DynamicParametersFullEvaluation,
|
||||
Hidden: true,
|
||||
YAML: "dynamicParametersFullEvaluation",
|
||||
},
|
||||
{
|
||||
Name: "Additional CSP Policy",
|
||||
Description: "Coder configures a Content Security Policy (CSP) to protect against XSS attacks. " +
|
||||
|
||||
Generated
+1
@@ -302,6 +302,7 @@ curl -X GET http://coder-server:8080/api/v2/deployment/config \
|
||||
"scheme": "string",
|
||||
"user": {}
|
||||
},
|
||||
"dynamic_parameters_full_evaluation": true,
|
||||
"enable_authz_recording": true,
|
||||
"enable_terraform_debug_mode": true,
|
||||
"ephemeral_deployment": true,
|
||||
|
||||
Generated
+3
@@ -7384,6 +7384,7 @@ CreateWorkspaceRequest provides options for creating a new workspace. Only one o
|
||||
"scheme": "string",
|
||||
"user": {}
|
||||
},
|
||||
"dynamic_parameters_full_evaluation": true,
|
||||
"enable_authz_recording": true,
|
||||
"enable_terraform_debug_mode": true,
|
||||
"ephemeral_deployment": true,
|
||||
@@ -7994,6 +7995,7 @@ CreateWorkspaceRequest provides options for creating a new workspace. Only one o
|
||||
"scheme": "string",
|
||||
"user": {}
|
||||
},
|
||||
"dynamic_parameters_full_evaluation": true,
|
||||
"enable_authz_recording": true,
|
||||
"enable_terraform_debug_mode": true,
|
||||
"ephemeral_deployment": true,
|
||||
@@ -8385,6 +8387,7 @@ CreateWorkspaceRequest provides options for creating a new workspace. Only one o
|
||||
| `disable_workspace_agent_context_sync` | boolean | false | | |
|
||||
| `disable_workspace_sharing` | boolean | false | | |
|
||||
| `docs_url` | [serpent.URL](#serpenturl) | false | | |
|
||||
| `dynamic_parameters_full_evaluation` | boolean | false | | |
|
||||
| `enable_authz_recording` | boolean | false | | |
|
||||
| `enable_terraform_debug_mode` | boolean | false | | |
|
||||
| `ephemeral_deployment` | boolean | false | | |
|
||||
|
||||
Generated
+1
@@ -4774,6 +4774,7 @@ export interface DeploymentValues {
|
||||
readonly disable_user_secret_file_path?: boolean;
|
||||
readonly proxy_health_status_interval?: number;
|
||||
readonly enable_terraform_debug_mode?: boolean;
|
||||
readonly dynamic_parameters_full_evaluation?: boolean;
|
||||
readonly user_quiet_hours_schedule?: UserQuietHoursScheduleConfig;
|
||||
readonly web_terminal_renderer?: string;
|
||||
/**
|
||||
|
||||
Reference in New Issue
Block a user