Skip to content

Commit 7ab81c2

Browse files
denikclaude
andauthored
direct: Fix drift in grants resource due to principal reordering (#4794)
When the remote API returns grants in a different order than configured, the index-based comparison detected false drift. Key grants by `principal` so comparison is order-independent, matching permissions behavior. Also modernize sortPriviliges to use slices.Sort. --------- Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
1 parent 0da96f9 commit 7ab81c2

6 files changed

Lines changed: 31 additions & 13 deletions

File tree

NEXT_CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
### CLI
88

99
### Bundles
10+
* engine/direct: Fix drift in grants resource due to privilege reordering ([#4794](https://github.com/databricks/cli/pull/4794))
1011

1112
### Dependency updates
1213

acceptance/bundle/invariant/configs/schema_grant_ref.yml.tmpl

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,13 +7,17 @@ resources:
77
catalog_name: main
88
name: test-schema-b-$UNIQUE_NAME
99
grants:
10-
- principal: account users
10+
# make sure principals are not sorted, to better detect drift
11+
- principal: $CURRENT_USER_NAME
1112
privileges:
1213
- USE_SCHEMA
13-
- principal: admins
14-
privileges:
1514
- CREATE_TABLE
15+
- principal: account users
16+
privileges:
17+
# make sure privileges are not sorted, to better detect drift
1618
- USE_SCHEMA
19+
- APPLY_TAG
20+
- CREATE_VOLUME
1721

1822
schema_a:
1923
catalog_name: main
@@ -23,6 +27,9 @@ resources:
2327
- principal: ${resources.schemas.schema_b.grants[0].principal}
2428
privileges:
2529
- USE_SCHEMA
30+
- APPLY_TAG
2631
- principal: ${resources.schemas.schema_b.grants[1].principal}
2732
privileges:
2833
- CREATE_TABLE
34+
- USE_SCHEMA
35+
- APPLY_TAG

acceptance/bundle/resources/grants/catalogs/out.plan2.direct.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -49,13 +49,13 @@
4949
]
5050
},
5151
"changes": {
52-
"[0].privileges[0]": {
52+
"[principal='deco-test-user@databricks.com'].privileges[0]": {
5353
"action": "update",
5454
"old": "CREATE_SCHEMA",
5555
"new": "USE_CATALOG",
5656
"remote": "CREATE_SCHEMA"
5757
},
58-
"[0].privileges[1]": {
58+
"[principal='deco-test-user@databricks.com'].privileges[1]": {
5959
"action": "update",
6060
"old": "USE_CATALOG",
6161
"new": "USE_SCHEMA",

acceptance/bundle/resources/grants/schemas/change_privilege/out.plan2.direct.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -54,13 +54,13 @@
5454
]
5555
},
5656
"changes": {
57-
"[0].privileges[0]": {
57+
"[principal='deco-test-user@databricks.com'].privileges[0]": {
5858
"action": "update",
5959
"old": "CREATE_TABLE",
6060
"new": "APPLY_TAG",
6161
"remote": "CREATE_TABLE"
6262
},
63-
"[0].privileges[1]": {
63+
"[principal='deco-test-user@databricks.com'].privileges[1]": {
6464
"action": "update",
6565
"old": "USE_SCHEMA",
6666
"new": "CREATE_TABLE",

acceptance/bundle/resources/grants/volumes/out.plan2.direct.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -81,13 +81,13 @@
8181
]
8282
},
8383
"changes": {
84-
"[0].privileges[0]": {
84+
"[principal='deco-test-user@databricks.com'].privileges[0]": {
8585
"action": "update",
8686
"old": "READ_VOLUME",
8787
"new": "MANAGE",
8888
"remote": "READ_VOLUME"
8989
},
90-
"[0].privileges[1]": {
90+
"[principal='deco-test-user@databricks.com'].privileges[1]": {
9191
"action": "update",
9292
"old": "WRITE_VOLUME",
9393
"new": "READ_VOLUME",

bundle/direct/dresources/grants.go

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import (
44
"context"
55
"errors"
66
"fmt"
7-
"sort"
7+
"slices"
88
"strings"
99

1010
"github.com/databricks/cli/libs/structs/structvar"
@@ -76,6 +76,18 @@ func (*ResourceGrants) PrepareState(state *GrantsState) *GrantsState {
7676
return state
7777
}
7878

79+
func grantKey(x catalog.PrivilegeAssignment) (string, string) {
80+
return "principal", x.Principal
81+
}
82+
83+
func (*ResourceGrants) KeyedSlices() map[string]any {
84+
// Empty key because EmbeddedSlice appears at the root path of
85+
// GrantsState (no "grants" prefix in struct walker paths).
86+
return map[string]any{
87+
"": grantKey,
88+
}
89+
}
90+
7991
func (r *ResourceGrants) DoRead(ctx context.Context, id string) (*GrantsState, error) {
8092
securableType, fullName, err := parseGrantsID(id)
8193
if err != nil {
@@ -174,9 +186,7 @@ func (r *ResourceGrants) listGrants(ctx context.Context, securableType, fullName
174186
}
175187

176188
func sortPriviliges(privileges []catalog.Privilege) {
177-
sort.Slice(privileges, func(i, j int) bool {
178-
return privileges[i] < privileges[j]
179-
})
189+
slices.Sort(privileges)
180190
}
181191

182192
func extractGrantResourceType(node string) (string, error) {

0 commit comments

Comments
 (0)