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
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
* direct: Allow clearing a genie space's `description` and a secret's `comment` by removing them from configuration. ([#6789](https://github.com/databricks/cli/pull/6789))
4 changes: 1 addition & 3 deletions acceptance/bundle/invariant/configs/genie_space.yml.tmpl
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,7 @@ resources:
foo:
warehouse_id: $TEST_DEFAULT_WAREHOUSE_ID
title: test-genie-space-$UNIQUE_NAME
# CANNOT_REMOVE: description is not force-sent on update, so clearing it drops the
# field from the request and the old value drifts back on every subsequent plan.
description: This is a test genie space
description: This is a test genie space # CAN_REMOVE
# Structured (inline) serialized_space is marshalled to a JSON string by
# ConfigureGenieSpaceSerializedSpace; this config doubles as a regression
# guard that the normalization produces a drift-free deploy. Kept minimal
Expand Down
4 changes: 1 addition & 3 deletions acceptance/bundle/invariant/configs/secret.yml.tmpl
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,7 @@ resources:
schema_name: default
name: test-secret-$UNIQUE_NAME
value: ${var.secret_value}
# CANNOT_REMOVE: comment is not force-sent on update, so clearing it drops the field
# from the request and the old value drifts back on every subsequent plan.
comment: This is a test secret
comment: This is a test secret # CAN_REMOVE
grants:
- principal: account users
privileges:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ Resources: 0 created, 1 changed, 0 deleted, 0 unchanged
"method": "PATCH",
"path": "/api/2.0/genie/spaces/[MY_SPACE_ID]",
"body": {
"description": "",
"parent_path": "/Workspace/Users/[USERNAME]/.bundle/change-serialized-space-[UNIQUE_NAME]/default/resources",
"serialized_space": "{\"version\":1,\"instructions\":{\"text_instructions\":[{\"id\":\"a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4\",\"content\":[\"Summarize sales revenue by month\"]}]}}\n",
"title": "test genie space",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ Resources: 0 created, 1 changed, 0 deleted, 0 unchanged
},
"body": {
"catalog_name": "main",
"comment": "",
"name": "test_secret_[UNIQUE_NAME]",
"schema_name": "default",
"value": "secret-value"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ Resources: 0 created, 1 changed, 0 deleted, 0 unchanged
},
"body": {
"catalog_name": "main",
"comment": "",
"expire_time": "[EXPIRE_TIME_AFTER]",
"name": "test_secret_[UNIQUE_NAME]",
"schema_name": "default",
Expand Down
8 changes: 7 additions & 1 deletion bundle/direct/dresources/genie_space.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (
"errors"
"fmt"
"net/http"
"slices"
"strings"

"github.com/databricks/cli/bundle/config/resources"
Expand Down Expand Up @@ -177,6 +178,11 @@ func (r *ResourceGenieSpace) DoCreate(ctx context.Context, config *resources.Gen
return createResp.SpaceId, responseToGenieSpaceConfig(createResp, serializedSpace), nil
}

// description is force-sent on update so that clearing it in config clears it on the space.
// Without this the omitempty field is dropped from the request and the old value drifts back on
// every plan. Verified against a real workspace: UpdateSpace accepts {"description": ""} as a clear.
var genieSpaceForceSend = []string{"Description"}

func (r *ResourceGenieSpace) DoUpdate(ctx context.Context, id string, config *resources.GenieSpaceConfig, _ *PlanEntry) (*resources.GenieSpaceConfig, error) {
serializedSpace, err := prepareGenieSpaceRequest(config)
if err != nil {
Expand All @@ -198,7 +204,7 @@ func (r *ResourceGenieSpace) DoUpdate(ctx context.Context, id string, config *re
SerializedSpace: serializedSpace,
Etag: "",

ForceSendFields: utils.FilterFields[dashboards.GenieUpdateSpaceRequest](config.ForceSendFields),
ForceSendFields: utils.FilterFields[dashboards.GenieUpdateSpaceRequest](append(slices.Clone(genieSpaceForceSend), config.ForceSendFields...)),
})
if err != nil {
return nil, err
Expand Down
6 changes: 4 additions & 2 deletions bundle/direct/dresources/genie_space_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -122,8 +122,9 @@ func TestGenieSpaceDoUpdateRoundTripsEtag(t *testing.T) {
// the response is persisted, for drift detection on the next plan.
m.GetMockGenieAPI().EXPECT().
UpdateSpace(ctx, dashboards.GenieUpdateSpaceRequest{
SpaceId: "space-id",
Title: "new",
SpaceId: "space-id",
Title: "new",
ForceSendFields: []string{"Description"},
}).
Return(&dashboards.GenieSpace{
SpaceId: "space-id",
Expand Down Expand Up @@ -159,6 +160,7 @@ func TestGenieSpaceDoUpdateAlwaysSendsSerializedSpace(t *testing.T) {
SpaceId: "space-id",
Title: "new",
SerializedSpace: "{\"converge\":\"me\"}",
ForceSendFields: []string{"Description"},
}).
Return(&dashboards.GenieSpace{
SpaceId: "space-id",
Expand Down
10 changes: 9 additions & 1 deletion bundle/direct/dresources/secret.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package dresources

import (
"context"
"slices"

"github.com/databricks/cli/bundle/config/resources"
"github.com/databricks/cli/bundle/deployplan"
Expand Down Expand Up @@ -86,11 +87,18 @@ func (r *ResourceSecret) DoCreate(ctx context.Context, state *catalog.Secret) (s
return response.FullName, response, nil
}

// comment is force-sent on update so that clearing it in config clears it on the secret. The
// update_mask is "*" and the backend merges, so an omitempty comment would be dropped from the
// body and the old value would drift back. Verified against a real workspace: {"comment": ""} clears it.
var secretForceSend = []string{"Comment"}

// DoUpdate updates the secret in place and returns remote state.
func (r *ResourceSecret) DoUpdate(ctx context.Context, id string, state *catalog.Secret, _ *PlanEntry) (*catalog.Secret, error) {
secret := *state
secret.ForceSendFields = utils.FilterFields[catalog.Secret](append(slices.Clone(secretForceSend), state.ForceSendFields...))
response, err := r.client.SecretsUc.UpdateSecret(ctx, catalog.UpdateSecretRequest{
FullName: id,
Secret: *state,
Secret: secret,
UpdateMask: fieldmask.FieldMask{
Paths: []string{"*"},
},
Expand Down
6 changes: 5 additions & 1 deletion libs/testserver/genie_spaces.go
Original file line number Diff line number Diff line change
Expand Up @@ -159,6 +159,10 @@ func (s *FakeWorkspace) GenieSpaceUpdate(req Request) Response {
}
}

// Which fields the caller actually sent, so an explicit "description": "" (the CLI
// force-sends it) clears the value instead of being treated as "not provided".
fields, _ := parseUpdateFields(req.Body)

// Optimistic concurrency: if the caller sent an etag, it must match the
// current one. Empty etag means apply unconditionally.
if updateReq.Etag != "" && updateReq.Etag != genieSpace.Etag {
Expand All @@ -174,7 +178,7 @@ func (s *FakeWorkspace) GenieSpaceUpdate(req Request) Response {
if updateReq.Title != "" {
genieSpace.Title = updateReq.Title
}
if updateReq.Description != "" {
if _, ok := fields["description"]; ok {
genieSpace.Description = updateReq.Description
}
if updateReq.WarehouseId != "" {
Expand Down
6 changes: 5 additions & 1 deletion libs/testserver/uc_secrets.go
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,10 @@ func (s *FakeWorkspace) SecretsUcUpdateSecret(req Request) Response {
}
}

// Which fields the caller actually sent, so an explicit "comment": "" (the CLI force-sends it
// under update_mask=*) clears the value instead of being treated as "not provided".
fields, _ := parseUpdateFields(req.Body)

secret, exists := s.UCSecrets[fullName]
if !exists {
return Response{
Expand All @@ -148,7 +152,7 @@ func (s *FakeWorkspace) SecretsUcUpdateSecret(req Request) Response {
if updateSecret.Value != "" {
secret.Value = updateSecret.Value
}
if updateSecret.Comment != "" {
if _, ok := fields["comment"]; ok {
secret.Comment = updateSecret.Comment
}
if updateSecret.Owner != "" {
Expand Down
Loading