diff --git a/acceptance/bundle/invariant/auto-migrate/out.test.toml b/acceptance/bundle/invariant/auto-migrate/out.test.toml new file mode 100644 index 00000000000..099b356372e --- /dev/null +++ b/acceptance/bundle/invariant/auto-migrate/out.test.toml @@ -0,0 +1,47 @@ +Cloud = true +EnvMatrix.DATABRICKS_BUNDLE_ENGINE = ["direct"] +EnvMatrix.DMS = [""] +EnvMatrix.INPUT_CONFIG = [ + "alert.yml.tmpl", + "app.yml.tmpl", + "cluster.yml.tmpl", + "cluster_apply_policy_default_values.yml.tmpl", + "dashboard.yml.tmpl", + "job_apply_policy_default_values_job_cluster.yml.tmpl", + "job_apply_policy_default_values_task_cluster.yml.tmpl", + "job_apply_policy_default_values_for_each_task.yml.tmpl", + "database_catalog.yml.tmpl", + "database_instance.yml.tmpl", + "experiment.yml.tmpl", + "job.yml.tmpl", + "job_pydabs_10_tasks.yml.tmpl", + "job_escaped_refs.yml.tmpl", + "job_run_job_ref.yml.tmpl", + "job_table_update_trigger.yml.tmpl", + "job_with_depends_on.yml.tmpl", + "job_with_task.yml.tmpl", + "model.yml.tmpl", + "model_with_permissions.yml.tmpl", + "model_serving_endpoint.yml.tmpl", + "pipeline.yml.tmpl", + "pipeline_allow_duplicate_names.yml.tmpl", + "pipeline_apply_policy_default_values.yml.tmpl", + "pipeline_config_dots.yml.tmpl", + "postgres_branch.yml.tmpl", + "postgres_catalog.yml.tmpl", + "postgres_database.yml.tmpl", + "postgres_endpoint.yml.tmpl", + "postgres_project.yml.tmpl", + "postgres_role.yml.tmpl", + "postgres_synced_table.yml.tmpl", + "registered_model.yml.tmpl", + "schema_tf_compat.yml.tmpl", + "schema_empty_grants.yml.tmpl", + "schema_uppercase_name.yml.tmpl", + "secret_scope.yml.tmpl", + "secret_scope_default_backend_type.yml.tmpl", + "synced_database_table.yml.tmpl", + "volume.yml.tmpl", + "volume_external.yml.tmpl", + "volume_path_job_ref.yml.tmpl" +] diff --git a/acceptance/bundle/invariant/auto-migrate/output.txt b/acceptance/bundle/invariant/auto-migrate/output.txt new file mode 100644 index 00000000000..7a28cb73a58 --- /dev/null +++ b/acceptance/bundle/invariant/auto-migrate/output.txt @@ -0,0 +1 @@ +INPUT_CONFIG_OK diff --git a/acceptance/bundle/invariant/auto-migrate/script b/acceptance/bundle/invariant/auto-migrate/script new file mode 100644 index 00000000000..e2bac3ac3bd --- /dev/null +++ b/acceptance/bundle/invariant/auto-migrate/script @@ -0,0 +1,17 @@ +# Invariant to test: auto-migration on deploy is successful, no drift after deploy +# Additional checks: no internal errors / panics in any commands + +unset DATABRICKS_BUNDLE_ENGINE + +invariant_setup + +invariant_deploy LOG.deploy DATABRICKS_BUNDLE_ENGINE=terraform $CLI bundle deploy + +# Deploy on the direct default (engine unset): this migrates the terraform state to the +# direct engine before deploying, exercising the auto path. Not invariant_deploy -- the +# config was already validated by the terraform deploy above, so a failure here is a bug, +# not an invalid config (and it must not re-emit the INPUT_CONFIG_OK marker). +trace $CLI bundle deploy &> LOG.migrate +cat LOG.migrate | contains.py '!panic:' '!internal error' > /dev/null + +invariant_verify_no_drift diff --git a/acceptance/bundle/invariant/auto-migrate/test.toml b/acceptance/bundle/invariant/auto-migrate/test.toml new file mode 100644 index 00000000000..1e7a9351e39 --- /dev/null +++ b/acceptance/bundle/invariant/auto-migrate/test.toml @@ -0,0 +1,64 @@ +# Recording needs a bundle it has seen from the start. This test seeds a state file, +# so recording refuses it. TODO(DMS): drop this once existing state can be +# handed over to the service (see the TODO in dstate.Open). +EnvMatrix.DMS = [""] + +# schema.yml sets custom_max_retention_hours, which the terraform databricks_schema resource cannot +# set (only direct does), so a terraform-first migrate would drift. schema_tf_compat.yml keeps migrate +# coverage for schemas. +EnvMatrixExclude.no_schema_custom_retention = ["INPUT_CONFIG=schema.yml.tmpl"] + +# vector_search_endpoints and vector_search_indexes have no terraform converter +EnvMatrixExclude.no_vector_search_endpoint = ["INPUT_CONFIG=vector_search_endpoint.yml.tmpl"] +EnvMatrixExclude.no_vector_search_index = ["INPUT_CONFIG=vector_search_index.yml.tmpl"] + +# job_runs is a direct-only resource with no terraform converter, so the +# terraform deploy that seeds the migration fails for it. +EnvMatrixExclude.no_job_run = ["INPUT_CONFIG=job_run.yml.tmpl"] + +# Error: Catalog resources are only supported with direct deployment mode +EnvMatrixExclude.no_catalog = ["INPUT_CONFIG=catalog.yml.tmpl"] +EnvMatrixExclude.no_catalog_optional_fields = ["INPUT_CONFIG=catalog_optional_fields.yml.tmpl"] +EnvMatrixExclude.no_external_location = ["INPUT_CONFIG=external_location.yml.tmpl"] +# uc_trailing_slash includes a catalog, which is direct-only (see above). +EnvMatrixExclude.no_uc_trailing_slash = ["INPUT_CONFIG=uc_trailing_slash.yml.tmpl"] +# Genie spaces are direct-only too; the terraform deploy that seeds the migration fails for them. +EnvMatrixExclude.no_genie_space = ["INPUT_CONFIG=genie_space.yml.tmpl"] +# Instance pools are direct-only; the terraform deploy that seeds the migration fails for them. +EnvMatrixExclude.no_instance_pool = ["INPUT_CONFIG=instance_pool.yml.tmpl"] +# Cluster policies are direct-only; the terraform deploy that seeds the migration fails for them. +EnvMatrixExclude.no_cluster_policy = ["INPUT_CONFIG=cluster_policy.yml.tmpl"] +# Cluster libraries are direct-only; the terraform deploy that seeds the migration fails for them. +EnvMatrixExclude.no_cluster_libraries = ["INPUT_CONFIG=cluster_libraries.yml.tmpl"] +# AI Gateway securables are direct-only; the terraform deploy that seeds the migration fails for them. +EnvMatrixExclude.no_model_service = ["INPUT_CONFIG=model_service.yml.tmpl"] + +# Cross-resource permission references (e.g. ${resources.jobs.job_b.permissions[0].level}) +# don't work in terraform mode: the terraform interpolator converts the path to +# ${databricks_job.job_b.permissions[0].level}, but Terraform's databricks_job resource +# does not expose permissions as output attributes (permissions are a separate +# databricks_permissions resource in terraform), so the literal unresolved string +# ends up as the permission level value. +EnvMatrixExclude.no_permission_ref = ["INPUT_CONFIG=job_permission_ref.yml.tmpl"] +EnvMatrixExclude.no_cross_resource_ref = ["INPUT_CONFIG=job_cross_resource_ref.yml.tmpl"] + +# Grant cross-references require the EmbeddedSlice pattern not present in terraform mode. +EnvMatrixExclude.no_grant_ref = ["INPUT_CONFIG=schema_grant_ref.yml.tmpl"] + +# SQL warehouses currently failing with migration with permanent drift. TODO: fix this. +EnvMatrixExclude.no_sql_warehouse = ["INPUT_CONFIG=sql_warehouse.yml.tmpl"] + +# The 1000-task scale case is covered by no_drift. Running it here adds ~1.5 min +# per variant (deploy + migrate + plan at 1000 tasks) without incremental coverage. +EnvMatrixExclude.no_pydabs_1000_tasks = ["INPUT_CONFIG=job_pydabs_1000_tasks.yml.tmpl"] + +# migrate deploys via Terraform first, and the TF provider rejects an uppercase +# volume schema_name ("inconsistent final plan"). Covered by no_drift on direct. +EnvMatrixExclude.no_volume_uppercase = ["INPUT_CONFIG=volume_uppercase_name.yml.tmpl"] + +# UC secrets are direct-only (no terraform converter), so the terraform deploy that seeds +# the migration fails for them. +EnvMatrixExclude.no_secret = ["INPUT_CONFIG=secret.yml.tmpl"] + +# Terraform types sampling_fraction as an integer and truncates 0.5; covered by no_drift. +EnvMatrixExclude.no_model_serving_endpoint_telemetry = ["INPUT_CONFIG=model_serving_endpoint_telemetry.yml.tmpl"] diff --git a/acceptance/selftest/selection/output.txt b/acceptance/selftest/selection/output.txt index 505dd11316c..49786a52b46 100644 --- a/acceptance/selftest/selection/output.txt +++ b/acceptance/selftest/selection/output.txt @@ -28,7 +28,8 @@ Selected 2 changed tests (limit=50, 0 not selected) === Touching an invariant config runs every invariant test, for that config only >>> selection M:acceptance/bundle/invariant/configs/job.yml.tmpl -Selected 5 changed tests (limit=50, 0 not selected) +Selected 6 changed tests (limit=50, 0 not selected) + 5 ^bundle$/^invariant$/^auto-migrate$/^DATABRICKS_BUNDLE_ENGINE=direct$/^DMS=$/^INPUT_CONFIG=job\.yml\.tmpl$ 5 ^bundle$/^invariant$/^continue_293$/^DATABRICKS_BUNDLE_ENGINE=direct$/^DMS=$/^INPUT_CONFIG=job\.yml\.tmpl$ 5 ^bundle$/^invariant$/^delete_idempotent$/^DATABRICKS_BUNDLE_ENGINE=direct$/^DMS=$/^INPUT_CONFIG=job\.yml\.tmpl$ 5 ^bundle$/^invariant$/^destroy_idempotent$/^DATABRICKS_BUNDLE_ENGINE=direct$/^DMS=$/^INPUT_CONFIG=job\.yml\.tmpl$ @@ -39,13 +40,15 @@ Selected 5 changed tests (limit=50, 0 not selected) === Adding one invariant config and touching another: only the new variant is new >>> selection A:acceptance/bundle/invariant/configs/pipeline.yml.tmpl M:acceptance/bundle/invariant/configs/job.yml.tmpl -Selected 10 changed tests (limit=50, 0 not selected) +Selected 12 changed tests (limit=50, 0 not selected) + 10 ^bundle$/^invariant$/^auto-migrate$/^DATABRICKS_BUNDLE_ENGINE=direct$/^DMS=$/^INPUT_CONFIG=pipeline\.yml\.tmpl$ 10 ^bundle$/^invariant$/^continue_293$/^DATABRICKS_BUNDLE_ENGINE=direct$/^DMS=$/^INPUT_CONFIG=pipeline\.yml\.tmpl$ 10 ^bundle$/^invariant$/^delete_idempotent$/^DATABRICKS_BUNDLE_ENGINE=direct$/^DMS=$/^INPUT_CONFIG=pipeline\.yml\.tmpl$ 10 ^bundle$/^invariant$/^destroy_idempotent$/^DATABRICKS_BUNDLE_ENGINE=direct$/^DMS=$/^INPUT_CONFIG=pipeline\.yml\.tmpl$ 10 ^bundle$/^invariant$/^migrate$/^DATABRICKS_BUNDLE_ENGINE=direct$/^DMS=$/^INPUT_CONFIG=pipeline\.yml\.tmpl$ 10 ^bundle$/^invariant$/^no_drift$/^DATABRICKS_BUNDLE_ENGINE=direct$/^DMS=$/^INPUT_CONFIG=pipeline\.yml\.tmpl$ 10 ^bundle$/^invariant$/^no_drift$/^DATABRICKS_BUNDLE_ENGINE=direct$/^DMS=true$/^INPUT_CONFIG=pipeline\.yml\.tmpl$ + 5 ^bundle$/^invariant$/^auto-migrate$/^DATABRICKS_BUNDLE_ENGINE=direct$/^DMS=$/^INPUT_CONFIG=job\.yml\.tmpl$ 5 ^bundle$/^invariant$/^continue_293$/^DATABRICKS_BUNDLE_ENGINE=direct$/^DMS=$/^INPUT_CONFIG=job\.yml\.tmpl$ 5 ^bundle$/^invariant$/^delete_idempotent$/^DATABRICKS_BUNDLE_ENGINE=direct$/^DMS=$/^INPUT_CONFIG=job\.yml\.tmpl$ 5 ^bundle$/^invariant$/^destroy_idempotent$/^DATABRICKS_BUNDLE_ENGINE=direct$/^DMS=$/^INPUT_CONFIG=job\.yml\.tmpl$ @@ -71,7 +74,7 @@ Selected 1 changed tests (limit=50, 0 not selected) === The limit keeps the highest scoring tests >>> selection -limit 3 A:acceptance/selftest/basic/script M:acceptance/selftest/diff/script M:acceptance/bundle/invariant/configs/job.yml.tmpl -Selected 3 changed tests (limit=3, 4 not selected) +Selected 3 changed tests (limit=3, 5 not selected) 10 ^selftest$/^basic$ + 5 ^bundle$/^invariant$/^auto-migrate$/^DATABRICKS_BUNDLE_ENGINE=direct$/^DMS=$/^INPUT_CONFIG=job\.yml\.tmpl$ 5 ^bundle$/^invariant$/^continue_293$/^DATABRICKS_BUNDLE_ENGINE=direct$/^DMS=$/^INPUT_CONFIG=job\.yml\.tmpl$ - 5 ^bundle$/^invariant$/^delete_idempotent$/^DATABRICKS_BUNDLE_ENGINE=direct$/^DMS=$/^INPUT_CONFIG=job\.yml\.tmpl$