Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .nextchanges/bundles/empty-list-stays-enforced.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
Emptying a resource list now keeps enforcing it. A bundle declaring `grants: []` revokes
any grant added out of band on every deploy, not just the first.
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
bundle:
name: schema-grants-oob-after-empty-$UNIQUE_NAME

resources:
schemas:
grants_schema:
name: schema_oob_after_empty_$UNIQUE_NAME
catalog_name: main
grants: [{ principal: deco-test-user@databricks.com, privileges: [USE_SCHEMA] }]
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
json = {};
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
json.privilege_assignments[].principal = "deco-test-user@databricks.com";
json.privilege_assignments[].privileges[] = "USE_SCHEMA";

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@

=== Deploy with one grant
>>> [CLI] bundle deploy -qq

=== Empty the list: the grant is revoked
>>> [CLI] bundle deploy -qq

>>> [CLI] grants get schema main.schema_oob_after_empty_[UNIQUE_NAME]
json = {};

=== Add a grant out of band
>>> [CLI] grants update schema main.schema_oob_after_empty_[UNIQUE_NAME] --json @update.json

>>> [CLI] grants get schema main.schema_oob_after_empty_[UNIQUE_NAME]
json.privilege_assignments[].principal = "deco-test-user@databricks.com";
json.privilege_assignments[].privileges[] = "USE_SCHEMA";

=== Redeploy: the enforced empty list revokes the out-of-band grant
>>> [CLI] bundle deploy -qq

>>> errcode [CLI] bundle destroy --auto-approve
The following resources will be deleted:
delete resources.schemas.grants_schema

This action will result in the deletion of the following UC schemas. Any underlying data may be lost:
delete resources.schemas.grants_schema

All files and directories at the following location will be deleted: /Workspace/Users/[USERNAME]/.bundle/schema-grants-oob-after-empty-[UNIQUE_NAME]/default

Destroy: 1 deleted
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
SCHEMA_FULL_NAME=main.schema_oob_after_empty_$UNIQUE_NAME

envsubst < databricks.yml.tmpl > databricks.yml

cleanup() {
trace errcode $CLI bundle destroy --auto-approve
rm -f out.requests.txt
}
trap cleanup EXIT

title "Deploy with one grant"
trace $CLI bundle deploy -qq

title "Empty the list: the grant is revoked"
update_file.py databricks.yml 'grants: [{ principal: deco-test-user@databricks.com, privileges: [USE_SCHEMA] }]' 'grants: []'
trace $CLI bundle deploy -qq
trace $CLI grants get schema "$SCHEMA_FULL_NAME" | gron.py --noindex | sort_lines.py --repl

title "Add a grant out of band"
trace $CLI grants update schema "$SCHEMA_FULL_NAME" --json @update.json > /dev/null
trace $CLI grants get schema "$SCHEMA_FULL_NAME" | gron.py --noindex | sort_lines.py --repl | contains.py 'deco-test-user@databricks.com'

# The engines diverge here, so the result goes to a per-engine file: direct re-plans the
# emptied node and revokes the out-of-band grant, terraform leaves it in place.
title "Redeploy: the enforced empty list revokes the out-of-band grant"
trace $CLI bundle deploy -qq
$CLI grants get schema "$SCHEMA_FULL_NAME" | gron.py --noindex | sort_lines.py --repl > out.grants.$DATABRICKS_BUNDLE_ENGINE.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{
"changes": [
{
"principal": "deco-test-user@databricks.com",
"add": ["USE_SCHEMA"]
}
]
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@

>>> [CLI] bundle plan
Plan: 0 to add, 0 to change, 0 to delete, 2 unchanged

>>> [CLI] bundle deploy
Uploading bundle files to /Workspace/Users/[USERNAME]/.bundle/schema-grants-remove-all-[UNIQUE_NAME]/default/files...
Files: 3 uploaded, 0 deleted
Resources: 0 created, 0 changed, 0 deleted, 2 unchanged

>>> [CLI] bundle plan
Plan: 0 to add, 0 to change, 0 to delete, 2 unchanged
Original file line number Diff line number Diff line change
Expand Up @@ -26,4 +26,4 @@ trace $CLI grants get schema main.schema_remove_all_$UNIQUE_NAME | gron.py --noi
trace $CLI bundle plan
trace $CLI bundle deploy
trace $CLI bundle plan
} &> out.plan.txt
} &> out.plan.$DATABRICKS_BUNDLE_ENGINE.txt
23 changes: 6 additions & 17 deletions bundle/direct/apply.go
Original file line number Diff line number Diff line change
Expand Up @@ -159,24 +159,13 @@ func (d *DeploymentUnit) Update(ctx context.Context, db *dstate.DeploymentState,
return err
}

empty, err := d.Adapter.IsEmptyState(newState)
// An update that empties the resource out (e.g. all grants revoked) saves the empty state
// rather than dropping the entry. The resource is still there - the schema keeps existing,
// it just grants nothing - so the node stays tracked, and a caller reading the state sees
// what the update applied instead of nothing at all.
err = d.saveState(db, id, newState, d.DependsOn)
if err != nil {
return err
}

if empty {
// The update emptied the resource out (e.g. all grants revoked). Keeping an entry
// would report the node as tracked-and-unchanged forever, while a fresh deploy of
// the same config plans no node at all; drop it so the two agree.
err = db.DeleteState(d.ResourceKey)
if err != nil {
return fmt.Errorf("deleting state id=%s: %w", id, err)
}
} else {
err = d.saveState(db, id, newState, d.DependsOn)
if err != nil {
return fmt.Errorf("saving state id=%s: %w", id, err)
}
return fmt.Errorf("saving state id=%s: %w", id, err)
}

waitRemoteState, err := retryOnTransient(ctx, func() (any, error) {
Expand Down
Loading