Skip to content

Commit 5e52c4c

Browse files
Allow changing deployment history on existing file state (#7198)
Stop enforcing the setting for DMS. No longer needed because we now we do best effort history tracking
1 parent 814bdfe commit 5e52c4c

6 files changed

Lines changed: 49 additions & 159 deletions

File tree

Lines changed: 16 additions & 68 deletions
Original file line numberDiff line numberDiff line change
@@ -1,78 +1,26 @@
11

2-
=== Deploy without recording: ordinary direct-engine state tracking one job, unknown to DMS
3-
>>> [CLI] bundle deploy
2+
=== Enable history on existing remote file state without replacing the job
3+
>>> DATABRICKS_BUNDLE_DEPLOYMENT_HISTORY=true [CLI] bundle deploy
44
Uploading bundle files to /Workspace/Users/[USERNAME]/.bundle/dms-existing-state-[UNIQUE_NAME]/default/files...
5-
Created jobs.one
6-
Files: 2 uploaded, 0 deleted
7-
Resources: 1 created, 0 changed, 0 deleted, 0 unchanged
5+
Files: 3 uploaded, 0 deleted
6+
Resources: 0 created, 0 changed, 0 deleted, 1 unchanged
87

9-
>>> [CLI] bundle plan -o json
8+
>>> jq {features: (.features // {}), job_id: .state["resources.jobs.one"].__id__} .databricks/bundle/default/resources.json
109
{
11-
"plan_version": [PLAN_VERSION],
12-
"cli_version": "[CLI_VERSION]",
13-
"lineage": "[UUID]",
14-
"serial": 1
15-
}
16-
17-
>>> [CLI] bundle plan
18-
Plan: 0 to add, 0 to change, 0 to delete, 1 unchanged
19-
20-
>>> print_requests.py --dms //api/2.0/bundle --oneline
21-
22-
>>> jq {state_version, features} .databricks/bundle/default/resources.json
23-
{
24-
"state_version": 3,
25-
"features": null
10+
"features": {
11+
"deployment_history_shadow": {}
12+
},
13+
"job_id": "[ONE_ID]"
2614
}
2715

28-
=== Enabling recording is refused because the existing deployment is not recorded
29-
>>> DATABRICKS_BUNDLE_DEPLOYMENT_HISTORY=true [CLI] bundle deploy
30-
Error: deployment history setting (true) does not match the existing state (false)
31-
32-
Update experimental.deployment_history to match the existing deployment, or run "databricks bundle destroy" to start over
33-
34-
35-
>>> print_requests.py --dms //api/2.0/bundle --oneline
36-
37-
=== Wiping the local cache still refuses after pulling the non-DMS state from the workspace
38-
>>> DATABRICKS_BUNDLE_DEPLOYMENT_HISTORY=true [CLI] bundle deploy
39-
Error: deployment history setting (true) does not match the existing state (false)
40-
41-
Update experimental.deployment_history to match the existing deployment, or run "databricks bundle destroy" to start over
42-
43-
44-
=== A leftover local WAL with the wrong lineage is still refused
45-
>>> DATABRICKS_BUNDLE_DEPLOYMENT_HISTORY=true [CLI] bundle deploy
46-
Error: reading state from [TEST_TMP_DIR]/.databricks/bundle/default/resources.json: WAL recovery failed: WAL lineage ("stale") does not match state lineage ("[UUID]")
47-
48-
49-
=== Destroy clears the deployment. With nothing recorded remotely the bundle is treated as new, so recording can be enabled once the stale WAL is gone
50-
>>> DATABRICKS_BUNDLE_DEPLOYMENT_HISTORY=true [CLI] bundle destroy --auto-approve
51-
The following resources will be deleted:
52-
delete resources.jobs.one
53-
54-
All files and directories at the following location will be deleted: /Workspace/Users/[USERNAME]/.bundle/dms-existing-state-[UNIQUE_NAME]/default
55-
56-
Destroy: 1 deleted
57-
58-
>>> DATABRICKS_BUNDLE_DEPLOYMENT_HISTORY=true [CLI] bundle deploy
16+
=== Disable history without replacing the job
17+
>>> DATABRICKS_BUNDLE_DEPLOYMENT_HISTORY=false [CLI] bundle deploy
5918
Uploading bundle files to /Workspace/Users/[USERNAME]/.bundle/dms-existing-state-[UNIQUE_NAME]/default/files...
60-
Created jobs.one
61-
Files: 2 uploaded, 0 deleted
62-
Resources: 1 created, 0 changed, 0 deleted, 0 unchanged
63-
64-
>>> print_requests.py --dms //api/2.0/bundle --oneline
65-
{"method": "POST", "path": "/api/2.0/bundle/deployments", "body": {"display_name": "dms-existing-state-[UNIQUE_NAME]", "initial_parent_path": "/Workspace/Users/[USERNAME]/.bundle/dms-existing-state-[UNIQUE_NAME]/default/state", "target_name": "default", "workspace_info": {"file_path": "/Workspace/Users/[USERNAME]/.bundle/dms-existing-state-[UNIQUE_NAME]/default/files", "root_path": "/Workspace/Users/[USERNAME]/.bundle/dms-existing-state-[UNIQUE_NAME]/default"}}}
66-
{"method": "POST", "path": "/api/2.0/bundle/deployments/[DEPLOYMENT_ID]/versions", "q": {"version_id": "1"}, "body": {"cli_version": "[CLI_VERSION]", "operations": [{"action_type": "OPERATION_ACTION_TYPE_CREATE", "resource_key": "jobs.one"}], "version_type": "VERSION_TYPE_DEPLOY"}}
67-
{"method": "PATCH", "path": "/api/2.0/bundle/deployments/[DEPLOYMENT_ID]/versions/1/operations/jobs.one", "q": {"update_mask": "state,error_message,resource_id,status"}, "body": {"error_message": "", "resource_id": "[ONE_ID]", "sequence_id": 0, "state": "{\"state\":{\"deployment\":{\"deployment_id\":\"[DEPLOYMENT_ID]\",\"kind\":\"BUNDLE\",\"metadata_file_path\":\"/Workspace/Users/[USERNAME]/.bundle/dms-existing-state-[UNIQUE_NAME]/default/state/metadata.json\",\"version_id\":\"1\"},\"edit_mode\":\"UI_LOCKED\",\"format\":\"MULTI_TASK\",\"max_concurrent_runs\":1,\"name\":\"one\",\"queue\":{\"enabled\":true}}}", "status": "OPERATION_STATUS_SUCCEEDED"}}
68-
{"method": "POST", "path": "/api/2.0/bundle/deployments/[DEPLOYMENT_ID]/versions/1/complete", "body": {"completion_reason": "VERSION_COMPLETE_SUCCESS"}}
19+
Files: 0 uploaded, 0 deleted
20+
Resources: 0 created, 0 changed, 0 deleted, 1 unchanged
6921

70-
>>> find.py resources[.]json[.]wal --expect 0
71-
72-
>>> jq {state_version, features} .databricks/bundle/default/resources.json
22+
>>> jq {features: (.features // {}), job_id: .state["resources.jobs.one"].__id__} .databricks/bundle/default/resources.json
7323
{
74-
"state_version": 3,
75-
"features": {
76-
"deployment_history_shadow": {}
77-
}
24+
"features": {},
25+
"job_id": "[ONE_ID]"
7826
}
Lines changed: 9 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -1,38 +1,16 @@
11
envsubst < databricks.yml.tmpl > databricks.yml
22

3-
title "Deploy without recording: ordinary direct-engine state tracking one job, unknown to DMS"
4-
trace $CLI bundle deploy
5-
dms_plan
6-
trace print_requests.py --dms //api/2.0/bundle --oneline
7-
trace jq '{state_version, features}' .databricks/bundle/default/resources.json
8-
9-
title "Enabling recording is refused because the existing deployment is not recorded"
10-
musterr trace DATABRICKS_BUNDLE_DEPLOYMENT_HISTORY=true $CLI bundle deploy
11-
trace print_requests.py --dms //api/2.0/bundle --oneline
12-
13-
title "Wiping the local cache still refuses after pulling the non-DMS state from the workspace"
3+
# Create ordinary file state, then force the next command to pull it from the workspace.
4+
$CLI bundle deploy &> LOG.setup
5+
read_id.py one > /dev/null
146
rm -rf .databricks
15-
musterr trace DATABRICKS_BUNDLE_DEPLOYMENT_HISTORY=true $CLI bundle deploy
167

17-
title "A leftover local WAL with the wrong lineage is still refused"
18-
mkdir -p .databricks/bundle/default
19-
printf '{"lineage":"stale","serial":99,"state_version":2,"features":{"deployment_history_shadow":{}}}\n' > .databricks/bundle/default/resources.json.wal
20-
musterr trace DATABRICKS_BUNDLE_DEPLOYMENT_HISTORY=true $CLI bundle deploy
21-
22-
title "Destroy clears the deployment. With nothing recorded remotely the bundle is treated as new, so recording can be enabled once the stale WAL is gone"
23-
# Clear the stale WAL first: it was refused, not deleted, and destroy runs without recording, so it
24-
# would otherwise try to replay it and fail on the lineage mismatch.
25-
rm -f .databricks/bundle/default/resources.json.wal
26-
# Keep the mismatched setting here to verify that destroy ignores configuration and follows state.
27-
trace DATABRICKS_BUNDLE_DEPLOYMENT_HISTORY=true $CLI bundle destroy --auto-approve
8+
title "Enable history on existing remote file state without replacing the job"
289
trace DATABRICKS_BUNDLE_DEPLOYMENT_HISTORY=true $CLI bundle deploy
29-
trace print_requests.py --dms //api/2.0/bundle --oneline
30-
trace find.py 'resources[.]json[.]wal' --expect 0
31-
trace jq '{state_version, features}' .databricks/bundle/default/resources.json
32-
DATABRICKS_BUNDLE_DEPLOYMENT_HISTORY=true replace_ids.py
33-
deployment_id=$(MSYS_NO_PATHCONV=1 $CLI workspace get-status "/Workspace/Users/${CURRENT_USER_NAME}/.bundle/dms-existing-state-${UNIQUE_NAME}/default/state/resources.deployment.json" | python3 -c 'import sys, json; print(json.load(sys.stdin)["object_id"])')
34-
add_repl "$deployment_id" DEPLOYMENT_ID
10+
trace jq '{features: (.features // {}), job_id: .state["resources.jobs.one"].__id__}' .databricks/bundle/default/resources.json
11+
12+
title "Disable history without replacing the job"
13+
trace DATABRICKS_BUNDLE_DEPLOYMENT_HISTORY=false $CLI bundle deploy
14+
trace jq '{features: (.features // {}), job_id: .state["resources.jobs.one"].__id__}' .databricks/bundle/default/resources.json
3515

36-
# The recorded requests are asserted above via print_requests.py; drop whatever the run added after
37-
# that (telemetry, and the state probes of the non-recording variant) so nothing is left to compare.
3816
rm -f "$OUT_REQUESTS"

‎acceptance/bundle/migrate/auto/dms-migration/output.txt‎

Lines changed: 19 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -2,19 +2,27 @@
22
=== Replay a prior terraform deployment (captured from a real terraform run)
33
>>> replay_tfstate.py terraform-requests.json terraform.tfstate
44

5-
=== Direct + deployment-history requested against a terraform state is rejected before commit
6-
>>> DATABRICKS_BUNDLE_ENGINE=direct DATABRICKS_BUNDLE_DEPLOYMENT_HISTORY=true errcode [CLI] bundle deploy
5+
=== Terraform state migrates to file-backed direct state with shadow history
6+
>>> DATABRICKS_BUNDLE_ENGINE=direct DATABRICKS_BUNDLE_DEPLOYMENT_HISTORY=true [CLI] bundle deploy
77
Notice: migrating your bundle to direct deployment engine (https://github.com/databricks/cli/issues/6765).
8-
Error: deployment history setting (true) does not match the existing state (false)
8+
Uploading bundle files to /Workspace/Users/[USERNAME]/.bundle/test-bundle/default/files...
9+
Migrated 1 resource to direct deployment engine.
10+
Files: 1 uploaded, 0 deleted
11+
Resources: 0 created, 0 changed, 0 deleted, 1 unchanged
912

10-
Update experimental.deployment_history to match the existing deployment, or run "databricks bundle destroy" to start over
11-
12-
13-
Exit code: 1
13+
>>> jq {features, job_id: .state["resources.jobs.test_job"].__id__} .databricks/bundle/default/resources.json
14+
{
15+
"features": {
16+
"deployment_history_shadow": {}
17+
},
18+
"job_id": "[TEST_JOB_ID]"
19+
}
1420

1521
>>> find.py \.databricks/.*(\.tfstate|resources.*\.json|\.wal)
16-
.databricks/bundle/default/terraform/terraform.tfstate
22+
.databricks/bundle/default/resources.json
23+
.databricks/bundle/default/terraform/terraform.tfstate.backup
1724

18-
>>> MSYS_NO_PATHCONV=1 [CLI] workspace list /Workspace/Users/[USERNAME]/.bundle/test-bundle/default/state
19-
ID Type Language Path
20-
[OBJECT_ID] FILE /Workspace/Users/[USERNAME]/.bundle/test-bundle/default/state/terraform.tfstate
25+
>>> MSYS_NO_PATHCONV=1 [CLI] workspace get-status /Workspace/Users/[USERNAME]/.bundle/test-bundle/default/state/terraform.tfstate.backup -o json
26+
{
27+
"object_type": "FILE"
28+
}

‎acceptance/bundle/migrate/auto/dms-migration/script‎

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,11 @@ export DATABRICKS_BUNDLE_ENGINE=
22

33
title "Replay a prior terraform deployment (captured from a real terraform run)"
44
trace replay_tfstate.py terraform-requests.json terraform.tfstate
5+
read_id.py test_job > /dev/null
56
rm -f "$OUT_REQUESTS"
67

7-
title "Direct + deployment-history requested against a terraform state is rejected before commit"
8-
# Migration produces a plain direct state (no deployment_history feature), so requesting
9-
# deployment history while migrating is a mismatch. It is rejected after the in-memory migration
10-
# but before CommitMigration writes or pushes anything, so the bundle stays on terraform with no
11-
# leaked direct state - migrate first (plain deploy), then enable deployment history.
12-
trace DATABRICKS_BUNDLE_ENGINE=direct DATABRICKS_BUNDLE_DEPLOYMENT_HISTORY=true errcode $CLI bundle deploy
8+
title "Terraform state migrates to file-backed direct state with shadow history"
9+
trace DATABRICKS_BUNDLE_ENGINE=direct DATABRICKS_BUNDLE_DEPLOYMENT_HISTORY=true $CLI bundle deploy
10+
trace jq '{features, job_id: .state["resources.jobs.test_job"].__id__}' .databricks/bundle/default/resources.json
1311
trace find.py '\.databricks/.*(\.tfstate|resources.*\.json|\.wal)'
14-
trace MSYS_NO_PATHCONV=1 $CLI workspace list "/Workspace/Users/${CURRENT_USER_NAME}/.bundle/test-bundle/default/state"
12+
trace MSYS_NO_PATHCONV=1 $CLI workspace get-status "/Workspace/Users/${CURRENT_USER_NAME}/.bundle/test-bundle/default/state/terraform.tfstate.backup" -o json | jq '{object_type}'

‎bundle/statemgmt/state_pull.go‎

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -77,12 +77,6 @@ func (s *StateDesc) IsDMS() bool {
7777
return ok
7878
}
7979

80-
// RecordsHistory includes both DMS-authoritative state and deployment history shadow.
81-
func (s *StateDesc) RecordsHistory() bool {
82-
_, bestEffort := s.Features["deployment_history_shadow"]
83-
return bestEffort || s.IsDMS()
84-
}
85-
8680
func localRead(ctx context.Context, fullPath string, engine engine.EngineType) *StateDesc {
8781
content, err := os.ReadFile(fullPath)
8882
if err != nil {

‎cmd/bundle/utils/process.go‎

Lines changed: 0 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -359,13 +359,6 @@ func ProcessBundleRet(cmd *cobra.Command, opts ProcessOptions) (b *bundle.Bundle
359359
}
360360
}
361361

362-
if stateDesc.Engine.IsDirect() && (opts.Deploy || opts.PreDeployChecks) {
363-
if err := enforceDeploymentHistorySetting(ctx, b, stateDesc, true); err != nil {
364-
logdiag.LogError(ctx, err)
365-
return b, stateDesc, root.ErrAlreadyPrinted
366-
}
367-
}
368-
369362
stateMode := dstate.WritingFile
370363
switch {
371364
case stateDesc.IsDMS():
@@ -668,9 +661,6 @@ func OpenDirectStateForRead(ctx context.Context, b *bundle.Bundle, stateDesc *st
668661
if err := b.DeploymentBundle.StateDB.Open(ctx, localPath, dstate.ReadingFile, dstate.WithRecovery(true), dstate.OpenDmsArgs{}); err != nil {
669662
return err
670663
}
671-
if err := enforceDeploymentHistorySetting(ctx, b, stateDesc, false); err != nil {
672-
return err
673-
}
674664
if configuresDeploymentHistory(ctx, b) {
675665
if _, err := prepareDeploymentHistoryShadow(ctx, b); err != nil {
676666
log.Warnf(ctx, "Could not read deployment history: %s", err)
@@ -694,32 +684,6 @@ func OpenDirectStateForRead(ctx context.Context, b *bundle.Bundle, stateDesc *st
694684
if err := b.DeploymentBundle.StateDB.Open(ctx, localPath, dstate.WritingDeploymentHistoryOnly, dstate.WithRecovery(false), dstate.OpenDmsArgs{DeploymentID: dmsDeploymentID, LastVersionID: lastVersionID}); err != nil {
695685
return err
696686
}
697-
return enforceDeploymentHistorySetting(ctx, b, stateDesc, false)
698-
}
699-
700-
func enforceDeploymentHistorySetting(ctx context.Context, b *bundle.Bundle, stateDesc *statemgmt.StateDesc, requireMatch bool) error {
701-
if stateDesc.SourcePath == "" {
702-
return nil
703-
}
704-
configured := configuresDeploymentHistory(ctx, b)
705-
recorded := stateDesc.RecordsHistory()
706-
if b.DeploymentBundle.StateDB.IsOpen() {
707-
recorded = b.DeploymentBundle.StateDB.RecordsHistory()
708-
}
709-
if configured == recorded || (!configured && !stateDesc.IsDMS()) {
710-
return nil
711-
}
712-
if requireMatch {
713-
return fmt.Errorf(`deployment history setting (%t) does not match the existing state (%t)
714-
715-
Update experimental.deployment_history to match the existing deployment, or run "databricks bundle destroy" to start over`, configured, recorded)
716-
}
717-
if configured {
718-
return errors.New(`enabling experimental.deployment_history for an existing deployment is not supported
719-
720-
Run "databricks bundle destroy" first, then deploy again with deployment history enabled`)
721-
}
722-
log.Warnf(ctx, "Deployment history setting (%t) does not match the existing state (%t). Using the existing state.", configured, recorded)
723687
return nil
724688
}
725689

0 commit comments

Comments
 (0)