Skip to content

Direct-engine reconcile classification fixes#5816

Merged
radakam merged 3 commits into
mainfrom
fix/direct-reconcile-classification
Jul 6, 2026
Merged

Direct-engine reconcile classification fixes#5816
radakam merged 3 commits into
mainfrom
fix/direct-reconcile-classification

Conversation

@radakam

@radakam radakam commented Jul 3, 2026

Copy link
Copy Markdown
Contributor

Why

Fuzzing the direct engine found fields that UC/serving GET responses either compute or never echo back. They weren't classified, so bundle plan showed a permanent update after deploy and redeploy could fail with 400 ... Nothing to update.

Changes

bundle/direct/dresources/resources.yml:

  • catalogs: skip UC-injected properties['unity.catalog.managed.*'] as backend_defaults (mirrors existing schemas fix).
  • registered_models: browse_onlyignore_remote_changes (output-only, backend-computed, not in update surface).
  • model_serving_endpoints: config.served_entities[*].burst_scaling_enabled + all external-model *_plaintext secrets → ignore_remote_changes (input-only, accepted on write but never returned on GET).

These aren't annotated in the OpenAPI spec, so they're declared manually (absent from resources.generated.yml).

libs/testserver/:

  • catalogs.go / registered_models.go: inject the computed values, scoped to dedicated names (catalog_managed_defaults, model_browse_only).
  • serving_endpoints.go: strip *_plaintext secrets on read (burst_scaling_enabled already wasn't round-tripped).

Tests

New direct-engine acceptance tests asserting no-op plan + no update call on redeploy:

  • acceptance/bundle/resources/catalogs/drift/managed_properties
  • acceptance/bundle/resources/registered_models/drift/browse_only
  • acceptance/bundle/resources/model_serving_endpoints/drift/write_only

@radakam radakam temporarily deployed to test-trigger-is July 3, 2026 09:12 — with GitHub Actions Inactive
@radakam radakam temporarily deployed to test-trigger-is July 3, 2026 09:12 — with GitHub Actions Inactive
… fields

Several fields returned (or intentionally not returned) by UC/serving GET
responses were not classified, so `bundle plan` reported a perpetual Update
after deploy and could hard-fail with "Nothing to update" on redeploy.

- catalogs: skip UC-injected `unity.catalog.managed.*` properties as
  backend defaults (mirrors the existing schemas handling).
- registered_models: treat backend-computed `browse_only` as output-only.
- model_serving_endpoints: ignore remote changes for `burst_scaling_enabled`
  and the write-only external-model `*_plaintext` secrets, none of which are
  echoed on GET.

The testserver is extended to reproduce each drift (scoped catalog/model
names, and stripping serving secrets on read) and new acceptance tests under
each resource's `drift/` dir lock in the no-op plan and redeploy behavior.
@radakam radakam force-pushed the fix/direct-reconcile-classification branch from c01de9a to f4ed131 Compare July 3, 2026 09:19
@radakam radakam temporarily deployed to test-trigger-is July 3, 2026 09:20 — with GitHub Actions Inactive
@radakam radakam temporarily deployed to test-trigger-is July 3, 2026 09:20 — with GitHub Actions Inactive
@radakam radakam marked this pull request as ready for review July 3, 2026 09:21
@eng-dev-ecosystem-bot

eng-dev-ecosystem-bot commented Jul 3, 2026

Copy link
Copy Markdown
Collaborator

Integration test report

Commit: 94729f7

Run: 28817024291

Env 💚​RECOVERED 🙈​SKIP ✅​pass 🙈​skip Time
💚​ aws linux 4 4 230 1059 5:01
💚​ aws windows 4 4 232 1057 6:17
💚​ aws-ucws linux 4 4 314 977 5:25
💚​ aws-ucws windows 4 4 316 975 6:00
💚​ azure linux 4 4 230 1058 5:12
💚​ azure windows 4 4 232 1056 6:12
💚​ azure-ucws linux 4 4 316 974 7:24
💚​ azure-ucws windows 4 4 318 972 6:17
💚​ gcp linux 4 4 229 1060 4:54
💚​ gcp windows 4 4 231 1058 6:06
8 interesting tests: 4 RECOVERED, 4 SKIP
Test Name aws linux aws windows aws-ucws linux aws-ucws windows azure linux azure windows azure-ucws linux azure-ucws windows gcp linux gcp windows
💚​ TestAccept 💚​R 💚​R 💚​R 💚​R 💚​R 💚​R 💚​R 💚​R 💚​R 💚​R
🙈​ TestAccept/bundle/invariant/no_drift 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S
🙈​ TestAccept/bundle/resources/vector_search_endpoints/drift/recreated_same_name 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S
🙈​ TestAccept/bundle/resources/vector_search_indexes/recreate/embedding_dimension 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S
🙈​ TestAccept/ssh/connection 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S
💚​ TestFetchRepositoryInfoAPI_FromRepo 💚​R 💚​R 💚​R 💚​R 💚​R 💚​R 💚​R 💚​R 💚​R 💚​R
💚​ TestFetchRepositoryInfoAPI_FromRepo/root 💚​R 💚​R 💚​R 💚​R 💚​R 💚​R 💚​R 💚​R 💚​R 💚​R
💚​ TestFetchRepositoryInfoAPI_FromRepo/subdir 💚​R 💚​R 💚​R 💚​R 💚​R 💚​R 💚​R 💚​R 💚​R 💚​R
Top 10 slowest tests (at least 2 minutes):
duration env testname
5:18 azure-ucws windows TestAccept
5:16 gcp windows TestAccept
5:14 azure windows TestAccept
5:06 aws-ucws windows TestAccept
5:03 aws windows TestAccept
3:00 aws linux TestAccept
2:56 azure linux TestAccept
2:51 gcp linux TestAccept
2:51 azure-ucws linux TestAccept
2:49 aws-ucws linux TestAccept

reason: output_only
- field: updated_by
reason: output_only
# Backend-computed; the user never sets it. Not annotated output_only in the spec.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should probably annotate browse_only as output-only (or drop it from the create request) in the universe OpenAPI spec so the regenerated SDK stops surfacing it as a writable field.

# Accepted on write but not returned by GET.
- field: config.served_entities[*].burst_scaling_enabled
reason: input_only
# Write-only secrets: the backend stores them and returns the reference field, not the plaintext.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can we wild-card match these? there will be more as more models are added

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure about that, wildcards can't match *_plaintext, and broader wildcards would also ignore legitimate GET fields. It might be better to mark these fields as write-only in the OpenAPI spec so they're generated automatically, like browse_only, what do you think?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

on second look there's quite a few different patterns here (I was hoping it'd just be

(?<model>)_config.\1_api_key_plaintext

but that won't work and won't be fun to implement/maintain)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

would be great if you can fix it upstream 🙌

resources:
catalogs:
catalog1:
name: catalog_managed_defaults

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We have acceptance/bundle/invariant/configs/catalog.yml.tmpl why did not it catch this?

Is it because fuzz testing was against dogfood which adds these fields but our test env does not?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Exactly -- the testserver wasn't populating these fields (dogfood does), so there was no drift to catch. I also added that to the testserver so it's now reproducible.

# Accepted on write but not returned by GET.
- field: config.served_entities[*].burst_scaling_enabled
reason: input_only
# Write-only secrets: the backend stores them and returns the reference field, not the plaintext.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

on second look there's quite a few different patterns here (I was hoping it'd just be

(?<model>)_config.\1_api_key_plaintext

but that won't work and won't be fun to implement/maintain)

# Accepted on write but not returned by GET.
- field: config.served_entities[*].burst_scaling_enabled
reason: input_only
# Write-only secrets: the backend stores them and returns the reference field, not the plaintext.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

would be great if you can fix it upstream 🙌

@radakam radakam enabled auto-merge July 6, 2026 13:08
@radakam radakam temporarily deployed to test-trigger-is July 6, 2026 13:12 — with GitHub Actions Inactive
@radakam radakam disabled auto-merge July 6, 2026 13:28
@radakam radakam temporarily deployed to test-trigger-is July 6, 2026 19:17 — with GitHub Actions Inactive
@radakam radakam temporarily deployed to test-trigger-is July 6, 2026 19:17 — with GitHub Actions Inactive
@radakam radakam added this pull request to the merge queue Jul 6, 2026
Merged via the queue into main with commit f5640af Jul 6, 2026
23 checks passed
@radakam radakam deleted the fix/direct-reconcile-classification branch July 6, 2026 20:09
@eng-dev-ecosystem-bot

Copy link
Copy Markdown
Collaborator

Integration test report

Commit: f5640af

Run: 28820038626

Env ❌​FAIL 🟨​KNOWN 🔄​flaky 💚​RECOVERED 🙈​SKIP ✅​pass 🙈​skip Time
💚​ aws linux 4 4 529 992 44:46
🔄​ aws windows 6 4 4 480 1005 61:49
❌​ aws-ucws linux 4 2 6 3 2 979 807 88:36
❌​ aws-ucws windows 4 2 6 4 2 920 825 97:06
💚​ azure linux 4 4 523 993 44:38
🔄​ azure windows 3 3 4 478 1006 59:17
❌​ azure-ucws linux 4 2 4 4 2 886 838 83:00
❌​ azure-ucws windows 4 2 4 4 2 828 856 92:33
💚​ gcp linux 4 4 514 998 44:03
🔄​ gcp windows 3 3 4 469 1011 52:06
23 interesting tests: 12 flaky, 4 FAIL, 3 RECOVERED, 2 KNOWN, 2 SKIP
Test Name aws linux aws windows aws-ucws linux aws-ucws windows azure linux azure windows azure-ucws linux azure-ucws windows gcp linux gcp windows
🟨​ TestAccept 💚​R 💚​R 🟨​K 🟨​K 💚​R 🔄​f 🟨​K 🟨​K 💚​R 🔄​f
🔄​ TestAccept/bundle/generate/pipeline_and_deploy ✅​p 🔄​f 🔄​f 🔄​f ✅​p 🔄​f 🔄​f 🔄​f ✅​p 🔄​f
🔄​ TestAccept/bundle/generate/pipeline_and_deploy/DATABRICKS_BUNDLE_ENGINE=direct ✅​p 🔄​f 🔄​f ✅​p ✅​p 🔄​f 🔄​f 🔄​f ✅​p ✅​p
🔄​ TestAccept/bundle/generate/pipeline_and_deploy/DATABRICKS_BUNDLE_ENGINE=terraform ✅​p ✅​p ✅​p 🔄​f ✅​p ✅​p ✅​p ✅​p ✅​p 🔄​f
🔄​ TestAccept/bundle/generate/python_job_and_deploy ✅​p 🔄​f ✅​p 🔄​f ✅​p ✅​p 🔄​f 🔄​f ✅​p ✅​p
🔄​ TestAccept/bundle/generate/python_job_and_deploy/DATABRICKS_BUNDLE_ENGINE=direct ✅​p ✅​p ✅​p ✅​p ✅​p ✅​p 🔄​f 🔄​f ✅​p ✅​p
🔄​ TestAccept/bundle/generate/python_job_and_deploy/DATABRICKS_BUNDLE_ENGINE=terraform ✅​p 🔄​f ✅​p 🔄​f ✅​p ✅​p ✅​p ✅​p ✅​p ✅​p
❌​ TestAccept/bundle/invariant/migrate 🙈​s 🙈​s ❌​F ❌​F 🙈​s 🙈​s ❌​F ❌​F 🙈​s 🙈​s
❌​ TestAccept/bundle/invariant/migrate/DATABRICKS_BUNDLE_ENGINE=direct/INPUT_CONFIG=volume_path_job_ref.yml.tmpl ❌​F ❌​F ❌​F ❌​F
🟨​ TestAccept/bundle/invariant/no_drift 🙈​S 🙈​S 🟨​K 🟨​K 🙈​S 🙈​S 🟨​K 🟨​K 🙈​S 🙈​S
❌​ TestAccept/bundle/invariant/no_drift/DATABRICKS_BUNDLE_ENGINE=direct/INPUT_CONFIG=volume_path_job_ref.yml.tmpl/READPLAN= ❌​F ❌​F ❌​F ❌​F
❌​ TestAccept/bundle/invariant/no_drift/DATABRICKS_BUNDLE_ENGINE=direct/INPUT_CONFIG=volume_path_job_ref.yml.tmpl/READPLAN=1 ❌​F ❌​F ❌​F ❌​F
🔄​ TestAccept/bundle/resources/clusters/lifecycle-started ✅​p 🔄​f ✅​p ✅​p ✅​p ✅​p ✅​p ✅​p ✅​p ✅​p
🔄​ TestAccept/bundle/resources/clusters/lifecycle-started/DATABRICKS_BUNDLE_ENGINE=direct ✅​p 🔄​f ✅​p ✅​p ✅​p ✅​p ✅​p ✅​p ✅​p ✅​p
🔄​ TestAccept/bundle/resources/dashboards/change-name ✅​p ✅​p 🔄​f 🔄​f ✅​p ✅​p ✅​p ✅​p ✅​p ✅​p
🔄​ TestAccept/bundle/resources/dashboards/change-name/DATABRICKS_BUNDLE_ENGINE=terraform ✅​p ✅​p 🔄​f 🔄​f ✅​p ✅​p ✅​p ✅​p ✅​p ✅​p
🔄​ TestAccept/bundle/resources/vector_search_endpoints/drift/recreated_same_name 🙈​S 🙈​S 🔄​f 💚​R 🙈​S 🙈​S 💚​R 💚​R 🙈​S 🙈​S
🔄​ TestAccept/bundle/resources/vector_search_endpoints/drift/recreated_same_name/DATABRICKS_BUNDLE_ENGINE=direct 🔄​f ✅​p ✅​p ✅​p
🙈​ TestAccept/bundle/resources/vector_search_indexes/recreate/embedding_dimension 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S
🙈​ TestAccept/ssh/connection 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S
💚​ TestFetchRepositoryInfoAPI_FromRepo 💚​R 💚​R 💚​R 💚​R 💚​R 💚​R 💚​R 💚​R 💚​R 💚​R
💚​ TestFetchRepositoryInfoAPI_FromRepo/root 💚​R 💚​R 💚​R 💚​R 💚​R 💚​R 💚​R 💚​R 💚​R 💚​R
💚​ TestFetchRepositoryInfoAPI_FromRepo/subdir 💚​R 💚​R 💚​R 💚​R 💚​R 💚​R 💚​R 💚​R 💚​R 💚​R
Top 50 slowest tests (at least 2 minutes):
duration env testname
14:24 aws windows TestAccept/bundle/resources/clusters/deploy/update-after-create/DATABRICKS_BUNDLE_ENGINE=terraform
14:05 azure windows TestAccept/bundle/resources/apps/lifecycle-started/DATABRICKS_BUNDLE_ENGINE=direct
12:48 gcp windows TestAccept/bundle/resources/apps/lifecycle-started/DATABRICKS_BUNDLE_ENGINE=direct
11:45 gcp linux TestAccept/bundle/resources/apps/lifecycle-started/DATABRICKS_BUNDLE_ENGINE=direct
11:45 azure-ucws linux TestAccept/bundle/resources/apps/lifecycle-started/DATABRICKS_BUNDLE_ENGINE=direct
9:35 gcp windows TestAccept/bundle/resources/clusters/lifecycle-started/DATABRICKS_BUNDLE_ENGINE=direct
9:35 azure-ucws windows TestAccept/bundle/resources/apps/lifecycle-started/DATABRICKS_BUNDLE_ENGINE=direct
9:28 aws linux TestAccept/bundle/deploy/spark-jar-task/DATABRICKS_BUNDLE_ENGINE=terraform
8:57 aws linux TestAccept/bundle/resources/clusters/lifecycle-started/DATABRICKS_BUNDLE_ENGINE=direct
8:37 aws linux TestAccept/bundle/deploy/spark-jar-task/DATABRICKS_BUNDLE_ENGINE=direct
8:28 gcp linux TestAccept/bundle/resources/clusters/deploy/local_ssd_count/DATABRICKS_BUNDLE_ENGINE=direct
8:24 azure linux TestAccept/bundle/resources/apps/lifecycle-started/DATABRICKS_BUNDLE_ENGINE=direct
7:59 aws linux TestAccept/bundle/resources/apps/lifecycle-started/DATABRICKS_BUNDLE_ENGINE=direct
7:55 aws-ucws linux TestAccept/bundle/resources/apps/lifecycle-started/DATABRICKS_BUNDLE_ENGINE=direct
7:42 azure windows TestAccept/bundle/resources/clusters/deploy/update-after-create/DATABRICKS_BUNDLE_ENGINE=terraform
7:37 azure linux TestAccept/bundle/resources/clusters/deploy/data_security_mode/DATABRICKS_BUNDLE_ENGINE=direct
7:34 azure windows TestAccept/bundle/resources/clusters/deploy/update-after-create/DATABRICKS_BUNDLE_ENGINE=direct
7:33 gcp linux TestAccept/bundle/resources/clusters/deploy/update-after-create/DATABRICKS_BUNDLE_ENGINE=direct
7:32 aws windows TestAccept/bundle/resources/clusters/lifecycle-started/DATABRICKS_BUNDLE_ENGINE=direct
7:27 gcp windows TestAccept/bundle/resources/clusters/deploy/local_ssd_count/DATABRICKS_BUNDLE_ENGINE=direct
7:24 aws windows TestAccept/bundle/resources/apps/lifecycle-started/DATABRICKS_BUNDLE_ENGINE=direct
7:23 aws-ucws linux TestAccept/bundle/resources/clusters/lifecycle-started/DATABRICKS_BUNDLE_ENGINE=direct
7:16 aws-ucws windows TestAccept/bundle/resources/apps/lifecycle-started/DATABRICKS_BUNDLE_ENGINE=direct
7:06 gcp windows TestAccept/bundle/resources/clusters/deploy/update-after-create/DATABRICKS_BUNDLE_ENGINE=direct
7:04 azure-ucws windows TestAccept/bundle/resources/apps/lifecycle-started-omitted/DATABRICKS_BUNDLE_ENGINE=direct
7:01 azure linux TestAccept/bundle/resources/apps/inline_config/DATABRICKS_BUNDLE_ENGINE=direct
6:53 aws-ucws windows TestAccept/bundle/resources/clusters/lifecycle-started/DATABRICKS_BUNDLE_ENGINE=direct
6:52 azure-ucws linux TestAccept/bundle/config-remote-sync/multiple_resources/DATABRICKS_BUNDLE_ENGINE=terraform
6:51 azure-ucws windows TestAccept/bundle/resources/apps/lifecycle-started-toggle/DATABRICKS_BUNDLE_ENGINE=direct
6:50 gcp linux TestAccept/bundle/resources/clusters/lifecycle-started/DATABRICKS_BUNDLE_ENGINE=direct
6:40 aws-ucws linux TestAccept/bundle/deploy/spark-jar-task/DATABRICKS_BUNDLE_ENGINE=direct
6:39 azure-ucws linux TestAccept/bundle/resources/clusters/lifecycle-started/DATABRICKS_BUNDLE_ENGINE=direct
6:33 aws linux TestAccept/bundle/resources/clusters/deploy/update-after-create/DATABRICKS_BUNDLE_ENGINE=terraform
6:18 aws windows TestAccept/bundle/resources/clusters/deploy/simple/DATABRICKS_BUNDLE_ENGINE=direct
6:17 aws-ucws linux TestAccept/bundle/config-remote-sync/multiple_resources/DATABRICKS_BUNDLE_ENGINE=terraform
5:52 aws-ucws linux TestAccept/bundle/resources/clusters/lifecycle-started-toggle/DATABRICKS_BUNDLE_ENGINE=direct
5:50 azure-ucws windows TestAccept/bundle/resources/apps/inline_config/DATABRICKS_BUNDLE_ENGINE=terraform
5:46 azure linux TestAccept/bundle/resources/apps/inline_config/DATABRICKS_BUNDLE_ENGINE=terraform
5:36 aws-ucws linux TestAccept/bundle/config-remote-sync/multiple_resources/DATABRICKS_BUNDLE_ENGINE=direct
5:33 gcp linux TestAccept/bundle/resources/clusters/deploy/simple/DATABRICKS_BUNDLE_ENGINE=direct
5:29 azure-ucws windows TestAccept/bundle/resources/clusters/lifecycle-started/DATABRICKS_BUNDLE_ENGINE=direct
5:28 gcp windows TestAccept/bundle/resources/clusters/lifecycle-started-toggle/DATABRICKS_BUNDLE_ENGINE=direct
5:23 azure linux TestAccept/bundle/resources/clusters/lifecycle-started/DATABRICKS_BUNDLE_ENGINE=direct
5:23 aws windows TestAccept/bundle/deploy/spark-jar-task/DATABRICKS_BUNDLE_ENGINE=terraform
5:20 gcp linux TestAccept/bundle/deploy/spark-jar-task/DATABRICKS_BUNDLE_ENGINE=terraform
5:19 aws windows TestAccept/bundle/resources/clusters/lifecycle-started-toggle/DATABRICKS_BUNDLE_ENGINE=direct
5:10 azure-ucws windows TestAccept/bundle/resources/apps/inline_config/DATABRICKS_BUNDLE_ENGINE=direct
5:09 aws windows TestAccept/bundle/deploy/spark-jar-task/DATABRICKS_BUNDLE_ENGINE=direct
5:00 gcp linux TestAccept/bundle/resources/clusters/lifecycle-started-toggle/DATABRICKS_BUNDLE_ENGINE=direct
4:59 azure windows TestAccept/bundle/resources/clusters/lifecycle-started/DATABRICKS_BUNDLE_ENGINE=direct

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants