From 4c5444f3d834d167ccab3d60a19e0c729edb300f Mon Sep 17 00:00:00 2001 From: Jan Rose Date: Wed, 8 Jul 2026 12:38:39 +0200 Subject: [PATCH 1/5] bugfix: Auto-include grants and permissions on --select --- NEXT_CHANGELOG.md | 1 + .../bundle/select/grants/databricks.yml.tmpl | 20 ++++++ acceptance/bundle/select/grants/out.test.toml | 5 ++ acceptance/bundle/select/grants/output.txt | 55 ++++++++++++++++ acceptance/bundle/select/grants/script | 21 +++++++ acceptance/bundle/select/grants/test.toml | 6 ++ .../select/permissions/databricks.yml.tmpl | 16 +++++ .../bundle/select/permissions/out.test.toml | 4 ++ .../bundle/select/permissions/output.txt | 63 +++++++++++++++++++ acceptance/bundle/select/permissions/script | 21 +++++++ .../bundle/select/permissions/test.toml | 5 ++ bundle/deployplan/plan.go | 24 ++++--- bundle/deployplan/plan_filter_test.go | 37 +++++++++++ 13 files changed, 270 insertions(+), 8 deletions(-) create mode 100644 acceptance/bundle/select/grants/databricks.yml.tmpl create mode 100644 acceptance/bundle/select/grants/out.test.toml create mode 100644 acceptance/bundle/select/grants/output.txt create mode 100644 acceptance/bundle/select/grants/script create mode 100644 acceptance/bundle/select/grants/test.toml create mode 100644 acceptance/bundle/select/permissions/databricks.yml.tmpl create mode 100644 acceptance/bundle/select/permissions/out.test.toml create mode 100644 acceptance/bundle/select/permissions/output.txt create mode 100644 acceptance/bundle/select/permissions/script create mode 100644 acceptance/bundle/select/permissions/test.toml diff --git a/NEXT_CHANGELOG.md b/NEXT_CHANGELOG.md index 13ea8ca7ce1..8e132da39b1 100644 --- a/NEXT_CHANGELOG.md +++ b/NEXT_CHANGELOG.md @@ -11,6 +11,7 @@ ### Bundles * Fix permissions added to a job or pipeline by a Python (PyDABs) mutator failing to deploy with "must have exactly one owner"; the deploying identity is now set as owner, matching resources whose permissions are declared in YAML ([#5821](https://github.com/databricks/cli/pull/5821)). +* Fix `bundle deploy --select ` skipping the resource's grants and permissions; they are now applied as part of the selected resource ([#XXXX](https://github.com/databricks/cli/pull/XXXX)). ### Dependency updates diff --git a/acceptance/bundle/select/grants/databricks.yml.tmpl b/acceptance/bundle/select/grants/databricks.yml.tmpl new file mode 100644 index 00000000000..3a671d9b15b --- /dev/null +++ b/acceptance/bundle/select/grants/databricks.yml.tmpl @@ -0,0 +1,20 @@ +bundle: + name: select-grants-$UNIQUE_NAME + +resources: + schemas: + bronze: + catalog_name: main + name: bronze_$UNIQUE_NAME + grants: + - principal: deco-test-user@databricks.com + privileges: + - ALL_PRIVILEGES + + silver: + catalog_name: main + name: silver_$UNIQUE_NAME + grants: + - principal: deco-test-user@databricks.com + privileges: + - ALL_PRIVILEGES diff --git a/acceptance/bundle/select/grants/out.test.toml b/acceptance/bundle/select/grants/out.test.toml new file mode 100644 index 00000000000..55ed5ee6619 --- /dev/null +++ b/acceptance/bundle/select/grants/out.test.toml @@ -0,0 +1,5 @@ +Local = true +Cloud = false +RequiresUnityCatalog = true +EnvMatrix.DATABRICKS_BUNDLE_ENGINE = ["direct"] +EnvMatrix.READPLAN = ["", "1"] diff --git a/acceptance/bundle/select/grants/output.txt b/acceptance/bundle/select/grants/output.txt new file mode 100644 index 00000000000..fe09ee396ac --- /dev/null +++ b/acceptance/bundle/select/grants/output.txt @@ -0,0 +1,55 @@ + +>>> [CLI] bundle plan --select schemas.bronze +create schemas.bronze +create schemas.bronze.grants + +Plan: 2 to add, 0 to change, 0 to delete, 0 unchanged + +>>> [CLI] bundle plan --select schemas.bronze.grants +Error: no such resource: schemas.bronze.grants + + +Exit code: 1 + +=== bundle deploy --select schemas.bronze +Uploading bundle files to /Workspace/Users/[USERNAME]/.bundle/select-grants-[UNIQUE_NAME]/default/files... +Deploying resources... +Updating deployment state... +Deployment complete! + +>>> print_requests.py --sort //permissions //schemas +{ + "method": "PATCH", + "path": "/api/2.1/unity-catalog/permissions/schema/main.bronze_[UNIQUE_NAME]", + "body": { + "changes": [ + { + "add": [ + "ALL_PRIVILEGES" + ], + "principal": "deco-test-user@databricks.com" + } + ] + } +} +{ + "method": "POST", + "path": "/api/2.1/unity-catalog/schemas", + "body": { + "catalog_name": "main", + "name": "bronze_[UNIQUE_NAME]" + } +} + +=== Destroy +>>> [CLI] bundle destroy --auto-approve +The following resources will be deleted: + delete resources.schemas.bronze + +This action will result in the deletion of the following UC schemas. Any underlying data may be lost: + delete resources.schemas.bronze + +All files and directories at the following location will be deleted: /Workspace/Users/[USERNAME]/.bundle/select-grants-[UNIQUE_NAME]/default + +Deleting files... +Destroy complete! diff --git a/acceptance/bundle/select/grants/script b/acceptance/bundle/select/grants/script new file mode 100644 index 00000000000..10960cd94f8 --- /dev/null +++ b/acceptance/bundle/select/grants/script @@ -0,0 +1,21 @@ +envsubst '$UNIQUE_NAME' < databricks.yml.tmpl > databricks.yml + +cleanup() { + title "Destroy" + trace $CLI bundle destroy --auto-approve + rm -f out.requests.txt +} +trap cleanup EXIT + +# Selecting the schema pulls in its grants sub-node even though nothing points +# schema -> grants (the dependency edge runs the other way). silver is untouched. +trace $CLI bundle plan --select schemas.bronze + +# Grants sub-nodes are not addressable via --select. +errcode trace $CLI bundle plan --select schemas.bronze.grants + +# Deploy only bronze and assert its grants PATCH is emitted (issue #5794). +$CLI bundle plan --select schemas.bronze -o json > plan.json +title "bundle deploy --select schemas.bronze\n" +$CLI bundle deploy --select schemas.bronze $(readplanarg plan.json) +trace print_requests.py --sort //permissions //schemas diff --git a/acceptance/bundle/select/grants/test.toml b/acceptance/bundle/select/grants/test.toml new file mode 100644 index 00000000000..fbae6016fa1 --- /dev/null +++ b/acceptance/bundle/select/grants/test.toml @@ -0,0 +1,6 @@ +RequiresUnityCatalog = true +RecordRequests = true +# --select is only supported by the direct engine. +EnvMatrix.DATABRICKS_BUNDLE_ENGINE = ["direct"] +EnvMatrix.READPLAN = ["", "1"] +Ignore = [".databricks", ".gitignore", "databricks.yml", "plan.json"] diff --git a/acceptance/bundle/select/permissions/databricks.yml.tmpl b/acceptance/bundle/select/permissions/databricks.yml.tmpl new file mode 100644 index 00000000000..2435b66c70a --- /dev/null +++ b/acceptance/bundle/select/permissions/databricks.yml.tmpl @@ -0,0 +1,16 @@ +bundle: + name: select-permissions-$UNIQUE_NAME + +resources: + jobs: + foo: + name: foo-$UNIQUE_NAME + permissions: + - level: CAN_VIEW + user_name: viewer@example.com + + bar: + name: bar-$UNIQUE_NAME + permissions: + - level: CAN_VIEW + user_name: viewer@example.com diff --git a/acceptance/bundle/select/permissions/out.test.toml b/acceptance/bundle/select/permissions/out.test.toml new file mode 100644 index 00000000000..71970b719d4 --- /dev/null +++ b/acceptance/bundle/select/permissions/out.test.toml @@ -0,0 +1,4 @@ +Local = true +Cloud = false +EnvMatrix.DATABRICKS_BUNDLE_ENGINE = ["direct"] +EnvMatrix.READPLAN = ["", "1"] diff --git a/acceptance/bundle/select/permissions/output.txt b/acceptance/bundle/select/permissions/output.txt new file mode 100644 index 00000000000..eb507c79896 --- /dev/null +++ b/acceptance/bundle/select/permissions/output.txt @@ -0,0 +1,63 @@ + +>>> [CLI] bundle plan --select jobs.foo +create jobs.foo +create jobs.foo.permissions + +Plan: 2 to add, 0 to change, 0 to delete, 0 unchanged + +>>> [CLI] bundle plan --select jobs.foo.permissions +Error: no such resource: jobs.foo.permissions + + +Exit code: 1 + +=== bundle deploy --select jobs.foo +Uploading bundle files to /Workspace/Users/[USERNAME]/.bundle/select-permissions-[UNIQUE_NAME]/default/files... +Deploying resources... +Updating deployment state... +Deployment complete! + +>>> print_requests.py --sort //permissions //jobs +{ + "method": "POST", + "path": "/api/2.2/jobs/create", + "body": { + "deployment": { + "kind": "BUNDLE", + "metadata_file_path": "/Workspace/Users/[USERNAME]/.bundle/select-permissions-[UNIQUE_NAME]/default/state/metadata.json" + }, + "edit_mode": "UI_LOCKED", + "format": "MULTI_TASK", + "max_concurrent_runs": 1, + "name": "foo-[UNIQUE_NAME]", + "queue": { + "enabled": true + } + } +} +{ + "method": "PUT", + "path": "/api/2.0/permissions/jobs/[NUMID]", + "body": { + "access_control_list": [ + { + "permission_level": "CAN_VIEW", + "user_name": "viewer@example.com" + }, + { + "permission_level": "IS_OWNER", + "user_name": "[USERNAME]" + } + ] + } +} + +=== Destroy +>>> [CLI] bundle destroy --auto-approve +The following resources will be deleted: + delete resources.jobs.foo + +All files and directories at the following location will be deleted: /Workspace/Users/[USERNAME]/.bundle/select-permissions-[UNIQUE_NAME]/default + +Deleting files... +Destroy complete! diff --git a/acceptance/bundle/select/permissions/script b/acceptance/bundle/select/permissions/script new file mode 100644 index 00000000000..080d46478f4 --- /dev/null +++ b/acceptance/bundle/select/permissions/script @@ -0,0 +1,21 @@ +envsubst '$UNIQUE_NAME' < databricks.yml.tmpl > databricks.yml + +cleanup() { + title "Destroy" + trace $CLI bundle destroy --auto-approve + rm -f out.requests.txt +} +trap cleanup EXIT + +# Selecting the job pulls in its permissions sub-node even though nothing points +# job -> permissions (the dependency edge runs the other way). bar is untouched. +trace $CLI bundle plan --select jobs.foo + +# Permissions sub-nodes are not addressable via --select. +errcode trace $CLI bundle plan --select jobs.foo.permissions + +# Deploy only foo and assert its permissions PUT is emitted. +$CLI bundle plan --select jobs.foo -o json > plan.json +title "bundle deploy --select jobs.foo\n" +$CLI bundle deploy --select jobs.foo $(readplanarg plan.json) +trace print_requests.py --sort //permissions //jobs diff --git a/acceptance/bundle/select/permissions/test.toml b/acceptance/bundle/select/permissions/test.toml new file mode 100644 index 00000000000..b6a45c49ca4 --- /dev/null +++ b/acceptance/bundle/select/permissions/test.toml @@ -0,0 +1,5 @@ +RecordRequests = true +# --select is only supported by the direct engine. +EnvMatrix.DATABRICKS_BUNDLE_ENGINE = ["direct"] +EnvMatrix.READPLAN = ["", "1"] +Ignore = [".databricks", ".gitignore", "databricks.yml", "plan.json"] diff --git a/bundle/deployplan/plan.go b/bundle/deployplan/plan.go index bc5ea03f6c7..21b057f4c59 100644 --- a/bundle/deployplan/plan.go +++ b/bundle/deployplan/plan.go @@ -214,25 +214,33 @@ func (p *Plan) FilterToSelected(selected []string) { // Convert "type.name" → "resources.type.name" (plan key format). queue := make([]string, 0, len(selected)) reachable := make(map[string]struct{}, len(selected)) - for _, s := range selected { - key := "resources." + s + enqueue := func(key string) { + if _, seen := reachable[key]; seen { + return + } if _, ok := p.Plan[key]; ok { reachable[key] = struct{}{} queue = append(queue, key) } } + for _, s := range selected { + key := "resources." + s + enqueue(key) + // Grants and permissions are modeled as separate plan nodes for internal + // reasons, but the user cannot address them via --select. Pull them in as + // part of the parent resource so selecting a resource applies its grants + // and permissions too. The dependency edge runs sub-node → parent, so the + // BFS below would never reach them from the parent otherwise. + enqueue(key + ".grants") + enqueue(key + ".permissions") + } // BFS following DependsOn edges to include transitive dependencies. for len(queue) > 0 { key := queue[0] queue = queue[1:] for _, dep := range p.Plan[key].DependsOn { - if _, seen := reachable[dep.Node]; !seen { - if _, ok := p.Plan[dep.Node]; ok { - reachable[dep.Node] = struct{}{} - queue = append(queue, dep.Node) - } - } + enqueue(dep.Node) } } diff --git a/bundle/deployplan/plan_filter_test.go b/bundle/deployplan/plan_filter_test.go index b16d78eb43d..18767e826d8 100644 --- a/bundle/deployplan/plan_filter_test.go +++ b/bundle/deployplan/plan_filter_test.go @@ -20,6 +20,23 @@ func planWithDeps() *deployplan.Plan { return p } +func planWithGrants() *deployplan.Plan { + p := deployplan.NewPlanDirect() + p.Plan["resources.schemas.bronze"] = &deployplan.PlanEntry{} + // Sub-nodes depend on the parent (to resolve full_name), not the other way around. + p.Plan["resources.schemas.bronze.grants"] = &deployplan.PlanEntry{ + DependsOn: []deployplan.DependsOnEntry{{Node: "resources.schemas.bronze"}}, + } + p.Plan["resources.schemas.bronze.permissions"] = &deployplan.PlanEntry{ + DependsOn: []deployplan.DependsOnEntry{{Node: "resources.schemas.bronze"}}, + } + p.Plan["resources.schemas.silver"] = &deployplan.PlanEntry{} + p.Plan["resources.schemas.silver.grants"] = &deployplan.PlanEntry{ + DependsOn: []deployplan.DependsOnEntry{{Node: "resources.schemas.silver"}}, + } + return p +} + func TestFilterToSelected_Direct(t *testing.T) { p := planWithDeps() p.FilterToSelected([]string{"jobs.foo"}) @@ -46,3 +63,23 @@ func TestFilterToSelected_Multiple(t *testing.T) { assert.NotContains(t, p.Plan, "resources.jobs.foo") assert.NotContains(t, p.Plan, "resources.jobs.bar") } + +func TestFilterToSelected_IncludesGrantsAndPermissions(t *testing.T) { + p := planWithGrants() + p.FilterToSelected([]string{"schemas.bronze"}) + assert.Contains(t, p.Plan, "resources.schemas.bronze") + assert.Contains(t, p.Plan, "resources.schemas.bronze.grants") + assert.Contains(t, p.Plan, "resources.schemas.bronze.permissions") + assert.NotContains(t, p.Plan, "resources.schemas.silver") + assert.NotContains(t, p.Plan, "resources.schemas.silver.grants") +} + +func TestFilterToSelected_MissingSubNodesAreSkipped(t *testing.T) { + p := planWithGrants() + // silver has grants but no permissions node; selecting it must not fail. + p.FilterToSelected([]string{"schemas.silver"}) + assert.Contains(t, p.Plan, "resources.schemas.silver") + assert.Contains(t, p.Plan, "resources.schemas.silver.grants") + assert.NotContains(t, p.Plan, "resources.schemas.silver.permissions") + assert.NotContains(t, p.Plan, "resources.schemas.bronze") +} From e3920f0e9741b1df133f2236023bd912fb99c188 Mon Sep 17 00:00:00 2001 From: Jan Rose Date: Wed, 8 Jul 2026 12:44:43 +0200 Subject: [PATCH 2/5] Use musterr --- acceptance/bundle/select/grants/output.txt | 2 -- acceptance/bundle/select/grants/script | 2 +- acceptance/bundle/select/permissions/output.txt | 2 -- acceptance/bundle/select/permissions/script | 2 +- 4 files changed, 2 insertions(+), 6 deletions(-) diff --git a/acceptance/bundle/select/grants/output.txt b/acceptance/bundle/select/grants/output.txt index fe09ee396ac..1a8b929e683 100644 --- a/acceptance/bundle/select/grants/output.txt +++ b/acceptance/bundle/select/grants/output.txt @@ -9,8 +9,6 @@ Plan: 2 to add, 0 to change, 0 to delete, 0 unchanged Error: no such resource: schemas.bronze.grants -Exit code: 1 - === bundle deploy --select schemas.bronze Uploading bundle files to /Workspace/Users/[USERNAME]/.bundle/select-grants-[UNIQUE_NAME]/default/files... Deploying resources... diff --git a/acceptance/bundle/select/grants/script b/acceptance/bundle/select/grants/script index 10960cd94f8..bb823d377a8 100644 --- a/acceptance/bundle/select/grants/script +++ b/acceptance/bundle/select/grants/script @@ -12,7 +12,7 @@ trap cleanup EXIT trace $CLI bundle plan --select schemas.bronze # Grants sub-nodes are not addressable via --select. -errcode trace $CLI bundle plan --select schemas.bronze.grants +musterr trace $CLI bundle plan --select schemas.bronze.grants # Deploy only bronze and assert its grants PATCH is emitted (issue #5794). $CLI bundle plan --select schemas.bronze -o json > plan.json diff --git a/acceptance/bundle/select/permissions/output.txt b/acceptance/bundle/select/permissions/output.txt index eb507c79896..3d52a8be930 100644 --- a/acceptance/bundle/select/permissions/output.txt +++ b/acceptance/bundle/select/permissions/output.txt @@ -9,8 +9,6 @@ Plan: 2 to add, 0 to change, 0 to delete, 0 unchanged Error: no such resource: jobs.foo.permissions -Exit code: 1 - === bundle deploy --select jobs.foo Uploading bundle files to /Workspace/Users/[USERNAME]/.bundle/select-permissions-[UNIQUE_NAME]/default/files... Deploying resources... diff --git a/acceptance/bundle/select/permissions/script b/acceptance/bundle/select/permissions/script index 080d46478f4..2d1a8c43d27 100644 --- a/acceptance/bundle/select/permissions/script +++ b/acceptance/bundle/select/permissions/script @@ -12,7 +12,7 @@ trap cleanup EXIT trace $CLI bundle plan --select jobs.foo # Permissions sub-nodes are not addressable via --select. -errcode trace $CLI bundle plan --select jobs.foo.permissions +musterr trace $CLI bundle plan --select jobs.foo.permissions # Deploy only foo and assert its permissions PUT is emitted. $CLI bundle plan --select jobs.foo -o json > plan.json From 25c0cc18b444c906e922a78ecc5c87299684afd4 Mon Sep 17 00:00:00 2001 From: Jan Rose Date: Wed, 8 Jul 2026 14:37:52 +0200 Subject: [PATCH 3/5] Add enqueueReachable instead of closure --- bundle/deployplan/plan.go | 29 ++++++++++++++++------------- 1 file changed, 16 insertions(+), 13 deletions(-) diff --git a/bundle/deployplan/plan.go b/bundle/deployplan/plan.go index 21b057f4c59..bb1b0d77bff 100644 --- a/bundle/deployplan/plan.go +++ b/bundle/deployplan/plan.go @@ -214,25 +214,16 @@ func (p *Plan) FilterToSelected(selected []string) { // Convert "type.name" → "resources.type.name" (plan key format). queue := make([]string, 0, len(selected)) reachable := make(map[string]struct{}, len(selected)) - enqueue := func(key string) { - if _, seen := reachable[key]; seen { - return - } - if _, ok := p.Plan[key]; ok { - reachable[key] = struct{}{} - queue = append(queue, key) - } - } for _, s := range selected { key := "resources." + s - enqueue(key) + p.enqueueReachable(reachable, &queue, key) // Grants and permissions are modeled as separate plan nodes for internal // reasons, but the user cannot address them via --select. Pull them in as // part of the parent resource so selecting a resource applies its grants // and permissions too. The dependency edge runs sub-node → parent, so the // BFS below would never reach them from the parent otherwise. - enqueue(key + ".grants") - enqueue(key + ".permissions") + p.enqueueReachable(reachable, &queue, key+".grants") + p.enqueueReachable(reachable, &queue, key+".permissions") } // BFS following DependsOn edges to include transitive dependencies. @@ -240,7 +231,7 @@ func (p *Plan) FilterToSelected(selected []string) { key := queue[0] queue = queue[1:] for _, dep := range p.Plan[key].DependsOn { - enqueue(dep.Node) + p.enqueueReachable(reachable, &queue, dep.Node) } } @@ -251,6 +242,18 @@ func (p *Plan) FilterToSelected(selected []string) { } } +// enqueueReachable marks key as reachable and appends it to queue, if key exists +// in the plan and has not been seen before. Missing or already-seen keys are ignored. +func (p *Plan) enqueueReachable(reachable map[string]struct{}, queue *[]string, key string) { + if _, seen := reachable[key]; seen { + return + } + if _, ok := p.Plan[key]; ok { + reachable[key] = struct{}{} + *queue = append(*queue, key) + } +} + type lockmap struct { state map[string]int } From 97ea4643cee2a607723c0bd055fcf89d080ff1cf Mon Sep 17 00:00:00 2001 From: Jan Rose Date: Wed, 8 Jul 2026 18:07:30 +0200 Subject: [PATCH 4/5] Change acceptance test --- .../bundle/select/grants/databricks.yml.tmpl | 20 ----- acceptance/bundle/select/grants/output.txt | 53 ------------- acceptance/bundle/select/grants/script | 21 ----- acceptance/bundle/select/grants/test.toml | 6 -- .../grants_permissions/databricks.yml.tmpl | 19 +++++ .../out.test.toml | 0 .../select/grants_permissions/output.txt | 78 +++++++++++++++++++ .../bundle/select/grants_permissions/script | 30 +++++++ .../select/grants_permissions/test.toml | 15 ++++ .../select/permissions/databricks.yml.tmpl | 16 ---- .../bundle/select/permissions/out.test.toml | 4 - .../bundle/select/permissions/output.txt | 61 --------------- acceptance/bundle/select/permissions/script | 21 ----- .../bundle/select/permissions/test.toml | 5 -- 14 files changed, 142 insertions(+), 207 deletions(-) delete mode 100644 acceptance/bundle/select/grants/databricks.yml.tmpl delete mode 100644 acceptance/bundle/select/grants/output.txt delete mode 100644 acceptance/bundle/select/grants/script delete mode 100644 acceptance/bundle/select/grants/test.toml create mode 100644 acceptance/bundle/select/grants_permissions/databricks.yml.tmpl rename acceptance/bundle/select/{grants => grants_permissions}/out.test.toml (100%) create mode 100644 acceptance/bundle/select/grants_permissions/output.txt create mode 100644 acceptance/bundle/select/grants_permissions/script create mode 100644 acceptance/bundle/select/grants_permissions/test.toml delete mode 100644 acceptance/bundle/select/permissions/databricks.yml.tmpl delete mode 100644 acceptance/bundle/select/permissions/out.test.toml delete mode 100644 acceptance/bundle/select/permissions/output.txt delete mode 100644 acceptance/bundle/select/permissions/script delete mode 100644 acceptance/bundle/select/permissions/test.toml diff --git a/acceptance/bundle/select/grants/databricks.yml.tmpl b/acceptance/bundle/select/grants/databricks.yml.tmpl deleted file mode 100644 index 3a671d9b15b..00000000000 --- a/acceptance/bundle/select/grants/databricks.yml.tmpl +++ /dev/null @@ -1,20 +0,0 @@ -bundle: - name: select-grants-$UNIQUE_NAME - -resources: - schemas: - bronze: - catalog_name: main - name: bronze_$UNIQUE_NAME - grants: - - principal: deco-test-user@databricks.com - privileges: - - ALL_PRIVILEGES - - silver: - catalog_name: main - name: silver_$UNIQUE_NAME - grants: - - principal: deco-test-user@databricks.com - privileges: - - ALL_PRIVILEGES diff --git a/acceptance/bundle/select/grants/output.txt b/acceptance/bundle/select/grants/output.txt deleted file mode 100644 index 1a8b929e683..00000000000 --- a/acceptance/bundle/select/grants/output.txt +++ /dev/null @@ -1,53 +0,0 @@ - ->>> [CLI] bundle plan --select schemas.bronze -create schemas.bronze -create schemas.bronze.grants - -Plan: 2 to add, 0 to change, 0 to delete, 0 unchanged - ->>> [CLI] bundle plan --select schemas.bronze.grants -Error: no such resource: schemas.bronze.grants - - -=== bundle deploy --select schemas.bronze -Uploading bundle files to /Workspace/Users/[USERNAME]/.bundle/select-grants-[UNIQUE_NAME]/default/files... -Deploying resources... -Updating deployment state... -Deployment complete! - ->>> print_requests.py --sort //permissions //schemas -{ - "method": "PATCH", - "path": "/api/2.1/unity-catalog/permissions/schema/main.bronze_[UNIQUE_NAME]", - "body": { - "changes": [ - { - "add": [ - "ALL_PRIVILEGES" - ], - "principal": "deco-test-user@databricks.com" - } - ] - } -} -{ - "method": "POST", - "path": "/api/2.1/unity-catalog/schemas", - "body": { - "catalog_name": "main", - "name": "bronze_[UNIQUE_NAME]" - } -} - -=== Destroy ->>> [CLI] bundle destroy --auto-approve -The following resources will be deleted: - delete resources.schemas.bronze - -This action will result in the deletion of the following UC schemas. Any underlying data may be lost: - delete resources.schemas.bronze - -All files and directories at the following location will be deleted: /Workspace/Users/[USERNAME]/.bundle/select-grants-[UNIQUE_NAME]/default - -Deleting files... -Destroy complete! diff --git a/acceptance/bundle/select/grants/script b/acceptance/bundle/select/grants/script deleted file mode 100644 index bb823d377a8..00000000000 --- a/acceptance/bundle/select/grants/script +++ /dev/null @@ -1,21 +0,0 @@ -envsubst '$UNIQUE_NAME' < databricks.yml.tmpl > databricks.yml - -cleanup() { - title "Destroy" - trace $CLI bundle destroy --auto-approve - rm -f out.requests.txt -} -trap cleanup EXIT - -# Selecting the schema pulls in its grants sub-node even though nothing points -# schema -> grants (the dependency edge runs the other way). silver is untouched. -trace $CLI bundle plan --select schemas.bronze - -# Grants sub-nodes are not addressable via --select. -musterr trace $CLI bundle plan --select schemas.bronze.grants - -# Deploy only bronze and assert its grants PATCH is emitted (issue #5794). -$CLI bundle plan --select schemas.bronze -o json > plan.json -title "bundle deploy --select schemas.bronze\n" -$CLI bundle deploy --select schemas.bronze $(readplanarg plan.json) -trace print_requests.py --sort //permissions //schemas diff --git a/acceptance/bundle/select/grants/test.toml b/acceptance/bundle/select/grants/test.toml deleted file mode 100644 index fbae6016fa1..00000000000 --- a/acceptance/bundle/select/grants/test.toml +++ /dev/null @@ -1,6 +0,0 @@ -RequiresUnityCatalog = true -RecordRequests = true -# --select is only supported by the direct engine. -EnvMatrix.DATABRICKS_BUNDLE_ENGINE = ["direct"] -EnvMatrix.READPLAN = ["", "1"] -Ignore = [".databricks", ".gitignore", "databricks.yml", "plan.json"] diff --git a/acceptance/bundle/select/grants_permissions/databricks.yml.tmpl b/acceptance/bundle/select/grants_permissions/databricks.yml.tmpl new file mode 100644 index 00000000000..c31a0df1c6e --- /dev/null +++ b/acceptance/bundle/select/grants_permissions/databricks.yml.tmpl @@ -0,0 +1,19 @@ +bundle: + name: select-subnodes-$UNIQUE_NAME + +resources: + jobs: + my_job: + name: my_job-$UNIQUE_NAME + permissions: + - level: CAN_VIEW + user_name: viewer@example.com + + schemas: + my_schema: + catalog_name: main + name: my_schema_$UNIQUE_NAME + grants: + - principal: deco-test-user@databricks.com + privileges: + - ALL_PRIVILEGES diff --git a/acceptance/bundle/select/grants/out.test.toml b/acceptance/bundle/select/grants_permissions/out.test.toml similarity index 100% rename from acceptance/bundle/select/grants/out.test.toml rename to acceptance/bundle/select/grants_permissions/out.test.toml diff --git a/acceptance/bundle/select/grants_permissions/output.txt b/acceptance/bundle/select/grants_permissions/output.txt new file mode 100644 index 00000000000..0575ed2a26c --- /dev/null +++ b/acceptance/bundle/select/grants_permissions/output.txt @@ -0,0 +1,78 @@ + +>>> [CLI] bundle plan --select jobs.my_job +create jobs.my_job +create jobs.my_job.permissions + +Plan: 2 to add, 0 to change, 0 to delete, 0 unchanged + +>>> [CLI] bundle plan --select schemas.my_schema +create schemas.my_schema +create schemas.my_schema.grants + +Plan: 2 to add, 0 to change, 0 to delete, 0 unchanged + +>>> [CLI] bundle plan --select jobs.my_job.permissions +Error: no such resource: jobs.my_job.permissions + + +>>> [CLI] bundle plan --select schemas.my_schema.grants +Error: no such resource: schemas.my_schema.grants + + +=== bundle deploy --select jobs.my_job +Uploading bundle files to /Workspace/Users/[USERNAME]/.bundle/select-subnodes-[UNIQUE_NAME]/default/files... +Deploying resources... +Updating deployment state... +Deployment complete! + +=== bundle deploy --select schemas.my_schema +Uploading bundle files to /Workspace/Users/[USERNAME]/.bundle/select-subnodes-[UNIQUE_NAME]/default/files... +Deploying resources... +Updating deployment state... +Deployment complete! + +>>> print_requests.py --sort //permissions +{ + "method": "PATCH", + "path": "/api/2.1/unity-catalog/permissions/schema/main.my_schema_[UNIQUE_NAME]", + "body": { + "changes": [ + { + "add": [ + "ALL_PRIVILEGES" + ], + "principal": "deco-test-user@databricks.com" + } + ] + } +} +{ + "method": "PUT", + "path": "/api/2.0/permissions/jobs/[NUMID]", + "body": { + "access_control_list": [ + { + "permission_level": "CAN_VIEW", + "user_name": "viewer@example.com" + }, + { + "permission_level": "IS_OWNER", + "user_name": "[USERNAME]" + } + ] + } +} + +=== Destroy +>>> [CLI] bundle destroy --auto-approve +The following resources will be deleted: + delete resources.jobs.my_job + delete resources.schemas.my_schema + +This action will result in the deletion of the following UC schemas. Any underlying data may be lost: + delete resources.schemas.my_schema + +All files and directories at the following location will be deleted: /Workspace/Users/[USERNAME]/.bundle/select-subnodes-[UNIQUE_NAME]/default + +Deleting files... +Destroy complete! diff --git a/acceptance/bundle/select/grants_permissions/script b/acceptance/bundle/select/grants_permissions/script new file mode 100644 index 00000000000..1aab6a33fa4 --- /dev/null +++ b/acceptance/bundle/select/grants_permissions/script @@ -0,0 +1,30 @@ +envsubst '$UNIQUE_NAME' < databricks.yml.tmpl > databricks.yml + +cleanup() { + title "Destroy" + trace $CLI bundle destroy --auto-approve + rm -f out.requests.txt +} +trap cleanup EXIT + +# Selecting a resource pulls in its grants/permissions sub-nodes even though the +# dependency edge runs sub-node -> parent, so the plan would never reach them from +# the parent otherwise (issue #5794). +trace $CLI bundle plan --select jobs.my_job +trace $CLI bundle plan --select schemas.my_schema + +# Grants and permissions sub-nodes are not addressable via --select. +musterr trace $CLI bundle plan --select jobs.my_job.permissions +musterr trace $CLI bundle plan --select schemas.my_schema.grants + +# Deploying a selected resource applies its permissions/grants too. +$CLI bundle plan --select jobs.my_job -o json > plan-job.json +title "bundle deploy --select jobs.my_job\n" +$CLI bundle deploy --select jobs.my_job $(readplanarg plan-job.json) + +$CLI bundle plan --select schemas.my_schema -o json > plan-schema.json +title "bundle deploy --select schemas.my_schema\n" +$CLI bundle deploy --select schemas.my_schema $(readplanarg plan-schema.json) + +# Assert the permissions PUT and grants PATCH were emitted for the selected resources. +trace print_requests.py --sort //permissions diff --git a/acceptance/bundle/select/grants_permissions/test.toml b/acceptance/bundle/select/grants_permissions/test.toml new file mode 100644 index 00000000000..6913a14ddac --- /dev/null +++ b/acceptance/bundle/select/grants_permissions/test.toml @@ -0,0 +1,15 @@ +# Local-only: exercises --select (issue #5794) end to end against the testserver. +# It deploys a schema, so it needs a UC metastore; --select is direct-engine only. +Local = true +Cloud = false +RecordRequests = true +RequiresUnityCatalog = true +EnvMatrix.DATABRICKS_BUNDLE_ENGINE = ["direct"] +EnvMatrix.READPLAN = ["", "1"] +Ignore = [".databricks", ".gitignore", "databricks.yml", "plan-job.json", "plan-schema.json"] + +[Env] +# The bundle name deliberately avoids the "permissions"/"schemas" tokens used in the +# print_requests.py filters, but set this too so Git Bash on Windows does not rewrite +# the leading "//" of "//permissions" into a path (see .agent/rules/testing.md). +MSYS_NO_PATHCONV = "1" diff --git a/acceptance/bundle/select/permissions/databricks.yml.tmpl b/acceptance/bundle/select/permissions/databricks.yml.tmpl deleted file mode 100644 index 2435b66c70a..00000000000 --- a/acceptance/bundle/select/permissions/databricks.yml.tmpl +++ /dev/null @@ -1,16 +0,0 @@ -bundle: - name: select-permissions-$UNIQUE_NAME - -resources: - jobs: - foo: - name: foo-$UNIQUE_NAME - permissions: - - level: CAN_VIEW - user_name: viewer@example.com - - bar: - name: bar-$UNIQUE_NAME - permissions: - - level: CAN_VIEW - user_name: viewer@example.com diff --git a/acceptance/bundle/select/permissions/out.test.toml b/acceptance/bundle/select/permissions/out.test.toml deleted file mode 100644 index 71970b719d4..00000000000 --- a/acceptance/bundle/select/permissions/out.test.toml +++ /dev/null @@ -1,4 +0,0 @@ -Local = true -Cloud = false -EnvMatrix.DATABRICKS_BUNDLE_ENGINE = ["direct"] -EnvMatrix.READPLAN = ["", "1"] diff --git a/acceptance/bundle/select/permissions/output.txt b/acceptance/bundle/select/permissions/output.txt deleted file mode 100644 index 3d52a8be930..00000000000 --- a/acceptance/bundle/select/permissions/output.txt +++ /dev/null @@ -1,61 +0,0 @@ - ->>> [CLI] bundle plan --select jobs.foo -create jobs.foo -create jobs.foo.permissions - -Plan: 2 to add, 0 to change, 0 to delete, 0 unchanged - ->>> [CLI] bundle plan --select jobs.foo.permissions -Error: no such resource: jobs.foo.permissions - - -=== bundle deploy --select jobs.foo -Uploading bundle files to /Workspace/Users/[USERNAME]/.bundle/select-permissions-[UNIQUE_NAME]/default/files... -Deploying resources... -Updating deployment state... -Deployment complete! - ->>> print_requests.py --sort //permissions //jobs -{ - "method": "POST", - "path": "/api/2.2/jobs/create", - "body": { - "deployment": { - "kind": "BUNDLE", - "metadata_file_path": "/Workspace/Users/[USERNAME]/.bundle/select-permissions-[UNIQUE_NAME]/default/state/metadata.json" - }, - "edit_mode": "UI_LOCKED", - "format": "MULTI_TASK", - "max_concurrent_runs": 1, - "name": "foo-[UNIQUE_NAME]", - "queue": { - "enabled": true - } - } -} -{ - "method": "PUT", - "path": "/api/2.0/permissions/jobs/[NUMID]", - "body": { - "access_control_list": [ - { - "permission_level": "CAN_VIEW", - "user_name": "viewer@example.com" - }, - { - "permission_level": "IS_OWNER", - "user_name": "[USERNAME]" - } - ] - } -} - -=== Destroy ->>> [CLI] bundle destroy --auto-approve -The following resources will be deleted: - delete resources.jobs.foo - -All files and directories at the following location will be deleted: /Workspace/Users/[USERNAME]/.bundle/select-permissions-[UNIQUE_NAME]/default - -Deleting files... -Destroy complete! diff --git a/acceptance/bundle/select/permissions/script b/acceptance/bundle/select/permissions/script deleted file mode 100644 index 2d1a8c43d27..00000000000 --- a/acceptance/bundle/select/permissions/script +++ /dev/null @@ -1,21 +0,0 @@ -envsubst '$UNIQUE_NAME' < databricks.yml.tmpl > databricks.yml - -cleanup() { - title "Destroy" - trace $CLI bundle destroy --auto-approve - rm -f out.requests.txt -} -trap cleanup EXIT - -# Selecting the job pulls in its permissions sub-node even though nothing points -# job -> permissions (the dependency edge runs the other way). bar is untouched. -trace $CLI bundle plan --select jobs.foo - -# Permissions sub-nodes are not addressable via --select. -musterr trace $CLI bundle plan --select jobs.foo.permissions - -# Deploy only foo and assert its permissions PUT is emitted. -$CLI bundle plan --select jobs.foo -o json > plan.json -title "bundle deploy --select jobs.foo\n" -$CLI bundle deploy --select jobs.foo $(readplanarg plan.json) -trace print_requests.py --sort //permissions //jobs diff --git a/acceptance/bundle/select/permissions/test.toml b/acceptance/bundle/select/permissions/test.toml deleted file mode 100644 index b6a45c49ca4..00000000000 --- a/acceptance/bundle/select/permissions/test.toml +++ /dev/null @@ -1,5 +0,0 @@ -RecordRequests = true -# --select is only supported by the direct engine. -EnvMatrix.DATABRICKS_BUNDLE_ENGINE = ["direct"] -EnvMatrix.READPLAN = ["", "1"] -Ignore = [".databricks", ".gitignore", "databricks.yml", "plan.json"] From faa96a29d6a715686306a82c95551265b5dbe5a0 Mon Sep 17 00:00:00 2001 From: Jan Rose Date: Wed, 8 Jul 2026 20:01:07 +0200 Subject: [PATCH 5/5] Remove env var from grants_permissions --- .../bundle/select/grants_permissions/test.toml | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/acceptance/bundle/select/grants_permissions/test.toml b/acceptance/bundle/select/grants_permissions/test.toml index 6913a14ddac..9f8b5c0ddc9 100644 --- a/acceptance/bundle/select/grants_permissions/test.toml +++ b/acceptance/bundle/select/grants_permissions/test.toml @@ -1,5 +1,11 @@ # Local-only: exercises --select (issue #5794) end to end against the testserver. # It deploys a schema, so it needs a UC metastore; --select is direct-engine only. +# The bundle name deliberately avoids the "permissions" token used in the +# print_requests.py filter: on Windows, Git Bash mangles the leading "//" of +# "//permissions" so the filter degrades to the bare substring "permissions", which +# would otherwise also match the bundle's own file-upload paths. (We can't set +# MSYS_NO_PATHCONV to keep the "//" intact, because that breaks the python helper's +# own path resolution -- see the env -u workaround in acceptance/script.prepare.) Local = true Cloud = false RecordRequests = true @@ -7,9 +13,3 @@ RequiresUnityCatalog = true EnvMatrix.DATABRICKS_BUNDLE_ENGINE = ["direct"] EnvMatrix.READPLAN = ["", "1"] Ignore = [".databricks", ".gitignore", "databricks.yml", "plan-job.json", "plan-schema.json"] - -[Env] -# The bundle name deliberately avoids the "permissions"/"schemas" tokens used in the -# print_requests.py filters, but set this too so Git Bash on Windows does not rewrite -# the leading "//" of "//permissions" into a path (see .agent/rules/testing.md). -MSYS_NO_PATHCONV = "1"