Skip to content
Draft
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
7 changes: 6 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -96,9 +96,14 @@ test-slow-acc:

# Updates acceptance test output (local tests)
.PHONY: test-update
test-update:
test-update: test-update-golden
-go test ./acceptance -run '^TestAccept$$' -update -timeout=${LOCAL_TIMEOUT}

# Updates golden files test output (other than acceptance tests)
.PHONY: test-update-golden
test-update-golden:
./tools/update_golden.py

# Updates acceptance test output for template tests only
.PHONY: test-update-templates
test-update-templates:
Expand Down
49 changes: 13 additions & 36 deletions bundle/direct/dresources/catalog.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ import (
"context"

"github.com/databricks/cli/bundle/config/resources"
"github.com/databricks/cli/libs/utils"
"github.com/databricks/databricks-sdk-go"
"github.com/databricks/databricks-sdk-go/service/catalog"
)
Expand All @@ -21,18 +20,11 @@ func (*ResourceCatalog) PrepareState(input *resources.Catalog) *catalog.CreateCa
return &input.CreateCatalog
}

// catalogRemapCopy maps CatalogInfo (remote GET response) to CreateCatalog (local state).
var catalogRemapCopy = newCopy[catalog.CatalogInfo, catalog.CreateCatalog]()

func (*ResourceCatalog) RemapState(info *catalog.CatalogInfo) *catalog.CreateCatalog {
return &catalog.CreateCatalog{
Comment: info.Comment,
ConnectionName: info.ConnectionName,
Name: info.Name,
Options: info.Options,
Properties: info.Properties,
ProviderName: info.ProviderName,
ShareName: info.ShareName,
StorageRoot: info.StorageRoot,
ForceSendFields: utils.FilterFields[catalog.CreateCatalog](info.ForceSendFields),
}
return catalogRemapCopy.Do(info)
}

func (r *ResourceCatalog) DoRead(ctx context.Context, id string) (*catalog.CatalogInfo, error) {
Expand All @@ -47,21 +39,15 @@ func (r *ResourceCatalog) DoCreate(ctx context.Context, config *catalog.CreateCa
return response.Name, response, nil
}

// catalogUpdateCopy maps CreateCatalog (local state) to UpdateCatalog (API request).
var catalogUpdateCopy = newCopy[catalog.CreateCatalog, catalog.UpdateCatalog]()

// DoUpdate updates the catalog in place and returns remote state.
func (r *ResourceCatalog) DoUpdate(ctx context.Context, id string, config *catalog.CreateCatalog, _ Changes) (*catalog.CatalogInfo, error) {
updateRequest := catalog.UpdateCatalog{
Comment: config.Comment,
EnablePredictiveOptimization: "", // Not supported by DABs
IsolationMode: "", // Not supported by DABs
Name: id,
NewName: "", // Only set if name actually changes (see DoUpdateWithID)
Options: config.Options,
Owner: "", // Not supported by DABs
Properties: config.Properties,
ForceSendFields: utils.FilterFields[catalog.UpdateCatalog](config.ForceSendFields, "EnablePredictiveOptimization", "IsolationMode", "Owner"),
}
updateRequest := catalogUpdateCopy.Do(config)
updateRequest.Name = id

response, err := r.client.Catalogs.Update(ctx, updateRequest)
response, err := r.client.Catalogs.Update(ctx, *updateRequest)
if err != nil {
return nil, err
}
Expand All @@ -71,23 +57,14 @@ func (r *ResourceCatalog) DoUpdate(ctx context.Context, id string, config *catal

// DoUpdateWithID updates the catalog and returns the new ID if the name changes.
func (r *ResourceCatalog) DoUpdateWithID(ctx context.Context, id string, config *catalog.CreateCatalog) (string, *catalog.CatalogInfo, error) {
updateRequest := catalog.UpdateCatalog{
Comment: config.Comment,
EnablePredictiveOptimization: "", // Not supported by DABs
IsolationMode: "", // Not supported by DABs
Name: id,
NewName: "", // Initialized below if needed
Options: config.Options,
Owner: "", // Not supported by DABs
Properties: config.Properties,
ForceSendFields: utils.FilterFields[catalog.UpdateCatalog](config.ForceSendFields, "EnablePredictiveOptimization", "IsolationMode", "Owner"),
}
updateRequest := catalogUpdateCopy.Do(config)
updateRequest.Name = id

if config.Name != id {
updateRequest.NewName = config.Name
}

response, err := r.client.Catalogs.Update(ctx, updateRequest)
response, err := r.client.Catalogs.Update(ctx, *updateRequest)
if err != nil {
return "", nil, err
}
Expand Down
155 changes: 22 additions & 133 deletions bundle/direct/dresources/cluster.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,44 +30,11 @@ func (r *ResourceCluster) PrepareState(input *resources.Cluster) *compute.Cluste
return &input.ClusterSpec
}

// clusterRemapCopy maps ClusterDetails (remote GET response) to ClusterSpec (local state).
var clusterRemapCopy = newCopy[compute.ClusterDetails, compute.ClusterSpec]()

func (r *ResourceCluster) RemapState(input *compute.ClusterDetails) *compute.ClusterSpec {
spec := &compute.ClusterSpec{
ApplyPolicyDefaultValues: false,
Autoscale: input.Autoscale,
AutoterminationMinutes: input.AutoterminationMinutes,
AwsAttributes: input.AwsAttributes,
AzureAttributes: input.AzureAttributes,
ClusterLogConf: input.ClusterLogConf,
ClusterName: input.ClusterName,
CustomTags: input.CustomTags,
DataSecurityMode: input.DataSecurityMode,
DockerImage: input.DockerImage,
DriverInstancePoolId: input.DriverInstancePoolId,
DriverNodeTypeId: input.DriverNodeTypeId,
DriverNodeTypeFlexibility: input.DriverNodeTypeFlexibility,
EnableElasticDisk: input.EnableElasticDisk,
EnableLocalDiskEncryption: input.EnableLocalDiskEncryption,
GcpAttributes: input.GcpAttributes,
InitScripts: input.InitScripts,
InstancePoolId: input.InstancePoolId,
IsSingleNode: input.IsSingleNode,
Kind: input.Kind,
NodeTypeId: input.NodeTypeId,
NumWorkers: input.NumWorkers,
PolicyId: input.PolicyId,
RemoteDiskThroughput: input.RemoteDiskThroughput,
RuntimeEngine: input.RuntimeEngine,
SingleUserName: input.SingleUserName,
SparkConf: input.SparkConf,
SparkEnvVars: input.SparkEnvVars,
SparkVersion: input.SparkVersion,
SshPublicKeys: input.SshPublicKeys,
TotalInitialRemoteDiskSize: input.TotalInitialRemoteDiskSize,
UseMlRuntime: input.UseMlRuntime,
WorkloadType: input.WorkloadType,
WorkerNodeTypeFlexibility: input.WorkerNodeTypeFlexibility,
ForceSendFields: utils.FilterFields[compute.ClusterSpec](input.ForceSendFields),
}
spec := clusterRemapCopy.Do(input)
if input.Spec != nil {
spec.ApplyPolicyDefaultValues = input.Spec.ApplyPolicyDefaultValues
}
Expand All @@ -78,20 +45,32 @@ func (r *ResourceCluster) DoRead(ctx context.Context, id string) (*compute.Clust
return r.client.Clusters.GetByClusterId(ctx, id)
}

// clusterCreateCopy maps ClusterSpec (local state) to CreateCluster (API request).
var clusterCreateCopy = newCopy[compute.ClusterSpec, compute.CreateCluster]()

func (r *ResourceCluster) DoCreate(ctx context.Context, config *compute.ClusterSpec) (string, *compute.ClusterDetails, error) {
wait, err := r.client.Clusters.Create(ctx, makeCreateCluster(config))
create := clusterCreateCopy.Do(config)
forceNumWorkers(config, &create.ForceSendFields)
wait, err := r.client.Clusters.Create(ctx, *create)
if err != nil {
return "", nil, err
}
return wait.ClusterId, nil, nil
}

// clusterEditCopy maps ClusterSpec (local state) to EditCluster (API request).
var clusterEditCopy = newCopy[compute.ClusterSpec, compute.EditCluster]()

func (r *ResourceCluster) DoUpdate(ctx context.Context, id string, config *compute.ClusterSpec, _ Changes) (*compute.ClusterDetails, error) {
edit := clusterEditCopy.Do(config)
edit.ClusterId = id
forceNumWorkers(config, &edit.ForceSendFields)

// Same retry as in TF provider logic
// https://github.com/databricks/terraform-provider-databricks/blob/3eecd0f90cf99d7777e79a3d03c41f9b2aafb004/clusters/resource_cluster.go#L624
timeout := 15 * time.Minute
_, err := retries.Poll(ctx, timeout, func() (*compute.WaitGetClusterRunning[struct{}], *retries.Err) {
wait, err := r.client.Clusters.Edit(ctx, makeEditCluster(id, config))
wait, err := r.client.Clusters.Edit(ctx, *edit)
if err == nil {
return wait, nil
}
Expand Down Expand Up @@ -151,100 +130,10 @@ func (r *ResourceCluster) OverrideChangeDesc(ctx context.Context, p *structpath.
return nil
}

func makeCreateCluster(config *compute.ClusterSpec) compute.CreateCluster {
create := compute.CreateCluster{
ApplyPolicyDefaultValues: config.ApplyPolicyDefaultValues,
Autoscale: config.Autoscale,
AutoterminationMinutes: config.AutoterminationMinutes,
AwsAttributes: config.AwsAttributes,
AzureAttributes: config.AzureAttributes,
ClusterLogConf: config.ClusterLogConf,
ClusterName: config.ClusterName,
CloneFrom: nil, // Not supported by DABs
CustomTags: config.CustomTags,
DataSecurityMode: config.DataSecurityMode,
DockerImage: config.DockerImage,
DriverInstancePoolId: config.DriverInstancePoolId,
DriverNodeTypeId: config.DriverNodeTypeId,
DriverNodeTypeFlexibility: config.DriverNodeTypeFlexibility,
EnableElasticDisk: config.EnableElasticDisk,
EnableLocalDiskEncryption: config.EnableLocalDiskEncryption,
GcpAttributes: config.GcpAttributes,
InitScripts: config.InitScripts,
InstancePoolId: config.InstancePoolId,
IsSingleNode: config.IsSingleNode,
Kind: config.Kind,
NodeTypeId: config.NodeTypeId,
NumWorkers: config.NumWorkers,
PolicyId: config.PolicyId,
RemoteDiskThroughput: config.RemoteDiskThroughput,
RuntimeEngine: config.RuntimeEngine,
SingleUserName: config.SingleUserName,
SparkConf: config.SparkConf,
SparkEnvVars: config.SparkEnvVars,
SparkVersion: config.SparkVersion,
SshPublicKeys: config.SshPublicKeys,
TotalInitialRemoteDiskSize: config.TotalInitialRemoteDiskSize,
UseMlRuntime: config.UseMlRuntime,
WorkloadType: config.WorkloadType,
WorkerNodeTypeFlexibility: config.WorkerNodeTypeFlexibility,
ForceSendFields: utils.FilterFields[compute.CreateCluster](config.ForceSendFields),
}

// If autoscale is not set, we need to send NumWorkers because one of them is required.
// If NumWorkers is not nil, we don't need to set it to ForceSendFields as it will be sent anyway.
// forceNumWorkers ensures NumWorkers is sent when Autoscale is not set,
// because the API requires one of them.
func forceNumWorkers(config *compute.ClusterSpec, fsf *[]string) {
if config.Autoscale == nil && config.NumWorkers == 0 {
create.ForceSendFields = append(create.ForceSendFields, "NumWorkers")
*fsf = append(*fsf, "NumWorkers")
}

return create
}

func makeEditCluster(id string, config *compute.ClusterSpec) compute.EditCluster {
edit := compute.EditCluster{
ClusterId: id,
ApplyPolicyDefaultValues: config.ApplyPolicyDefaultValues,
Autoscale: config.Autoscale,
AutoterminationMinutes: config.AutoterminationMinutes,
AwsAttributes: config.AwsAttributes,
AzureAttributes: config.AzureAttributes,
ClusterLogConf: config.ClusterLogConf,
ClusterName: config.ClusterName,
CustomTags: config.CustomTags,
DataSecurityMode: config.DataSecurityMode,
DockerImage: config.DockerImage,
DriverInstancePoolId: config.DriverInstancePoolId,
DriverNodeTypeId: config.DriverNodeTypeId,
DriverNodeTypeFlexibility: config.DriverNodeTypeFlexibility,
EnableElasticDisk: config.EnableElasticDisk,
EnableLocalDiskEncryption: config.EnableLocalDiskEncryption,
GcpAttributes: config.GcpAttributes,
InitScripts: config.InitScripts,
InstancePoolId: config.InstancePoolId,
IsSingleNode: config.IsSingleNode,
Kind: config.Kind,
NodeTypeId: config.NodeTypeId,
NumWorkers: config.NumWorkers,
PolicyId: config.PolicyId,
RemoteDiskThroughput: config.RemoteDiskThroughput,
RuntimeEngine: config.RuntimeEngine,
SingleUserName: config.SingleUserName,
SparkConf: config.SparkConf,
SparkEnvVars: config.SparkEnvVars,
SparkVersion: config.SparkVersion,
SshPublicKeys: config.SshPublicKeys,
TotalInitialRemoteDiskSize: config.TotalInitialRemoteDiskSize,
UseMlRuntime: config.UseMlRuntime,
WorkloadType: config.WorkloadType,
WorkerNodeTypeFlexibility: config.WorkerNodeTypeFlexibility,
ForceSendFields: utils.FilterFields[compute.EditCluster](config.ForceSendFields),
}

// If autoscale is not set, we need to send NumWorkers because one of them is required.
// If NumWorkers is not nil, we don't need to set it to ForceSendFields as it will be sent anyway.
if config.Autoscale == nil && config.NumWorkers == 0 {
edit.ForceSendFields = append(edit.ForceSendFields, "NumWorkers")
}

return edit
}
69 changes: 13 additions & 56 deletions bundle/direct/dresources/external_location.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ import (
"context"

"github.com/databricks/cli/bundle/config/resources"
"github.com/databricks/cli/libs/utils"
"github.com/databricks/databricks-sdk-go"
"github.com/databricks/databricks-sdk-go/service/catalog"
)
Expand All @@ -21,22 +20,11 @@ func (*ResourceExternalLocation) PrepareState(input *resources.ExternalLocation)
return &input.CreateExternalLocation
}

// externalLocationRemapCopy maps ExternalLocationInfo (remote GET response) to CreateExternalLocation (local state).
var externalLocationRemapCopy = newCopy[catalog.ExternalLocationInfo, catalog.CreateExternalLocation]()

func (*ResourceExternalLocation) RemapState(info *catalog.ExternalLocationInfo) *catalog.CreateExternalLocation {
return &catalog.CreateExternalLocation{
Comment: info.Comment,
CredentialName: info.CredentialName,
// Output-only field mirrored into state to avoid churn in remapped config.
EffectiveEnableFileEvents: info.EffectiveEnableFileEvents,
EnableFileEvents: info.EnableFileEvents,
EncryptionDetails: info.EncryptionDetails,
Fallback: info.Fallback,
FileEventQueue: info.FileEventQueue,
Name: info.Name,
ReadOnly: info.ReadOnly,
SkipValidation: false, // This is an input-only parameter, never returned by API
Url: info.Url,
ForceSendFields: utils.FilterFields[catalog.CreateExternalLocation](info.ForceSendFields),
}
return externalLocationRemapCopy.Do(info)
}

func (r *ResourceExternalLocation) DoRead(ctx context.Context, id string) (*catalog.ExternalLocationInfo, error) {
Expand All @@ -51,58 +39,27 @@ func (r *ResourceExternalLocation) DoCreate(ctx context.Context, config *catalog
return response.Name, response, nil
}

// externalLocationUpdateCopy maps CreateExternalLocation (local state) to UpdateExternalLocation (API request).
var externalLocationUpdateCopy = newCopy[catalog.CreateExternalLocation, catalog.UpdateExternalLocation]()

// DoUpdate updates the external location in place and returns remote state.
func (r *ResourceExternalLocation) DoUpdate(ctx context.Context, id string, config *catalog.CreateExternalLocation, _ Changes) (*catalog.ExternalLocationInfo, error) {
updateRequest := catalog.UpdateExternalLocation{
Comment: config.Comment,
CredentialName: config.CredentialName,
// Output-only field; never sent in update payload.
EffectiveEnableFileEvents: false,
EnableFileEvents: config.EnableFileEvents,
EncryptionDetails: config.EncryptionDetails,
Fallback: config.Fallback,
FileEventQueue: config.FileEventQueue,
Force: false,
IsolationMode: "", // Not supported by DABs
Name: id,
NewName: "", // Only set if name actually changes (see DoUpdateWithID)
Owner: "", // Not supported by DABs
ReadOnly: config.ReadOnly,
SkipValidation: config.SkipValidation,
Url: config.Url,
ForceSendFields: utils.FilterFields[catalog.UpdateExternalLocation](config.ForceSendFields, "IsolationMode", "Owner"),
}
updateRequest := externalLocationUpdateCopy.Do(config)
updateRequest.Name = id

return r.client.ExternalLocations.Update(ctx, updateRequest)
return r.client.ExternalLocations.Update(ctx, *updateRequest)
}

// DoUpdateWithID updates the external location and returns the new ID if the name changes.
func (r *ResourceExternalLocation) DoUpdateWithID(ctx context.Context, id string, config *catalog.CreateExternalLocation) (string, *catalog.ExternalLocationInfo, error) {
updateRequest := catalog.UpdateExternalLocation{
Comment: config.Comment,
CredentialName: config.CredentialName,
// Output-only field; never sent in update payload.
EffectiveEnableFileEvents: false,
EnableFileEvents: config.EnableFileEvents,
EncryptionDetails: config.EncryptionDetails,
Fallback: config.Fallback,
FileEventQueue: config.FileEventQueue,
Force: false,
IsolationMode: "", // Not supported by DABs
Name: id,
NewName: "", // Initialized below if needed
Owner: "", // Not supported by DABs
ReadOnly: config.ReadOnly,
SkipValidation: config.SkipValidation,
Url: config.Url,
ForceSendFields: utils.FilterFields[catalog.UpdateExternalLocation](config.ForceSendFields, "IsolationMode", "Owner"),
}
updateRequest := externalLocationUpdateCopy.Do(config)
updateRequest.Name = id

if config.Name != id {
updateRequest.NewName = config.Name
}

response, err := r.client.ExternalLocations.Update(ctx, updateRequest)
response, err := r.client.ExternalLocations.Update(ctx, *updateRequest)
if err != nil {
return "", nil, err
}
Expand Down
Loading
Loading