Skip to content
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

Add account-level API support for Unity Catalog objects #2182

Merged
merged 73 commits into from Aug 24, 2023
Merged
Show file tree
Hide file tree
Changes from 69 commits
Commits
Show all changes
73 commits
Select commit Hold shift + click to select a range
13aa1b0
first draft
nkvuong Mar 31, 2023
06f498c
account client check
nkvuong Mar 31, 2023
b7bd999
account client check
nkvuong Mar 31, 2023
1088718
Fixed `databricks_service_principals` data source issue with empty fi…
nkvuong Apr 3, 2023
1eb68b8
Allow rotating `token` block in `databricks_mws_workspaces` resource …
shreyas-goenka Apr 3, 2023
9817b17
Excludes roles in scim API list calls to reduce load on databricks sc…
shreyas-goenka Apr 3, 2023
ea0bff1
Update SDK to v0.6.0 (#2186)
nfx Apr 3, 2023
faf6ef6
update sdk to 0.7.0
nkvuong Apr 5, 2023
fa7b49e
Merge branch 'master' into feature/uc-account-api
nkvuong Apr 5, 2023
66082bd
add integration tests
nkvuong Apr 6, 2023
d2493ac
fix acceptance tests
nkvuong Apr 6, 2023
27ae351
Merge branch 'master' into feature/uc-account-api
nkvuong Jul 4, 2023
1c17c94
fix tests
nkvuong Jul 4, 2023
b7373c9
add account-level API support for `metastore_data_access`
nkvuong Jul 4, 2023
fe62ae9
add account API support for `databricks_storage_credential`
nkvuong Jul 5, 2023
cf35d8c
Merge branch 'master' into feature/uc-account-api
nkvuong Jul 5, 2023
246cc10
address feedback
nkvuong Jul 5, 2023
d5d69e6
refactor to `WorkspaceOrAccountRequest`
nkvuong Jul 5, 2023
ccc1ed9
Merge branch 'master' into feature/uc-account-api
nkvuong Jul 7, 2023
89414e2
fix acceptance tests
nkvuong Jul 7, 2023
8a8919f
Release v1.21.0 (#2471)
mgyucht Jul 7, 2023
931068d
Install mlflow cluster using in model serving test if the cluster is…
shreyas-goenka Jul 10, 2023
2a6b5ea
Bump golang.org/x/mod from 0.11.0 to 0.12.0 (#2462)
dependabot[bot] Jul 10, 2023
c90f32c
Exporter: make resource names more unique to avoid duplicate resource…
alexott Jul 10, 2023
d41bf7a
Add documentation notes about legacy cluster type & data access (#2437)
alexott Jul 10, 2023
c2e9ba1
Use random catalog name in SQL table integration tests (#2473)
pietern Jul 10, 2023
bd20876
Link model serving docs to top level README (#2474)
shreyas-goenka Jul 10, 2023
80bb97a
Add one more item to the troubleshooting guide (#2477)
alexott Jul 14, 2023
413af63
Added `databricks_access_control_rule_set` resource for managing acco…
gauthamsunjay Jul 17, 2023
ad2ec68
Added `acl_principal_id` attribute to `databricks_user`, `databricks_…
alexott Jul 19, 2023
d7c9abc
Added support for Unity Catalog `databricks_metastores` data source …
guillesd Jul 19, 2023
42511f2
Added support for Unity Catalog `databricks_metastore` data source (#…
tanmay-db Jul 20, 2023
b3e5783
Supported new Delve binary name format (#2497)
mgyucht Jul 20, 2023
08da329
Add code owners for Terraform (#2498)
mgyucht Jul 20, 2023
3d0bd59
Removed unused dlvLoadConfig configuration from settings.json (#2499)
mgyucht Jul 20, 2023
e85ecb4
Fix provider after updating SDK to 0.13 (#2494)
fjakobs Jul 21, 2023
6ad968a
Added `control_run_state` flag to the `databricks_job` resource for c…
mgyucht Jul 21, 2023
59bd6c7
Added exporter for `databricks_workspace_file` resource (#2493)
alexott Jul 21, 2023
0273661
Supported boolean values in `databricks_sql_alert` alerts (#2506)
fjakobs Jul 24, 2023
6d6df52
Added more common issues for troubleshooting (#2486)
nkvuong Jul 24, 2023
848e295
Fixed handling of comments in `databricks_sql_table` resource (#2472)
karolusz Jul 24, 2023
8f8fdfe
Added clarification that `databricks_schema` and `databricks_sql_tabl…
alexott Jul 24, 2023
76773a3
Updated `databricks_user` with `force = true` to check for error mess…
alexott Jul 25, 2023
e4e4125
fix force delete
nkvuong Jul 26, 2023
f52ff5f
remove orphaned code
nkvuong Jul 26, 2023
c8b690c
Merge branch 'master' into feature/uc-account-api
nkvuong Jul 26, 2023
4a605d2
fix acceptance tests
nkvuong Jul 26, 2023
ce19fc5
upgrade go sdk
nkvuong Jul 26, 2023
76687a0
fix metastoreinfo struct
nkvuong Jul 26, 2023
e74553d
docs update
nkvuong Jul 26, 2023
69767d3
fix acceptance tests
nkvuong Jul 27, 2023
a6b0f52
fix tests
nkvuong Jul 27, 2023
3af0d61
updated docs
nkvuong Jul 27, 2023
77a00aa
fix tests
nkvuong Jul 27, 2023
97bbb94
rename test
nkvuong Jul 27, 2023
4ecb890
Merge branch 'master' into feature/uc-account-api
nkvuong Jul 31, 2023
6710c03
update tests
nkvuong Jul 31, 2023
ac5d350
fix tests
nkvuong Jul 31, 2023
8e9e737
fix test
nkvuong Jul 31, 2023
e5550d9
add state upgrader
nkvuong Aug 8, 2023
edf484c
Merge branch 'master' into feature/uc-account-api
nkvuong Aug 14, 2023
771589e
fix struct
nkvuong Aug 14, 2023
c515b6d
fix tests
nkvuong Aug 14, 2023
34d72c5
feedback
nkvuong Aug 23, 2023
7f1d324
feedback
nkvuong Aug 23, 2023
2cfb18a
fix acc test
nkvuong Aug 23, 2023
4b90b8a
fix test
nkvuong Aug 23, 2023
894ff92
fix test
nkvuong Aug 23, 2023
bcb7c87
fix test
nkvuong Aug 23, 2023
7434f21
feedback
nkvuong Aug 24, 2023
af89e52
fix acc tests
nkvuong Aug 24, 2023
4a4fbf5
Merge branch 'master' into feature/uc-account-api
nkvuong Aug 24, 2023
5b28e6e
feedback
nkvuong Aug 24, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
159 changes: 91 additions & 68 deletions catalog/resource_metastore.go
Expand Up @@ -5,69 +5,35 @@ import (
"log"
"strings"

"github.com/databricks/databricks-sdk-go"
"github.com/databricks/databricks-sdk-go/service/catalog"
"github.com/databricks/terraform-provider-databricks/common"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation"
)

type MetastoresAPI struct {
client *common.DatabricksClient
context context.Context
}

func NewMetastoresAPI(ctx context.Context, m any) MetastoresAPI {
return MetastoresAPI{m.(*common.DatabricksClient), context.WithValue(ctx, common.Api, common.API_2_1)}
}

type MetastoreInfo struct {
Name string `json:"name"`
StorageRoot string `json:"storage_root" tf:"force_new"`
DefaultDacID string `json:"default_data_access_config_id,omitempty" tf:"suppress_diff"`
Owner string `json:"owner,omitempty" tf:"computed"`
MetastoreID string `json:"metastore_id,omitempty" tf:"computed"`
WorkspaceIDs []int64 `json:"workspace_ids,omitempty" tf:"computed"`
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This field is not in the new structure?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we could keep this for compatibility, but it does not exist in the SDK anymore

Region string `json:"region,omitempty" tf:"computed"`
Cloud string `json:"cloud,omitempty" tf:"computed"`
GlobalMetastoreId string `json:"global_metastore_id,omitempty" tf:"computed"`
CreatedAt int64 `json:"created_at,omitempty" tf:"computed"`
CreatedBy string `json:"created_by,omitempty" tf:"computed"`
UpdatedAt int64 `json:"updated_at,omitempty" tf:"computed"`
UpdatedBy string `json:"updated_by,omitempty" tf:"computed"`
DeltaSharingScope string `json:"delta_sharing_scope,omitempty" tf:"suppress_diff"`
DeltaSharingRecipientTokenLifetimeInSeconds int64 `json:"delta_sharing_recipient_token_lifetime_in_seconds,omitempty"`
DeltaSharingOrganizationName string `json:"delta_sharing_organization_name,omitempty"`
}

type CreateMetastore struct {
Name string `json:"name"`
StorageRoot string `json:"storage_root"`
}

func (a MetastoresAPI) createMetastore(cm CreateMetastore) (mi MetastoreInfo, err error) {
err = a.client.Post(a.context, "/unity-catalog/metastores", cm, &mi)
return
}

func (a MetastoresAPI) getMetastore(id string) (mi MetastoreInfo, err error) {
err = a.client.Get(a.context, "/unity-catalog/metastores/"+id, nil, &mi)
return
}

func (a MetastoresAPI) updateMetastore(metastoreID string, update map[string]any) error {
return a.client.Patch(a.context, "/unity-catalog/metastores/"+metastoreID, update)
}

func (a MetastoresAPI) deleteMetastore(id string, force bool) error {
return a.client.Delete(a.context, "/unity-catalog/metastores/"+id, map[string]any{
"force": force,
})
Name string `json:"name"`
StorageRoot string `json:"storage_root" tf:"force_new"`
DefaultDacID string `json:"default_data_access_config_id,omitempty" tf:"suppress_diff"`
StorageRootCredentialId string `json:"storage_root_credential_id,omitempty" tf:"suppress_diff"`
Owner string `json:"owner,omitempty" tf:"computed"`
MetastoreID string `json:"metastore_id,omitempty" tf:"computed"`
Region string `json:"region,omitempty" tf:"computed"`
Cloud string `json:"cloud,omitempty" tf:"computed"`
GlobalMetastoreId string `json:"global_metastore_id,omitempty" tf:"computed"`
CreatedAt int64 `json:"created_at,omitempty" tf:"computed"`
CreatedBy string `json:"created_by,omitempty" tf:"computed"`
UpdatedAt int64 `json:"updated_at,omitempty" tf:"computed"`
UpdatedBy string `json:"updated_by,omitempty" tf:"computed"`
DeltaSharingScope string `json:"delta_sharing_scope,omitempty" tf:"suppress_diff"`
DeltaSharingRecipientTokenLifetimeInSeconds int64 `json:"delta_sharing_recipient_token_lifetime_in_seconds,omitempty"`
DeltaSharingOrganizationName string `json:"delta_sharing_organization_name,omitempty"`
}

func ResourceMetastore() *schema.Resource {
s := common.StructToSchema(MetastoreInfo{},
func(m map[string]*schema.Schema) map[string]*schema.Schema {
delete(m, "metastore_id")
delete(m, "workspace_ids") // todo: bring it back when it works
m["force_destroy"] = &schema.Schema{
Type: schema.TypeBool,
Optional: true,
Expand All @@ -84,32 +50,89 @@ func ResourceMetastore() *schema.Resource {
}
return m
})
update := updateFunctionFactory("/unity-catalog/metastores", []string{"owner", "name", "delta_sharing_scope",
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This seems to have a lot of logic in it. Is this more or less obsolete when using DataToStructPointer and the Go SDK?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this update logic was used to determine what fields could be sent for create vs. upload. In the SDK, this is defined within the different structs

"delta_sharing_recipient_token_lifetime_in_seconds", "delta_sharing_organization_name"})

return common.Resource{
Schema: s,
Create: func(ctx context.Context, d *schema.ResourceData, c *common.DatabricksClient) error {
var create CreateMetastore
var create catalog.CreateMetastore
var update catalog.UpdateMetastore
common.DataToStructPointer(d, s, &create)
mi, err := NewMetastoresAPI(ctx, c).createMetastore(create)
if err != nil {
return err
}
d.SetId(mi.MetastoreID)
return update(ctx, d, c)
common.DataToStructPointer(d, s, &update)
return c.WorkspaceOrAccountRequest(func(acc *databricks.AccountClient) error {
mi, err := acc.Metastores.Create(ctx,
catalog.AccountsCreateMetastore{
MetastoreInfo: &create,
})
if err != nil {
return err
}
d.SetId(mi.MetastoreInfo.MetastoreId)
_, err = acc.Metastores.Update(ctx, catalog.AccountsUpdateMetastore{
MetastoreId: mi.MetastoreInfo.MetastoreId,
MetastoreInfo: &update,
})
if err != nil {
return err
}
return nil
}, func(w *databricks.WorkspaceClient) error {
mi, err := w.Metastores.Create(ctx, create)
if err != nil {
return err
}
d.SetId(mi.MetastoreId)
update.Id = mi.MetastoreId
_, err = w.Metastores.Update(ctx, update)
if err != nil {
return err
}
return nil
})
},
Read: func(ctx context.Context, d *schema.ResourceData, c *common.DatabricksClient) error {
mi, err := NewMetastoresAPI(ctx, c).getMetastore(d.Id())
if err != nil {
return err
}
return common.StructToData(mi, s, d)
return c.WorkspaceOrAccountRequest(func(acc *databricks.AccountClient) error {
mi, err := acc.Metastores.GetByMetastoreId(ctx, d.Id())
if err != nil {
return err
}
return common.StructToData(mi, s, d)
}, func(w *databricks.WorkspaceClient) error {
mi, err := w.Metastores.GetById(ctx, d.Id())
if err != nil {
return err
}
return common.StructToData(mi, s, d)
})
},
Update: func(ctx context.Context, d *schema.ResourceData, c *common.DatabricksClient) error {
var update catalog.UpdateMetastore
common.DataToStructPointer(d, s, &update)

return c.WorkspaceOrAccountRequest(func(acc *databricks.AccountClient) error {
_, err := acc.Metastores.Update(ctx, catalog.AccountsUpdateMetastore{
MetastoreId: d.Id(),
MetastoreInfo: &update,
})
if err != nil {
return err
}
return nil
}, func(w *databricks.WorkspaceClient) error {
update.Id = d.Id()
_, err := w.Metastores.Update(ctx, update)
if err != nil {
return err
}
return nil
})
},
Update: update,
Delete: func(ctx context.Context, d *schema.ResourceData, c *common.DatabricksClient) error {
force := d.Get("force_destroy").(bool)
return NewMetastoresAPI(ctx, c).deleteMetastore(d.Id(), force)
return c.WorkspaceOrAccountRequest(func(acc *databricks.AccountClient) error {
return acc.Metastores.Delete(ctx, catalog.DeleteAccountMetastoreRequest{Force: force, MetastoreId: d.Id()})
}, func(w *databricks.WorkspaceClient) error {
return w.Metastores.Delete(ctx, catalog.DeleteMetastoreRequest{Force: force, Id: d.Id()})
})
},
}.ToResource()
}
140 changes: 85 additions & 55 deletions catalog/resource_metastore_assignment.go
Expand Up @@ -2,53 +2,18 @@ package catalog

import (
"context"
"fmt"
"strconv"

"github.com/databricks/databricks-sdk-go"
"github.com/databricks/databricks-sdk-go/service/catalog"
"github.com/databricks/terraform-provider-databricks/common"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
)

type MetastoreAssignmentAPI struct {
client *common.DatabricksClient
context context.Context
}

func NewMetastoreAssignmentAPI(ctx context.Context, m any) MetastoreAssignmentAPI {
return MetastoreAssignmentAPI{m.(*common.DatabricksClient), context.WithValue(ctx, common.Api, common.API_2_1)}
}

type MetastoreAssignment struct {
WorkspaceID int64 `json:"workspace_id" tf:"force_new"`
MetastoreID string `json:"metastore_id"`
DefaultCatalogName string `json:"default_catalog_name,omitempty" tf:"default:hive_metastore"`
}

func (a MetastoreAssignmentAPI) createMetastoreAssignment(ma MetastoreAssignment) error {
path := fmt.Sprintf("/unity-catalog/workspaces/%d/metastore", ma.WorkspaceID)
return a.client.Put(a.context, path, ma)
}

func (a MetastoreAssignmentAPI) updateMetastoreAssignment(ma MetastoreAssignment) error {
path := fmt.Sprintf("/unity-catalog/workspaces/%d/metastore", ma.WorkspaceID)
return a.client.Patch(a.context, path, ma)
}

func (a MetastoreAssignmentAPI) getAssignedMetastoreID() (string, error) {
var mi MetastoreInfo
err := a.client.Get(a.context, "/unity-catalog/metastore_summary", nil, &mi)
return mi.MetastoreID, err
}

func (a MetastoreAssignmentAPI) deleteMetastoreAssignment(workspaceID, metastoreID string) error {
path := fmt.Sprintf("/unity-catalog/workspaces/%s/metastore", workspaceID)
return a.client.Delete(a.context, path, map[string]string{
"metastore_id": metastoreID,
})
}

func ResourceMetastoreAssignment() *schema.Resource {
s := common.StructToSchema(MetastoreAssignment{},
s := common.StructToSchema(catalog.MetastoreAssignment{},
func(m map[string]*schema.Schema) map[string]*schema.Schema {
m["default_catalog_name"].Default = "hive_metastore"
return m
})
pi := common.NewPairID("workspace_id", "metastore_id").Schema(
Expand All @@ -58,30 +23,95 @@ func ResourceMetastoreAssignment() *schema.Resource {
return common.Resource{
Schema: s,
Create: func(ctx context.Context, d *schema.ResourceData, c *common.DatabricksClient) error {
var ma MetastoreAssignment
common.DataToStructPointer(d, s, &ma)
if err := NewMetastoreAssignmentAPI(ctx, c).createMetastoreAssignment(ma); err != nil {
return err
}
pi.Pack(d)
return nil
workspaceId := int64(d.Get("workspace_id").(int))
metastoreId := d.Get("metastore_id").(string)
var create catalog.CreateMetastoreAssignment
common.DataToStructPointer(d, s, &create)
create.WorkspaceId = workspaceId

return c.WorkspaceOrAccountRequest(func(acc *databricks.AccountClient) error {
err := acc.MetastoreAssignments.Create(ctx,
catalog.AccountsCreateMetastoreAssignment{
WorkspaceId: workspaceId,
MetastoreId: metastoreId,
MetastoreAssignment: &create,
})
mgyucht marked this conversation as resolved.
Show resolved Hide resolved
if err != nil {
return err
}
pi.Pack(d)
return nil
}, func(w *databricks.WorkspaceClient) error {
err := w.Metastores.Assign(ctx, create)
if err != nil {
return err
}
pi.Pack(d)
return nil
})
},
Read: func(ctx context.Context, d *schema.ResourceData, c *common.DatabricksClient) error {
metastoreID, err := NewMetastoreAssignmentAPI(ctx, c).getAssignedMetastoreID()
d.Set("metastore_id", metastoreID)
return err
first, _, err := pi.Unpack(d)
if err != nil {
return err
}
workspaceId, err := strconv.ParseInt(first, 10, 64)
if err != nil {
return err
}

return c.WorkspaceOrAccountRequest(func(acc *databricks.AccountClient) error {
ma, err := acc.MetastoreAssignments.GetByWorkspaceId(ctx, workspaceId)
if err != nil {
return err
}
return common.StructToData(ma, s, d)
}, func(w *databricks.WorkspaceClient) error {
//this only works when managing the metastore assigned to the current workspace.
//plus we don't know the workspace we're logged into.
ma, err := w.Metastores.Current(ctx)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggestion: let's leave a comment here to say that this only works when managing the metastore assigned to the current workspace. It's definitely weird... Also it's very annoying that we don't know the workspace we're logged into.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

if err != nil {
return err
}
d.Set("metastore_id", ma.MetastoreId)
return nil
})
},
Update: func(ctx context.Context, d *schema.ResourceData, c *common.DatabricksClient) error {
var ma MetastoreAssignment
common.DataToStructPointer(d, s, &ma)
return NewMetastoreAssignmentAPI(ctx, c).updateMetastoreAssignment(ma)
workspaceId := int64(d.Get("workspace_id").(int))
metastoreId := d.Get("metastore_id").(string)
var update catalog.UpdateMetastoreAssignment
common.DataToStructPointer(d, s, &update)
update.WorkspaceId = workspaceId

return c.WorkspaceOrAccountRequest(func(acc *databricks.AccountClient) error {
return acc.MetastoreAssignments.Update(ctx,
catalog.AccountsUpdateMetastoreAssignment{
WorkspaceId: workspaceId,
MetastoreId: metastoreId,
MetastoreAssignment: &update,
})
}, func(w *databricks.WorkspaceClient) error {
return w.Metastores.UpdateAssignment(ctx, update)
})
},
Delete: func(ctx context.Context, d *schema.ResourceData, c *common.DatabricksClient) error {
workspaceID, metastoreID, err := pi.Unpack(d)
first, metastoreId, err := pi.Unpack(d)
if err != nil {
return err
}
workspaceId, err := strconv.ParseInt(first, 10, 64)
if err != nil {
return err
}
return NewMetastoreAssignmentAPI(ctx, c).deleteMetastoreAssignment(workspaceID, metastoreID)
return c.WorkspaceOrAccountRequest(func(acc *databricks.AccountClient) error {
return acc.MetastoreAssignments.DeleteByWorkspaceIdAndMetastoreId(ctx, workspaceId, metastoreId)
}, func(w *databricks.WorkspaceClient) error {
return w.Metastores.Unassign(ctx, catalog.UnassignRequest{
MetastoreId: metastoreId,
WorkspaceId: workspaceId,
})
})
},
}.ToResource()
}