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
1 change: 1 addition & 0 deletions NEXT_CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
* direct: Cluster resize now falls back to regular update if resize fails due to `INVALID_STATE` ([#5716](https://github.com/databricks/cli/pull/5716)).
* `bundle generate dashboard` now honors the `--key` flag when naming the generated resource, and rejects combining `--existing-path`, `--existing-id`, and `--resource` instead of silently ignoring all but one ([#5492](https://github.com/databricks/cli/pull/5492)).
* Fixed `bundle deployment migrate` failing on `model_serving_endpoints`/`database_instances` with permissions (regression since v1.5.0) ([#5775](https://github.com/databricks/cli/pull/5775)).
* Support `replace_existing: true` on `postgres_databases` and `postgres_roles` so bundles can take over a database or role that already exists on a Lakebase branch instead of failing with `ALREADY_EXISTS` ([#5803](https://github.com/databricks/cli/pull/5803)).
* After a terraform deploy, the CLI now dry-runs a migration to the direct engine (writing nothing locally or remotely) and reports the outcome via telemetry, warning if the migration could not be completed ([#5797](https://github.com/databricks/cli/pull/5797)).

### Dependency updates
Expand Down
2 changes: 2 additions & 0 deletions acceptance/bundle/refschema/out.fields.txt
Original file line number Diff line number Diff line change
Expand Up @@ -2940,6 +2940,7 @@ resources.postgres_databases.*.modified_status string INPUT
resources.postgres_databases.*.name string REMOTE
resources.postgres_databases.*.parent string ALL
resources.postgres_databases.*.postgres_database string ALL
resources.postgres_databases.*.replace_existing bool INPUT STATE
resources.postgres_databases.*.role string ALL
resources.postgres_databases.*.status *postgres.DatabaseDatabaseStatus REMOTE
resources.postgres_databases.*.status.database_id string REMOTE
Expand Down Expand Up @@ -3075,6 +3076,7 @@ resources.postgres_roles.*.modified_status string INPUT
resources.postgres_roles.*.name string REMOTE
resources.postgres_roles.*.parent string ALL
resources.postgres_roles.*.postgres_role string ALL
resources.postgres_roles.*.replace_existing bool INPUT STATE
resources.postgres_roles.*.role_id string ALL
resources.postgres_roles.*.status *postgres.RoleRoleStatus REMOTE
resources.postgres_roles.*.status.attributes *postgres.RoleAttributes REMOTE
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
bundle:
name: replace-existing-database-$UNIQUE_NAME

sync:
paths: []

resources:
postgres_projects:
my_project:
project_id: test-pg-proj-$UNIQUE_NAME
display_name: "Test Project for Database replace_existing"
pg_version: 16
history_retention_duration: "604800s"

postgres_branches:
main:
parent: ${resources.postgres_projects.my_project.id}
branch_id: main
no_expiry: true

postgres_roles:
owner:
parent: ${resources.postgres_branches.main.id}
role_id: app-owner
postgres_role: app_owner

# replace_existing takes over a database that already exists on the branch
# instead of erroring with ALREADY_EXISTS. The field is input-only and not
# surfaced in get-database, but it is visible in the recorded request body so
# the diff confirms it was sent.
postgres_databases:
my_database:
parent: ${resources.postgres_branches.main.id}
database_id: my-database
postgres_database: app_db
# The live API requires `role`. Declare an explicit role so the bundle is
# portable across users (the auto-created project-owner role's id is
# derived from the creator's identity).
role: ${resources.postgres_roles.owner.id}
replace_existing: true
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
{
"method": "GET",
"path": "/api/2.0/postgres/projects/test-pg-proj-[UNIQUE_NAME]"
}
{
"method": "GET",
"path": "/api/2.0/postgres/projects/test-pg-proj-[UNIQUE_NAME]/branches/main"
}
{
"method": "GET",
"path": "/api/2.0/postgres/projects/test-pg-proj-[UNIQUE_NAME]/branches/main/roles/app-owner"
}
{
"method": "GET",
"path": "/api/2.0/postgres/projects/test-pg-proj-[UNIQUE_NAME]"
}
{
"method": "GET",
"path": "/api/2.0/postgres/projects/test-pg-proj-[UNIQUE_NAME]/branches/main"
}
{
"method": "GET",
"path": "/api/2.0/postgres/projects/test-pg-proj-[UNIQUE_NAME]/branches/main/roles/app-owner"
}
{
"method": "POST",
"path": "/api/2.0/postgres/projects/test-pg-proj-[UNIQUE_NAME]/branches/main/databases",
"q": {
"database_id": "my-database",
"replace_existing": "true"
},
"body": {
"spec": {
"postgres_database": "app_db",
"role": "projects/test-pg-proj-[UNIQUE_NAME]/branches/main/roles/app-owner"
}
}
}
{
"method": "GET",
"path": "/api/2.0/postgres/projects/test-pg-proj-[UNIQUE_NAME]/branches/main/databases/my-database"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
{
"method": "GET",
"path": "/api/2.0/postgres/projects/test-pg-proj-[UNIQUE_NAME]"
}
{
"method": "GET",
"path": "/api/2.0/postgres/projects/test-pg-proj-[UNIQUE_NAME]/branches/main"
}
{
"method": "GET",
"path": "/api/2.0/postgres/projects/test-pg-proj-[UNIQUE_NAME]/branches/main/roles/app-owner"
}
{
"method": "POST",
"path": "/api/2.0/postgres/projects/test-pg-proj-[UNIQUE_NAME]/branches/main/databases",
"q": {
"database_id": "my-database",
"replace_existing": "true"
},
"body": {
"parent": "projects/test-pg-proj-[UNIQUE_NAME]/branches/main",
"spec": {
"postgres_database": "app_db",
"role": "projects/test-pg-proj-[UNIQUE_NAME]/branches/main/roles/app-owner"
}
}
}
{
"method": "GET",
"path": "/api/2.0/postgres/projects/test-pg-proj-[UNIQUE_NAME]/branches/main/databases/my-database"
}

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,63 @@

=== Deploy project, branch, role, and database
>>> [CLI] bundle deploy
Uploading bundle files to /Workspace/Users/[USERNAME]/.bundle/replace-existing-database-[UNIQUE_NAME]/default/files...
Deploying resources...
Updating deployment state...
Deployment complete!

=== Unbind the database: it now exists on the branch but is no longer tracked by the bundle
>>> [CLI] bundle deployment unbind my_database
Updating deployment state...

=== Plan after unbind: the bundle wants to (re-)create the database
>>> [CLI] bundle plan
create postgres_databases.my_database

Plan: 1 to add, 0 to change, 0 to delete, 3 unchanged

=== Re-deploy: replace_existing adopts the already-existing database instead of erroring
>>> [CLI] bundle deploy
Uploading bundle files to /Workspace/Users/[USERNAME]/.bundle/replace-existing-database-[UNIQUE_NAME]/default/files...
Deploying resources...
Updating deployment state...
Deployment complete!

=== Confirm the database is under management again
>>> [CLI] postgres get-database projects/test-pg-proj-[UNIQUE_NAME]/branches/main/databases/my-database
{
"database_id": "my-database",
"name": "projects/test-pg-proj-[UNIQUE_NAME]/branches/main/databases/my-database",
"parent": "projects/test-pg-proj-[UNIQUE_NAME]/branches/main",
"status": {
"database_id": "my-database",
"postgres_database": "app_db",
"role": "projects/test-pg-proj-[UNIQUE_NAME]/branches/main/roles/app-owner"
}
}

>>> print_requests.py --del-body project_id,branch_id,endpoint_id,database_id,role_id,catalog_id,synced_table_id --keep --get //postgres ^//workspace-files/ ^//workspace/ ^//telemetry-ext ^//operations/

>>> [CLI] bundle destroy --auto-approve
The following resources will be deleted:
delete resources.postgres_branches.main
delete resources.postgres_databases.my_database
delete resources.postgres_projects.my_project
delete resources.postgres_roles.owner

This action will result in the deletion of the following Lakebase projects along with
all their branches, databases, and endpoints. All data stored in them will be permanently lost:
delete resources.postgres_projects.my_project

This action will result in the deletion of the following Lakebase branches.
All data stored in them will be permanently lost:
delete resources.postgres_branches.main

This action will result in the deletion of the following Lakebase databases.
All data stored in them will be permanently lost:
delete resources.postgres_databases.my_database

All files and directories at the following location will be deleted: /Workspace/Users/[USERNAME]/.bundle/replace-existing-database-[UNIQUE_NAME]/default

Deleting files...
Destroy complete!
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
envsubst < databricks.yml.tmpl > databricks.yml

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

title "Deploy project, branch, role, and database"
trace $CLI bundle deploy

title "Unbind the database: it now exists on the branch but is no longer tracked by the bundle"
# Mirrors a database that already exists on the branch but is not created or
# owned by this bundle.
trace $CLI bundle deployment unbind my_database

title "Plan after unbind: the bundle wants to (re-)create the database"
trace $CLI bundle plan

title "Re-deploy: replace_existing adopts the already-existing database instead of erroring"
rm -f out.requests.txt
trace $CLI bundle deploy

title "Confirm the database is under management again"
trace $CLI postgres get-database "projects/test-pg-proj-${UNIQUE_NAME}/branches/main/databases/my-database" | jq 'del(.create_time, .update_time)'

trace print_requests.py --del-body project_id,branch_id,endpoint_id,database_id,role_id,catalog_id,synced_table_id --keep --get '//postgres' '^//workspace-files/' '^//workspace/' '^//telemetry-ext' '^//operations/' > out.requests.deploy.$DATABRICKS_BUNDLE_ENGINE.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# The pre-existing database is staged locally via `bundle deployment unbind`,
# so this is local-only.
Cloud = false
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
bundle:
name: replace-existing-role-$UNIQUE_NAME

sync:
paths: []

resources:
postgres_projects:
my_project:
project_id: test-pg-proj-$UNIQUE_NAME
display_name: "Test Project for Role replace_existing"
pg_version: 16
history_retention_duration: 604800s

postgres_branches:
main:
parent: ${resources.postgres_projects.my_project.id}
branch_id: main
no_expiry: true

# replace_existing takes over a role that already exists on the branch (e.g.
# inherited from the parent branch) instead of erroring with ALREADY_EXISTS.
# The field is input-only and not surfaced in get-role, but it is visible in
# the recorded request body so the diff confirms it was sent.
postgres_roles:
my_role:
parent: ${resources.postgres_branches.main.id}
role_id: test-role
postgres_role: app_role
replace_existing: true
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
{
"method": "GET",
"path": "/api/2.0/postgres/projects/test-pg-proj-[UNIQUE_NAME]"
}
{
"method": "GET",
"path": "/api/2.0/postgres/projects/test-pg-proj-[UNIQUE_NAME]/branches/main"
}
{
"method": "GET",
"path": "/api/2.0/postgres/projects/test-pg-proj-[UNIQUE_NAME]"
}
{
"method": "GET",
"path": "/api/2.0/postgres/projects/test-pg-proj-[UNIQUE_NAME]/branches/main"
}
{
"method": "POST",
"path": "/api/2.0/postgres/projects/test-pg-proj-[UNIQUE_NAME]/branches/main/roles",
"q": {
"replace_existing": "true",
"role_id": "test-role"
},
"body": {
"spec": {
"postgres_role": "app_role"
}
}
}
{
"method": "GET",
"path": "/api/2.0/postgres/projects/test-pg-proj-[UNIQUE_NAME]/branches/main/roles/test-role"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
{
"method": "GET",
"path": "/api/2.0/postgres/projects/test-pg-proj-[UNIQUE_NAME]"
}
{
"method": "GET",
"path": "/api/2.0/postgres/projects/test-pg-proj-[UNIQUE_NAME]/branches/main"
}
{
"method": "POST",
"path": "/api/2.0/postgres/projects/test-pg-proj-[UNIQUE_NAME]/branches/main/roles",
"q": {
"replace_existing": "true",
"role_id": "test-role"
},
"body": {
"parent": "projects/test-pg-proj-[UNIQUE_NAME]/branches/main",
"spec": {
"postgres_role": "app_role"
}
}
}
{
"method": "GET",
"path": "/api/2.0/postgres/projects/test-pg-proj-[UNIQUE_NAME]/branches/main/roles/test-role"
}

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

Loading
Loading