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

[Internal] Added databricks_quality_monitor resource and databricks_volumes data source to plugin framework #3958

Merged
merged 23 commits into from
Aug 30, 2024
Merged
Show file tree
Hide file tree
Changes from 8 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
17 changes: 17 additions & 0 deletions common/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import (
"github.com/databricks/databricks-sdk-go/config"
"github.com/databricks/databricks-sdk-go/service/iam"
"github.com/golang-jwt/jwt/v4"
"github.com/hashicorp/terraform-plugin-framework/diag"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
)

Expand Down Expand Up @@ -56,6 +57,14 @@ type DatabricksClient struct {
mu sync.Mutex
}

func (c *DatabricksClient) GetWorkspaceClient() (*databricks.WorkspaceClient, diag.Diagnostics) {
tanmay-db marked this conversation as resolved.
Show resolved Hide resolved
w, err := c.WorkspaceClient()
if err != nil {
return nil, diag.Diagnostics{diag.NewErrorDiagnostic("Failed to get workspace client", err.Error())}
}
return w, nil
}

func (c *DatabricksClient) WorkspaceClient() (*databricks.WorkspaceClient, error) {
c.mu.Lock()
defer c.mu.Unlock()
Expand Down Expand Up @@ -99,6 +108,14 @@ func (c *DatabricksClient) setAccountId(accountId string) error {
return nil
}

func (c *DatabricksClient) GetAccountClient() (*databricks.AccountClient, diag.Diagnostics) {
a, err := c.AccountClient()
if err != nil {
return nil, diag.Diagnostics{diag.NewErrorDiagnostic("Failed to get account client", err.Error())}
}
return a, nil
}

func (c *DatabricksClient) AccountClient() (*databricks.AccountClient, error) {
c.mu.Lock()
defer c.mu.Unlock()
Expand Down
33 changes: 33 additions & 0 deletions internal/providers/pluginfw/common/common.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
package common

import (
"fmt"

"github.com/databricks/terraform-provider-databricks/common"
"github.com/hashicorp/terraform-plugin-framework/datasource"
"github.com/hashicorp/terraform-plugin-framework/resource"
)

func ConfigureDataSource(req datasource.ConfigureRequest, resp *datasource.ConfigureResponse) *common.DatabricksClient {
tanmay-db marked this conversation as resolved.
Show resolved Hide resolved
client, ok := req.ProviderData.(*common.DatabricksClient)
if !ok {
resp.Diagnostics.AddError(
"Unexpected Data Source Configure Type",
fmt.Sprintf("Expected *common.DatabricksClient, got: %T. Please report this issue to the provider developers.", req.ProviderData),
)
return nil
}
return client
}

func ConfigureResource(req resource.ConfigureRequest, resp *resource.ConfigureResponse) *common.DatabricksClient {
client, ok := req.ProviderData.(*common.DatabricksClient)
if !ok {
resp.Diagnostics.AddError(
"Unexpected Resource Configure Type",
fmt.Sprintf("Expected *common.DatabricksClient, got: %T. Please report this issue to the provider developers.", req.ProviderData),
)
return nil
}
return client
}
10 changes: 8 additions & 2 deletions internal/providers/pluginfw/pluginfw.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ import (
"github.com/databricks/terraform-provider-databricks/commands"
"github.com/databricks/terraform-provider-databricks/common"
providercommon "github.com/databricks/terraform-provider-databricks/internal/providers/common"
"github.com/databricks/terraform-provider-databricks/internal/providers/pluginfw/resources/qualitymonitor"
"github.com/databricks/terraform-provider-databricks/internal/providers/pluginfw/resources/volume"

"github.com/hashicorp/terraform-plugin-framework/datasource"
"github.com/hashicorp/terraform-plugin-framework/diag"
Expand All @@ -38,11 +40,15 @@ type DatabricksProviderPluginFramework struct {
var _ provider.Provider = (*DatabricksProviderPluginFramework)(nil)

func (p *DatabricksProviderPluginFramework) Resources(ctx context.Context) []func() resource.Resource {
return []func() resource.Resource{}
return []func() resource.Resource{
qualitymonitor.ResourceQualityMonitor,
}
}

func (p *DatabricksProviderPluginFramework) DataSources(ctx context.Context) []func() datasource.DataSource {
return []func() datasource.DataSource{}
return []func() datasource.DataSource{
volume.DataSourceVolumes,
}
}

func (p *DatabricksProviderPluginFramework) Schema(ctx context.Context, req provider.SchemaRequest, resp *provider.SchemaResponse) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,214 @@
package qualitymonitor

import (
"context"
"fmt"
"time"

"github.com/databricks/databricks-sdk-go"
"github.com/databricks/databricks-sdk-go/service/catalog"
"github.com/databricks/terraform-provider-databricks/common"
pluginfwcommon "github.com/databricks/terraform-provider-databricks/internal/providers/pluginfw/common"
"github.com/databricks/terraform-provider-databricks/internal/providers/pluginfw/converters"
"github.com/databricks/terraform-provider-databricks/internal/providers/pluginfw/tfschema"
"github.com/databricks/terraform-provider-databricks/internal/service/catalog_tf"
"github.com/hashicorp/terraform-plugin-framework/diag"
"github.com/hashicorp/terraform-plugin-framework/path"
"github.com/hashicorp/terraform-plugin-framework/resource"
"github.com/hashicorp/terraform-plugin-framework/resource/schema"
"github.com/hashicorp/terraform-plugin-framework/types"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/retry"
tanmay-db marked this conversation as resolved.
Show resolved Hide resolved
)

const qualityMonitorDefaultProvisionTimeout = 15 * time.Minute

var _ resource.Resource = &QualityMonitorResource{}
tanmay-db marked this conversation as resolved.
Show resolved Hide resolved

func ResourceQualityMonitor() resource.Resource {
return &QualityMonitorResource{}
}

func waitForMonitor(ctx context.Context, w *databricks.WorkspaceClient, monitor *catalog.MonitorInfo) diag.Diagnostics {
tanmay-db marked this conversation as resolved.
Show resolved Hide resolved
monitorName := monitor.TableName
err := retry.RetryContext(ctx, qualityMonitorDefaultProvisionTimeout, func() *retry.RetryError {
monitor, err := w.QualityMonitors.GetByTableName(ctx, monitorName)
if err != nil {
return retry.NonRetryableError(err)
}

switch monitor.Status {
case catalog.MonitorInfoStatusMonitorStatusActive:
return nil
case catalog.MonitorInfoStatusMonitorStatusError, catalog.MonitorInfoStatusMonitorStatusFailed:
return retry.NonRetryableError(fmt.Errorf("monitor status retrund %s for monitor: %s", monitor.Status, monitorName))
tanmay-db marked this conversation as resolved.
Show resolved Hide resolved
}
return retry.RetryableError(fmt.Errorf("monitor %s is still pending", monitorName))
})
if err != nil {
return diag.Diagnostics{diag.NewErrorDiagnostic("Failed to get Monitor", err.Error())}
tanmay-db marked this conversation as resolved.
Show resolved Hide resolved
}
return nil
}

type MonitorInfoExtended struct {
catalog_tf.MonitorInfo
WarehouseId types.String `tfsdk:"warehouse_id" tf:"optional"`
SkipBuiltinDashboard types.Bool `tfsdk:"skip_builtin_dashboard" tf:"optional"`
}

type QualityMonitorResource struct {
Client *common.DatabricksClient
}

func (r *QualityMonitorResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) {
resp.TypeName = "databricks_quality_monitor_pluginframework"
}

func (r *QualityMonitorResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) {
resp.Schema = schema.Schema{
Description: "Terraform schema for Databricks Quality Monitor",
Attributes: tfschema.ResourceStructToSchemaMap(MonitorInfoExtended{}, func(c tfschema.CustomizableSchema) tfschema.CustomizableSchema {
c.SetRequired("assets_dir")
c.SetRequired("output_schema_name")
c.SetRequired("table_name")
c.SetReadOnly("monitor_version")
c.SetReadOnly("drift_metrics_table_name")
c.SetReadOnly("profile_metrics_table_name")
c.SetReadOnly("status")
c.SetReadOnly("dashboard_id")
c.SetReadOnly("schedule", "pause_status")
return c
}),
}
}

func (d *QualityMonitorResource) Configure(ctx context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) {
d.Client = pluginfwcommon.ConfigureResource(req, resp)
tanmay-db marked this conversation as resolved.
Show resolved Hide resolved
}

func (r *QualityMonitorResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) {
w, diags := r.Client.GetWorkspaceClient()
if diags.HasError() {
tanmay-db marked this conversation as resolved.
Show resolved Hide resolved
return
}
var monitorInfoTfSDK MonitorInfoExtended
diags = req.Plan.Get(ctx, &monitorInfoTfSDK)
resp.Diagnostics.Append(diags...)
tanmay-db marked this conversation as resolved.
Show resolved Hide resolved
if resp.Diagnostics.HasError() {
return
}

var createMonitorGoSDK catalog.CreateMonitor
diags = converters.TfSdkToGoSdkStruct(ctx, monitorInfoTfSDK, &createMonitorGoSDK)
if diags.HasError() {
return
}
monitor, err := w.QualityMonitors.Create(ctx, createMonitorGoSDK)
if err != nil {
resp.Diagnostics.AddError("Failed to get created monitor", err.Error())
return
}
resp.Diagnostics.Append(waitForMonitor(ctx, w, monitor)...)
if resp.Diagnostics.HasError() {
return
}

var newMonitorInfoTfSDK MonitorInfoExtended
resp.Diagnostics.Append(converters.GoSdkToTfSdkStruct(ctx, monitor, &newMonitorInfoTfSDK)...)
tanmay-db marked this conversation as resolved.
Show resolved Hide resolved
if resp.Diagnostics.HasError() {
return
}

resp.Diagnostics.Append(resp.State.Set(ctx, newMonitorInfoTfSDK)...)
}

func (r *QualityMonitorResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) {
w, diags := r.Client.GetWorkspaceClient()
if diags.HasError() {
tanmay-db marked this conversation as resolved.
Show resolved Hide resolved
return
}

var getMonitor catalog_tf.GetQualityMonitorRequest
diags = req.State.GetAttribute(ctx, path.Root("table_name"), &getMonitor.TableName)
resp.Diagnostics.Append(diags...)
tanmay-db marked this conversation as resolved.
Show resolved Hide resolved
if resp.Diagnostics.HasError() {
return
}
endpoint, err := w.QualityMonitors.GetByTableName(ctx, getMonitor.TableName.ValueString())
if err != nil {
resp.Diagnostics.AddError("Failed to get monitor", err.Error())
return
}
var monitorInfoTfSDK MonitorInfoExtended
diags = converters.GoSdkToTfSdkStruct(ctx, endpoint, &monitorInfoTfSDK)
tanmay-db marked this conversation as resolved.
Show resolved Hide resolved
if diags.HasError() {
return
}
diags = resp.State.Set(ctx, monitorInfoTfSDK)
resp.Diagnostics.Append(diags...)
tanmay-db marked this conversation as resolved.
Show resolved Hide resolved
if resp.Diagnostics.HasError() {
return
}
tanmay-db marked this conversation as resolved.
Show resolved Hide resolved
}

func (r *QualityMonitorResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) {
w, diags := r.Client.GetWorkspaceClient()
if diags.HasError() {
tanmay-db marked this conversation as resolved.
Show resolved Hide resolved
return
}

var monitorInfoTfSDK MonitorInfoExtended
diags = req.Plan.Get(ctx, &monitorInfoTfSDK)
resp.Diagnostics.Append(diags...)
tanmay-db marked this conversation as resolved.
Show resolved Hide resolved
if resp.Diagnostics.HasError() {
return
}
// Plan is not adding `dashboard_id`, but it is in the state.
diags = req.State.GetAttribute(ctx, path.Root("dashboard_id"), &monitorInfoTfSDK.DashboardId)
tanmay-db marked this conversation as resolved.
Show resolved Hide resolved
resp.Diagnostics.Append(diags...)
tanmay-db marked this conversation as resolved.
Show resolved Hide resolved
if resp.Diagnostics.HasError() {
return
}

var updateMonitorGoSDK catalog.UpdateMonitor
diags = converters.TfSdkToGoSdkStruct(ctx, monitorInfoTfSDK, &updateMonitorGoSDK)
if diags.HasError() {
tanmay-db marked this conversation as resolved.
Show resolved Hide resolved
return
}
monitor, err := w.QualityMonitors.Update(ctx, updateMonitorGoSDK)
if err != nil {
resp.Diagnostics.AddError("Failed to update monitor", err.Error())
return
}
resp.Diagnostics.Append(waitForMonitor(ctx, w, monitor)...)
if resp.Diagnostics.HasError() {
return
}

var newMonitorInfoTfSDK MonitorInfoExtended
diags = converters.GoSdkToTfSdkStruct(ctx, monitor, &newMonitorInfoTfSDK)
if diags.HasError() {
tanmay-db marked this conversation as resolved.
Show resolved Hide resolved
return
}

resp.Diagnostics.Append(resp.State.Set(ctx, newMonitorInfoTfSDK)...)
}

func (r *QualityMonitorResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) {
w, diags := r.Client.GetWorkspaceClient()
if diags.HasError() {
tanmay-db marked this conversation as resolved.
Show resolved Hide resolved
return
}

var deleteRequest catalog_tf.DeleteQualityMonitorRequest
diags = req.State.GetAttribute(ctx, path.Root("table_name"), &deleteRequest.TableName)
resp.Diagnostics.Append(diags...)
tanmay-db marked this conversation as resolved.
Show resolved Hide resolved
if resp.Diagnostics.HasError() {
return
}
err := w.QualityMonitors.DeleteByTableName(ctx, deleteRequest.TableName.ValueString())
if err != nil {
tanmay-db marked this conversation as resolved.
Show resolved Hide resolved
tanmay-db marked this conversation as resolved.
Show resolved Hide resolved
resp.Diagnostics.AddError("Failed to delete monitor", err.Error())
return
}
}
69 changes: 69 additions & 0 deletions internal/providers/pluginfw/resources/volume/data_volumes.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
package volume

import (
"context"

"github.com/databricks/databricks-sdk-go/service/catalog"
"github.com/databricks/terraform-provider-databricks/common"
pluginfwcommon "github.com/databricks/terraform-provider-databricks/internal/providers/pluginfw/common"
"github.com/databricks/terraform-provider-databricks/internal/providers/pluginfw/converters"
"github.com/databricks/terraform-provider-databricks/internal/providers/pluginfw/tfschema"
"github.com/hashicorp/terraform-plugin-framework/datasource"
"github.com/hashicorp/terraform-plugin-framework/datasource/schema"
"github.com/hashicorp/terraform-plugin-framework/types"
)

func DataSourceVolumes() datasource.DataSource {
return &VolumesDataSource{}
}

var _ datasource.DataSource = &VolumesDataSource{}
tanmay-db marked this conversation as resolved.
Show resolved Hide resolved

type VolumesDataSource struct {
Client *common.DatabricksClient
}

type VolumesList struct {
CatalogName types.String `tfsdk:"catalog_name"`
SchemaName types.String `tfsdk:"schema_name"`
Ids []types.String `tfsdk:"ids" tf:"computed,optional"`
}

func (d *VolumesDataSource) Metadata(ctx context.Context, req datasource.MetadataRequest, resp *datasource.MetadataResponse) {
resp.TypeName = "databricks_volumes_pluginframework"
}

func (d *VolumesDataSource) Schema(ctx context.Context, req datasource.SchemaRequest, resp *datasource.SchemaResponse) {
resp.Schema = schema.Schema{
Attributes: tfschema.DataSourceStructToSchemaMap(VolumesList{}, nil),
}
}

func (d *VolumesDataSource) Configure(_ context.Context, req datasource.ConfigureRequest, resp *datasource.ConfigureResponse) {
d.Client = pluginfwcommon.ConfigureDataSource(req, resp)
tanmay-db marked this conversation as resolved.
Show resolved Hide resolved
}

func (d *VolumesDataSource) Read(ctx context.Context, req datasource.ReadRequest, resp *datasource.ReadResponse) {
w, diags := d.Client.GetWorkspaceClient()
if diags.HasError() {
tanmay-db marked this conversation as resolved.
Show resolved Hide resolved
return
}

var volumesList VolumesList
diags = req.Config.Get(ctx, &volumesList)
resp.Diagnostics.Append(diags...)
tanmay-db marked this conversation as resolved.
Show resolved Hide resolved
if resp.Diagnostics.HasError() {
return
}
var listVolumesRequest catalog.ListVolumesRequest
converters.TfSdkToGoSdkStruct(ctx, volumesList, &listVolumesRequest)
tanmay-db marked this conversation as resolved.
Show resolved Hide resolved
volumes, err := w.Volumes.ListAll(ctx, listVolumesRequest)
if err != nil {
resp.Diagnostics.AddError("Failed to get volumes for the catalog and schema", err.Error())
return
}
for _, v := range volumes {
volumesList.Ids = append(volumesList.Ids, types.StringValue(v.FullName))
}
resp.State.Set(ctx, volumesList)
}
Loading