diff --git a/acceptance/bundle/resources/dashboards/change-embed-credentials/out.patch.requests.terraform.txt b/acceptance/bundle/resources/dashboards/change-embed-credentials/out.patch.requests.terraform.txt index 761d19479b9..7697866cf48 100644 --- a/acceptance/bundle/resources/dashboards/change-embed-credentials/out.patch.requests.terraform.txt +++ b/acceptance/bundle/resources/dashboards/change-embed-credentials/out.patch.requests.terraform.txt @@ -5,7 +5,7 @@ "create_time": "[TIMESTAMP]", "dashboard_id": "[DASHBOARD_ID]", "display_name": "test dashboard", - "etag": [ETAG], + "etag": "[ETAG]", "lifecycle_state": "ACTIVE", "parent_path": "/Users/[USERNAME]/.bundle/change-embed-credentials-[UNIQUE_NAME]/default/resources", "path": "/Users/[USERNAME]/.bundle/change-embed-credentials-[UNIQUE_NAME]/default/resources/test dashboard.lvdash.json", diff --git a/acceptance/bundle/resources/dashboards/change-embed-credentials/script b/acceptance/bundle/resources/dashboards/change-embed-credentials/script index 640ca7da1ef..d1a8743c75e 100755 --- a/acceptance/bundle/resources/dashboards/change-embed-credentials/script +++ b/acceptance/bundle/resources/dashboards/change-embed-credentials/script @@ -25,6 +25,7 @@ deploy_dashboard # Capture the dashboard ID as a replacement. dashboard_id=$($CLI bundle summary --output json | jq -r '.resources.dashboards.my_dashboard.id') echo "$dashboard_id:DASHBOARD_ID" >> ACC_REPLS +echo "$($CLI lakeview get $dashboard_id | jq -r '.etag'):ETAG" >> ACC_REPLS # Change embed_credentials to true - this should trigger an update export EMBED_CREDENTIALS="true" diff --git a/acceptance/bundle/resources/dashboards/change-name/out.patch.requests.terraform.txt b/acceptance/bundle/resources/dashboards/change-name/out.patch.requests.terraform.txt index 2e2e8bc108c..1a3df84f499 100644 --- a/acceptance/bundle/resources/dashboards/change-name/out.patch.requests.terraform.txt +++ b/acceptance/bundle/resources/dashboards/change-name/out.patch.requests.terraform.txt @@ -5,7 +5,7 @@ "create_time": "[TIMESTAMP]", "dashboard_id": "[DASHBOARD_ID]", "display_name": "dashboard2", - "etag": [ETAG], + "etag": "[ETAG]", "lifecycle_state": "ACTIVE", "parent_path": "/Users/[USERNAME]/.bundle/change-name-[UNIQUE_NAME]/default/resources", "path": "/Users/[USERNAME]/.bundle/change-name-[UNIQUE_NAME]/default/resources/dashboard1.lvdash.json", diff --git a/acceptance/bundle/resources/dashboards/change-name/script b/acceptance/bundle/resources/dashboards/change-name/script index 5a20f19f7a3..93a90794786 100644 --- a/acceptance/bundle/resources/dashboards/change-name/script +++ b/acceptance/bundle/resources/dashboards/change-name/script @@ -21,6 +21,7 @@ deploy_dashboard # Capture the dashboard ID as a replacement. dashboard_id=$($CLI bundle summary --output json | jq -r '.resources.dashboards.my_dashboard.id') echo "$dashboard_id:DASHBOARD_ID" >> ACC_REPLS +echo "$($CLI lakeview get $dashboard_id | jq -r '.etag'):ETAG" >> ACC_REPLS # Change the name export NAME="dashboard2" diff --git a/acceptance/bundle/resources/dashboards/dataset-catalog-schema/out.plan.direct.json b/acceptance/bundle/resources/dashboards/dataset-catalog-schema/out.plan.direct.json index dd36d61502f..96ec1597f9e 100644 --- a/acceptance/bundle/resources/dashboards/dataset-catalog-schema/out.plan.direct.json +++ b/acceptance/bundle/resources/dashboards/dataset-catalog-schema/out.plan.direct.json @@ -11,7 +11,7 @@ "dashboard_id": "[DASHBOARD_ID]", "display_name": "test bundle-deploy-dashboard-dataset [UUID]", "embed_credentials": true, - "etag": [ETAG], + "etag": "[ETAG]", "lifecycle_state": "ACTIVE", "parent_path": "/Workspace/Users/[USERNAME]", "path": "/Users/[USERNAME]/test bundle-deploy-dashboard-dataset [UUID].lvdash.json", @@ -36,8 +36,8 @@ "etag": { "action": "skip", "reason": "custom", - "old": [ETAG], - "remote": [ETAG] + "old": "[ETAG]", + "remote": "[ETAG]" }, "serialized_dashboard": { "action": "skip", diff --git a/acceptance/bundle/resources/dashboards/dataset-catalog-schema/script b/acceptance/bundle/resources/dashboards/dataset-catalog-schema/script index e4c6e6dfec7..f4a5750e820 100755 --- a/acceptance/bundle/resources/dashboards/dataset-catalog-schema/script +++ b/acceptance/bundle/resources/dashboards/dataset-catalog-schema/script @@ -19,6 +19,7 @@ DASHBOARD_ID=$($CLI bundle summary --output json | jq -r '.resources.dashboards. # Capture the dashboard ID as a replacement. echo "$DASHBOARD_ID:DASHBOARD_ID" >> ACC_REPLS +echo "$($CLI lakeview get $DASHBOARD_ID | jq -r '.etag'):ETAG" >> ACC_REPLS trace $CLI lakeview get $DASHBOARD_ID | jq '{lifecycle_state, parent_path, path}' diff --git a/acceptance/bundle/resources/dashboards/delete-trashed-out-of-band/out.plan.direct.json b/acceptance/bundle/resources/dashboards/delete-trashed-out-of-band/out.plan.direct.json index dcc05760f7a..63f02f163f2 100644 --- a/acceptance/bundle/resources/dashboards/delete-trashed-out-of-band/out.plan.direct.json +++ b/acceptance/bundle/resources/dashboards/delete-trashed-out-of-band/out.plan.direct.json @@ -20,7 +20,7 @@ "etag": { "action": "update", "reason": "custom", - "old": [ETAG] + "old": "[ETAG]" } } } diff --git a/acceptance/bundle/resources/dashboards/delete-trashed-out-of-band/script b/acceptance/bundle/resources/dashboards/delete-trashed-out-of-band/script index 0868adbc13b..bc67448e8a6 100755 --- a/acceptance/bundle/resources/dashboards/delete-trashed-out-of-band/script +++ b/acceptance/bundle/resources/dashboards/delete-trashed-out-of-band/script @@ -13,6 +13,7 @@ DASHBOARD_ID=$($CLI bundle summary --output json | jq -r '.resources.dashboards. # Capture the dashboard ID as a replacement. echo "$DASHBOARD_ID:DASHBOARD_ID" >> ACC_REPLS +echo "$($CLI lakeview get $DASHBOARD_ID | jq -r '.etag'):ETAG" >> ACC_REPLS # Verify dashboard was deployed trace $CLI lakeview get $DASHBOARD_ID | jq '{lifecycle_state, parent_path}' diff --git a/acceptance/bundle/resources/dashboards/detect-change/out.plan.direct.json b/acceptance/bundle/resources/dashboards/detect-change/out.plan.direct.json index 7af15357c25..b113b73d33a 100644 --- a/acceptance/bundle/resources/dashboards/detect-change/out.plan.direct.json +++ b/acceptance/bundle/resources/dashboards/detect-change/out.plan.direct.json @@ -21,7 +21,7 @@ "dashboard_id": "[DASHBOARD_ID]", "display_name": "test-dashboard-[UNIQUE_NAME]", "embed_credentials": false, - "etag": [ETAG], + "etag": "[ETAG_2]", "lifecycle_state": "ACTIVE", "parent_path": "/Workspace/Users/[USERNAME]/.bundle/[UNIQUE_NAME]/resources", "path": "/Users/[USERNAME]/.bundle/[UNIQUE_NAME]/resources/test-dashboard-[UNIQUE_NAME].lvdash.json", @@ -33,8 +33,8 @@ "changes": { "etag": { "action": "update", - "old": [ETAG], - "remote": [ETAG] + "old": "[ETAG_1]", + "remote": "[ETAG_2]" }, "serialized_dashboard": { "action": "skip", diff --git a/acceptance/bundle/resources/dashboards/detect-change/output.txt b/acceptance/bundle/resources/dashboards/detect-change/output.txt index 8e06bc10e80..fe3f8962ec7 100644 --- a/acceptance/bundle/resources/dashboards/detect-change/output.txt +++ b/acceptance/bundle/resources/dashboards/detect-change/output.txt @@ -30,7 +30,7 @@ Deployment complete! { "display_name": "test-dashboard-[UNIQUE_NAME]", "embed_credentials": false, - "etag": [ETAG], + "etag": "[ETAG_1]", "file_path": "dashboard.lvdash.json", "id": "[DASHBOARD_ID]", "parent_path": "/Workspace/Users/[USERNAME]/.bundle/[UNIQUE_NAME]/resources", diff --git a/acceptance/bundle/resources/dashboards/detect-change/script b/acceptance/bundle/resources/dashboards/detect-change/script index ccf26142303..36649c77cad 100644 --- a/acceptance/bundle/resources/dashboards/detect-change/script +++ b/acceptance/bundle/resources/dashboards/detect-change/script @@ -22,6 +22,7 @@ DASHBOARD_ID=$(jq -r '.id' out.summary.json) # Capture the dashboard ID as a replacement. echo "$DASHBOARD_ID:DASHBOARD_ID" >> ACC_REPLS +echo "$($CLI lakeview get "$DASHBOARD_ID" | jq -r '.etag'):ETAG_1" >> ACC_REPLS rm out.summary.json title "Load the dashboard by its ID and confirm its display name: " @@ -31,6 +32,7 @@ title "Make an out of band modification to the dashboard and confirm that it is RESOURCE_ID=$($CLI workspace get-status "${DASHBOARD_PATH}" | jq -r '.resource_id') DASHBOARD_JSON="{\"serialized_dashboard\": \"{}\", \"warehouse_id\": \"$TEST_DEFAULT_WAREHOUSE_ID\"}" $CLI lakeview update "${RESOURCE_ID}" --json "${DASHBOARD_JSON}" | jq '{lifecycle_state}' +echo "$($CLI lakeview get "$DASHBOARD_ID" | jq -r '.etag'):ETAG_2" >> ACC_REPLS title "Try to redeploy the bundle and confirm that the out of band modification is detected:" trace $CLI bundle plan diff --git a/acceptance/bundle/resources/dashboards/publish-failure-stale-content/dashboard.lvdash.json b/acceptance/bundle/resources/dashboards/publish-failure-stale-content/dashboard.lvdash.json new file mode 100644 index 00000000000..0bfc5797ff0 --- /dev/null +++ b/acceptance/bundle/resources/dashboards/publish-failure-stale-content/dashboard.lvdash.json @@ -0,0 +1 @@ +{"pages":[{"name":"test-page","displayName":"Test Dashboard"}]} diff --git a/acceptance/bundle/resources/dashboards/publish-failure-stale-content/databricks.yml.tmpl b/acceptance/bundle/resources/dashboards/publish-failure-stale-content/databricks.yml.tmpl new file mode 100644 index 00000000000..25e359dd010 --- /dev/null +++ b/acceptance/bundle/resources/dashboards/publish-failure-stale-content/databricks.yml.tmpl @@ -0,0 +1,9 @@ +bundle: + name: update-publish-failure-stale-content + +resources: + dashboards: + dashboard1: + display_name: my dashboard + warehouse_id: $TEST_DEFAULT_WAREHOUSE_ID + file_path: ./dashboard.lvdash.json diff --git a/acceptance/bundle/resources/dashboards/publish-failure-stale-content/out.test.toml b/acceptance/bundle/resources/dashboards/publish-failure-stale-content/out.test.toml new file mode 100644 index 00000000000..a29f11b9ab2 --- /dev/null +++ b/acceptance/bundle/resources/dashboards/publish-failure-stale-content/out.test.toml @@ -0,0 +1,4 @@ +Local = true +Cloud = false +RequiresWarehouse = true +EnvMatrix.DATABRICKS_BUNDLE_ENGINE = ["direct"] diff --git a/acceptance/bundle/resources/dashboards/publish-failure-stale-content/output.txt b/acceptance/bundle/resources/dashboards/publish-failure-stale-content/output.txt new file mode 100644 index 00000000000..394da5bea52 --- /dev/null +++ b/acceptance/bundle/resources/dashboards/publish-failure-stale-content/output.txt @@ -0,0 +1,172 @@ + +>>> [CLI] bundle deploy +Uploading bundle files to /Workspace/Users/[USERNAME]/.bundle/update-publish-failure-stale-content/default/files... +Deploying resources... +Updating deployment state... +Deployment complete! + +>>> [CLI] lakeview get [DASHBOARD1_ID] +{ + "display_name": "my dashboard", + "etag": "[ETAG_1]" +} + +>>> [CLI] lakeview get-published [DASHBOARD1_ID] +{ + "display_name": "my dashboard" +} + +>>> [CLI] bundle plan -o json +json.plan.resources.dashboards.dashboard1.remote_state.etag = "[ETAG_1]"; +json.plan.resources.dashboards.dashboard1.remote_state.published = true; +json.plan.resources.dashboards.dashboard1.changes.etag.action = "skip"; +json.plan.resources.dashboards.dashboard1.changes.etag.reason = "custom"; +json.plan.resources.dashboards.dashboard1.changes.etag.old = "[ETAG_1]"; +json.plan.resources.dashboards.dashboard1.changes.etag.remote = "[ETAG_1]"; +json.plan.resources.dashboards.dashboard1.changes.serialized_dashboard.reason = "etag_based"; + +>>> [CLI] bundle deploy +Uploading bundle files to /Workspace/Users/[USERNAME]/.bundle/update-publish-failure-stale-content/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 + +>>> print_requests.py //lakeview/dashboards +{ + "method": "PATCH", + "path": "/api/2.0/lakeview/dashboards/[DASHBOARD1_ID]", + "body": { + "display_name": "my dashboard renamed", + "parent_path": "/Workspace/Users/[USERNAME]/.bundle/update-publish-failure-stale-content/default/resources", + "serialized_dashboard": "{\"pages\":[{\"name\":\"test-page\",\"displayName\":\"Test Dashboard\"}]}\n", + "warehouse_id": "[TEST_DEFAULT_WAREHOUSE_ID]" + } +} +{ + "method": "POST", + "path": "/api/2.0/lakeview/dashboards/[DASHBOARD1_ID]/published", + "body": { + "embed_credentials": false, + "warehouse_id": "[TEST_DEFAULT_WAREHOUSE_ID]" + } +} + +>>> [CLI] lakeview get [DASHBOARD1_ID] +{ + "display_name": "my dashboard renamed", + "etag": "[ETAG_2]" +} + +>>> [CLI] lakeview get-published [DASHBOARD1_ID] +{ + "display_name": "my dashboard" +} + +>>> [CLI] bundle plan -o json +Warning: dashboard "dashboard1" has been modified remotely + at resources.dashboards.dashboard1 + in databricks.yml:7:7 + +This dashboard has been modified remotely since the last bundle deployment. +These modifications are untracked and will be overwritten on deploy. + +Make sure that the local dashboard definition matches what you intend to deploy +before proceeding with the deployment. + +To overwrite the remote changes with your local version, use --force. +The remote modifications will be lost. + +json.plan.resources.dashboards.dashboard1.new_state.value.published = true; +json.plan.resources.dashboards.dashboard1.remote_state.etag = "[ETAG_2]"; +json.plan.resources.dashboards.dashboard1.remote_state.published = true; +json.plan.resources.dashboards.dashboard1.changes.etag.action = "update"; +json.plan.resources.dashboards.dashboard1.changes.etag.old = "[ETAG_1]"; +json.plan.resources.dashboards.dashboard1.changes.etag.remote = "[ETAG_2]"; +json.plan.resources.dashboards.dashboard1.changes.serialized_dashboard.reason = "etag_based"; + +>>> [CLI] bundle deploy +Error: dashboard "dashboard1" has been modified remotely + at resources.dashboards.dashboard1 + in databricks.yml:7:7 + +This dashboard has been modified remotely since the last bundle deployment. +These modifications are untracked and will be overwritten on deploy. + +Make sure that the local dashboard definition matches what you intend to deploy +before proceeding with the deployment. + +To overwrite the remote changes with your local version, use --force. +The remote modifications will be lost. + + +Exit code: 1 + +>>> print_requests.py //lakeview/dashboards + +>>> [CLI] lakeview get [DASHBOARD1_ID] +{ + "display_name": "my dashboard renamed", + "etag": "[ETAG_2]" +} + +>>> [CLI] lakeview get-published [DASHBOARD1_ID] +{ + "display_name": "my dashboard" +} + +>>> [CLI] bundle deploy --force +Uploading bundle files to /Workspace/Users/[USERNAME]/.bundle/update-publish-failure-stale-content/default/files... +Deploying resources... +Updating deployment state... +Deployment complete! + +>>> print_requests.py //lakeview/dashboards +{ + "method": "PATCH", + "path": "/api/2.0/lakeview/dashboards/[DASHBOARD1_ID]", + "body": { + "display_name": "my dashboard renamed", + "parent_path": "/Workspace/Users/[USERNAME]/.bundle/update-publish-failure-stale-content/default/resources", + "serialized_dashboard": "{\"pages\":[{\"name\":\"test-page\",\"displayName\":\"Test Dashboard\"}]}\n", + "warehouse_id": "[TEST_DEFAULT_WAREHOUSE_ID]" + } +} +{ + "method": "POST", + "path": "/api/2.0/lakeview/dashboards/[DASHBOARD1_ID]/published", + "body": { + "embed_credentials": false, + "warehouse_id": "[TEST_DEFAULT_WAREHOUSE_ID]" + } +} + +>>> [CLI] lakeview get [DASHBOARD1_ID] +{ + "display_name": "my dashboard renamed", + "etag": "[ETAG_3]" +} + +>>> [CLI] lakeview get-published [DASHBOARD1_ID] +{ + "display_name": "my dashboard renamed" +} + +>>> [CLI] bundle plan +Plan: 0 to add, 0 to change, 0 to delete, 1 unchanged + +>>> [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/update-publish-failure-stale-content/default + +Deleting files... +Destroy complete! diff --git a/acceptance/bundle/resources/dashboards/publish-failure-stale-content/script b/acceptance/bundle/resources/dashboards/publish-failure-stale-content/script new file mode 100644 index 00000000000..500d87c5d76 --- /dev/null +++ b/acceptance/bundle/resources/dashboards/publish-failure-stale-content/script @@ -0,0 +1,51 @@ +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 + +# First deploy: dashboard is created and published successfully. +trace $CLI bundle deploy +replace_ids.py +DASHBOARD_ID=$($CLI bundle summary --output json | jq -r '.resources.dashboards.dashboard1.id') +add_repl.py "$($CLI lakeview get $DASHBOARD_ID | jq -r '.etag')" ETAG_1 +trace $CLI lakeview get $DASHBOARD_ID | jq '{display_name, etag}' +trace $CLI lakeview get-published $DASHBOARD_ID | jq '{display_name}' +trace $CLI bundle plan -o json | gron.py | grep -E "etag|published" +rm out.requests.txt + +# Inject a single publish failure for the update below. +fault.py "POST /api/2.0/lakeview/dashboards/*" 400 0 1 + +# Rename the dashboard to trigger an Update. +update_file.py databricks.yml "my dashboard" "my dashboard renamed" + +# Deploy: PATCH succeeds (bumping the remote etag) but publish fails. +# SaveState is only called on success, so state retains the pre-PATCH etag. +errcode trace $CLI bundle deploy +trace print_requests.py //lakeview/dashboards +add_repl.py "$($CLI lakeview get $DASHBOARD_ID | jq -r '.etag')" ETAG_2 +trace $CLI lakeview get $DASHBOARD_ID | jq '{display_name, etag}' +trace $CLI lakeview get-published $DASHBOARD_ID | jq '{display_name}' +trace $CLI bundle plan -o json | gron.py | grep -E "etag|published" + +# Bug: re-running deploy fails with "modified remotely" because the stored etag +# (pre-PATCH) no longer matches the remote etag (bumped by the PATCH above). +# No API writes are attempted — the stale published content is stuck. +errcode trace $CLI bundle deploy +trace print_requests.py //lakeview/dashboards +trace $CLI lakeview get $DASHBOARD_ID | jq '{display_name, etag}' +trace $CLI lakeview get-published $DASHBOARD_ID | jq '{display_name}' + +# --force bypasses the etag check and forces a full re-deploy (PATCH + POST /published), +# which fixes the stale published content. +trace $CLI bundle deploy --force +trace print_requests.py //lakeview/dashboards +add_repl.py "$($CLI lakeview get $DASHBOARD_ID | jq -r '.etag')" ETAG_3 +trace $CLI lakeview get $DASHBOARD_ID | jq '{display_name, etag}' +trace $CLI lakeview get-published $DASHBOARD_ID | jq '{display_name}' +trace $CLI bundle plan diff --git a/acceptance/bundle/resources/dashboards/publish-failure-stale-content/test.toml b/acceptance/bundle/resources/dashboards/publish-failure-stale-content/test.toml new file mode 100644 index 00000000000..41a8ad88f69 --- /dev/null +++ b/acceptance/bundle/resources/dashboards/publish-failure-stale-content/test.toml @@ -0,0 +1,15 @@ +Cloud = false +Local = true +RecordRequests = true + +Ignore = [ + ".databricks", + "databricks.yml", +] + +[EnvMatrix] +DATABRICKS_BUNDLE_ENGINE = ["direct"] + +[[Repls]] +Old = '\?[ow]=\d+' +New = "?[WSPARAM]=[NUMID]" diff --git a/acceptance/bundle/resources/dashboards/simple/out.plan.direct.json b/acceptance/bundle/resources/dashboards/simple/out.plan.direct.json index 59ab070e00e..f0e8fb6f047 100644 --- a/acceptance/bundle/resources/dashboards/simple/out.plan.direct.json +++ b/acceptance/bundle/resources/dashboards/simple/out.plan.direct.json @@ -11,7 +11,7 @@ "dashboard_id": "[DASHBOARD_ID]", "display_name": "test bundle-deploy-dashboard [UUID]", "embed_credentials": true, - "etag": [ETAG], + "etag": "[ETAG]", "lifecycle_state": "ACTIVE", "parent_path": "/Workspace/Users/[USERNAME]", "path": "/Users/[USERNAME]/test bundle-deploy-dashboard [UUID].lvdash.json", @@ -24,8 +24,8 @@ "etag": { "action": "skip", "reason": "custom", - "old": [ETAG], - "remote": [ETAG] + "old": "[ETAG]", + "remote": "[ETAG]" }, "serialized_dashboard": { "action": "skip", diff --git a/acceptance/bundle/resources/dashboards/simple/script b/acceptance/bundle/resources/dashboards/simple/script index f834edbeadd..0b1adec55d6 100644 --- a/acceptance/bundle/resources/dashboards/simple/script +++ b/acceptance/bundle/resources/dashboards/simple/script @@ -17,6 +17,7 @@ DASHBOARD_ID=$($CLI bundle summary --output json | jq -r '.resources.dashboards. # Capture the dashboard ID as a replacement. echo "$DASHBOARD_ID:DASHBOARD_ID" >> ACC_REPLS +echo "$($CLI lakeview get $DASHBOARD_ID | jq -r '.etag'):ETAG" >> ACC_REPLS trace $CLI lakeview get $DASHBOARD_ID | jq '{lifecycle_state, parent_path, path, serialized_dashboard}' diff --git a/acceptance/bundle/resources/dashboards/test.toml b/acceptance/bundle/resources/dashboards/test.toml index d932390098f..a0d0d5ea3f1 100644 --- a/acceptance/bundle/resources/dashboards/test.toml +++ b/acceptance/bundle/resources/dashboards/test.toml @@ -10,12 +10,3 @@ RequiresWarehouse = true # C:/Program Files/Git/Users/$username/UNIQUE_NAME before passing it to the CLI # Setting this environment variable prevents that conversion on windows. MSYS_NO_PATHCONV = "1" - -# Etag can be both negative and positive. -[[Repls]] -Old = "\"[-0-9]{8,}\"" -New = "[ETAG]" - -[[Repls]] -Old = "\"[0-9]{8,}\"" -New = "[ETAG]" diff --git a/acceptance/bundle/resources/dashboards/unpublish-out-of-band/out.plan.direct.json b/acceptance/bundle/resources/dashboards/unpublish-out-of-band/out.plan.direct.json index 558a4ddcfd8..7b3761ca39e 100644 --- a/acceptance/bundle/resources/dashboards/unpublish-out-of-band/out.plan.direct.json +++ b/acceptance/bundle/resources/dashboards/unpublish-out-of-band/out.plan.direct.json @@ -21,7 +21,7 @@ "dashboard_id": "[DASHBOARD1_ID]", "display_name": "test bundle-deploy-dashboard [UNIQUE_NAME]", "embed_credentials": false, - "etag": [ETAG], + "etag": "[ETAG]", "lifecycle_state": "ACTIVE", "parent_path": "/Workspace/Users/[USERNAME]/.bundle/unpublish-out-of-band-[UNIQUE_NAME]/default/resources", "path": "/Users/[USERNAME]/.bundle/unpublish-out-of-band-[UNIQUE_NAME]/default/resources/test bundle-deploy-dashboard [UNIQUE_NAME].lvdash.json", @@ -34,8 +34,8 @@ "etag": { "action": "skip", "reason": "custom", - "old": [ETAG], - "remote": [ETAG] + "old": "[ETAG]", + "remote": "[ETAG]" }, "published": { "action": "update", diff --git a/acceptance/bundle/resources/dashboards/unpublish-out-of-band/script b/acceptance/bundle/resources/dashboards/unpublish-out-of-band/script index 3e75d6d0a9a..2dae2d0bf24 100644 --- a/acceptance/bundle/resources/dashboards/unpublish-out-of-band/script +++ b/acceptance/bundle/resources/dashboards/unpublish-out-of-band/script @@ -11,6 +11,7 @@ trace $CLI bundle plan -o json > out.plan_initial.$DATABRICKS_BUNDLE_ENGINE.json # Deploy the dashboard trace $CLI bundle deploy DASHBOARD_ID=$(read_id.py dashboard1) +add_repl.py "$($CLI lakeview get $DASHBOARD_ID | jq -r '.etag')" ETAG trace print_state.py | grep publish > out.state.$DATABRICKS_BUNDLE_ENGINE.json diff --git a/bundle/direct/dresources/all_test.go b/bundle/direct/dresources/all_test.go index 30adb4640cc..4f8f8f5e269 100644 --- a/bundle/direct/dresources/all_test.go +++ b/bundle/direct/dresources/all_test.go @@ -889,8 +889,18 @@ func testCRUD(t *testing.T, group string, adapter *Adapter, client *databricks.W if remoteStateFromUpdate != nil { remappedStateFromUpdate, err := adapter.RemapState(remoteStateFromUpdate) require.NoError(t, err) - ignoreFilter.requireEqual(t, remappedState, remappedStateFromUpdate, + // Compare DoUpdate's result against a fresh DoRead: server-generated + // fields (e.g. etag) may change on any write, so DoUpdate's return + // value must match what DoRead returns right after. + remotePostUpdate, err := adapter.DoRead(ctx, createdID) + require.NoError(t, err) + remappedPostUpdate, err := adapter.RemapState(remotePostUpdate) + require.NoError(t, err) + ignoreFilter.requireEqual(t, remappedPostUpdate, remappedStateFromUpdate, "unexpected differences between remappedState and remappedStateFromUpdate") + // DoUpdate may mutate newState in place (e.g. etag), so update remappedState + // to match the post-update server state for the field checks below. + remappedState = remappedStateFromUpdate } remoteStateFromWaitUpdate, err := adapter.WaitAfterUpdate(ctx, createdID, newState) diff --git a/libs/testserver/dashboards.go b/libs/testserver/dashboards.go index eab45bdbdc1..10cc03a477c 100644 --- a/libs/testserver/dashboards.go +++ b/libs/testserver/dashboards.go @@ -166,21 +166,19 @@ func (s *FakeWorkspace) DashboardUpdate(req Request) Response { } } - if updateReq.SerializedDashboard != dashboard.InputSerializedDashboard { - // Update etag. - prevEtag, err := strconv.Atoi(dashboard.Etag) - if err != nil { - return Response{ - Body: map[string]string{ - "message": "Invalid etag: " + dashboard.Etag, - }, - StatusCode: 400, - } + // Bump etag on every write, matching cloud behavior. + prevEtag, err := strconv.Atoi(dashboard.Etag) + if err != nil { + return Response{ + Body: map[string]string{ + "message": "Invalid etag: " + dashboard.Etag, + }, + StatusCode: 400, } - nextEtag := prevEtag + 1 - dashboard.Etag = strconv.Itoa(nextEtag) + } + dashboard.Etag = strconv.Itoa(prevEtag + 1) - // Update the input serialized dashboard. + if updateReq.SerializedDashboard != dashboard.InputSerializedDashboard { dashboard.InputSerializedDashboard = updateReq.SerializedDashboard }