Skip to content
Open
Show file tree
Hide file tree
Changes from 2 commits
Commits
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,16 @@
resources:
dashboards:
foo:
display_name: test-dashboard
warehouse_id: 123abc
parent_path: /Users/[email protected]/folder1
serialized_dashboard: '{"pages":[{"name":"page1","displayName":"Page 1"}]}'
permissions:
- level: CAN_READ
user_name: [email protected]
- level: CAN_MANAGE
group_name: data-team
- level: CAN_MANAGE
service_principal_name: f37d18cd-98a8-4db5-8112-12dd0a6bfe38
- level: CAN_MANAGE
user_name: [email protected]
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
{
"plan": {
"resources.dashboards.foo": {
"action": "create",
"new_state": {
"config": {
"display_name": "test-dashboard",
"parent_path": "/Users/[USERNAME]/folder1",
"warehouse_id": "123abc"
}
}
},
"resources.dashboards.foo.permissions": {
"depends_on": [
{
"node": "resources.dashboards.foo",
"label": "${resources.dashboards.foo.id}"
}
],
"action": "create",
"new_state": {
"config": {
"object_id": "",
"permissions": [
{
"permission_level": "CAN_READ",
"user_name": "[email protected]"
},
{
"group_name": "data-team",
"permission_level": "CAN_MANAGE"
},
{
"permission_level": "CAN_MANAGE",
"service_principal_name": "[UUID]"
},
{
"permission_level": "CAN_MANAGE",
"user_name": "[USERNAME]"
}
]
},
"vars": {
"object_id": "/dashboards/${resources.dashboards.foo.id}"
}
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"plan": {
"resources.dashboards.foo": {
"action": "create"
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
{
"method": "PUT",
"path": "/api/2.0/permissions/dashboards/[DASHBOARD_ID]",
"body": {
"access_control_list": [
{
"permission_level": "CAN_READ",
"user_name": "[email protected]"
},
{
"group_name": "data-team",
"permission_level": "CAN_MANAGE"
},
{
"permission_level": "CAN_MANAGE",
"service_principal_name": "[UUID]"
},
{
"permission_level": "CAN_MANAGE",
"user_name": "[USERNAME]"
}
]
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
{
"method": "PUT",
"path": "/api/2.0/permissions/dashboards/[DASHBOARD_ID]",
"body": {
"access_control_list": [
{
"permission_level": "CAN_READ",
"user_name": "[email protected]"
},
{
"permission_level": "CAN_MANAGE",
"service_principal_name": "[UUID]"
},
{
"group_name": "data-team",
"permission_level": "CAN_MANAGE"
},
{
"permission_level": "CAN_MANAGE",
"user_name": "[USERNAME]"
}
]
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"method": "PUT",
"path": "/api/2.0/permissions/dashboards/[DASHBOARD_ID]",
"body": {}
}

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

>>> [CLI] bundle validate -o json
[
{
"level": "CAN_READ",
"user_name": "[email protected]"
},
{
"group_name": "data-team",
"level": "CAN_MANAGE"
},
{
"level": "CAN_MANAGE",
"service_principal_name": "[UUID]"
},
{
"level": "CAN_MANAGE",
"user_name": "[USERNAME]"
}
]

>>> [CLI] workspace mkdirs /Users/[USERNAME]/folder1

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

>>> [CLI] bundle destroy --auto-approve
The following resources will be deleted:
delete dashboard foo

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

Deleting files...
Destroy complete!
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
trace $CLI bundle validate -o json | jq .resources.dashboards.foo.permissions
rm out.requests.txt

$CLI bundle debug plan > out.plan.$DATABRICKS_BUNDLE_ENGINE.json

print_requests() {
jq -c < out.requests.txt | jq 'select(.method != "GET" and (.path | contains("permissions")))'
rm out.requests.txt
}

# create the parent path
trace $CLI workspace mkdirs /Users/[email protected]/folder1

rm out.requests.txt
trace $CLI bundle deploy

dashboard_id=$($CLI bundle summary --output json | jq -r '.resources.dashboards.foo.id')
echo "$dashboard_id:DASHBOARD_ID" >> ACC_REPLS

print_requests > out.requests.deploy.$DATABRICKS_BUNDLE_ENGINE.json

trace $CLI bundle destroy --auto-approve
print_requests > out.requests.destroy.$DATABRICKS_BUNDLE_ENGINE.json
1 change: 1 addition & 0 deletions bundle/direct/dresources/all.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ var SupportedResources = map[string]any{
"jobs.permissions": (*ResourcePermissions)(nil),
"pipelines.permissions": (*ResourcePermissions)(nil),
"apps.permissions": (*ResourcePermissions)(nil),
"dashboards.permissions": (*ResourcePermissions)(nil),
"clusters.permissions": (*ResourcePermissions)(nil),
"database_instances.permissions": (*ResourcePermissions)(nil),
"experiments.permissions": (*ResourcePermissions)(nil),
Expand Down
46 changes: 45 additions & 1 deletion bundle/direct/dresources/all_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import (
"github.com/databricks/databricks-sdk-go"
"github.com/databricks/databricks-sdk-go/service/apps"
"github.com/databricks/databricks-sdk-go/service/catalog"
"github.com/databricks/databricks-sdk-go/service/dashboards"
"github.com/databricks/databricks-sdk-go/service/database"
"github.com/databricks/databricks-sdk-go/service/iam"
"github.com/databricks/databricks-sdk-go/service/jobs"
Expand Down Expand Up @@ -97,6 +98,16 @@ var testConfig map[string]any = map[string]any{
},
},
},

"dashboards": &resources.Dashboard{
DashboardConfig: resources.DashboardConfig{
Dashboard: dashboards.Dashboard{
DisplayName: "my_dashboard",
SerializedDashboard: `{"pages":[{"name":"page1","displayName":"Page 1"}]}`,
WarehouseId: "warehouse123",
},
},
},
}

type prepareWorkspace func(client *databricks.WorkspaceClient) (any, error)
Expand Down Expand Up @@ -252,6 +263,27 @@ var testDeps = map[string]prepareWorkspace{
}},
}, nil
},

"dashboards.permissions": func(client *databricks.WorkspaceClient) (any, error) {
resp, err := client.Lakeview.Create(context.Background(), dashboards.CreateDashboardRequest{
Dashboard: dashboards.Dashboard{
DisplayName: "dashboard-permissions",
SerializedDashboard: `{"pages":[{"name":"page1","displayName":"Page 1"}]}`,
WarehouseId: "warehouse123",
},
})
if err != nil {
return nil, err
}

return &PermissionsState{
ObjectID: "/dashboards/" + resp.DashboardId,
Permissions: []iam.AccessControlRequest{{
PermissionLevel: "CAN_MANAGE",
UserName: "[email protected]",
}},
}, nil
},
}

func TestAll(t *testing.T) {
Expand Down Expand Up @@ -340,7 +372,13 @@ func testCRUD(t *testing.T, group string, adapter *Adapter, client *databricks.W
var relevantChanges []structdiff.Change
for _, change := range changes {
fieldName := change.Path.String()
if fieldName != "updated_at" {
// Filter out fields that are expected to change between DoRefresh and DoUpdate
// - updated_at, update_time: timestamps that change on updates
// - etag: version field that changes on updates
// - path: computed field for dashboards
// - serialized_dashboard: test server adds pageType and formatting
if fieldName != "updated_at" && fieldName != "update_time" && fieldName != "etag" &&
fieldName != "path" && fieldName != "serialized_dashboard" {
relevantChanges = append(relevantChanges, change)
}
}
Expand Down Expand Up @@ -373,6 +411,12 @@ func testCRUD(t *testing.T, group string, adapter *Adapter, client *databricks.W
// t.Logf("Testing %s v=%#v, remoteValue=%#v", path.String(), val, remoteValue)
// We expect fields set explicitly to be preserved by testserver, which is true for all resources as of today.
// If not true for your resource, add exception here:

// Dashboard serialized_dashboard is modified by the server (adds pageType, reorders keys, adds newline)
if path.String() == "serialized_dashboard" {
return
}

assert.Equal(t, val, remoteValue, "path=%q\nnewState=%s\nremappedState=%s", path.String(), jsonDump(newState), jsonDump(remappedState))
}))

Expand Down
Loading