Skip to content

Match keyed slices by value via a type registry - #6757

Draft
denik wants to merge 3 commits into
mainfrom
denik/keyed-path-drop-field
Draft

denik wants to merge 3 commits into
mainfrom
denik/keyed-path-drop-field

Conversation

@denik

@denik denik commented Sep 18, 2026

Copy link
Copy Markdown
Contributor

Problem

Keyed-slice elements (job tasks, permissions, grants, secret ACLs, …) are matched by value, so encoding the key field in the change path is redundant. For permissions it is also wrong: the Permissions API may return a principal under a different field than configured (a user_name holding a service principal's application ID comes back as service_principal_name), which surfaced as a perpetual no-op update of the ACL on every plan/deploy.

Approach

Introduce libs/structs/registry — a declarative, type-keyed record of which fields identify a keyed-slice element, registered from init() next to each type:

func init() { registry.Register[jobs.Task]("task_key") }
  • structdiff keys off the registry by element type (no more per-call KeyFunc maps) and addresses an element as [='value'] — the key field is omitted. A diff on a matched element's own key field is dropped, so a principal carried under a different field is no longer reported as a change.
  • structaccess resolves [='value'] back to an element via the registry (it has the Go type).
  • configsync resolves it for dynamic values by recognising the element's key field with registry.IsKeyField, so remote-drift write-back stays correct.
  • Removes the KeyFunc machinery and the adapter's KeyedSlices method; the job path-pattern map collapses to nine type registrations.

This is the generic-diff line of the permissions false-positive fix (cf. #6710 hook / #6732 side-key merge): here the key field simply leaves the path, and the resolvers recover it from type/registry.

Validation

New registry unit tests; structpath round-trips [='value']. All keyed-slice acceptance goldens regenerated ([task_key='x'][='x']). Unit tests across libs/structs, bundle/direct, configsync, terraform_dabs_map; acceptance invariant no_drift and full config-remote-sync pass — the config-remote-sync write-back edits the correct element. gofmt + lint clean.

This pull request and its description were written by Isaac.

denik and others added 3 commits September 17, 2026 15:02
Keyed slices are matched by key value alone (the key field is only used to
render the path). A diff on a matched element's own key field therefore means
the two sides carry the same identity under a different field — e.g. a
permission declared under user_name that the Permissions API stores and returns
as service_principal_name — which is not a real change. Drop those field diffs;
non-key fields still diff normally.

This fixes a perpetual no-op "update" of dashboard/job/etc permissions when a
service principal is declared under user_name, generically for any keyed slice,
without per-resource logic. Alternative to the resource-level fix in #6710.

The testserver models the backend's user_name(UUID) -> service_principal_name
readback so the case reproduces locally.

Co-authored-by: Isaac <no-reply@databricks.com>
Keyed-slice elements are matched by value, so encoding the key field in the
path is redundant — and for a permission it is actively wrong, because the
backend may return a principal under a different field (a user_name holding a
service principal's application ID comes back as service_principal_name),
producing a perpetual no-op "update".

Introduce libs/structs/registry: a declarative, type-keyed record of which
fields identify a slice element (registered from init() next to each type).
structdiff keys off it instead of per-call KeyFunc maps and addresses elements
as [='value'] (key field omitted); a diff on a matched element's own key field
is dropped, so the field difference is no longer a change. structaccess and
configsync resolve [='value'] back to an element through the registry (the type
for structaccess, the element's members for configsync's dynamic values).

This removes the KeyFunc machinery and the adapter's KeyedSlices method, and
collapses the job path patterns into nine type registrations.

Co-authored-by: Isaac <no-reply@databricks.com>
Co-authored-by: Isaac <no-reply@databricks.com>
@eng-dev-ecosystem-bot

Copy link
Copy Markdown
Collaborator

Integration test report

Commit: f8c7ac1

Run: 35367761446

Env ✅​pass 🙈​skip Time
✅​ aws linux 331 50 6:53
✅​ aws windows 302 56 6:04
✅​ azure linux 330 50 5:54
✅​ azure windows 301 56 7:59
✅​ gcp linux 331 50 6:23
✅​ gcp windows 302 56 6:15
Top 3 slowest tests (at least 2 minutes):
duration env testname
5:40 azure windows TestAccept
3:45 gcp windows TestAccept
3:37 aws windows TestAccept

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.

2 participants