From 10fa41134c0b6e84c7c26e4ff6619635d3c090a4 Mon Sep 17 00:00:00 2001 From: Shreyas Goenka Date: Tue, 28 Jul 2026 01:11:25 +0000 Subject: [PATCH 1/3] bundle: validate recorded state size during bundle validate The deployment metadata service rejects a resource state over 64 KiB (MAX_STATE_BYTES) with InvalidArgument. Hitting that mid-apply fails a deploy after some resources have already been created, so check it up front instead. The state checked here is what the deploy path uploads: the state prepared from config, serialized with sensitive fields redacted. It is a pure config transform, so it needs no workspace request and belongs in FastValidate, which runs for both `bundle validate` and `bundle deploy`. The check is scoped to direct-engine deployments that opted into experimental.record_deployment_history, since no other deployment uploads this state. FastValidate now takes the resolved engine rather than reading bundle.engine, which reports terraform when DATABRICKS_BUNDLE_ENGINE selects it. Co-authored-by: Isaac --- .../state_size_limit/databricks.yml.tmpl | 13 ++ .../validate/state_size_limit/out.test.toml | 3 + .../validate/state_size_limit/output.txt | 20 +++ .../bundle/validate/state_size_limit/script | 7 + .../validate/state_size_limit/test.toml | 7 + bundle/config/validate/fast_validate.go | 11 +- bundle/config/validate/state_size.go | 121 ++++++++++++++++++ bundle/config/validate/state_size_test.go | 104 +++++++++++++++ bundle/config/validate/validate.go | 5 +- cmd/bundle/utils/process.go | 4 +- 10 files changed, 288 insertions(+), 7 deletions(-) create mode 100644 acceptance/bundle/validate/state_size_limit/databricks.yml.tmpl create mode 100644 acceptance/bundle/validate/state_size_limit/out.test.toml create mode 100644 acceptance/bundle/validate/state_size_limit/output.txt create mode 100644 acceptance/bundle/validate/state_size_limit/script create mode 100644 acceptance/bundle/validate/state_size_limit/test.toml create mode 100644 bundle/config/validate/state_size.go create mode 100644 bundle/config/validate/state_size_test.go diff --git a/acceptance/bundle/validate/state_size_limit/databricks.yml.tmpl b/acceptance/bundle/validate/state_size_limit/databricks.yml.tmpl new file mode 100644 index 00000000000..5da71e5150e --- /dev/null +++ b/acceptance/bundle/validate/state_size_limit/databricks.yml.tmpl @@ -0,0 +1,13 @@ +bundle: + name: state_size_limit + +experimental: + record_deployment_history: true + +resources: + jobs: + oversized: + name: oversized + # DESCRIPTION is generated in 'script' to keep this fixture readable. It is + # large enough to push the recorded state past the 64 KiB limit. + description: $DESCRIPTION diff --git a/acceptance/bundle/validate/state_size_limit/out.test.toml b/acceptance/bundle/validate/state_size_limit/out.test.toml new file mode 100644 index 00000000000..e90b6d5d1ba --- /dev/null +++ b/acceptance/bundle/validate/state_size_limit/out.test.toml @@ -0,0 +1,3 @@ +Local = true +Cloud = false +EnvMatrix.DATABRICKS_BUNDLE_ENGINE = ["direct"] diff --git a/acceptance/bundle/validate/state_size_limit/output.txt b/acceptance/bundle/validate/state_size_limit/output.txt new file mode 100644 index 00000000000..7c0563f452d --- /dev/null +++ b/acceptance/bundle/validate/state_size_limit/output.txt @@ -0,0 +1,20 @@ + +>>> [CLI] bundle validate +Error: resources.jobs.oversized has a serialized state of 70278 bytes, which exceeds the 65536 byte limit for recording deployment history + at resources.jobs.oversized + in databricks.yml:10:7 + +Deployment history records the state of each resource, and this resource is too +large to record. Move the oversized field out of the resource definition, for +example by pointing a task at a workspace file instead of inlining its contents. + + +Name: state_size_limit +Target: default +Workspace: + User: [USERNAME] + Path: /Workspace/Users/[USERNAME]/.bundle/state_size_limit/default + +Found 1 error + +Exit code: 1 diff --git a/acceptance/bundle/validate/state_size_limit/script b/acceptance/bundle/validate/state_size_limit/script new file mode 100644 index 00000000000..e2fd78b7335 --- /dev/null +++ b/acceptance/bundle/validate/state_size_limit/script @@ -0,0 +1,7 @@ +# A description over the 64 KiB state limit, generated so the fixture stays small. +export DESCRIPTION=$(printf 'x%.0s' $(seq 1 70000)) +envsubst < databricks.yml.tmpl > databricks.yml + +trace $CLI bundle validate + +rm databricks.yml diff --git a/acceptance/bundle/validate/state_size_limit/test.toml b/acceptance/bundle/validate/state_size_limit/test.toml new file mode 100644 index 00000000000..d1240963e01 --- /dev/null +++ b/acceptance/bundle/validate/state_size_limit/test.toml @@ -0,0 +1,7 @@ +Local = true +Cloud = false + +Ignore = [".databricks"] + +[EnvMatrix] + DATABRICKS_BUNDLE_ENGINE = ["direct"] diff --git a/bundle/config/validate/fast_validate.go b/bundle/config/validate/fast_validate.go index d01eb8c1491..1ca4d139775 100644 --- a/bundle/config/validate/fast_validate.go +++ b/bundle/config/validate/fast_validate.go @@ -4,6 +4,7 @@ import ( "context" "github.com/databricks/cli/bundle" + "github.com/databricks/cli/bundle/config/engine" "github.com/databricks/cli/libs/diag" ) @@ -14,10 +15,13 @@ import ( // 2. The validation is blocking for bundle deployments. // // The full suite of validation mutators is available in the [Validate] mutator. -type fastValidate struct{ bundle.RO } +type fastValidate struct { + bundle.RO + engine engine.EngineType +} -func FastValidate() bundle.ReadOnlyMutator { - return &fastValidate{} +func FastValidate(e engine.EngineType) bundle.ReadOnlyMutator { + return &fastValidate{engine: e} } func (f *fastValidate) Name() string { @@ -29,6 +33,7 @@ func (f *fastValidate) Apply(ctx context.Context, rb *bundle.Bundle) diag.Diagno // Fast mutators with only in-memory checks JobClusterKeyDefined(), JobTaskClusterSpec(), + ValidateStateSize(f.engine), // Blocking mutators. Deployments will fail if these checks fail. ValidateArtifactPath(), diff --git a/bundle/config/validate/state_size.go b/bundle/config/validate/state_size.go new file mode 100644 index 00000000000..ec28fad261a --- /dev/null +++ b/bundle/config/validate/state_size.go @@ -0,0 +1,121 @@ +package validate + +import ( + "context" + "fmt" + "slices" + + "github.com/databricks/cli/bundle" + "github.com/databricks/cli/bundle/config" + "github.com/databricks/cli/bundle/config/engine" + "github.com/databricks/cli/bundle/direct/dresources" + "github.com/databricks/cli/libs/diag" + "github.com/databricks/cli/libs/dyn" + "github.com/databricks/cli/libs/structs/structwalk" +) + +// MaxStateSizeBytes is the largest serialized resource state the deployment +// metadata service accepts, matching MAX_STATE_BYTES on the service side. A +// resource above it is rejected there with InvalidArgument, so we check it up +// front to fail during validate rather than midway through a deploy. +const MaxStateSizeBytes = 64 * 1024 + +type validateStateSize struct { + bundle.RO + engine engine.EngineType +} + +// ValidateStateSize reports resources whose serialized state exceeds +// MaxStateSizeBytes and so cannot be recorded as deployment history. +func ValidateStateSize(e engine.EngineType) bundle.ReadOnlyMutator { + return &validateStateSize{engine: e} +} + +func (v *validateStateSize) Name() string { + return "validate:state_size" +} + +func (v *validateStateSize) Apply(ctx context.Context, b *bundle.Bundle) diag.Diagnostics { + // Only the direct engine records state with the metadata service, and only + // when the bundle opted in. Terraform deployments never upload this state. + if !v.engine.IsDirect() { + return nil + } + if b.Config.Experimental == nil || !b.Config.Experimental.RecordDeploymentHistory { + return nil + } + + // The adapters only hold the client, so a nil one is enough to reach + // PrepareState. Nothing here issues a request, which keeps this check in + // the fast, in-memory half of validation. + adapters, err := dresources.InitAll(nil) + if err != nil { + return diag.FromErr(err) + } + + var diags diag.Diagnostics + for _, key := range sortedResourceKeys(b) { + adapter, ok := adapters[config.GetResourceTypeFromKey(key)] + if !ok { + // Resource types the direct engine does not support are rejected + // during planning, with a better message than we could give here. + continue + } + + size, err := stateSize(b, adapter, key) + if err != nil { + return diag.FromErr(err) + } + if size <= MaxStateSizeBytes { + continue + } + + diags = diags.Append(diag.Diagnostic{ + Severity: diag.Error, + Summary: fmt.Sprintf("%s has a serialized state of %d bytes, which exceeds the %d byte limit for recording deployment history", key, size, MaxStateSizeBytes), + Detail: `Deployment history records the state of each resource, and this resource is too +large to record. Move the oversized field out of the resource definition, for +example by pointing a task at a workspace file instead of inlining its contents. +`, + Locations: b.Config.GetLocations(key), + Paths: []dyn.Path{dyn.MustPathFromString(key)}, + }) + } + + return diags +} + +// stateSize returns the size of the state that would be recorded for the +// resource at key. It mirrors what the deploy path uploads: the state prepared +// from config, serialized with sensitive fields redacted. +func stateSize(b *bundle.Bundle, adapter *dresources.Adapter, key string) (int, error) { + inputConfig, err := b.Config.GetResourceConfig(key) + if err != nil { + return 0, fmt.Errorf("cannot read config for %s: %w", key, err) + } + + state, err := adapter.PrepareState(inputConfig) + if err != nil { + return 0, fmt.Errorf("cannot prepare state for %s: %w", key, err) + } + + raw, err := structwalk.RedactSensitiveFields(state, dyn.SensitiveValueRedacted) + if err != nil { + return 0, fmt.Errorf("cannot serialize state for %s: %w", key, err) + } + + return len(raw), nil +} + +// sortedResourceKeys returns the bundle's resource keys ("resources.jobs.foo") +// in a stable order, so diagnostics do not depend on map iteration order. +func sortedResourceKeys(b *bundle.Bundle) []string { + var keys []string + for _, group := range b.Config.Resources.AllResources() { + for name := range group.Resources { + keys = append(keys, "resources."+group.Description.PluralName+"."+name) + } + } + slices.Sort(keys) + return keys +} diff --git a/bundle/config/validate/state_size_test.go b/bundle/config/validate/state_size_test.go new file mode 100644 index 00000000000..dd9eb50a2ad --- /dev/null +++ b/bundle/config/validate/state_size_test.go @@ -0,0 +1,104 @@ +package validate + +import ( + "strings" + "testing" + + "github.com/databricks/cli/bundle" + "github.com/databricks/cli/bundle/config" + "github.com/databricks/cli/bundle/config/engine" + "github.com/databricks/cli/bundle/config/resources" + "github.com/databricks/cli/bundle/internal/bundletest" + "github.com/databricks/cli/libs/diag" + "github.com/databricks/cli/libs/dyn" + "github.com/databricks/databricks-sdk-go/service/jobs" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +// stateSizeBundle returns a bundle with one job whose description is n bytes, +// which lands in the recorded state and so drives its size. +func stateSizeBundle(t *testing.T, n int) *bundle.Bundle { + t.Helper() + b := &bundle.Bundle{ + Config: config.Root{ + Experimental: &config.Experimental{RecordDeploymentHistory: true}, + Resources: config.Resources{ + Jobs: map[string]*resources.Job{ + "big": {JobSettings: jobs.JobSettings{Name: "big"}}, + }, + }, + }, + } + description := strings.Repeat("x", n) + b.Config.Resources.Jobs["big"].Description = description + bundletest.Mutate(t, b, func(v dyn.Value) (dyn.Value, error) { + return dyn.Set(v, "resources.jobs.big.description", dyn.V(description)) + }) + return b +} + +func TestValidateStateSizeUnderLimit(t *testing.T) { + b := stateSizeBundle(t, 1024) + + diags := ValidateStateSize(engine.EngineDirect).Apply(t.Context(), b) + require.Empty(t, diags) +} + +func TestValidateStateSizeOverLimit(t *testing.T) { + b := stateSizeBundle(t, MaxStateSizeBytes+1) + + diags := ValidateStateSize(engine.EngineDirect).Apply(t.Context(), b) + require.Len(t, diags, 1) + assert.Equal(t, diag.Error, diags[0].Severity) + assert.Contains(t, diags[0].Summary, "resources.jobs.big") + assert.Contains(t, diags[0].Summary, "exceeds the 65536 byte limit") + assert.Equal(t, []dyn.Path{dyn.MustPathFromString("resources.jobs.big")}, diags[0].Paths) +} + +// The state is only uploaded by the direct engine, so a terraform deployment of +// the same oversized resource must not be blocked. +func TestValidateStateSizeSkippedForTerraform(t *testing.T) { + b := stateSizeBundle(t, MaxStateSizeBytes+1) + + diags := ValidateStateSize(engine.EngineTerraform).Apply(t.Context(), b) + require.Empty(t, diags) +} + +func TestValidateStateSizeSkippedWithoutOptIn(t *testing.T) { + b := stateSizeBundle(t, MaxStateSizeBytes+1) + b.Config.Experimental.RecordDeploymentHistory = false + + diags := ValidateStateSize(engine.EngineDirect).Apply(t.Context(), b) + require.Empty(t, diags) +} + +func TestValidateStateSizeNoExperimentalBlock(t *testing.T) { + b := stateSizeBundle(t, MaxStateSizeBytes+1) + b.Config.Experimental = nil + + diags := ValidateStateSize(engine.EngineDirect).Apply(t.Context(), b) + require.Empty(t, diags) +} + +// Every oversized resource is reported, so one deploy attempt surfaces them all. +func TestValidateStateSizeReportsEveryResource(t *testing.T) { + b := stateSizeBundle(t, MaxStateSizeBytes+1) + description := strings.Repeat("y", MaxStateSizeBytes+1) + b.Config.Resources.Jobs["also_big"] = &resources.Job{ + JobSettings: jobs.JobSettings{Name: "also_big", Description: description}, + } + bundletest.Mutate(t, b, func(v dyn.Value) (dyn.Value, error) { + return dyn.Set(v, "resources.jobs.also_big", dyn.V(map[string]dyn.Value{ + "name": dyn.V("also_big"), + "description": dyn.V(description), + })) + }) + + diags := ValidateStateSize(engine.EngineDirect).Apply(t.Context(), b) + require.Len(t, diags, 2) + // Diagnostics are ordered by resource key so output does not depend on map + // iteration order. + assert.Contains(t, diags[0].Summary, "resources.jobs.also_big") + assert.Contains(t, diags[1].Summary, "resources.jobs.big") +} diff --git a/bundle/config/validate/validate.go b/bundle/config/validate/validate.go index e531d87684c..c18a1e8e86b 100644 --- a/bundle/config/validate/validate.go +++ b/bundle/config/validate/validate.go @@ -4,11 +4,12 @@ import ( "context" "github.com/databricks/cli/bundle" + "github.com/databricks/cli/bundle/config/engine" ) -func Validate(ctx context.Context, b *bundle.Bundle) { +func Validate(ctx context.Context, b *bundle.Bundle, e engine.EngineType) { bundle.ApplyParallel(ctx, b, - FastValidate(), + FastValidate(e), // Slow mutators that require network or file i/o. These are only // run in the `bundle validate` command. diff --git a/cmd/bundle/utils/process.go b/cmd/bundle/utils/process.go index e4f232605ce..4cb04a5810b 100644 --- a/cmd/bundle/utils/process.go +++ b/cmd/bundle/utils/process.go @@ -283,7 +283,7 @@ func ProcessBundleRet(cmd *cobra.Command, opts ProcessOptions) (b *bundle.Bundle if opts.FastValidate { t1 := time.Now() - bundle.ApplyContext(ctx, b, validate.FastValidate()) + bundle.ApplyContext(ctx, b, validate.FastValidate(requiredEngine.Type)) b.Metrics.ExecutionTimes = append(b.Metrics.ExecutionTimes, protos.IntMapEntry{ Key: "validate.FastValidate", Value: time.Since(t1).Milliseconds(), @@ -303,7 +303,7 @@ func ProcessBundleRet(cmd *cobra.Command, opts ProcessOptions) (b *bundle.Bundle } if opts.Validate { - validate.Validate(ctx, b) + validate.Validate(ctx, b, requiredEngine.Type) if logdiag.HasError(ctx) { return b, stateDesc, root.ErrAlreadyPrinted } From 1e460c9bdc38a9045e4403cbb08c1183c0449f5f Mon Sep 17 00:00:00 2001 From: Shreyas Goenka Date: Tue, 28 Jul 2026 12:58:25 +0000 Subject: [PATCH 2/3] bundle: tailor oversized-state advice to the resource type "Split it up" means a different edit per resource type, so name the split that applies: multiple jobs with fewer tasks, multiple alerts with fewer conditions, multiple pipelines with fewer libraries. file_path on alerts (and dashboards/genie spaces) is deliberately not suggested. LoadDBAlertFiles inlines the file into AlertV2 during initialize, so the recorded state is byte-identical whether the body came from file_path or from inline YAML; recommending it would send users on a refactor that does not reduce the size. Co-authored-by: Isaac --- .../validate/state_size_limit/output.txt | 6 ++-- bundle/config/validate/state_size.go | 32 +++++++++++++---- bundle/config/validate/state_size_test.go | 35 +++++++++++++++++++ 3 files changed, 62 insertions(+), 11 deletions(-) diff --git a/acceptance/bundle/validate/state_size_limit/output.txt b/acceptance/bundle/validate/state_size_limit/output.txt index 7c0563f452d..4c95a124968 100644 --- a/acceptance/bundle/validate/state_size_limit/output.txt +++ b/acceptance/bundle/validate/state_size_limit/output.txt @@ -4,10 +4,8 @@ Error: resources.jobs.oversized has a serialized state of 70278 bytes, which exc at resources.jobs.oversized in databricks.yml:10:7 -Deployment history records the state of each resource, and this resource is too -large to record. Move the oversized field out of the resource definition, for -example by pointing a task at a workspace file instead of inlining its contents. - +Deployment history records the state of each resource, and this resource is too large to record. +The limit applies per resource, so split this job into multiple jobs, each with fewer tasks. Name: state_size_limit Target: default diff --git a/bundle/config/validate/state_size.go b/bundle/config/validate/state_size.go index ec28fad261a..4fdd72a5e50 100644 --- a/bundle/config/validate/state_size.go +++ b/bundle/config/validate/state_size.go @@ -55,7 +55,8 @@ func (v *validateStateSize) Apply(ctx context.Context, b *bundle.Bundle) diag.Di var diags diag.Diagnostics for _, key := range sortedResourceKeys(b) { - adapter, ok := adapters[config.GetResourceTypeFromKey(key)] + resourceType := config.GetResourceTypeFromKey(key) + adapter, ok := adapters[resourceType] if !ok { // Resource types the direct engine does not support are rejected // during planning, with a better message than we could give here. @@ -71,12 +72,9 @@ func (v *validateStateSize) Apply(ctx context.Context, b *bundle.Bundle) diag.Di } diags = diags.Append(diag.Diagnostic{ - Severity: diag.Error, - Summary: fmt.Sprintf("%s has a serialized state of %d bytes, which exceeds the %d byte limit for recording deployment history", key, size, MaxStateSizeBytes), - Detail: `Deployment history records the state of each resource, and this resource is too -large to record. Move the oversized field out of the resource definition, for -example by pointing a task at a workspace file instead of inlining its contents. -`, + Severity: diag.Error, + Summary: fmt.Sprintf("%s has a serialized state of %d bytes, which exceeds the %d byte limit for recording deployment history", key, size, MaxStateSizeBytes), + Detail: "Deployment history records the state of each resource, and this resource is too large to record.\n" + sizeAdvice(resourceType), Locations: b.Config.GetLocations(key), Paths: []dyn.Path{dyn.MustPathFromString(key)}, }) @@ -85,6 +83,26 @@ example by pointing a task at a workspace file instead of inlining its contents. return diags } +// sizeAdvice returns the remediation hint for an oversized resource of the given +// type. The limit is per resource, so the way out is always to split the +// definition into smaller ones; the wording names the split that applies. +// +// Note that file_path on alerts, dashboards and genie spaces is deliberately not +// suggested: those mutators inline the file into the resource during initialize, +// so the recorded state is the same size either way. +func sizeAdvice(resourceType string) string { + switch resourceType { + case "jobs": + return "The limit applies per resource, so split this job into multiple jobs, each with fewer tasks." + case "alerts": + return "The limit applies per resource, so split this alert into multiple alerts, each covering fewer conditions." + case "pipelines": + return "The limit applies per resource, so split this pipeline into multiple pipelines, each with fewer libraries." + default: + return "The limit applies per resource, so split this resource into multiple smaller resources." + } +} + // stateSize returns the size of the state that would be recorded for the // resource at key. It mirrors what the deploy path uploads: the state prepared // from config, serialized with sensitive fields redacted. diff --git a/bundle/config/validate/state_size_test.go b/bundle/config/validate/state_size_test.go index dd9eb50a2ad..5fdf909214c 100644 --- a/bundle/config/validate/state_size_test.go +++ b/bundle/config/validate/state_size_test.go @@ -12,6 +12,7 @@ import ( "github.com/databricks/cli/libs/diag" "github.com/databricks/cli/libs/dyn" "github.com/databricks/databricks-sdk-go/service/jobs" + "github.com/databricks/databricks-sdk-go/service/sql" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) @@ -54,6 +55,16 @@ func TestValidateStateSizeOverLimit(t *testing.T) { assert.Contains(t, diags[0].Summary, "resources.jobs.big") assert.Contains(t, diags[0].Summary, "exceeds the 65536 byte limit") assert.Equal(t, []dyn.Path{dyn.MustPathFromString("resources.jobs.big")}, diags[0].Paths) + assert.Contains(t, diags[0].Detail, "split this job into multiple jobs") +} + +// The advice names the split that applies to the resource type, since "split it +// up" means a different edit for a job than for an alert. +func TestSizeAdvice(t *testing.T) { + assert.Contains(t, sizeAdvice("jobs"), "split this job into multiple jobs") + assert.Contains(t, sizeAdvice("alerts"), "split this alert into multiple alerts") + assert.Contains(t, sizeAdvice("pipelines"), "split this pipeline into multiple pipelines") + assert.Contains(t, sizeAdvice("schemas"), "split this resource into multiple smaller resources") } // The state is only uploaded by the direct engine, so a terraform deployment of @@ -81,6 +92,30 @@ func TestValidateStateSizeNoExperimentalBlock(t *testing.T) { require.Empty(t, diags) } +func TestValidateStateSizeAlertAdvice(t *testing.T) { + queryText := strings.Repeat("s", MaxStateSizeBytes+1) + b := &bundle.Bundle{ + Config: config.Root{ + Experimental: &config.Experimental{RecordDeploymentHistory: true}, + Resources: config.Resources{ + Alerts: map[string]*resources.Alert{ + "noisy": {AlertV2: sql.AlertV2{QueryText: queryText}}, + }, + }, + }, + } + bundletest.Mutate(t, b, func(v dyn.Value) (dyn.Value, error) { + return dyn.Set(v, "resources.alerts.noisy", dyn.V(map[string]dyn.Value{ + "query_text": dyn.V(queryText), + })) + }) + + diags := ValidateStateSize(engine.EngineDirect).Apply(t.Context(), b) + require.Len(t, diags, 1) + assert.Contains(t, diags[0].Summary, "resources.alerts.noisy") + assert.Contains(t, diags[0].Detail, "split this alert into multiple alerts") +} + // Every oversized resource is reported, so one deploy attempt surfaces them all. func TestValidateStateSizeReportsEveryResource(t *testing.T) { b := stateSizeBundle(t, MaxStateSizeBytes+1) From d808f7f47a380a9aefe84d3fcb285a966b84848f Mon Sep 17 00:00:00 2001 From: Shreyas Goenka Date: Wed, 29 Jul 2026 14:34:23 +0000 Subject: [PATCH 3/3] bundle: drop redaction from the state size measurement Redaction is being removed, so measuring through RedactSensitiveFields would couple this check to a code path on its way out. Plain json.Marshal is what the size measurement actually needs: redaction swaps secret strings for a fixed placeholder, which does not meaningfully change the byte count either way. Addresses review feedback on #6087. Co-authored-by: Isaac --- bundle/config/validate/state_size.go | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/bundle/config/validate/state_size.go b/bundle/config/validate/state_size.go index 4fdd72a5e50..8ecab8d1898 100644 --- a/bundle/config/validate/state_size.go +++ b/bundle/config/validate/state_size.go @@ -2,6 +2,7 @@ package validate import ( "context" + "encoding/json" "fmt" "slices" @@ -11,7 +12,6 @@ import ( "github.com/databricks/cli/bundle/direct/dresources" "github.com/databricks/cli/libs/diag" "github.com/databricks/cli/libs/dyn" - "github.com/databricks/cli/libs/structs/structwalk" ) // MaxStateSizeBytes is the largest serialized resource state the deployment @@ -104,8 +104,7 @@ func sizeAdvice(resourceType string) string { } // stateSize returns the size of the state that would be recorded for the -// resource at key. It mirrors what the deploy path uploads: the state prepared -// from config, serialized with sensitive fields redacted. +// resource at key: the state prepared from config, serialized as JSON. func stateSize(b *bundle.Bundle, adapter *dresources.Adapter, key string) (int, error) { inputConfig, err := b.Config.GetResourceConfig(key) if err != nil { @@ -117,7 +116,7 @@ func stateSize(b *bundle.Bundle, adapter *dresources.Adapter, key string) (int, return 0, fmt.Errorf("cannot prepare state for %s: %w", key, err) } - raw, err := structwalk.RedactSensitiveFields(state, dyn.SensitiveValueRedacted) + raw, err := json.Marshal(state) if err != nil { return 0, fmt.Errorf("cannot serialize state for %s: %w", key, err) }