Skip to content

Commit 6145144

Browse files
Downgrade remote modification check for dashboards to warning during planning (#3760)
## Why This check should not block planning. Thus, this should be downgraded to a warning during planning. ## Tests New unit test. --------- Co-authored-by: Claude <[email protected]>
1 parent 7a2f8b1 commit 6145144

File tree

6 files changed

+65
-13
lines changed

6 files changed

+65
-13
lines changed

acceptance/bundle/resources/dashboards/detect-change/output.txt

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,23 @@ Deployment complete!
4848
}
4949

5050
=== Try to redeploy the bundle and confirm that the out of band modification is detected:
51+
>>> [CLI] bundle plan
52+
Warning: dashboard "file_reference" has been modified remotely
53+
at resources.dashboards.file_reference
54+
in databricks.yml:10:7
55+
56+
This dashboard has been modified remotely since the last bundle deployment.
57+
These modifications are untracked and will be overwritten on deploy.
58+
59+
Make sure that the local dashboard definition matches what you intend to deploy
60+
before proceeding with the deployment.
61+
62+
Run `databricks bundle deploy --force` to bypass this error.
63+
64+
update dashboards.file_reference
65+
66+
Plan: 0 to add, 1 to change, 0 to delete, 0 unchanged
67+
5168
>>> errcode [CLI] bundle deploy
5269
Error: dashboard "file_reference" has been modified remotely
5370
at resources.dashboards.file_reference

acceptance/bundle/resources/dashboards/detect-change/script

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ DASHBOARD_JSON=$($CLI bundle summary --output json | jq '{serialized_dashboard:
2828
$CLI lakeview update "${RESOURCE_ID}" --json "${DASHBOARD_JSON}" | jq '{lifecycle_state}'
2929

3030
title "Try to redeploy the bundle and confirm that the out of band modification is detected:"
31+
trace $CLI bundle plan
3132
trace errcode $CLI bundle deploy
3233

3334
title "Redeploy the bundle with the --force flag and confirm that the out of band modification is ignored:"

bundle/deploy/terraform/check_dashboards_modified_remotely.go

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,9 @@ func collectDashboardsFromState(ctx context.Context, b *bundle.Bundle) ([]dashbo
3333
return dashboards, nil
3434
}
3535

36-
type checkDashboardsModifiedRemotely struct{}
36+
type checkDashboardsModifiedRemotely struct {
37+
isPlan bool
38+
}
3739

3840
func (l *checkDashboardsModifiedRemotely) Name() string {
3941
return "CheckDashboardsModifiedRemotely"
@@ -87,8 +89,14 @@ func (l *checkDashboardsModifiedRemotely) Apply(ctx context.Context, b *bundle.B
8789
continue
8890
}
8991

92+
// Downgrade this to a warning in plan mode.
93+
severity := diag.Error
94+
if l.isPlan {
95+
severity = diag.Warning
96+
}
97+
9098
diags = diags.Append(diag.Diagnostic{
91-
Severity: diag.Error,
99+
Severity: severity,
92100
Summary: fmt.Sprintf("dashboard %q has been modified remotely", dashboard.Name),
93101
Detail: "" +
94102
"This dashboard has been modified remotely since the last bundle deployment.\n" +
@@ -107,6 +115,6 @@ func (l *checkDashboardsModifiedRemotely) Apply(ctx context.Context, b *bundle.B
107115
return diags
108116
}
109117

110-
func CheckDashboardsModifiedRemotely() *checkDashboardsModifiedRemotely {
111-
return &checkDashboardsModifiedRemotely{}
118+
func CheckDashboardsModifiedRemotely(isPlan bool) *checkDashboardsModifiedRemotely {
119+
return &checkDashboardsModifiedRemotely{isPlan: isPlan}
112120
}

bundle/deploy/terraform/check_dashboards_modified_remotely_test.go

Lines changed: 31 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -53,13 +53,13 @@ func TestCheckDashboardsModifiedRemotely_NoDashboards(t *testing.T) {
5353
},
5454
}
5555

56-
diags := bundle.Apply(context.Background(), b, CheckDashboardsModifiedRemotely())
56+
diags := bundle.Apply(context.Background(), b, CheckDashboardsModifiedRemotely(false))
5757
assert.Empty(t, diags)
5858
}
5959

6060
func TestCheckDashboardsModifiedRemotely_FirstDeployment(t *testing.T) {
6161
b := mockDashboardBundle(t)
62-
diags := bundle.Apply(context.Background(), b, CheckDashboardsModifiedRemotely())
62+
diags := bundle.Apply(context.Background(), b, CheckDashboardsModifiedRemotely(false))
6363
assert.Empty(t, diags)
6464
}
6565

@@ -82,7 +82,7 @@ func TestCheckDashboardsModifiedRemotely_ExistingStateNoChange(t *testing.T) {
8282
b.SetWorkpaceClient(m.WorkspaceClient)
8383

8484
// No changes, so no diags.
85-
diags := bundle.Apply(ctx, b, CheckDashboardsModifiedRemotely())
85+
diags := bundle.Apply(ctx, b, CheckDashboardsModifiedRemotely(false))
8686
assert.Empty(t, diags)
8787
}
8888

@@ -105,7 +105,7 @@ func TestCheckDashboardsModifiedRemotely_ExistingStateChange(t *testing.T) {
105105
b.SetWorkpaceClient(m.WorkspaceClient)
106106

107107
// The dashboard has changed, so expect an error.
108-
diags := bundle.Apply(ctx, b, CheckDashboardsModifiedRemotely())
108+
diags := bundle.Apply(ctx, b, CheckDashboardsModifiedRemotely(false))
109109
if assert.Len(t, diags, 1) {
110110
assert.Equal(t, diag.Error, diags[0].Severity)
111111
assert.Equal(t, `dashboard "dash1" has been modified remotely`, diags[0].Summary)
@@ -128,13 +128,39 @@ func TestCheckDashboardsModifiedRemotely_ExistingStateFailureToGet(t *testing.T)
128128
b.SetWorkpaceClient(m.WorkspaceClient)
129129

130130
// Unable to get the dashboard, so expect an error.
131-
diags := bundle.Apply(ctx, b, CheckDashboardsModifiedRemotely())
131+
diags := bundle.Apply(ctx, b, CheckDashboardsModifiedRemotely(false))
132132
if assert.Len(t, diags, 1) {
133133
assert.Equal(t, diag.Error, diags[0].Severity)
134134
assert.Equal(t, `failed to get dashboard "dash1"`, diags[0].Summary)
135135
}
136136
}
137137

138+
func TestCheckDashboardsModifiedRemotely_ExistingStateChangePlanMode(t *testing.T) {
139+
ctx := context.Background()
140+
141+
b := mockDashboardBundle(t)
142+
writeFakeDashboardState(t, ctx, b)
143+
144+
// Mock the call to the API.
145+
m := mocks.NewMockWorkspaceClient(t)
146+
dashboardsAPI := m.GetMockLakeviewAPI()
147+
dashboardsAPI.EXPECT().
148+
GetByDashboardId(mock.Anything, "id1").
149+
Return(&dashboards.Dashboard{
150+
DisplayName: "My Special Dashboard",
151+
Etag: "1234",
152+
}, nil).
153+
Once()
154+
b.SetWorkpaceClient(m.WorkspaceClient)
155+
156+
// The dashboard has changed, but in plan mode expect a warning instead of an error.
157+
diags := bundle.Apply(ctx, b, CheckDashboardsModifiedRemotely(true))
158+
if assert.Len(t, diags, 1) {
159+
assert.Equal(t, diag.Warning, diags[0].Severity)
160+
assert.Equal(t, `dashboard "dash1" has been modified remotely`, diags[0].Summary)
161+
}
162+
}
163+
138164
func writeFakeDashboardState(t *testing.T, ctx context.Context, b *bundle.Bundle) {
139165
path, err := b.StateLocalPath(ctx)
140166
require.NoError(t, err)

bundle/phases/deploy.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -153,7 +153,7 @@ func Deploy(ctx context.Context, b *bundle.Bundle, outputHandler sync.OutputHand
153153
bundle.ApplyContext(ctx, b, lock.Release(lock.GoalDeploy))
154154
}()
155155

156-
libs := deployPrepare(ctx, b)
156+
libs := deployPrepare(ctx, b, false)
157157
if logdiag.HasError(ctx) {
158158
return
159159
}
@@ -243,7 +243,7 @@ func planWithoutPrepare(ctx context.Context, b *bundle.Bundle) *deployplan.Plan
243243
}
244244

245245
func Plan(ctx context.Context, b *bundle.Bundle) *deployplan.Plan {
246-
deployPrepare(ctx, b)
246+
deployPrepare(ctx, b, true)
247247
if logdiag.HasError(ctx) {
248248
return nil
249249
}

bundle/phases/plan.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,10 +19,10 @@ import (
1919

2020
// deployPrepare is common set of mutators between "bundle plan" and "bundle deploy".
2121
// This function does not make any mutations in the workspace remotely, only in-memory bundle config mutations
22-
func deployPrepare(ctx context.Context, b *bundle.Bundle) map[string][]libraries.LocationToUpdate {
22+
func deployPrepare(ctx context.Context, b *bundle.Bundle, isPlan bool) map[string][]libraries.LocationToUpdate {
2323
bundle.ApplySeqContext(ctx, b,
2424
statemgmt.StatePull(),
25-
terraform.CheckDashboardsModifiedRemotely(),
25+
terraform.CheckDashboardsModifiedRemotely(isPlan),
2626
deploy.StatePull(),
2727
mutator.ValidateGitDetails(),
2828
terraform.CheckRunningResource(),

0 commit comments

Comments
 (0)