Skip to content

Commit 2b97994

Browse files
authored
[Exporter] Correctly handle DB-managed UC objects (#4323)
## Changes <!-- Summary of your changes that are easy to understand --> Databricks has started to introduce UC objects that are created and managed by Databricks, i.e., storage credentials and external locations, so we can't create resources to manage them. This PR converts such managed UC objects into data sources so we can attach permissions to them and handle references. The change of ownership is still not implemented - filed #4321 to handle it. Other changes include: - refactoring of creation of new resource data objects - fixing a problem with the codegen of data sources, so we don't need a dedicated `Body` implementation. ## Tests <!-- How is this tested? Please see the checklist below and also describe any other relevant tests --> - [x] `make test` run locally - [ ] relevant change in `docs/` folder - [ ] covered with integration tests in `internal/acceptance` - [ ] using Go SDK - [ ] using TF Plugin Framework
1 parent f716018 commit 2b97994

File tree

4 files changed

+47
-27
lines changed

4 files changed

+47
-27
lines changed

exporter/codegen.go

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -662,8 +662,13 @@ func (ic *importContext) processSingleResource(resourcesChan resourceChannel,
662662
log.Printf("[ERROR] error calling ir.Body for %v: %s", r, err.Error())
663663
}
664664
} else {
665-
resourceBlock := body.AppendNewBlock("resource", []string{r.Resource, r.Name})
666-
err = ic.dataToHcl(ir, []string{}, ic.Resources[r.Resource], r, resourceBlock.Body())
665+
blockType := "resource"
666+
if r.Mode == "data" {
667+
blockType = r.Mode
668+
}
669+
resourceBlock := body.AppendNewBlock(blockType, []string{r.Resource, r.Name})
670+
err = ic.dataToHcl(ic.Importables[r.Resource],
671+
[]string{}, ic.Resources[r.Resource], r, resourceBlock.Body())
667672
if err != nil {
668673
log.Printf("[ERROR] error generating body for %v: %s", r, err.Error())
669674
}

exporter/importables.go

Lines changed: 29 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -89,7 +89,8 @@ var (
8989
"storage_credential": {`CREATE_EXTERNAL_LOCATION`, `CREATE_EXTERNAL_TABLE`},
9090
"foreign_connection": {`CREATE_FOREIGN_CATALOG`},
9191
}
92-
ParentDirectoryExtraKey = "parent_directory"
92+
ParentDirectoryExtraKey = "parent_directory"
93+
dbManagedExternalLocation = "__databricks_managed_storage_location"
9394
)
9495

9596
func generateMountBody(ic *importContext, body *hclwrite.Body, r *resource) error {
@@ -899,9 +900,8 @@ var resourcesMap map[string]importable = map[string]importable{
899900
{Path: "libraries.jar", Resource: "databricks_repo", Match: "workspace_path",
900901
MatchType: MatchPrefix, SearchValueTransformFunc: appendEndingSlashToDirName},
901902
},
902-
// TODO: special formatting required, where JSON is written line by line
903-
// so that we're able to do the references
904-
Body: resourceOrDataBlockBody,
903+
// TODO: implement a custom Body that will write with special formatting, where
904+
// JSON is written line by line so that we're able to do the references
905905
},
906906
"databricks_group": {
907907
Service: "groups",
@@ -1051,7 +1051,6 @@ var resourcesMap map[string]importable = map[string]importable{
10511051

10521052
return nil
10531053
},
1054-
Body: resourceOrDataBlockBody,
10551054
},
10561055
"databricks_group_member": {
10571056
Service: "groups",
@@ -2291,7 +2290,6 @@ var resourcesMap map[string]importable = map[string]importable{
22912290
}
22922291
return nil
22932292
},
2294-
Body: resourceOrDataBlockBody,
22952293
Depends: []reference{
22962294
{Path: "path", Resource: "databricks_user", Match: "home"},
22972295
{Path: "path", Resource: "databricks_service_principal", Match: "home"},
@@ -2964,6 +2962,14 @@ var resourcesMap map[string]importable = map[string]importable{
29642962
WorkspaceLevel: true,
29652963
Service: "uc-storage-credentials",
29662964
Import: func(ic *importContext, r *resource) error {
2965+
if r.ID == "__databricks_managed_storage_credential" {
2966+
// it's created by default and can't be imported
2967+
// TODO: add check for "securable_kind":"STORAGE_CREDENTIAL_DB_AWS_IAM" when we get it in the credential
2968+
r.Mode = "data"
2969+
data := tfcatalog.ResourceStorageCredential().ToResource().TestResourceData()
2970+
obj := tfcatalog.StorageCredentialInfo{Name: r.ID}
2971+
r.Data = ic.generateNewData(data, "databricks_storage_credential", r.ID, obj)
2972+
}
29672973
ic.emitUCGrantsWithOwner("storage_credential/"+r.ID, r)
29682974
if r.Data != nil {
29692975
isolationMode := r.Data.Get("isolation_mode").(string)
@@ -3036,6 +3042,14 @@ var resourcesMap map[string]importable = map[string]importable{
30363042
WorkspaceLevel: true,
30373043
Service: "uc-external-locations",
30383044
Import: func(ic *importContext, r *resource) error {
3045+
if r.ID == dbManagedExternalLocation {
3046+
// it's created by default and can't be imported
3047+
// TODO: add check for "securable_kind":"EXTERNAL_LOCATION_DB_STORAGE" when we get it in the credential
3048+
r.Mode = "data"
3049+
data := tfcatalog.ResourceExternalLocation().ToResource().TestResourceData()
3050+
obj := tfcatalog.ExternalLocationInfo{Name: r.ID}
3051+
r.Data = ic.generateNewData(data, "databricks_external_location", r.ID, obj)
3052+
}
30393053
ic.emitUCGrantsWithOwner("external_location/"+r.ID, r)
30403054
credentialName := r.Data.Get("credential_name").(string)
30413055
ic.Emit(&resource{
@@ -3067,7 +3081,15 @@ var resourcesMap map[string]importable = map[string]importable{
30673081
}
30683082
return nil
30693083
},
3070-
ShouldOmitField: shouldOmitWithIsolationMode,
3084+
ShouldOmitField: func(ic *importContext, pathString string, as *schema.Schema, d *schema.ResourceData) bool {
3085+
if (pathString == "url" || pathString == "credential_name") && d.Get("name").(string) == dbManagedExternalLocation {
3086+
return true
3087+
}
3088+
if pathString == "isolation_mode" {
3089+
return d.Get(pathString).(string) != "ISOLATION_MODE_ISOLATED"
3090+
}
3091+
return shouldOmitForUnityCatalog(ic, pathString, as, d)
3092+
},
30713093
// This external location is automatically created when metastore is created with the `storage_root`
30723094
Ignore: func(ic *importContext, r *resource) bool {
30733095
return r.ID == "metastore_default_location"

exporter/util.go

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,6 @@ import (
1919

2020
"github.com/databricks/databricks-sdk-go/service/catalog"
2121

22-
"github.com/hashicorp/hcl/v2/hclwrite"
2322
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
2423
"github.com/hashicorp/terraform-plugin-sdk/v2/terraform"
2524
)
@@ -310,7 +309,7 @@ func (ic *importContext) saveFileIn(dir, name string, content []byte) (string, e
310309
return relativeName, nil
311310
}
312311

313-
func defaultShouldOmitFieldFunc(ic *importContext, pathString string, as *schema.Schema, d *schema.ResourceData) bool {
312+
func defaultShouldOmitFieldFunc(_ *importContext, pathString string, as *schema.Schema, d *schema.ResourceData) bool {
314313
if as.Computed {
315314
return true
316315
} else if as.Default != nil && d.Get(pathString) == as.Default {
@@ -320,14 +319,16 @@ func defaultShouldOmitFieldFunc(ic *importContext, pathString string, as *schema
320319
return false
321320
}
322321

323-
func resourceOrDataBlockBody(ic *importContext, body *hclwrite.Body, r *resource) error {
324-
blockType := "resource"
325-
if r.Mode == "data" {
326-
blockType = r.Mode
322+
func (ic *importContext) generateNewData(data *schema.ResourceData, resourceType, rID string, obj any) *schema.ResourceData {
323+
data.MarkNewResource()
324+
data.SetId(rID)
325+
scm := ic.Resources[resourceType].Schema
326+
err := common.StructToData(obj, scm, data)
327+
if err != nil {
328+
log.Printf("[ERROR] can't convert %s object to data: %v. obj=%v", resourceType, err, obj)
329+
return nil
327330
}
328-
resourceBlock := body.AppendNewBlock(blockType, []string{r.Resource, r.Name})
329-
return ic.dataToHcl(ic.Importables[r.Resource],
330-
[]string{}, ic.Resources[r.Resource], r, resourceBlock.Body())
331+
return data
331332
}
332333

333334
func generateUniqueID(v string) string {

exporter/util_workspace.go

Lines changed: 1 addition & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@ import (
99
"sync/atomic"
1010
"time"
1111

12-
"github.com/databricks/terraform-provider-databricks/common"
1312
"github.com/databricks/terraform-provider-databricks/workspace"
1413

1514
"golang.org/x/exp/slices"
@@ -246,14 +245,7 @@ func (ic *importContext) maybeEmitWorkspaceObject(resourceType, path string, obj
246245
data = workspace.ResourceDirectory().ToResource().TestResourceData()
247246
}
248247
if data != nil {
249-
scm := ic.Resources[resourceType].Schema
250-
data.MarkNewResource()
251-
data.SetId(path)
252-
err := common.StructToData(obj, scm, data)
253-
if err != nil {
254-
log.Printf("[ERROR] can't convert %s object to data: %v. obj=%v", resourceType, err, obj)
255-
data = nil
256-
}
248+
data = ic.generateNewData(data, resourceType, path, obj)
257249
}
258250
}
259251
ic.Emit(&resource{

0 commit comments

Comments
 (0)