diff --git a/.nextchanges/bundles/7036.md b/.nextchanges/bundles/7036.md new file mode 100644 index 00000000000..b07601212bb --- /dev/null +++ b/.nextchanges/bundles/7036.md @@ -0,0 +1 @@ +* Fix bundle plan reporting an Update on every deploy for schemas and catalogs with properties the config never declared. ([#7036](https://github.com/databricks/cli/pull/7036)) diff --git a/bundle/direct/bundle_plan_test.go b/bundle/direct/bundle_plan_test.go index 025ef91df6e..a23a8cbab49 100644 --- a/bundle/direct/bundle_plan_test.go +++ b/bundle/direct/bundle_plan_test.go @@ -175,6 +175,38 @@ func TestShouldSkipBackendDefault_ManagedPropertiesOnly(t *testing.T) { } } +func TestSchemaCatalogRemoteOnlyPropertiesSkipped(t *testing.T) { + for _, resource := range []string{"schemas", "catalogs"} { + t.Run(resource, func(t *testing.T) { + cfg := dresources.GetResourceConfig(resource) + path, err := structpath.ParsePath("properties") + require.NoError(t, err) + _, ok := shouldSkipBackendDefault(cfg, path, &deployplan.ChangeDesc{ + Old: nil, + New: nil, + Remote: map[string]string{"owner": "root"}, + }) + assert.True(t, ok) + + _, ok = shouldSkipBackendDefault(cfg, path, &deployplan.ChangeDesc{ + Old: map[string]string{"owner": "root"}, + New: map[string]string{"owner": "root"}, + Remote: map[string]string{"owner": "other"}, + }) + assert.False(t, ok) + + child, err := structpath.ParsePath("properties['owner']") + require.NoError(t, err) + _, ok = shouldSkipBackendDefault(cfg, child, &deployplan.ChangeDesc{ + Old: "root", + New: "root", + Remote: "other", + }) + assert.False(t, ok) + }) + } +} + // A field present in StateType but absent from RemoteType (input-only, e.g. external // locations' skip_validation) reads back nil, so a coincidental new == remote must NOT // trip RemoteAlreadySet — that would skip a real local change. A field present in RemoteType diff --git a/bundle/direct/dresources/configs/catalogs.yml b/bundle/direct/dresources/configs/catalogs.yml index 143a74c447d..538304a8a66 100644 --- a/bundle/direct/dresources/configs/catalogs.yml +++ b/bundle/direct/dresources/configs/catalogs.yml @@ -17,5 +17,8 @@ updatable_id_fields: - field: name reason: id_changes backend_defaults: - # UC auto-populates unity.catalog.managed..defaults.* keys after create. - - field: properties['unity.catalog.managed.*.defaults.*'] + # Skip properties present only on the remote when the config declares none, + # including unity.catalog.managed.*.defaults.* keys UC sets after create. + # The SDK cannot send an empty map to clear them. Declared properties + # still take part in drift detection. + - field: properties diff --git a/bundle/direct/dresources/configs/schemas.yml b/bundle/direct/dresources/configs/schemas.yml index 8ef27e71d15..80d2c1e6e87 100644 --- a/bundle/direct/dresources/configs/schemas.yml +++ b/bundle/direct/dresources/configs/schemas.yml @@ -11,7 +11,8 @@ normalize_slash: - field: storage_root reason: uc_strips_trailing_slash backend_defaults: - # UC auto-populates unity.catalog.managed..defaults.* keys after create. - # Without this, every subsequent plan produces an Update whose payload is empty, - # and UC rejects it with "UpdateSchema Nothing to update". - - field: properties['unity.catalog.managed.*.defaults.*'] + # Skip properties present only on the remote when the config declares none, + # including unity.catalog.managed.*.defaults.* keys UC sets after create. + # The SDK cannot send an empty map to clear them. Declared properties + # still take part in drift detection. + - field: properties