From 40caa2b6dba933b369bada68b12ab888c40881d5 Mon Sep 17 00:00:00 2001 From: Denis Bilenko Date: Thu, 13 Aug 2026 11:41:50 +0200 Subject: [PATCH 1/3] direct: make migration ignore direct-only only resources Co-authored-by: Isaac --- .../bundles/migrate-direct-only-resources.md | 1 + .../migrate/added-direct-only/databricks.yml | 7 ++ .../migrate/added-direct-only/out.test.toml | 2 + .../migrate/added-direct-only/output.txt | 63 ++++++++++++++++ .../bundle/migrate/added-direct-only/script | 26 +++++++ .../migrate/added-direct-only/test.toml | 2 + .../auto-migrate-direct-only/databricks.yml | 7 ++ .../auto-migrate-direct-only/out.test.toml | 2 + .../auto-migrate-direct-only/output.txt | 72 +++++++++++++++++++ .../migrate/auto-migrate-direct-only/script | 27 +++++++ .../auto-migrate-direct-only/test.toml | 2 + .../mutator/ignore_direct_only_resources.go | 67 +++++++++++++++++ .../ignore_direct_only_resources_test.go | 57 +++++++++++++++ cmd/bundle/utils/process.go | 11 +++ 14 files changed, 346 insertions(+) create mode 100644 .nextchanges/bundles/migrate-direct-only-resources.md create mode 100644 acceptance/bundle/migrate/added-direct-only/databricks.yml create mode 100644 acceptance/bundle/migrate/added-direct-only/out.test.toml create mode 100644 acceptance/bundle/migrate/added-direct-only/output.txt create mode 100644 acceptance/bundle/migrate/added-direct-only/script create mode 100644 acceptance/bundle/migrate/added-direct-only/test.toml create mode 100644 acceptance/bundle/migrate/auto-migrate-direct-only/databricks.yml create mode 100644 acceptance/bundle/migrate/auto-migrate-direct-only/out.test.toml create mode 100644 acceptance/bundle/migrate/auto-migrate-direct-only/output.txt create mode 100644 acceptance/bundle/migrate/auto-migrate-direct-only/script create mode 100644 acceptance/bundle/migrate/auto-migrate-direct-only/test.toml create mode 100644 bundle/config/mutator/ignore_direct_only_resources.go create mode 100644 bundle/config/mutator/ignore_direct_only_resources_test.go diff --git a/.nextchanges/bundles/migrate-direct-only-resources.md b/.nextchanges/bundles/migrate-direct-only-resources.md new file mode 100644 index 00000000000..c2442a94b00 --- /dev/null +++ b/.nextchanges/bundles/migrate-direct-only-resources.md @@ -0,0 +1 @@ +When migrating a bundle to the direct deployment engine, resources that only the direct engine supports (e.g. instance pools, catalogs) are now ignored during the migrating deploy instead of failing it. They are created by the next deploy, which runs on the migrated state. diff --git a/acceptance/bundle/migrate/added-direct-only/databricks.yml b/acceptance/bundle/migrate/added-direct-only/databricks.yml new file mode 100644 index 00000000000..e2a561b2525 --- /dev/null +++ b/acceptance/bundle/migrate/added-direct-only/databricks.yml @@ -0,0 +1,7 @@ +bundle: + name: test-bundle + +resources: + jobs: + job_a: {name: "Job A"} + #instance_pools: {pool: {instance_pool_name: "My Pool", node_type_id: "Standard_DS3_v2", idle_instance_autotermination_minutes: 60}} diff --git a/acceptance/bundle/migrate/added-direct-only/out.test.toml b/acceptance/bundle/migrate/added-direct-only/out.test.toml new file mode 100644 index 00000000000..0938e678987 --- /dev/null +++ b/acceptance/bundle/migrate/added-direct-only/out.test.toml @@ -0,0 +1,2 @@ +Cloud = false +EnvMatrix.DATABRICKS_BUNDLE_ENGINE = ["direct"] diff --git a/acceptance/bundle/migrate/added-direct-only/output.txt b/acceptance/bundle/migrate/added-direct-only/output.txt new file mode 100644 index 00000000000..2529c0f8503 --- /dev/null +++ b/acceptance/bundle/migrate/added-direct-only/output.txt @@ -0,0 +1,63 @@ + +=== Deploy with terraform (instance_pools is commented out; terraform cannot deploy it) +>>> [CLI] bundle deploy +Uploading bundle files to /Workspace/Users/[USERNAME]/.bundle/test-bundle/default/files... +Deploying resources... +Updating deployment state... +Deployment complete! + +=== Add a direct-only resource to the config without deploying it +>>> update_file.py databricks.yml #instance_pools instance_pools + +=== Deploying on terraform rejects it, which is what sends the user to migrate +>>> musterr [CLI] bundle deploy +Error: Instance Pool resources are only supported with direct deployment mode + in databricks.yml:7:19 + +Instance Pool resources require direct deployment mode. Please set the DATABRICKS_BUNDLE_ENGINE environment variable to 'direct' or set 'bundle.engine: direct' in your databricks.yml to use instance_pool resources. +Learn more at https://docs.databricks.com/dev-tools/bundles/direct + + +=== Migration ignores the direct-only resource; it will be created on the next deploy +>>> [CLI] bundle deployment migrate +Success! Migrated 1 resources to direct engine state file: [TEST_TMP_DIR]/.databricks/bundle/default/resources.json + +Validate the migration by running "databricks bundle plan", there should be no actions planned. + +The state file is not synchronized to the workspace yet. To do that and finalize the migration, run "bundle deploy". + +To undo the migration, remove [TEST_TMP_DIR]/.databricks/bundle/default/resources.json and rename [TEST_TMP_DIR]/.databricks/bundle/default/terraform/terraform.tfstate.backup to [TEST_TMP_DIR]/.databricks/bundle/default/terraform/terraform.tfstate + + +=== After migration: plan shows the direct-only resource as "to add" +>>> DATABRICKS_BUNDLE_ENGINE=direct [CLI] bundle plan +create instance_pools.pool + +Plan: 1 to add, 0 to change, 0 to delete, 1 unchanged + +=== Deploy creates it; verify via recorded requests +>>> DATABRICKS_BUNDLE_ENGINE=direct [CLI] bundle deploy +Uploading bundle files to /Workspace/Users/[USERNAME]/.bundle/test-bundle/default/files... +Deploying resources... +Updating deployment state... +Deployment complete! + +>>> print_requests.py //instance-pools/create +{ + "headers": { + "User-Agent": [ + "cli/[CLI_VERSION] databricks-sdk-go/[SDK_VERSION] go/[GO_VERSION] os/[OS] cmd/bundle_deploy cmd-exec-id/[UUID] interactive/none engine/direct auth/pat" + ] + }, + "method": "POST", + "path": "/api/2.0/instance-pools/create", + "body": { + "idle_instance_autotermination_minutes": 60, + "instance_pool_name": "My Pool", + "node_type_id": "Standard_DS3_v2" + } +} + +=== No further actions planned +>>> DATABRICKS_BUNDLE_ENGINE=direct [CLI] bundle plan +Plan: 0 to add, 0 to change, 0 to delete, 2 unchanged diff --git a/acceptance/bundle/migrate/added-direct-only/script b/acceptance/bundle/migrate/added-direct-only/script new file mode 100644 index 00000000000..5302ea89b89 --- /dev/null +++ b/acceptance/bundle/migrate/added-direct-only/script @@ -0,0 +1,26 @@ +export DATABRICKS_BUNDLE_ENGINE=terraform + +title "Deploy with terraform (instance_pools is commented out; terraform cannot deploy it)" +trace $CLI bundle deploy + +title "Add a direct-only resource to the config without deploying it" +trace update_file.py databricks.yml "#instance_pools" "instance_pools" + +title "Deploying on terraform rejects it, which is what sends the user to migrate" +trace musterr $CLI bundle deploy + +title "Migration ignores the direct-only resource; it will be created on the next deploy" +trace $CLI bundle deployment migrate + +title "After migration: plan shows the direct-only resource as \"to add\"" +trace DATABRICKS_BUNDLE_ENGINE=direct $CLI bundle plan + +title "Deploy creates it; verify via recorded requests" +rm out.requests.txt +trace DATABRICKS_BUNDLE_ENGINE=direct $CLI bundle deploy +trace print_requests.py //instance-pools/create + +title "No further actions planned" +trace DATABRICKS_BUNDLE_ENGINE=direct $CLI bundle plan + +rm out.requests.txt diff --git a/acceptance/bundle/migrate/added-direct-only/test.toml b/acceptance/bundle/migrate/added-direct-only/test.toml new file mode 100644 index 00000000000..0938e678987 --- /dev/null +++ b/acceptance/bundle/migrate/added-direct-only/test.toml @@ -0,0 +1,2 @@ +Cloud = false +EnvMatrix.DATABRICKS_BUNDLE_ENGINE = ["direct"] diff --git a/acceptance/bundle/migrate/auto-migrate-direct-only/databricks.yml b/acceptance/bundle/migrate/auto-migrate-direct-only/databricks.yml new file mode 100644 index 00000000000..e2a561b2525 --- /dev/null +++ b/acceptance/bundle/migrate/auto-migrate-direct-only/databricks.yml @@ -0,0 +1,7 @@ +bundle: + name: test-bundle + +resources: + jobs: + job_a: {name: "Job A"} + #instance_pools: {pool: {instance_pool_name: "My Pool", node_type_id: "Standard_DS3_v2", idle_instance_autotermination_minutes: 60}} diff --git a/acceptance/bundle/migrate/auto-migrate-direct-only/out.test.toml b/acceptance/bundle/migrate/auto-migrate-direct-only/out.test.toml new file mode 100644 index 00000000000..0938e678987 --- /dev/null +++ b/acceptance/bundle/migrate/auto-migrate-direct-only/out.test.toml @@ -0,0 +1,2 @@ +Cloud = false +EnvMatrix.DATABRICKS_BUNDLE_ENGINE = ["direct"] diff --git a/acceptance/bundle/migrate/auto-migrate-direct-only/output.txt b/acceptance/bundle/migrate/auto-migrate-direct-only/output.txt new file mode 100644 index 00000000000..81728ffce35 --- /dev/null +++ b/acceptance/bundle/migrate/auto-migrate-direct-only/output.txt @@ -0,0 +1,72 @@ + +=== Deploy with terraform (instance_pools is commented out; terraform cannot deploy it) +>>> DATABRICKS_BUNDLE_ENGINE=terraform [CLI] bundle deploy +Uploading bundle files to /Workspace/Users/[USERNAME]/.bundle/test-bundle/default/files... +Deploying resources... +Updating deployment state... +Deployment complete! + +=== Add a direct-only resource and opt in to the direct engine via config +>>> update_file.py databricks.yml #instance_pools instance_pools + +>>> update_file.py databricks.yml name: test-bundle name: test-bundle + engine: direct + +=== Plan ignores the direct-only resource, since this run still uses terraform +>>> [CLI] bundle plan +Warning: ignoring instance_pool resources in this deploy + in databricks.yml:8:19 + +Instance Pool resources are only supported by the direct deployment engine, but the existing state uses terraform. They will be created by the next deploy, after the state is migrated to the direct engine. + +Plan: 0 to add, 0 to change, 0 to delete, 1 unchanged + +=== Deploy migrates the state, ignoring the direct-only resource +>>> [CLI] bundle deploy +Warn: Direct engine requested in bundle.engine setting at [TEST_TMP_DIR]/databricks.yml:3:11 but the existing state uses "terraform". Deploying on "terraform"; will attempt to migrate the state to the direct engine after this deploy. +Warning: ignoring instance_pool resources in this deploy + in databricks.yml:8:19 + +Instance Pool resources are only supported by the direct deployment engine, but the existing state uses terraform. They will be created by the next deploy, after the state is migrated to the direct engine. + +Uploading bundle files to /Workspace/Users/[USERNAME]/.bundle/test-bundle/default/files... +Deploying resources... +Updating deployment state... +Deployment complete! +Migrating state to direct deployment engine (opted in via bundle.engine setting at [TEST_TMP_DIR]/databricks.yml:3:11)... +Migrated 1 resource to direct deployment engine. + +>>> print_migration_telemetry +direct_migrated_via_config true + +=== The direct-only resource is created on the next deploy +>>> [CLI] bundle plan +create instance_pools.pool + +Plan: 1 to add, 0 to change, 0 to delete, 1 unchanged + +>>> [CLI] bundle deploy +Uploading bundle files to /Workspace/Users/[USERNAME]/.bundle/test-bundle/default/files... +Deploying resources... +Updating deployment state... +Deployment complete! + +>>> print_requests.py //instance-pools/create +{ + "headers": { + "User-Agent": [ + "cli/[CLI_VERSION] databricks-sdk-go/[SDK_VERSION] go/[GO_VERSION] os/[OS] cmd/bundle_deploy cmd-exec-id/[UUID] interactive/none engine/direct auth/pat" + ] + }, + "method": "POST", + "path": "/api/2.0/instance-pools/create", + "body": { + "idle_instance_autotermination_minutes": 60, + "instance_pool_name": "My Pool", + "node_type_id": "Standard_DS3_v2" + } +} + +=== No further actions planned +>>> [CLI] bundle plan +Plan: 0 to add, 0 to change, 0 to delete, 2 unchanged diff --git a/acceptance/bundle/migrate/auto-migrate-direct-only/script b/acceptance/bundle/migrate/auto-migrate-direct-only/script new file mode 100644 index 00000000000..43015caf6eb --- /dev/null +++ b/acceptance/bundle/migrate/auto-migrate-direct-only/script @@ -0,0 +1,27 @@ +export DATABRICKS_BUNDLE_ENGINE= + +title "Deploy with terraform (instance_pools is commented out; terraform cannot deploy it)" +trace DATABRICKS_BUNDLE_ENGINE=terraform $CLI bundle deploy +rm -f out.requests.txt + +title "Add a direct-only resource and opt in to the direct engine via config" +trace update_file.py databricks.yml "#instance_pools" "instance_pools" +trace update_file.py databricks.yml "name: test-bundle" $'name: test-bundle\n engine: direct' + +title "Plan ignores the direct-only resource, since this run still uses terraform" +trace $CLI bundle plan + +title "Deploy migrates the state, ignoring the direct-only resource" +trace $CLI bundle deploy +trace print_migration_telemetry +rm -f out.requests.txt + +title "The direct-only resource is created on the next deploy" +trace $CLI bundle plan +trace $CLI bundle deploy +trace print_requests.py //instance-pools/create + +title "No further actions planned" +trace $CLI bundle plan + +rm -f out.requests.txt diff --git a/acceptance/bundle/migrate/auto-migrate-direct-only/test.toml b/acceptance/bundle/migrate/auto-migrate-direct-only/test.toml new file mode 100644 index 00000000000..0938e678987 --- /dev/null +++ b/acceptance/bundle/migrate/auto-migrate-direct-only/test.toml @@ -0,0 +1,2 @@ +Cloud = false +EnvMatrix.DATABRICKS_BUNDLE_ENGINE = ["direct"] diff --git a/bundle/config/mutator/ignore_direct_only_resources.go b/bundle/config/mutator/ignore_direct_only_resources.go new file mode 100644 index 00000000000..0a481a63662 --- /dev/null +++ b/bundle/config/mutator/ignore_direct_only_resources.go @@ -0,0 +1,67 @@ +package mutator + +import ( + "context" + "fmt" + + "github.com/databricks/cli/bundle" + "github.com/databricks/cli/libs/diag" + "github.com/databricks/cli/libs/dyn" +) + +type ignoreDirectOnlyResources struct{} + +// IgnoreDirectOnlyResources removes resources that only the direct engine supports +// from the configuration. +// +// It is applied when the user opted in to the direct engine but the existing state +// still uses terraform: this run happens on the terraform engine, which cannot +// deploy these resources. They are new by definition — terraform could never have +// deployed them — so they are absent from the terraform state and the migration +// that follows this deploy does not need them. The next deploy, which runs on the +// migrated state, creates them. +func IgnoreDirectOnlyResources() bundle.Mutator { + return &ignoreDirectOnlyResources{} +} + +func (m *ignoreDirectOnlyResources) Name() string { + return "IgnoreDirectOnlyResources" +} + +func (m *ignoreDirectOnlyResources) Apply(_ context.Context, b *bundle.Bundle) diag.Diagnostics { + var diags diag.Diagnostics + var groups []string + + for _, group := range b.Config.Resources.AllResources() { + if len(group.Resources) == 0 { + continue + } + if !isDirectOnly(group.Description.PluralName) { + continue + } + groups = append(groups, group.Description.PluralName) + diags = diags.Append(diag.Diagnostic{ + Severity: diag.Warning, + Summary: fmt.Sprintf("ignoring %s resources in this deploy", group.Description.SingularName), + Detail: fmt.Sprintf("%s resources are only supported by the direct deployment engine, but the existing state uses terraform. "+ + "They will be created by the next deploy, after the state is migrated to the direct engine.", + group.Description.SingularTitle), + Locations: b.Config.GetLocations("resources." + group.Description.PluralName), + }) + } + + if len(groups) == 0 { + return nil + } + + err := b.Config.Mutate(func(root dyn.Value) (dyn.Value, error) { + return dyn.Map(root, "resources", func(_ dyn.Path, resources dyn.Value) (dyn.Value, error) { + return dyn.DropKeys(resources, groups) + }) + }) + if err != nil { + return diags.Extend(diag.FromErr(err)) + } + + return diags +} diff --git a/bundle/config/mutator/ignore_direct_only_resources_test.go b/bundle/config/mutator/ignore_direct_only_resources_test.go new file mode 100644 index 00000000000..d498cbc4c8b --- /dev/null +++ b/bundle/config/mutator/ignore_direct_only_resources_test.go @@ -0,0 +1,57 @@ +package mutator_test + +import ( + "testing" + + "github.com/databricks/cli/bundle" + "github.com/databricks/cli/bundle/config" + "github.com/databricks/cli/bundle/config/mutator" + "github.com/databricks/cli/bundle/config/resources" + "github.com/databricks/databricks-sdk-go/service/catalog" + "github.com/databricks/databricks-sdk-go/service/compute" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestIgnoreDirectOnlyResourcesNoDirectOnlyReturnsNil(t *testing.T) { + b := &bundle.Bundle{ + Config: config.Root{ + Resources: config.Resources{ + Jobs: map[string]*resources.Job{ + "my_job": {}, + }, + }, + }, + } + + diags := bundle.Apply(t.Context(), b, mutator.IgnoreDirectOnlyResources()) + assert.Empty(t, diags) + assert.Len(t, b.Config.Resources.Jobs, 1) +} + +func TestIgnoreDirectOnlyResourcesRemovesThem(t *testing.T) { + b := &bundle.Bundle{ + Config: config.Root{ + Resources: config.Resources{ + Jobs: map[string]*resources.Job{ + "my_job": {}, + }, + Catalogs: map[string]*resources.Catalog{ + "my_catalog": {CreateCatalog: catalog.CreateCatalog{Name: "my_catalog"}}, + }, + InstancePools: map[string]*resources.InstancePool{ + "my_pool": {CreateInstancePool: compute.CreateInstancePool{InstancePoolName: "my_pool"}}, + }, + }, + }, + } + + diags := bundle.Apply(t.Context(), b, mutator.IgnoreDirectOnlyResources()) + require.Len(t, diags, 2) + assert.Equal(t, "ignoring catalog resources in this deploy", diags[0].Summary) + assert.Equal(t, "ignoring instance_pool resources in this deploy", diags[1].Summary) + + assert.Empty(t, b.Config.Resources.Catalogs) + assert.Empty(t, b.Config.Resources.InstancePools) + assert.Len(t, b.Config.Resources.Jobs, 1) +} diff --git a/cmd/bundle/utils/process.go b/cmd/bundle/utils/process.go index e4f232605ce..fc493b30d82 100644 --- a/cmd/bundle/utils/process.go +++ b/cmd/bundle/utils/process.go @@ -325,6 +325,17 @@ func ProcessBundleRet(cmd *cobra.Command, opts ProcessOptions) (b *bundle.Bundle } if opts.PreDeployChecks { + // The state is about to be migrated to the direct engine, but this run still + // uses terraform, which cannot deploy direct-only resources. Drop them from + // the config so this run succeeds and the migration can proceed; the next + // deploy, which runs on the migrated state, creates them. + if requiredEngine.Type == engine.EngineDirect && !stateDesc.Engine.IsDirect() { + bundle.ApplyContext(ctx, b, mutator.IgnoreDirectOnlyResources()) + if logdiag.HasError(ctx) { + return b, stateDesc, root.ErrAlreadyPrinted + } + } + downgradeWarningToError := !opts.Deploy phases.PreDeployChecks(ctx, b, downgradeWarningToError, stateDesc.Engine) From 3f789daaaefa222a306a2e9c4c607e6618530b04 Mon Sep 17 00:00:00 2001 From: Denis Bilenko Date: Thu, 13 Aug 2026 12:41:13 +0200 Subject: [PATCH 2/3] Add env var migration test; drop mutator unit tests Co-authored-by: Isaac --- .../migrate/added-direct-only/databricks.yml | 2 +- .../migrate/added-direct-only/output.txt | 2 +- .../databricks.yml | 7 +++ .../out.test.toml} | 0 .../output.txt | 60 +++++++++++++++++++ .../auto-migrate-direct-only-envvar/script | 23 +++++++ .../auto-migrate-direct-only/databricks.yml | 2 +- .../auto-migrate-direct-only/output.txt | 4 +- .../auto-migrate-direct-only/test.toml | 2 - .../ignore_direct_only_resources_test.go | 57 ------------------ 10 files changed, 95 insertions(+), 64 deletions(-) create mode 100644 acceptance/bundle/migrate/auto-migrate-direct-only-envvar/databricks.yml rename acceptance/bundle/migrate/{added-direct-only/test.toml => auto-migrate-direct-only-envvar/out.test.toml} (100%) create mode 100644 acceptance/bundle/migrate/auto-migrate-direct-only-envvar/output.txt create mode 100644 acceptance/bundle/migrate/auto-migrate-direct-only-envvar/script delete mode 100644 acceptance/bundle/migrate/auto-migrate-direct-only/test.toml delete mode 100644 bundle/config/mutator/ignore_direct_only_resources_test.go diff --git a/acceptance/bundle/migrate/added-direct-only/databricks.yml b/acceptance/bundle/migrate/added-direct-only/databricks.yml index e2a561b2525..8f2a40d4f2b 100644 --- a/acceptance/bundle/migrate/added-direct-only/databricks.yml +++ b/acceptance/bundle/migrate/added-direct-only/databricks.yml @@ -2,6 +2,6 @@ bundle: name: test-bundle resources: + #instance_pools: {pool: {instance_pool_name: "My Pool", node_type_id: "Standard_DS3_v2", idle_instance_autotermination_minutes: 60}} jobs: job_a: {name: "Job A"} - #instance_pools: {pool: {instance_pool_name: "My Pool", node_type_id: "Standard_DS3_v2", idle_instance_autotermination_minutes: 60}} diff --git a/acceptance/bundle/migrate/added-direct-only/output.txt b/acceptance/bundle/migrate/added-direct-only/output.txt index 2529c0f8503..13ddf672f43 100644 --- a/acceptance/bundle/migrate/added-direct-only/output.txt +++ b/acceptance/bundle/migrate/added-direct-only/output.txt @@ -12,7 +12,7 @@ Deployment complete! === Deploying on terraform rejects it, which is what sends the user to migrate >>> musterr [CLI] bundle deploy Error: Instance Pool resources are only supported with direct deployment mode - in databricks.yml:7:19 + in databricks.yml:5:19 Instance Pool resources require direct deployment mode. Please set the DATABRICKS_BUNDLE_ENGINE environment variable to 'direct' or set 'bundle.engine: direct' in your databricks.yml to use instance_pool resources. Learn more at https://docs.databricks.com/dev-tools/bundles/direct diff --git a/acceptance/bundle/migrate/auto-migrate-direct-only-envvar/databricks.yml b/acceptance/bundle/migrate/auto-migrate-direct-only-envvar/databricks.yml new file mode 100644 index 00000000000..8f2a40d4f2b --- /dev/null +++ b/acceptance/bundle/migrate/auto-migrate-direct-only-envvar/databricks.yml @@ -0,0 +1,7 @@ +bundle: + name: test-bundle + +resources: + #instance_pools: {pool: {instance_pool_name: "My Pool", node_type_id: "Standard_DS3_v2", idle_instance_autotermination_minutes: 60}} + jobs: + job_a: {name: "Job A"} diff --git a/acceptance/bundle/migrate/added-direct-only/test.toml b/acceptance/bundle/migrate/auto-migrate-direct-only-envvar/out.test.toml similarity index 100% rename from acceptance/bundle/migrate/added-direct-only/test.toml rename to acceptance/bundle/migrate/auto-migrate-direct-only-envvar/out.test.toml diff --git a/acceptance/bundle/migrate/auto-migrate-direct-only-envvar/output.txt b/acceptance/bundle/migrate/auto-migrate-direct-only-envvar/output.txt new file mode 100644 index 00000000000..c6b076421c8 --- /dev/null +++ b/acceptance/bundle/migrate/auto-migrate-direct-only-envvar/output.txt @@ -0,0 +1,60 @@ + +=== Deploy with terraform (instance_pools is commented out; terraform cannot deploy it) +>>> DATABRICKS_BUNDLE_ENGINE=terraform [CLI] bundle deploy +Uploading bundle files to /Workspace/Users/[USERNAME]/.bundle/test-bundle/default/files... +Deploying resources... +Updating deployment state... +Deployment complete! + +=== Add a direct-only resource to the config +>>> update_file.py databricks.yml #instance_pools instance_pools + +=== Opt in via env var only: deploy migrates the state, ignoring the direct-only resource +>>> DATABRICKS_BUNDLE_ENGINE=direct [CLI] bundle deploy +Warn: Direct engine requested in DATABRICKS_BUNDLE_ENGINE environment variable but the existing state uses "terraform". Deploying on "terraform"; will attempt to migrate the state to the direct engine after this deploy. +Warning: ignoring instance_pool resources in this deploy + in databricks.yml:5:19 + +Instance Pool resources are only supported by the direct deployment engine, but the existing state uses terraform. They will be created by the next deploy, after the state is migrated to the direct engine. + +Uploading bundle files to /Workspace/Users/[USERNAME]/.bundle/test-bundle/default/files... +Deploying resources... +Updating deployment state... +Deployment complete! +Migrating state to direct deployment engine (opted in via DATABRICKS_BUNDLE_ENGINE environment variable)... +Migrated 1 resource to direct deployment engine. + +>>> print_migration_telemetry +direct_migrated_via_env true + +=== The direct-only resource is created on the next deploy +>>> [CLI] bundle plan +create instance_pools.pool + +Plan: 1 to add, 0 to change, 0 to delete, 1 unchanged + +>>> [CLI] bundle deploy +Uploading bundle files to /Workspace/Users/[USERNAME]/.bundle/test-bundle/default/files... +Deploying resources... +Updating deployment state... +Deployment complete! + +>>> print_requests.py //instance-pools/create +{ + "headers": { + "User-Agent": [ + "cli/[CLI_VERSION] databricks-sdk-go/[SDK_VERSION] go/[GO_VERSION] os/[OS] cmd/bundle_deploy cmd-exec-id/[UUID] interactive/none engine/direct auth/pat" + ] + }, + "method": "POST", + "path": "/api/2.0/instance-pools/create", + "body": { + "idle_instance_autotermination_minutes": 60, + "instance_pool_name": "My Pool", + "node_type_id": "Standard_DS3_v2" + } +} + +=== No further actions planned +>>> [CLI] bundle plan +Plan: 0 to add, 0 to change, 0 to delete, 2 unchanged diff --git a/acceptance/bundle/migrate/auto-migrate-direct-only-envvar/script b/acceptance/bundle/migrate/auto-migrate-direct-only-envvar/script new file mode 100644 index 00000000000..b605d8edb3c --- /dev/null +++ b/acceptance/bundle/migrate/auto-migrate-direct-only-envvar/script @@ -0,0 +1,23 @@ +export DATABRICKS_BUNDLE_ENGINE= + +title "Deploy with terraform (instance_pools is commented out; terraform cannot deploy it)" +trace DATABRICKS_BUNDLE_ENGINE=terraform $CLI bundle deploy +rm -f out.requests.txt + +title "Add a direct-only resource to the config" +trace update_file.py databricks.yml "#instance_pools" "instance_pools" + +title "Opt in via env var only: deploy migrates the state, ignoring the direct-only resource" +trace DATABRICKS_BUNDLE_ENGINE=direct $CLI bundle deploy +trace print_migration_telemetry +rm -f out.requests.txt + +title "The direct-only resource is created on the next deploy" +trace $CLI bundle plan +trace $CLI bundle deploy +trace print_requests.py //instance-pools/create + +title "No further actions planned" +trace $CLI bundle plan + +rm -f out.requests.txt diff --git a/acceptance/bundle/migrate/auto-migrate-direct-only/databricks.yml b/acceptance/bundle/migrate/auto-migrate-direct-only/databricks.yml index e2a561b2525..8f2a40d4f2b 100644 --- a/acceptance/bundle/migrate/auto-migrate-direct-only/databricks.yml +++ b/acceptance/bundle/migrate/auto-migrate-direct-only/databricks.yml @@ -2,6 +2,6 @@ bundle: name: test-bundle resources: + #instance_pools: {pool: {instance_pool_name: "My Pool", node_type_id: "Standard_DS3_v2", idle_instance_autotermination_minutes: 60}} jobs: job_a: {name: "Job A"} - #instance_pools: {pool: {instance_pool_name: "My Pool", node_type_id: "Standard_DS3_v2", idle_instance_autotermination_minutes: 60}} diff --git a/acceptance/bundle/migrate/auto-migrate-direct-only/output.txt b/acceptance/bundle/migrate/auto-migrate-direct-only/output.txt index 81728ffce35..180592df44f 100644 --- a/acceptance/bundle/migrate/auto-migrate-direct-only/output.txt +++ b/acceptance/bundle/migrate/auto-migrate-direct-only/output.txt @@ -15,7 +15,7 @@ Deployment complete! === Plan ignores the direct-only resource, since this run still uses terraform >>> [CLI] bundle plan Warning: ignoring instance_pool resources in this deploy - in databricks.yml:8:19 + in databricks.yml:6:19 Instance Pool resources are only supported by the direct deployment engine, but the existing state uses terraform. They will be created by the next deploy, after the state is migrated to the direct engine. @@ -25,7 +25,7 @@ Plan: 0 to add, 0 to change, 0 to delete, 1 unchanged >>> [CLI] bundle deploy Warn: Direct engine requested in bundle.engine setting at [TEST_TMP_DIR]/databricks.yml:3:11 but the existing state uses "terraform". Deploying on "terraform"; will attempt to migrate the state to the direct engine after this deploy. Warning: ignoring instance_pool resources in this deploy - in databricks.yml:8:19 + in databricks.yml:6:19 Instance Pool resources are only supported by the direct deployment engine, but the existing state uses terraform. They will be created by the next deploy, after the state is migrated to the direct engine. diff --git a/acceptance/bundle/migrate/auto-migrate-direct-only/test.toml b/acceptance/bundle/migrate/auto-migrate-direct-only/test.toml deleted file mode 100644 index 0938e678987..00000000000 --- a/acceptance/bundle/migrate/auto-migrate-direct-only/test.toml +++ /dev/null @@ -1,2 +0,0 @@ -Cloud = false -EnvMatrix.DATABRICKS_BUNDLE_ENGINE = ["direct"] diff --git a/bundle/config/mutator/ignore_direct_only_resources_test.go b/bundle/config/mutator/ignore_direct_only_resources_test.go deleted file mode 100644 index d498cbc4c8b..00000000000 --- a/bundle/config/mutator/ignore_direct_only_resources_test.go +++ /dev/null @@ -1,57 +0,0 @@ -package mutator_test - -import ( - "testing" - - "github.com/databricks/cli/bundle" - "github.com/databricks/cli/bundle/config" - "github.com/databricks/cli/bundle/config/mutator" - "github.com/databricks/cli/bundle/config/resources" - "github.com/databricks/databricks-sdk-go/service/catalog" - "github.com/databricks/databricks-sdk-go/service/compute" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" -) - -func TestIgnoreDirectOnlyResourcesNoDirectOnlyReturnsNil(t *testing.T) { - b := &bundle.Bundle{ - Config: config.Root{ - Resources: config.Resources{ - Jobs: map[string]*resources.Job{ - "my_job": {}, - }, - }, - }, - } - - diags := bundle.Apply(t.Context(), b, mutator.IgnoreDirectOnlyResources()) - assert.Empty(t, diags) - assert.Len(t, b.Config.Resources.Jobs, 1) -} - -func TestIgnoreDirectOnlyResourcesRemovesThem(t *testing.T) { - b := &bundle.Bundle{ - Config: config.Root{ - Resources: config.Resources{ - Jobs: map[string]*resources.Job{ - "my_job": {}, - }, - Catalogs: map[string]*resources.Catalog{ - "my_catalog": {CreateCatalog: catalog.CreateCatalog{Name: "my_catalog"}}, - }, - InstancePools: map[string]*resources.InstancePool{ - "my_pool": {CreateInstancePool: compute.CreateInstancePool{InstancePoolName: "my_pool"}}, - }, - }, - }, - } - - diags := bundle.Apply(t.Context(), b, mutator.IgnoreDirectOnlyResources()) - require.Len(t, diags, 2) - assert.Equal(t, "ignoring catalog resources in this deploy", diags[0].Summary) - assert.Equal(t, "ignoring instance_pool resources in this deploy", diags[1].Summary) - - assert.Empty(t, b.Config.Resources.Catalogs) - assert.Empty(t, b.Config.Resources.InstancePools) - assert.Len(t, b.Config.Resources.Jobs, 1) -} From e0d19d0e8d3cf694172e229ab7df5f9d9a7d42c9 Mon Sep 17 00:00:00 2001 From: Denis Bilenko Date: Thu, 13 Aug 2026 13:28:37 +0200 Subject: [PATCH 3/3] Skip direct-only resources instead of dropping them from config Co-authored-by: Isaac --- .../bundles/migrate-direct-only-resources.md | 2 +- .../output.txt | 5 -- .../auto-migrate-direct-only/output.txt | 17 ++--- .../migrate/auto-migrate-direct-only/script | 5 +- bundle/bundle.go | 8 +++ .../mutator/ignore_direct_only_resources.go | 67 ------------------- .../mutator/validate_direct_only_resources.go | 9 ++- bundle/deploy/terraform/convert.go | 14 +++- bundle/deploy/terraform/convert_test.go | 4 +- bundle/deploy/terraform/lifecycle_test.go | 2 +- bundle/deploy/terraform/write.go | 2 +- cmd/bundle/utils/process.go | 15 +---- 12 files changed, 44 insertions(+), 106 deletions(-) delete mode 100644 bundle/config/mutator/ignore_direct_only_resources.go diff --git a/.nextchanges/bundles/migrate-direct-only-resources.md b/.nextchanges/bundles/migrate-direct-only-resources.md index c2442a94b00..7da46efea12 100644 --- a/.nextchanges/bundles/migrate-direct-only-resources.md +++ b/.nextchanges/bundles/migrate-direct-only-resources.md @@ -1 +1 @@ -When migrating a bundle to the direct deployment engine, resources that only the direct engine supports (e.g. instance pools, catalogs) are now ignored during the migrating deploy instead of failing it. They are created by the next deploy, which runs on the migrated state. +When migrating a bundle to the direct deployment engine, resources that only the direct engine supports (e.g. instance pools, catalogs) are now skipped by the deploy that migrates the state instead of failing it. They are created by the next deploy, which runs on the migrated state. diff --git a/acceptance/bundle/migrate/auto-migrate-direct-only-envvar/output.txt b/acceptance/bundle/migrate/auto-migrate-direct-only-envvar/output.txt index c6b076421c8..dcc6efc8264 100644 --- a/acceptance/bundle/migrate/auto-migrate-direct-only-envvar/output.txt +++ b/acceptance/bundle/migrate/auto-migrate-direct-only-envvar/output.txt @@ -12,11 +12,6 @@ Deployment complete! === Opt in via env var only: deploy migrates the state, ignoring the direct-only resource >>> DATABRICKS_BUNDLE_ENGINE=direct [CLI] bundle deploy Warn: Direct engine requested in DATABRICKS_BUNDLE_ENGINE environment variable but the existing state uses "terraform". Deploying on "terraform"; will attempt to migrate the state to the direct engine after this deploy. -Warning: ignoring instance_pool resources in this deploy - in databricks.yml:5:19 - -Instance Pool resources are only supported by the direct deployment engine, but the existing state uses terraform. They will be created by the next deploy, after the state is migrated to the direct engine. - Uploading bundle files to /Workspace/Users/[USERNAME]/.bundle/test-bundle/default/files... Deploying resources... Updating deployment state... diff --git a/acceptance/bundle/migrate/auto-migrate-direct-only/output.txt b/acceptance/bundle/migrate/auto-migrate-direct-only/output.txt index 180592df44f..95b2067d012 100644 --- a/acceptance/bundle/migrate/auto-migrate-direct-only/output.txt +++ b/acceptance/bundle/migrate/auto-migrate-direct-only/output.txt @@ -12,23 +12,16 @@ Deployment complete! >>> update_file.py databricks.yml name: test-bundle name: test-bundle engine: direct -=== Plan ignores the direct-only resource, since this run still uses terraform +=== Plan skips the direct-only resource, since this run still uses terraform >>> [CLI] bundle plan -Warning: ignoring instance_pool resources in this deploy - in databricks.yml:6:19 - -Instance Pool resources are only supported by the direct deployment engine, but the existing state uses terraform. They will be created by the next deploy, after the state is migrated to the direct engine. +Plan: 0 to add, 0 to change, 0 to delete, 2 unchanged -Plan: 0 to add, 0 to change, 0 to delete, 1 unchanged +>>> [CLI] bundle plan -o json +json.plan.resources.instance_pools.pool.action = "skip"; -=== Deploy migrates the state, ignoring the direct-only resource +=== Deploy migrates the state, skipping the direct-only resource >>> [CLI] bundle deploy Warn: Direct engine requested in bundle.engine setting at [TEST_TMP_DIR]/databricks.yml:3:11 but the existing state uses "terraform". Deploying on "terraform"; will attempt to migrate the state to the direct engine after this deploy. -Warning: ignoring instance_pool resources in this deploy - in databricks.yml:6:19 - -Instance Pool resources are only supported by the direct deployment engine, but the existing state uses terraform. They will be created by the next deploy, after the state is migrated to the direct engine. - Uploading bundle files to /Workspace/Users/[USERNAME]/.bundle/test-bundle/default/files... Deploying resources... Updating deployment state... diff --git a/acceptance/bundle/migrate/auto-migrate-direct-only/script b/acceptance/bundle/migrate/auto-migrate-direct-only/script index 43015caf6eb..3cb22cb053a 100644 --- a/acceptance/bundle/migrate/auto-migrate-direct-only/script +++ b/acceptance/bundle/migrate/auto-migrate-direct-only/script @@ -8,10 +8,11 @@ title "Add a direct-only resource and opt in to the direct engine via config" trace update_file.py databricks.yml "#instance_pools" "instance_pools" trace update_file.py databricks.yml "name: test-bundle" $'name: test-bundle\n engine: direct' -title "Plan ignores the direct-only resource, since this run still uses terraform" +title "Plan skips the direct-only resource, since this run still uses terraform" trace $CLI bundle plan +trace $CLI bundle plan -o json | gron.py | grep instance_pools -title "Deploy migrates the state, ignoring the direct-only resource" +title "Deploy migrates the state, skipping the direct-only resource" trace $CLI bundle deploy trace print_migration_telemetry rm -f out.requests.txt diff --git a/bundle/bundle.go b/bundle/bundle.go index ff04c3479af..8ce12211c3d 100644 --- a/bundle/bundle.go +++ b/bundle/bundle.go @@ -172,6 +172,14 @@ type Bundle struct { // When non-empty, only the specified resources are included in deployment. Select []string + // MigratingToDirect is set when the direct engine is requested but the existing + // state still uses terraform, so the state is migrated to the direct engine after + // this deploy. Resources that only the direct engine supports are skipped by this + // run rather than rejected: terraform cannot deploy them, and since terraform + // could never have deployed them they are absent from its state. The next deploy, + // which runs on the migrated state, creates them. + MigratingToDirect bool + // SkipLocalFileValidation makes path translation tolerant of missing local files. // When set, TranslatePaths computes workspace paths without verifying files exist. // Used by config-remote-sync: a user may modify resource paths remotely (e.g., diff --git a/bundle/config/mutator/ignore_direct_only_resources.go b/bundle/config/mutator/ignore_direct_only_resources.go deleted file mode 100644 index 0a481a63662..00000000000 --- a/bundle/config/mutator/ignore_direct_only_resources.go +++ /dev/null @@ -1,67 +0,0 @@ -package mutator - -import ( - "context" - "fmt" - - "github.com/databricks/cli/bundle" - "github.com/databricks/cli/libs/diag" - "github.com/databricks/cli/libs/dyn" -) - -type ignoreDirectOnlyResources struct{} - -// IgnoreDirectOnlyResources removes resources that only the direct engine supports -// from the configuration. -// -// It is applied when the user opted in to the direct engine but the existing state -// still uses terraform: this run happens on the terraform engine, which cannot -// deploy these resources. They are new by definition — terraform could never have -// deployed them — so they are absent from the terraform state and the migration -// that follows this deploy does not need them. The next deploy, which runs on the -// migrated state, creates them. -func IgnoreDirectOnlyResources() bundle.Mutator { - return &ignoreDirectOnlyResources{} -} - -func (m *ignoreDirectOnlyResources) Name() string { - return "IgnoreDirectOnlyResources" -} - -func (m *ignoreDirectOnlyResources) Apply(_ context.Context, b *bundle.Bundle) diag.Diagnostics { - var diags diag.Diagnostics - var groups []string - - for _, group := range b.Config.Resources.AllResources() { - if len(group.Resources) == 0 { - continue - } - if !isDirectOnly(group.Description.PluralName) { - continue - } - groups = append(groups, group.Description.PluralName) - diags = diags.Append(diag.Diagnostic{ - Severity: diag.Warning, - Summary: fmt.Sprintf("ignoring %s resources in this deploy", group.Description.SingularName), - Detail: fmt.Sprintf("%s resources are only supported by the direct deployment engine, but the existing state uses terraform. "+ - "They will be created by the next deploy, after the state is migrated to the direct engine.", - group.Description.SingularTitle), - Locations: b.Config.GetLocations("resources." + group.Description.PluralName), - }) - } - - if len(groups) == 0 { - return nil - } - - err := b.Config.Mutate(func(root dyn.Value) (dyn.Value, error) { - return dyn.Map(root, "resources", func(_ dyn.Path, resources dyn.Value) (dyn.Value, error) { - return dyn.DropKeys(resources, groups) - }) - }) - if err != nil { - return diags.Extend(diag.FromErr(err)) - } - - return diags -} diff --git a/bundle/config/mutator/validate_direct_only_resources.go b/bundle/config/mutator/validate_direct_only_resources.go index a76feb9482f..1c3cd41a76c 100644 --- a/bundle/config/mutator/validate_direct_only_resources.go +++ b/bundle/config/mutator/validate_direct_only_resources.go @@ -34,11 +34,18 @@ func isDirectOnly(pluralName string) bool { return hasDirect && !hasTerraform } -func (m *validateDirectOnlyResources) Apply(ctx context.Context, b *bundle.Bundle) diag.Diagnostics { +func (m *validateDirectOnlyResources) Apply(_ context.Context, b *bundle.Bundle) diag.Diagnostics { if m.engine.IsDirect() { return nil } + // The state is migrated to the direct engine right after this deploy, so + // direct-only resources are skipped by this run instead of rejected. + // BundleToTerraformWithDynValue logs each resource it skips. + if b.MigratingToDirect { + return nil + } + var diags diag.Diagnostics for _, group := range b.Config.Resources.AllResources() { if len(group.Resources) == 0 { diff --git a/bundle/deploy/terraform/convert.go b/bundle/deploy/terraform/convert.go index 551cbb0a762..8303c2b3a0d 100644 --- a/bundle/deploy/terraform/convert.go +++ b/bundle/deploy/terraform/convert.go @@ -8,11 +8,17 @@ import ( "github.com/databricks/cli/bundle/deploy/terraform/tfdyn" "github.com/databricks/cli/bundle/internal/tf/schema" "github.com/databricks/cli/libs/dyn" + "github.com/databricks/cli/libs/log" ) // BundleToTerraformWithDynValue converts resources in a bundle configuration // to the equivalent Terraform JSON representation. -func BundleToTerraformWithDynValue(ctx context.Context, root dyn.Value) (*schema.Root, error) { +// +// Resource types without a terraform converter are only supported by the direct +// engine. They are an error here, except when skipUnsupported is set: the state is +// migrated to the direct engine right after this deploy, so they are skipped by +// this run and deployed by the next one. See bundle.Bundle.MigratingToDirect. +func BundleToTerraformWithDynValue(ctx context.Context, root dyn.Value, skipUnsupported bool) (*schema.Root, error) { tfroot := schema.NewRoot() tfroot.Provider = schema.NewProviders() @@ -49,7 +55,11 @@ func BundleToTerraformWithDynValue(ctx context.Context, root dyn.Value) (*schema // Lookup the converter based on the resource type. c, ok := tfdyn.GetConverter(typ) if !ok { - return dyn.InvalidValue, fmt.Errorf("no converter for resource type %s", typ) + if !skipUnsupported { + return dyn.InvalidValue, fmt.Errorf("no converter for resource type %s", typ) + } + log.Infof(ctx, "%s.%s: skipping, only supported by the direct engine; will be deployed after the state is migrated", typ, key) + return v, dyn.ErrSkip } // Convert resource to Terraform representation. diff --git a/bundle/deploy/terraform/convert_test.go b/bundle/deploy/terraform/convert_test.go index 564340190d4..2b709787969 100644 --- a/bundle/deploy/terraform/convert_test.go +++ b/bundle/deploy/terraform/convert_test.go @@ -21,7 +21,7 @@ import ( func produceTerraformConfiguration(t *testing.T, config config.Root) *schema.Root { vin, err := convert.FromTyped(config, dyn.NilValue) require.NoError(t, err) - out, err := BundleToTerraformWithDynValue(t.Context(), vin) + out, err := BundleToTerraformWithDynValue(t.Context(), vin, false) require.NoError(t, err) return out } @@ -588,7 +588,7 @@ func TestBundleToTerraformDeletedResources(t *testing.T) { vin, err := convert.FromTyped(config, dyn.NilValue) require.NoError(t, err) - out, err := BundleToTerraformWithDynValue(t.Context(), vin) + out, err := BundleToTerraformWithDynValue(t.Context(), vin, false) require.NoError(t, err) _, ok := out.Resource.Job["my_job1"] diff --git a/bundle/deploy/terraform/lifecycle_test.go b/bundle/deploy/terraform/lifecycle_test.go index e44b86509ef..f8a5140576f 100644 --- a/bundle/deploy/terraform/lifecycle_test.go +++ b/bundle/deploy/terraform/lifecycle_test.go @@ -45,7 +45,7 @@ func TestConvertLifecycleForAllResources(t *testing.T) { }, nil), }, nil) - tfroot, err := BundleToTerraformWithDynValue(ctx, vin) + tfroot, err := BundleToTerraformWithDynValue(ctx, vin, false) require.NoError(t, err) bytes, err := json.Marshal(tfroot.Resource) diff --git a/bundle/deploy/terraform/write.go b/bundle/deploy/terraform/write.go index bee777ffe00..3a87d1baabe 100644 --- a/bundle/deploy/terraform/write.go +++ b/bundle/deploy/terraform/write.go @@ -26,7 +26,7 @@ func (w *write) Apply(ctx context.Context, b *bundle.Bundle) diag.Diagnostics { var root *schema.Root err = b.Config.Mutate(func(v dyn.Value) (dyn.Value, error) { - root, err = BundleToTerraformWithDynValue(ctx, v) + root, err = BundleToTerraformWithDynValue(ctx, v, b.MigratingToDirect) return v, err }) if err != nil { diff --git a/cmd/bundle/utils/process.go b/cmd/bundle/utils/process.go index fc493b30d82..8bbe98bd1a0 100644 --- a/cmd/bundle/utils/process.go +++ b/cmd/bundle/utils/process.go @@ -189,12 +189,14 @@ func ProcessBundleRet(cmd *cobra.Command, opts ProcessOptions) (b *bundle.Bundle } cmd.SetContext(ctx) + b.MigratingToDirect = requiredEngine.Type == engine.EngineDirect && !stateDesc.Engine.IsDirect() + // Announce the auto-migration path here (only on deploy) so the user // isn't surprised when MigrateToDirect commits state changes at the // end. PullResourcesState is shared with non-deploy commands like // `bundle debug states`, which would otherwise print the same hint // even though they will not migrate. - if opts.Deploy && requiredEngine.Type == engine.EngineDirect && !stateDesc.Engine.IsDirect() { + if opts.Deploy && b.MigratingToDirect { log.Warnf(ctx, "Direct engine requested in %s but the existing state uses %q. Deploying on %q; will attempt to migrate the state to the direct engine after this deploy.", requiredEngine.Source, stateDesc.Engine, stateDesc.Engine) } @@ -325,17 +327,6 @@ func ProcessBundleRet(cmd *cobra.Command, opts ProcessOptions) (b *bundle.Bundle } if opts.PreDeployChecks { - // The state is about to be migrated to the direct engine, but this run still - // uses terraform, which cannot deploy direct-only resources. Drop them from - // the config so this run succeeds and the migration can proceed; the next - // deploy, which runs on the migrated state, creates them. - if requiredEngine.Type == engine.EngineDirect && !stateDesc.Engine.IsDirect() { - bundle.ApplyContext(ctx, b, mutator.IgnoreDirectOnlyResources()) - if logdiag.HasError(ctx) { - return b, stateDesc, root.ErrAlreadyPrinted - } - } - downgradeWarningToError := !opts.Deploy phases.PreDeployChecks(ctx, b, downgradeWarningToError, stateDesc.Engine)