Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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
25 changes: 25 additions & 0 deletions acceptance/bundle/migrate/dashboards/out.plan_after_migrate.json
Original file line number Diff line number Diff line change
Expand Up @@ -21,18 +21,43 @@
"warehouse_id": "123456"
},
"changes": {
"create_time": {
"action": "skip",
"reason": "spec:output_only",
"remote": "[TIMESTAMP]"
},
"dashboard_id": {
"action": "skip",
"reason": "spec:output_only",
"remote": "[DASHBOARD1_ID]"
},
"etag": {
"action": "skip",
"reason": "custom",
"old": "[NUMID]",
"remote": "[NUMID]"
},
"lifecycle_state": {
"action": "skip",
"reason": "spec:output_only",
"remote": "ACTIVE"
},
"path": {
"action": "skip",
"reason": "spec:output_only",
"remote": "/Users/[USERNAME]/my dashboard.lvdash.json"
},
"serialized_dashboard": {
"action": "skip",
"reason": "etag_based",
"old": "{\"pages\":[{\"name\":\"02724bf2\",\"displayName\":\"Dashboard test bundle-deploy-dashboard\"}]}\n",
"new": "{\"pages\":[{\"name\":\"02724bf2\",\"displayName\":\"Dashboard test bundle-deploy-dashboard\"}]}\n",
"remote": "{\"pages\":[{\"displayName\":\"Dashboard test bundle-deploy-dashboard\",\"name\":\"02724bf2\",\"pageType\":\"PAGE_TYPE_CANVAS\"}]}\n"
},
"update_time": {
"action": "skip",
"reason": "spec:output_only",
"remote": "[TIMESTAMP]"
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,16 @@
"warehouse_id": "[TEST_DEFAULT_WAREHOUSE_ID]"
},
"changes": {
"create_time": {
"action": "skip",
"reason": "spec:output_only",
"remote": "[TIMESTAMP]"
},
"dashboard_id": {
"action": "skip",
"reason": "spec:output_only",
"remote": "[DASHBOARD_ID]"
},
"dataset_catalog": {
"action": "skip",
"reason": "input_only",
Expand All @@ -39,10 +49,25 @@
"old": "[ETAG]",
"remote": "[ETAG]"
},
"lifecycle_state": {
"action": "skip",
"reason": "spec:output_only",
"remote": "ACTIVE"
},
"path": {
"action": "skip",
"reason": "spec:output_only",
"remote": "/Users/[USERNAME]/test bundle-deploy-dashboard-dataset [UUID].lvdash.json"
},
"serialized_dashboard": {
"action": "skip",
"old": "[SERIALIZED_FIXTURE_OLD]",
"new": "[SERIALIZED_FIXTURE_NEW]"
},
"update_time": {
"action": "skip",
"reason": "spec:output_only",
"remote": "[TIMESTAMP]"
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,17 +31,42 @@
"warehouse_id": "[TEST_DEFAULT_WAREHOUSE_ID]"
},
"changes": {
"create_time": {
"action": "skip",
"reason": "spec:output_only",
"remote": "[TIMESTAMP]"
},
"dashboard_id": {
"action": "skip",
"reason": "spec:output_only",
"remote": "[DASHBOARD_ID]"
},
"etag": {
"action": "update",
"old": "[ETAG_1]",
"remote": "[ETAG_2]"
},
"lifecycle_state": {
"action": "skip",
"reason": "spec:output_only",
"remote": "ACTIVE"
},
"path": {
"action": "skip",
"reason": "spec:output_only",
"remote": "/Users/[USERNAME]/.bundle/[UNIQUE_NAME]/resources/test-dashboard-[UNIQUE_NAME].lvdash.json"
},
"serialized_dashboard": {
"action": "skip",
"reason": "etag_based",
"old": "{\n \"pages\": [\n {\n \"displayName\": \"New Page\",\n \"layout\": [\n {\n \"position\": {\n \"height\": 2,\n \"width\": 6,\n \"x\": 0,\n \"y\": 0\n },\n \"widget\": {\n \"name\": \"82eb9107\",\n \"textbox_spec\": \"# I'm a title\"\n }\n },\n {\n \"position\": {\n \"height\": 2,\n \"width\": 6,\n \"x\": 0,\n \"y\": 2\n },\n \"widget\": {\n \"name\": \"ffa6de4f\",\n \"textbox_spec\": \"Text\"\n }\n }\n ],\n \"name\": \"fdd21a3c\"\n }\n ]\n}\n",
"new": "{\n \"pages\": [\n {\n \"displayName\": \"New Page\",\n \"layout\": [\n {\n \"position\": {\n \"height\": 2,\n \"width\": 6,\n \"x\": 0,\n \"y\": 0\n },\n \"widget\": {\n \"name\": \"82eb9107\",\n \"textbox_spec\": \"# I'm a title\"\n }\n },\n {\n \"position\": {\n \"height\": 2,\n \"width\": 6,\n \"x\": 0,\n \"y\": 2\n },\n \"widget\": {\n \"name\": \"ffa6de4f\",\n \"textbox_spec\": \"Text\"\n }\n }\n ],\n \"name\": \"fdd21a3c\"\n }\n ]\n}\n",
"remote": "{}\n"
},
"update_time": {
"action": "skip",
"reason": "spec:output_only",
"remote": "[TIMESTAMP]"
}
}
}
Expand Down
25 changes: 25 additions & 0 deletions acceptance/bundle/resources/dashboards/simple/out.plan.direct.json
Original file line number Diff line number Diff line change
Expand Up @@ -21,18 +21,43 @@
"warehouse_id": "[TEST_DEFAULT_WAREHOUSE_ID]"
},
"changes": {
"create_time": {
"action": "skip",
"reason": "spec:output_only",
"remote": "[TIMESTAMP]"
},
"dashboard_id": {
"action": "skip",
"reason": "spec:output_only",
"remote": "[DASHBOARD_ID]"
},
"etag": {
"action": "skip",
"reason": "custom",
"old": "[ETAG]",
"remote": "[ETAG]"
},
"lifecycle_state": {
"action": "skip",
"reason": "spec:output_only",
"remote": "ACTIVE"
},
"path": {
"action": "skip",
"reason": "spec:output_only",
"remote": "/Users/[USERNAME]/test bundle-deploy-dashboard [UUID].lvdash.json"
},
"serialized_dashboard": {
"action": "skip",
"reason": "etag_based",
"old": "{ }\n",
"new": "{ }\n",
"remote": "{}\n"
},
"update_time": {
"action": "skip",
"reason": "spec:output_only",
"remote": "[TIMESTAMP]"
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,12 +31,32 @@
"warehouse_id": "[TEST_DEFAULT_WAREHOUSE_ID]"
},
"changes": {
"create_time": {
"action": "skip",
"reason": "spec:output_only",
"remote": "[TIMESTAMP]"
},
"dashboard_id": {
"action": "skip",
"reason": "spec:output_only",
"remote": "[DASHBOARD1_ID]"
},
"etag": {
"action": "skip",
"reason": "custom",
"old": "[ETAG]",
"remote": "[ETAG]"
},
"lifecycle_state": {
"action": "skip",
"reason": "spec:output_only",
"remote": "ACTIVE"
},
"path": {
"action": "skip",
"reason": "spec:output_only",
"remote": "/Users/[USERNAME]/.bundle/unpublish-out-of-band-[UNIQUE_NAME]/default/resources/test bundle-deploy-dashboard [UNIQUE_NAME].lvdash.json"
},
"published": {
"action": "update",
"old": true,
Expand All @@ -49,6 +69,11 @@
"old": "{\"pages\":[{\"displayName\":\"Test Dashboard\",\"name\":\"test-page\"}]}",
"new": "{\"pages\":[{\"displayName\":\"Test Dashboard\",\"name\":\"test-page\"}]}",
"remote": "{\"pages\":[{\"displayName\":\"Test Dashboard\",\"name\":\"test-page\",\"pageType\":\"PAGE_TYPE_CANVAS\"}]}"
},
"update_time": {
"action": "skip",
"reason": "spec:output_only",
"remote": "[TIMESTAMP]"
}
}
}
Expand Down
2 changes: 1 addition & 1 deletion bundle/direct/dresources/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
Each field with special plan/deploy behavior must be declared in `resources.yml`. Choose the right category:

- **`backend_defaults`**: The backend may fill in a value when the user doesn't specify one. Suppresses the diff when the user's config is nil/empty but remote has a value. Optionally restrict to specific allowed remote values via `values:`. Use for fields the API fills in as defaults (e.g., `format`, `run_if`, `node_type_id`). Link to TF provider suppression comment in the same format as existing entries.
- **`ignore_remote_changes`**: Ignore changes the remote makes to this field. Use for fields the backend manages (e.g., cloud-provider attributes like `aws_attributes`, `gcp_attributes`) or fields not returned by the update endpoint. Reason codes:
- **`ignore_remote_changes`**: Ignore changes the remote makes to this field. Use for fields the backend manages (e.g., cloud-provider attributes like `aws_attributes`, `gcp_attributes`) or fields not returned by the update endpoint. Do not zero out such fields in `RemapState` to hide them from diff computation: carry the real remote value through and declare the field here instead, since zeroing discards information and duplicates the suppression logic. For `output_only` fields this rule is often already produced by `resources.generated.yml` from the OpenAPI annotation. Reason codes:
- `output_only` — the field is computed by the backend; the user never sets it
- `input_only` — accepted on create/update but not returned by GET (e.g., write-only tokens, flags)
- `managed` — managed by the cloud provider or platform, not by the user config
Expand Down
13 changes: 7 additions & 6 deletions bundle/direct/dresources/dashboard.go
Original file line number Diff line number Diff line change
Expand Up @@ -95,12 +95,13 @@ func (r *ResourceDashboard) RemapState(state *DashboardState) *DashboardState {

ForceSendFields: forceSendFields,

// Clear output only fields. They should not show up on remote diff computation.
CreateTime: "",
DashboardId: "",
LifecycleState: dashboards.LifecycleState(""),
Path: "",
UpdateTime: "",
// Output only fields. Remote changes to these are ignored via
// ignore_remote_changes in resources.yml rather than zeroed here.
CreateTime: state.CreateTime,
DashboardId: state.DashboardId,
LifecycleState: state.LifecycleState,
Path: state.Path,
UpdateTime: state.UpdateTime,
},
Published: state.Published,
}
Expand Down
21 changes: 16 additions & 5 deletions bundle/direct/dresources/model.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package dresources

import (
"context"
"errors"

"github.com/databricks/cli/bundle/config/resources"
"github.com/databricks/cli/libs/utils"
Expand Down Expand Up @@ -57,6 +58,9 @@ func (r *ResourceMlflowModel) DoCreate(ctx context.Context, config *ml.CreateMod
if err != nil {
return "", nil, err
}
if response.RegisteredModel == nil {
return "", nil, errors.New("CreateModel returned no registered_model")
}
// Return nil for refresh output; the engine will call DoRead to populate the full state
// including the numeric model ID needed for permissions.
return response.RegisteredModel.Name, nil, nil
Expand All @@ -73,25 +77,32 @@ func (r *ResourceMlflowModel) DoUpdate(ctx context.Context, id string, config *m
if err != nil {
return nil, err
}
if response.RegisteredModel == nil {
return nil, errors.New("UpdateModel returned no registered_model")
}

// Carry forward model_id from existing state since UpdateModelResponse doesn't include it.
var modelId string
if old, ok := entry.RemoteState.(*MlflowModelRemote); ok {
modelId = old.ModelId
}

// Id and PermissionLevel are left empty because ml.Model (the UpdateModel
// response) does not carry them; every other field is taken from the
// response so references against the post-update remote state resolve to
// real values until the next plan's DoRead.
return &MlflowModelRemote{
ModelDatabricks: ml.ModelDatabricks{
CreationTimestamp: 0,
CreationTimestamp: response.RegisteredModel.CreationTimestamp,
Description: response.RegisteredModel.Description,
Id: "",
LastUpdatedTimestamp: 0,
LatestVersions: nil,
LastUpdatedTimestamp: response.RegisteredModel.LastUpdatedTimestamp,
LatestVersions: response.RegisteredModel.LatestVersions,
Name: response.RegisteredModel.Name,
PermissionLevel: "",
Tags: response.RegisteredModel.Tags,
UserId: "",
ForceSendFields: utils.FilterFields[ml.ModelDatabricks](response.RegisteredModel.ForceSendFields, "CreationTimestamp", "Id", "LastUpdatedTimestamp", "LatestVersions", "PermissionLevel", "UserId"),
UserId: response.RegisteredModel.UserId,
ForceSendFields: utils.FilterFields[ml.ModelDatabricks](response.RegisteredModel.ForceSendFields, "Id", "PermissionLevel"),
},
ModelId: modelId,
}, nil
Expand Down
11 changes: 6 additions & 5 deletions bundle/direct/dresources/registered_model.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,11 +38,12 @@ func (*ResourceRegisteredModel) RemapState(model *catalog.RegisteredModelInfo) *
MetastoreId: model.MetastoreId,
Owner: model.Owner,

// Clear output only fields. They should not show up on remote diff computation.
CreatedAt: 0,
CreatedBy: "",
UpdatedAt: 0,
UpdatedBy: "",
// Output only fields. Remote changes to these are ignored via
// ignore_remote_changes in resources.yml rather than zeroed here.
CreatedAt: model.CreatedAt,
CreatedBy: model.CreatedBy,
UpdatedAt: model.UpdatedAt,
UpdatedBy: model.UpdatedBy,
}
}

Expand Down
5 changes: 3 additions & 2 deletions bundle/direct/dresources/resources.yml
Original file line number Diff line number Diff line change
Expand Up @@ -271,8 +271,9 @@ resources:

registered_models:
ignore_remote_changes:
# Output-only timestamp/user fields zeroed in RemapState but still show up
# via ForceSendFields. These should never participate in diffs.
# Output-only timestamp/user fields populated by the backend on read.
# The user never sets them, so remote-only differences are ignored here
# rather than zeroed in RemapState.
- field: created_at
reason: output_only
- field: created_by
Expand Down
Loading