Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
bef54ed
direct: replace WaitAfterCreate/WaitAfterUpdate with Engine callback …
denik May 28, 2026
2898f55
direct/testserver: improve app test realism
denik May 29, 2026
c30f8d1
testserver: remove unused GetWorkspace method
denik Jun 1, 2026
da50fee
direct: simplify Engine API: SaveState(id, state) replaces SetID+Save…
denik Jun 1, 2026
966b659
direct: Engine.SaveState takes ctx, returns void; logs I/O failures i…
denik Jun 1, 2026
d851cdd
direct: save state before wait in postgres and dashboard resources
denik Jun 1, 2026
7ba5d95
direct: save state before publishing dashboard; add acceptance tests
denik Jun 2, 2026
21c9e21
acceptance/dashboards: normalize ?o=/?w= workspace param in URL repla…
denik Jun 2, 2026
da308cb
acceptance/dashboards: fix ?o=/?w= workspace param normalization
denik Jun 2, 2026
4b584f1
acceptance/dashboards: fix &>> redirect for bash 3.2 compatibility
denik Jun 2, 2026
e160704
acceptance/dashboards: unset MSYS_NO_PATHCONV before fault.py on Windows
denik Jun 3, 2026
3f11951
direct: Engine dedup+logging; dashboard saves draft state as Publishe…
denik Jun 3, 2026
bf602f1
direct: inline WaitAfterCreate/WaitAfterUpdate into sql_warehouse DoC…
denik Jun 3, 2026
51392cd
acceptance/dashboards: use replace_ids.py and inline plan output in p…
denik Jun 4, 2026
72e62da
direct: save state before wait/publish in sql_warehouse DoCreate and …
denik Jun 5, 2026
775a0c9
direct: save state before wait in model_serving_endpoint DoUpdate
denik Jun 5, 2026
694e048
direct: revert postgres SaveState (waiter.Name() is the LRO operation…
denik Jun 5, 2026
9cd0b64
direct: route final Create/Update state saves through Engine
denik Jun 5, 2026
339e386
direct: rename Engine -> StateSaver
denik Jun 5, 2026
e6131bc
direct: add SaveStateWith helper; avoid double JSON marshal in StateS…
denik Jun 5, 2026
79818c6
direct: fix SaveStateWith parameter order (ctx first)
denik Jun 5, 2026
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
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
{
"method": "POST",
"path": "/api/2.0/workspace/mkdirs",
"body": {
"path": "/Workspace/Users/[USERNAME]/.bundle/publish-failure-cleans-up-dashboard/default/artifacts/.internal"
}
}
{
"method": "POST",
"path": "/api/2.0/workspace/mkdirs",
"body": {
"path": "/Workspace/Users/[USERNAME]/.bundle/publish-failure-cleans-up-dashboard/default/files"
}
}
{
"method": "POST",
"path": "/api/2.0/workspace/mkdirs",
"body": {
"path": "/Workspace/Users/[USERNAME]/.bundle/publish-failure-cleans-up-dashboard/default/resources"
}
}
{
"method": "POST",
"path": "/api/2.0/lakeview/dashboards",
"body": {
"display_name": "my dashboard",
"parent_path": "/Workspace/Users/[USERNAME]/.bundle/publish-failure-cleans-up-dashboard/default/resources",
"serialized_dashboard": "{\"pages\":[{\"name\":\"test-page\",\"displayName\":\"Test Dashboard\"}]}\n",
"warehouse_id": "doesnotexist"
}
}
{
"method": "POST",
"path": "/api/2.0/lakeview/dashboards/[DASHBOARD_ID]/published",
"body": {
"embed_credentials": false,
"warehouse_id": "doesnotexist"
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,5 +9,6 @@ HTTP Status: 400 Bad Request
API error_code: RESOURCE_DOES_NOT_EXIST
API message: Warehouse doesnotexist does not exist

Updating deployment state...

Exit code: 1
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@

>>> [CLI] bundle summary
Name: publish-failure-cleans-up-dashboard
Target: default
Workspace:
User: [USERNAME]
Path: /Workspace/Users/[USERNAME]/.bundle/publish-failure-cleans-up-dashboard/default
Resources:
Dashboards:
dashboard1:
Name: my dashboard
URL: [DATABRICKS_URL]/dashboardsv3/[DASHBOARD_ID]/published?[WSPARAM]=[NUMID]
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@

>>> [CLI] bundle summary
Name: publish-failure-cleans-up-dashboard
Target: default
Workspace:
User: [USERNAME]
Path: /Workspace/Users/[USERNAME]/.bundle/publish-failure-cleans-up-dashboard/default
Resources:
Dashboards:
dashboard1:
Name: my dashboard
URL: (not deployed)
Original file line number Diff line number Diff line change
@@ -1,12 +0,0 @@

>>> [CLI] bundle summary
Name: publish-failure-cleans-up-dashboard
Target: default
Workspace:
User: [USERNAME]
Path: /Workspace/Users/[USERNAME]/.bundle/publish-failure-cleans-up-dashboard/default
Resources:
Dashboards:
dashboard1:
Name: my dashboard
URL: (not deployed)
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,9 @@ envsubst < databricks.yml.tmpl > databricks.yml
# Deploy the dashboard. The dashboard will be created but publish will fail because the warehouse does not exist.
errcode trace $CLI bundle deploy &> out.deploy.$DATABRICKS_BUNDLE_ENGINE.txt

trace $CLI bundle summary
# After publish failure the dashboard draft should be in state (direct) or cleaned up (terraform).
trace $CLI bundle summary >> out.summary.$DATABRICKS_BUNDLE_ENGINE.txt 2>&1

# API should record a DELETE call to clean up the draft dashboard that was not published.
# Request sequence is identical across terraform and direct modes.
# API request sequence differs between engines (direct: no DELETE; terraform: DELETE to clean up).
unset MSYS_NO_PATHCONV
print_requests.py //lakeview/dashboards //workspace/mkdirs > out.dashboardrequests.txt
print_requests.py //lakeview/dashboards //workspace/mkdirs > out.dashboardrequests.$DATABRICKS_BUNDLE_ENGINE.txt
Original file line number Diff line number Diff line change
Expand Up @@ -10,3 +10,8 @@ Response.Body = '{"error_code": "RESOURCE_DOES_NOT_EXIST", "message": "Warehouse
[[Repls]]
Old = "[0-9a-f]{32}"
New = "[DASHBOARD_ID]"

# Dashboard published URLs use ?o= (local testserver) or ?w= (cloud) for the workspace/org ID.
[[Repls]]
Old = '\?[ow]=\d+'
New = "?[WSPARAM]=[NUMID]"
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{"pages":[{"name":"test-page","displayName":"Test Dashboard"}]}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
bundle:
name: publish-failure-retry-on-update

resources:
dashboards:
dashboard1:
display_name: my dashboard
warehouse_id: someid
file_path: ./dashboard.lvdash.json

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

>>> [CLI] bundle deploy
Uploading bundle files to /Workspace/Users/[USERNAME]/.bundle/publish-failure-retry-on-update/default/files...
Deploying resources...
Updating deployment state...
Deployment complete!

>>> [CLI] bundle deploy
Uploading bundle files to /Workspace/Users/[USERNAME]/.bundle/publish-failure-retry-on-update/default/files...
Deploying resources...
Error: cannot update resources.dashboards.dashboard1: updating id=[DASHBOARD1_ID]: Fault injected by test. (400 INJECTED)

Endpoint: POST [DATABRICKS_URL]/api/2.0/lakeview/dashboards/[DASHBOARD1_ID]/published
HTTP Status: 400 Bad Request
API error_code: INJECTED
API message: Fault injected by test.

Updating deployment state...

Exit code: 1
{
"method": "PATCH",
"path": "/api/2.0/lakeview/dashboards/[DASHBOARD1_ID]",
"body": {
"display_name": "my dashboard renamed",
"parent_path": "/Workspace/Users/[USERNAME]/.bundle/publish-failure-retry-on-update/default/resources",
"serialized_dashboard": "{\"pages\":[{\"name\":\"test-page\",\"displayName\":\"Test Dashboard\"}]}\n",
"warehouse_id": "someid"
}
}
{
"method": "POST",
"path": "/api/2.0/lakeview/dashboards/[DASHBOARD1_ID]/published",
"body": {
"embed_credentials": false,
"warehouse_id": "someid"
}
}

>>> [CLI] bundle plan -o json
{
"action": "skip",
"reason": "remote_already_set",
"old": false,
"new": true,
"remote": true
}

>>> [CLI] bundle destroy --auto-approve
The following resources will be deleted:
delete resources.dashboards.dashboard1

All files and directories at the following location will be deleted: /Workspace/Users/[USERNAME]/.bundle/publish-failure-retry-on-update/default

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

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

# unset MSYS_NO_PATHCONV so MSYS2 converts the script path to a Windows path
# when invoking the Python interpreter (required for fault.py to be found on Windows).
unset MSYS_NO_PATHCONV

# First deploy succeeds fully: the dashboard is created and published.
trace $CLI bundle deploy
replace_ids.py
rm out.requests.txt

# Inject a single publish failure for the update below.
fault.py "POST /api/2.0/lakeview/dashboards/*" 400 0 1

# Change the dashboard to trigger an Update, which bumps the server-side etag.
update_file.py databricks.yml "my dashboard" "my dashboard renamed"

# Deploy: Update (PATCH) succeeds and bumps the etag, but the publish (POST) fails.
errcode trace $CLI bundle deploy

# The failed deploy issued a PATCH (update) and a POST (publish), but no CREATE
# (POST /api/2.0/lakeview/dashboards): the existing dashboard was updated in place.
print_requests.py //lakeview/dashboards

# Plan shows published old=false: state was saved with published=false (and the bumped
# etag) before the failed publish, so the next deploy knows publishing is still pending.
trace $CLI bundle plan -o json | jq '.plan["resources.dashboards.dashboard1"].changes.published'
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
Cloud = false
Local = true
RecordRequests = true

# Only run with the direct engine: the test verifies direct engine's SaveState
# behavior during DoUpdate (new etag + published=false persisted before a failed publish).
[EnvMatrix]
DATABRICKS_BUNDLE_ENGINE = ["direct"]

# Dashboard published URLs use ?o= (local testserver) or ?w= (cloud) for the workspace/org ID.
[[Repls]]
Old = '\?[ow]=\d+'
New = "?[WSPARAM]=[NUMID]"
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{"pages":[{"name":"test-page","displayName":"Test Dashboard"}]}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
bundle:
name: publish-failure-retry

resources:
dashboards:
dashboard1:
display_name: my dashboard
warehouse_id: someid
file_path: ./dashboard.lvdash.json

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

>>> [CLI] bundle deploy
Uploading bundle files to /Workspace/Users/[USERNAME]/.bundle/publish-failure-retry/default/files...
Deploying resources...
Error: cannot create resources.dashboards.dashboard1: Fault injected by test. (400 INJECTED)

Endpoint: POST [DATABRICKS_URL]/api/2.0/lakeview/dashboards/[DASHBOARD1_ID]/published
HTTP Status: 400 Bad Request
API error_code: INJECTED
API message: Fault injected by test.

Updating deployment state...

Exit code: 1

>>> [CLI] bundle summary
Name: publish-failure-retry
Target: default
Workspace:
User: [USERNAME]
Path: /Workspace/Users/[USERNAME]/.bundle/publish-failure-retry/default
Resources:
Dashboards:
dashboard1:
Name: my dashboard
URL: [DATABRICKS_URL]/dashboardsv3/[DASHBOARD1_ID]/published?[WSPARAM]=[NUMID]

>>> [CLI] bundle plan -o json
{
"action": "update",
"old": false,
"new": true,
"remote": false
}

>>> [CLI] bundle deploy
Uploading bundle files to /Workspace/Users/[USERNAME]/.bundle/publish-failure-retry/default/files...
Deploying resources...
Updating deployment state...
Deployment complete!
{
"method": "PATCH",
"path": "/api/2.0/lakeview/dashboards/[DASHBOARD1_ID]",
"body": {
"display_name": "my dashboard",
"parent_path": "/Workspace/Users/[USERNAME]/.bundle/publish-failure-retry/default/resources",
"serialized_dashboard": "{\"pages\":[{\"name\":\"test-page\",\"displayName\":\"Test Dashboard\"}]}\n",
"warehouse_id": "someid"
}
}
{
"method": "POST",
"path": "/api/2.0/lakeview/dashboards/[DASHBOARD1_ID]/published",
"body": {
"embed_credentials": false,
"warehouse_id": "someid"
}
}

>>> [CLI] bundle destroy --auto-approve
The following resources will be deleted:
delete resources.dashboards.dashboard1

All files and directories at the following location will be deleted: /Workspace/Users/[USERNAME]/.bundle/publish-failure-retry/default

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

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

# unset MSYS_NO_PATHCONV so MSYS2 converts the script path to a Windows path
# when invoking the Python interpreter (required for fault.py to be found on Windows).
unset MSYS_NO_PATHCONV

# Inject a single publish failure so the first deploy creates the dashboard
# draft but fails to publish it.
fault.py "POST /api/2.0/lakeview/dashboards/*" 400 0 1

# First deploy: dashboard is created and saved to state, but publish fails.
errcode trace $CLI bundle deploy

# Capture the dashboard ID from state so subsequent output is normalized.
replace_ids.py

# Dashboard should be in state (tracked) despite the publish failure.
trace $CLI bundle summary

# Plan should show published: false (saved state) -> true (desired).
trace $CLI bundle plan -o json | jq '.plan["resources.dashboards.dashboard1"].changes.published'

# Discard first-deploy requests so the output only contains second-deploy
# calls, making it easy to confirm no CREATE was issued.
rm out.requests.txt

# Second deploy: fault is gone; must publish the existing draft, not create a new one.
trace $CLI bundle deploy

# Confirm: second deploy issued an UPDATE and a PUBLISH call but no CREATE.
print_requests.py //lakeview/dashboards
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
Cloud = false
Local = true
RecordRequests = true

# Only run with the direct engine: the test verifies direct engine's SaveState
# behavior (draft persists on publish failure and is re-published on retry).
[EnvMatrix]
DATABRICKS_BUNDLE_ENGINE = ["direct"]

# Dashboard published URLs use ?o= (local testserver) or ?w= (cloud) for the workspace/org ID.
[[Repls]]
Old = '\?[ow]=\d+'
New = "?[WSPARAM]=[NUMID]"
Loading
Loading