Skip to content

Commit b4a882c

Browse files
committed
Check plan before migration
1 parent 6ab1806 commit b4a882c

10 files changed

Lines changed: 126 additions & 12 deletions

File tree

acceptance/bundle/help/bundle-deployment-migrate/output.txt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,8 @@ Usage:
1414
databricks bundle deployment migrate [flags]
1515

1616
Flags:
17-
-h, --help help for migrate
17+
-h, --help help for migrate
18+
--noplancheck Skip running bundle plan before migration.
1819

1920
Global Flags:
2021
--debug enable debug logging

acceptance/bundle/migrate/basic/output.txt

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,9 +11,11 @@ Updating deployment state...
1111
Deployment complete!
1212

1313
>>> [CLI] bundle deployment migrate
14+
Migration should be done after a full deploy. Running plan now to verify that deployment was done:
15+
Plan: 0 to add, 0 to change, 0 to delete, 3 unchanged
1416
Migrated 3 resources to direct engine state file: [TEST_TMP_DIR]/.databricks/bundle/dev/resources.json
1517

16-
Validate the migration by running "bundle plan", there should be no actions planned.
18+
Validate the migration by running "databricks bundle plan", there should be no actions planned.
1719

1820
The state file is not synchronized to the workspace yet. To do that and finalize the migration, run "bundle deploy".
1921

acceptance/bundle/migrate/dashboards/output.txt

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,11 @@ Updating deployment state...
66
Deployment complete!
77

88
>>> [CLI] bundle deployment migrate
9+
Migration should be done after a full deploy. Running plan now to verify that deployment was done:
10+
Plan: 0 to add, 0 to change, 0 to delete, 1 unchanged
911
Migrated 1 resources to direct engine state file: [TEST_TMP_DIR]/.databricks/bundle/default/resources.json
1012

11-
Validate the migration by running "bundle plan", there should be no actions planned.
13+
Validate the migration by running "databricks bundle plan", there should be no actions planned.
1214

1315
The state file is not synchronized to the workspace yet. To do that and finalize the migration, run "bundle deploy".
1416

acceptance/bundle/migrate/default-python/output.txt

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,11 +22,20 @@ Deployment complete!
2222

2323
>>> print_state.py
2424

25-
>>> [CLI] bundle deployment migrate
25+
>>> musterr [CLI] bundle deployment migrate
26+
Building python_artifact...
27+
Migration should be done after a full deploy. Running plan now to verify that deployment was done:
28+
Building python_artifact...
29+
update jobs.sample_job
30+
31+
Plan: 0 to add, 1 to change, 0 to delete, 1 unchanged
32+
Error: 'databricks bundle plan' shows actions planned, aborting migration. Please run 'databricks bundle deploy' first to ensure your bundle is up to date, If actions persist after deploy, skip plan check with --noplancheck option
33+
34+
>>> [CLI] bundle deployment migrate --noplancheck
2635
Building python_artifact...
2736
Migrated 2 resources to direct engine state file: [TEST_TMP_DIR]/my_default_python/.databricks/bundle/dev/resources.json
2837

29-
Validate the migration by running "bundle plan", there should be no actions planned.
38+
Validate the migration by running "databricks bundle plan", there should be no actions planned.
3039

3140
The state file is not synchronized to the workspace yet. To do that and finalize the migration, run "bundle deploy".
3241

acceptance/bundle/migrate/default-python/script

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,10 @@ cd my_default_python
33

44
trace DATABRICKS_BUNDLE_ENGINE=terraform $CLI bundle deploy
55
trace print_state.py > ../out.state_original.json
6-
trace $CLI bundle deployment migrate
6+
7+
trace musterr $CLI bundle deployment migrate
8+
trace $CLI bundle deployment migrate --noplancheck
9+
710
trace print_state.py > ../out.state_after_migration.json
811
trace jq '.. | .libraries? | select(.)' ../out.state_after_migration.json
912

acceptance/bundle/migrate/grants/output.txt

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,11 @@ Updating deployment state...
66
Deployment complete!
77

88
>>> [CLI] bundle deployment migrate
9+
Migration should be done after a full deploy. Running plan now to verify that deployment was done:
10+
Plan: 0 to add, 0 to change, 0 to delete, 6 unchanged
911
Migrated 6 resources to direct engine state file: [TEST_TMP_DIR]/.databricks/bundle/default/resources.json
1012

11-
Validate the migration by running "bundle plan", there should be no actions planned.
13+
Validate the migration by running "databricks bundle plan", there should be no actions planned.
1214

1315
The state file is not synchronized to the workspace yet. To do that and finalize the migration, run "bundle deploy".
1416

acceptance/bundle/migrate/permissions/output.txt

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,11 @@ Updating deployment state...
66
Deployment complete!
77

88
>>> [CLI] bundle deployment migrate
9+
Migration should be done after a full deploy. Running plan now to verify that deployment was done:
10+
Plan: 0 to add, 0 to change, 0 to delete, 4 unchanged
911
Migrated 4 resources to direct engine state file: [TEST_TMP_DIR]/.databricks/bundle/default/resources.json
1012

11-
Validate the migration by running "bundle plan", there should be no actions planned.
13+
Validate the migration by running "databricks bundle plan", there should be no actions planned.
1214

1315
The state file is not synchronized to the workspace yet. To do that and finalize the migration, run "bundle deploy".
1416

acceptance/bundle/migrate/runas/output.txt

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -81,9 +81,23 @@ Consider using a adding a top-level permissions section such as the following:
8181
See https://docs.databricks.com/dev-tools/bundles/permissions.html to learn more about permission configuration.
8282
in databricks.yml:5:3
8383

84+
Migration should be done after a full deploy. Running plan now to verify that deployment was done:
85+
Recommendation: permissions section should explicitly include the current deployment identity '[USERNAME]' or one of its groups
86+
If it is not included, CAN_MANAGE permissions are only applied if the present identity is used to deploy.
87+
88+
Consider using a adding a top-level permissions section such as the following:
89+
90+
permissions:
91+
- user_name: [USERNAME]
92+
level: CAN_MANAGE
93+
94+
See https://docs.databricks.com/dev-tools/bundles/permissions.html to learn more about permission configuration.
95+
in databricks.yml:5:3
96+
97+
Plan: 0 to add, 0 to change, 0 to delete, 2 unchanged
8498
Migrated 2 resources to direct engine state file: [TEST_TMP_DIR]/.databricks/bundle/production/resources.json
8599

86-
Validate the migration by running "bundle plan", there should be no actions planned.
100+
Validate the migration by running "databricks bundle plan", there should be no actions planned.
87101

88102
The state file is not synchronized to the workspace yet. To do that and finalize the migration, run "bundle deploy".
89103

cmd/bundle/deployment/migrate.go

Lines changed: 81 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,12 @@
11
package deployment
22

33
import (
4+
"bytes"
45
"errors"
56
"fmt"
67
"os"
8+
"os/exec"
9+
"strings"
710

811
"github.com/databricks/cli/bundle/deploy/terraform"
912
"github.com/databricks/cli/bundle/direct"
@@ -19,6 +22,60 @@ import (
1922

2023
const backupSuffix = ".backup"
2124

25+
// runPlanCheck runs bundle plan and checks if there are any actions planned.
26+
// Returns error if plan fails or if there are actions planned.
27+
func runPlanCheck(cmd *cobra.Command, target string) error {
28+
ctx := cmd.Context()
29+
30+
// Build command arguments
31+
executable, err := os.Executable()
32+
if err != nil {
33+
return fmt.Errorf("failed to get executable path: %w", err)
34+
}
35+
36+
args := []string{"bundle", "plan"}
37+
targetArgs := ""
38+
if target != "" {
39+
targetArgs = " -t " + target
40+
args = append(args, "-t", target)
41+
}
42+
43+
// Create and run command
44+
planCmd := exec.CommandContext(ctx, executable, args...)
45+
var stdout bytes.Buffer
46+
planCmd.Stdout = &stdout
47+
planCmd.Stderr = cmd.ErrOrStderr()
48+
planCmd.Env = append(os.Environ(), "DATABRICKS_BUNDLE_ENGINE=")
49+
50+
err = planCmd.Run()
51+
52+
// Output the plan stdout as is
53+
output := stdout.String()
54+
fmt.Fprint(cmd.OutOrStdout(), output)
55+
56+
// Check error code
57+
if err != nil {
58+
var exitErr *exec.ExitError
59+
msg := ""
60+
if errors.As(err, &exitErr) {
61+
msg = fmt.Sprintf("exit code %d", exitErr.ExitCode())
62+
} else {
63+
msg = exitErr.String()
64+
}
65+
return fmt.Errorf("bundle plan failed with %s, aborting migration. To proceed with migration anyway, re-run the command with --noplancheck option", msg)
66+
}
67+
68+
if !strings.Contains(output, "Plan:") {
69+
return fmt.Errorf("cannot parse 'databricks bundle plan%s' output, aborting migration. Skip plan check with --noplancheck option", targetArgs)
70+
}
71+
72+
if !strings.Contains(output, "Plan: 0 to add, 0 to change, 0 to delete") {
73+
return fmt.Errorf("'databricks bundle plan%s' shows actions planned, aborting migration. Please run 'databricks bundle deploy%s' first to ensure your bundle is up to date, If actions persist after deploy, skip plan check with --noplancheck option", targetArgs, targetArgs)
74+
}
75+
76+
return nil
77+
}
78+
2279
func newMigrateCommand() *cobra.Command {
2380
cmd := &cobra.Command{
2481
Use: "migrate",
@@ -36,7 +93,20 @@ WARNING: Both direct deployment engine and this command are experimental and not
3693
Args: root.NoArgs,
3794
}
3895

96+
var noPlanCheck bool
97+
cmd.Flags().BoolVar(&noPlanCheck, "noplancheck", false, "Skip running bundle plan before migration.")
98+
3999
cmd.RunE = func(cmd *cobra.Command, args []string) error {
100+
// Get the target if it was explicitly passed
101+
target := ""
102+
if flag := cmd.Flag("target"); flag != nil && flag.Changed {
103+
target = flag.Value.String()
104+
}
105+
targetArgs := ""
106+
if target != "" {
107+
targetArgs += " -t " + target
108+
}
109+
40110
opts := utils.ProcessOptions{
41111
SkipEngineEnvVar: true,
42112
AlwaysPull: true,
@@ -63,6 +133,14 @@ To start using direct engine, deploy with DATABRICKS_BUNDLE_ENGINE=direct env va
63133
return fmt.Errorf("already using direct engine\nDetails: %s", stateDesc.String())
64134
}
65135

136+
// Run plan check unless --noplancheck is set
137+
if !noPlanCheck {
138+
fmt.Fprintf(cmd.OutOrStdout(), "Migration should be done after a full deploy. Running plan now to verify that deployment was done:\n")
139+
if err = runPlanCheck(cmd, target); err != nil {
140+
return err
141+
}
142+
}
143+
66144
_, localTerraformPath := b.StateFilenameTerraform(ctx)
67145
if _, err = os.Stat(localTerraformPath); err != nil {
68146
return fmt.Errorf("reading %s: %w", localTerraformPath, err)
@@ -155,12 +233,12 @@ To start using direct engine, deploy with DATABRICKS_BUNDLE_ENGINE=direct env va
155233

156234
cmdio.LogString(ctx, fmt.Sprintf(`Migrated %d resources to direct engine state file: %s
157235
158-
Validate the migration by running "bundle plan", there should be no actions planned.
236+
Validate the migration by running "databricks bundle plan%s", there should be no actions planned.
159237
160-
The state file is not synchronized to the workspace yet. To do that and finalize the migration, run "bundle deploy".
238+
The state file is not synchronized to the workspace yet. To do that and finalize the migration, run "bundle deploy%s".
161239
162240
To undo the migration, remove %s and rename %s to %s
163-
`, len(deploymentBundle.StateDB.Data.State), localPath, localPath, localTerraformBackupPath, localTerraformPath))
241+
`, len(deploymentBundle.StateDB.Data.State), localPath, targetArgs, targetArgs, localPath, localTerraformBackupPath, localTerraformPath))
164242
return nil
165243
}
166244

cmd/bundle/plan.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,7 @@ It is useful for previewing changes before running 'bundle deploy'.`,
107107
}
108108
fmt.Fprintln(out)
109109
}
110+
// Note, this string should not be changed, "bundle deployment migrate" depends on this format:
110111
fmt.Fprintf(out, "Plan: %d to add, %d to change, %d to delete, %d unchanged\n", createCount, updateCount, deleteCount, unchangedCount)
111112
case flags.OutputJSON:
112113
buf, err := json.MarshalIndent(plan, "", " ")

0 commit comments

Comments
 (0)