Uh oh!
There was an error while loading. Please reload this page.
- Notifications
You must be signed in to change notification settings - Fork 215
Read dashboard definition from file_path via filer interface#2738
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Uh oh!
There was an error while loading. Please reload this page.
Changes from all commits
399a8a6c286774efd5faa2e760eef6a27a78a8c084e7e0b45c3f2bdaFile filter
Filter by extension
Conversations
Uh oh!
There was an error while loading. Please reload this page.
Jump to
Uh oh!
There was an error while loading. Please reload this page.
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,11 @@ | ||
| bundle: | ||
| name: deploy-dashboard-test-$UNIQUE_NAME | ||
| resources: | ||
| dashboards: | ||
| dashboard1: | ||
| display_name: $DASHBOARD_DISPLAY_NAME | ||
| warehouse_id: $TEST_DEFAULT_WAREHOUSE_ID | ||
| embed_credentials: true | ||
| file_path: "sample-dashboard.lvdash.json" | ||
| parent_path: /Users/$CURRENT_USER_NAME |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,23 @@ | ||
| >>> [CLI] bundle deploy | ||
| Uploading bundle files to /Workspace/Users/[USERNAME]/.bundle/deploy-dashboard-test-[UNIQUE_NAME]/default/files... | ||
| Deploying resources... | ||
| Updating deployment state... | ||
| Deployment complete! | ||
| >>> [CLI] lakeview get [DASHBOARD_ID] | ||
| { | ||
| "lifecycle_state": "ACTIVE", | ||
| "parent_path": "/Users/[USERNAME]", | ||
| "path": "/Users/[USERNAME]/test bundle-deploy-dashboard [UUID].lvdash.json", | ||
| "serialized_dashboard": "{\"pages\":[{\"name\":\"02724bf2\",\"displayName\":\"Dashboard test bundle-deploy-dashboard\"}]}" | ||
| } | ||
| >>> [CLI] bundle destroy --auto-approve | ||
| The following resources will be deleted: | ||
| delete dashboard dashboard1 | ||
| All files and directories at the following location will be deleted: /Workspace/Users/[USERNAME]/.bundle/deploy-dashboard-test-[UNIQUE_NAME]/default | ||
| Deleting files... | ||
| Destroy complete! |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1 @@ | ||
| {"pages":[{"name":"02724bf2","displayName":"Dashboard test bundle-deploy-dashboard"}]} |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,17 @@ | ||
| DASHBOARD_DISPLAY_NAME="test bundle-deploy-dashboard $(uuid)" | ||
| if [ -z "$CLOUD_ENV" ]; then | ||
| DASHBOARD_DISPLAY_NAME="test bundle/deploy/ 6260d50f-e8ff-4905-8f28-812345678903" # use hard-coded uuid when running locally | ||
| export TEST_DEFAULT_WAREHOUSE_ID="warehouse-1234" | ||
| fi | ||
| export DASHBOARD_DISPLAY_NAME | ||
| envsubst < databricks.yml.tmpl > databricks.yml | ||
| cleanup() { | ||
| trace $CLI bundle destroy --auto-approve | ||
| } | ||
| trap cleanup EXIT | ||
| trace $CLI bundle deploy | ||
| DASHBOARD_ID=$($CLI bundle summary --output json | jq -r '.resources.dashboards.dashboard1.id') | ||
| trace $CLI lakeview get $DASHBOARD_ID | jq '{lifecycle_state, parent_path, path, serialized_dashboard}' | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,38 @@ | ||
| Local = true | ||
| Cloud = true | ||
anton-107 marked this conversation as resolved.
Uh oh!There was an error while loading. Please reload this page. | ||
| RequiresWarehouse = true | ||
| Ignore = [ | ||
| "databricks.yml", | ||
| ] | ||
| [[Repls]] | ||
| Old = "[0-9a-f]{32}" | ||
| New = "[DASHBOARD_ID]" | ||
| [[Server]] | ||
| Pattern = "POST /api/2.0/lakeview/dashboards" | ||
| Response.Body = ''' | ||
| { | ||
| "dashboard_id":"1234567890abcdef1234567890abcdef" | ||
| } | ||
| ''' | ||
| [[Server]] | ||
| Pattern = "POST /api/2.0/lakeview/dashboards/{dashboard_id}/published" | ||
| [[Server]] | ||
| Pattern = "GET /api/2.0/lakeview/dashboards/{dashboard_id}" | ||
| Response.Body = ''' | ||
| { | ||
| "dashboard_id":"1234567890abcdef1234567890abcdef", | ||
| "display_name": "test dashboard 6260d50f-e8ff-4905-8f28-812345678903", | ||
| "lifecycle_state": "ACTIVE", | ||
| "path": "/Users/[USERNAME]/test bundle-deploy-dashboard 6260d50f-e8ff-4905-8f28-812345678903.lvdash.json", | ||
| "parent_path": "/Users/tester@databricks.com", | ||
| "serialized_dashboard": "{\"pages\":[{\"name\":\"02724bf2\",\"displayName\":\"Dashboard test bundle-deploy-dashboard\"}]}" | ||
| } | ||
| ''' | ||
| [[Server]] | ||
| Pattern = "DELETE /api/2.0/lakeview/dashboards/{dashboard_id}" | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -13,6 +13,6 @@ func VisitDashboardPaths(value dyn.Value, fn VisitFunc) (dyn.Value, error) { | ||
| ) | ||
| return dyn.MapByPattern(value, pattern, func(path dyn.Path, value dyn.Value) (dyn.Value, error) { | ||
| return fn(path, TranslateModeLocalAbsoluteFile, value) | ||
| return fn(path, TranslateModeLocalRelative, value) | ||
anton-107 marked this conversation as resolved.
Uh oh!There was an error while loading. Please reload this page. | ||
| }) | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,76 @@ | ||
| package resourcemutator | ||
| import ( | ||
| "context" | ||
| "fmt" | ||
| "github.com/databricks/cli/bundle" | ||
| "github.com/databricks/cli/libs/diag" | ||
| "github.com/databricks/cli/libs/dyn" | ||
| ) | ||
| const ( | ||
| filePathFieldName = "file_path" | ||
| serializedDashboardFieldName = "serialized_dashboard" | ||
anton-107 marked this conversation as resolved.
Uh oh!There was an error while loading. Please reload this page. | ||
| ) | ||
| type configureDashboardSerializedDashboard struct{} | ||
| func ConfigureDashboardSerializedDashboard() bundle.Mutator { | ||
| return &configureDashboardSerializedDashboard{} | ||
| } | ||
| func (c configureDashboardSerializedDashboard) Name() string { | ||
| return "ConfigureDashboardSerializedDashboard" | ||
| } | ||
| func (c configureDashboardSerializedDashboard) Apply(_ context.Context, b *bundle.Bundle) diag.Diagnostics { | ||
| var diags diag.Diagnostics | ||
| pattern := dyn.NewPattern( | ||
| dyn.Key("resources"), | ||
| dyn.Key("dashboards"), | ||
| dyn.AnyKey(), | ||
| ) | ||
| // Configure serialized_dashboard field for all dashboards. | ||
| err := b.Config.Mutate(func(v dyn.Value) (dyn.Value, error) { | ||
| return dyn.MapByPattern(v, pattern, func(p dyn.Path, v dyn.Value) (dyn.Value, error) { | ||
anton-107 marked this conversation as resolved.
Uh oh!There was an error while loading. Please reload this page. | ||
| // Include "serialized_dashboard" field if "file_path" is set. | ||
| // Note: the Terraform resource supports "file_path" natively, but we read the contents of the dashboard here | ||
anton-107 marked this conversation as resolved.
Uh oh!There was an error while loading. Please reload this page. | ||
| // to be able to read file contents in Databricks Workspace (reading a dashboard file via file system fails there) | ||
| path, ok := v.Get(filePathFieldName).AsString() | ||
| if !ok { | ||
| return v, nil | ||
| } | ||
| contents, err := b.SyncRoot.ReadFile(path) | ||
| if err != nil { | ||
| return dyn.InvalidValue, fmt.Errorf("failed to read serialized dashboard from file_path %s: %w", path, err) | ||
| } | ||
| v, err = dyn.Set(v, serializedDashboardFieldName, dyn.V(string(contents))) | ||
| if err != nil { | ||
| return dyn.InvalidValue, fmt.Errorf("failed to set serialized_dashboard: %w", err) | ||
| } | ||
| // Drop the "file_path" field. It is mutually exclusive with "serialized_dashboard". | ||
| return dyn.Walk(v, func(p dyn.Path, v dyn.Value) (dyn.Value, error) { | ||
anton-107 marked this conversation as resolved.
Uh oh!There was an error while loading. Please reload this page. | ||
| switch len(p) { | ||
| case 0: | ||
| return v, nil | ||
| case 1: | ||
| if p[0] == dyn.Key(filePathFieldName) { | ||
| return v, dyn.ErrDrop | ||
| } | ||
| } | ||
| // Skip everything else. | ||
| return v, dyn.ErrSkip | ||
| }) | ||
| }) | ||
| }) | ||
| diags = diags.Extend(diag.FromErr(err)) | ||
| return diags | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -12,7 +12,6 @@ import ( | ||
| ) | ||
| const ( | ||
| filePathFieldName = "file_path" | ||
| serializedDashboardFieldName = "serialized_dashboard" | ||
anton-107 marked this conversation as resolved.
Uh oh!There was an error while loading. Please reload this page. | ||
| ) | ||
| @@ -48,33 +47,6 @@ func convertDashboardResource(ctx context.Context, vin dyn.Value) (dyn.Value, er | ||
| log.Debugf(ctx, "dashboard normalization diagnostic: %s", diag.Summary) | ||
| } | ||
| // Include "serialized_dashboard" field if "file_path" is set. | ||
| // Note: the Terraform resource supports "file_path" natively, but its | ||
| // change detection mechanism doesn't work as expected at the time of writing (Sep 30). | ||
| if path, ok := vout.Get(filePathFieldName).AsString(); ok { | ||
anton-107 marked this conversation as resolved.
Uh oh!There was an error while loading. Please reload this page. | ||
| vout, err = dyn.Set(vout, serializedDashboardFieldName, dyn.V(fmt.Sprintf("${file(%q)}", path))) | ||
| if err != nil { | ||
| return dyn.InvalidValue, fmt.Errorf("failed to set serialized_dashboard: %w", err) | ||
| } | ||
| // Drop the "file_path" field. It is mutually exclusive with "serialized_dashboard". | ||
| vout, err = dyn.Walk(vout, func(p dyn.Path, v dyn.Value) (dyn.Value, error) { | ||
| switch len(p) { | ||
| case 0: | ||
| return v, nil | ||
| case 1: | ||
| if p[0] == dyn.Key(filePathFieldName) { | ||
| return v, dyn.ErrDrop | ||
| } | ||
| } | ||
| // Skip everything else. | ||
| return v, dyn.ErrSkip | ||
| }) | ||
| if err != nil { | ||
| return dyn.InvalidValue, fmt.Errorf("failed to drop file_path: %w", err) | ||
| } | ||
| } | ||
| // Marshal "serialized_dashboard" as JSON if it is set in the input but not in the output. | ||
| vout, err = marshalSerializedDashboard(vin, vout) | ||
| if err != nil { | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -119,6 +119,11 @@ func Initialize(ctx context.Context, b *bundle.Bundle) diag.Diagnostics { | ||
| // ApplyPresets through ResourceProcessor. | ||
| resourcemutator.ApplyTargetMode(), | ||
| // Reads (typed): b.SyncRoot (checks if bundle root is in /Workspace/) | ||
| // Updates (typed): b.SyncRoot (replaces with extension-aware path when running on Databricks Runtime) | ||
| // Configure use of WSFS for reads if the CLI is running on Databricks. | ||
| mutator.ConfigureWSFS(), | ||
anton-107 marked this conversation as resolved.
Uh oh!There was an error while loading. Please reload this page. | ||
| // Static resources (e.g. YAML) are already loaded, we initialize and normalize them before Python | ||
| resourcemutator.ProcessStaticResources(), | ||
| @@ -136,11 +141,6 @@ func Initialize(ctx context.Context, b *bundle.Bundle) diag.Diagnostics { | ||
| // Provides diagnostic recommendations if the current deployment identity isn't explicitly granted CAN_MANAGE permissions | ||
| permissions.PermissionDiagnostics(), | ||
| // Reads (typed): b.SyncRoot (checks if bundle root is in /Workspace/) | ||
| // Updates (typed): b.SyncRoot (replaces with extension-aware path when running on Databricks Runtime) | ||
| // Configure use of WSFS for reads if the CLI is running on Databricks. | ||
| mutator.ConfigureWSFS(), | ||
| mutator.TranslatePaths(), | ||
| // Reads (typed): b.Config.Experimental.PythonWheelWrapper, b.Config.Presets.SourceLinkedDeployment (checks Python wheel wrapper and deployment mode settings) | ||
Uh oh!
There was an error while loading. Please reload this page.