Skip to content

Commit 555f7a7

Browse files
authored
Added a new resource (#1038)
1 parent 9f12dda commit 555f7a7

File tree

8 files changed

+467
-3
lines changed

8 files changed

+467
-3
lines changed

docs/resources/mlflow_experiment.md

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,6 @@ The following resources are often used in the same context:
3535

3636
* [End to end workspace management](../guides/workspace-management.md) guide.
3737
* [databricks_directory](directory.md) to manage directories in [Databricks Workpace](https://docs.databricks.com/workspace/workspace-objects.html).
38-
* [databricks_mlflow_experiment](mlflow_experiment.md) to manage [MLflow experiments](https://docs.databricks.com/data/data-sources/mlflow-experiment.html) in Databricks.
3938
* [databricks_mlflow_model](mlflow_model.md) to create [MLflow models](https://docs.databricks.com/applications/mlflow/models.html) in Databricks.
4039
* [databricks_notebook](notebook.md) to manage [Databricks Notebooks](https://docs.databricks.com/notebooks/index.html).
4140
* [databricks_notebook](../data-sources/notebook.md) data to export a notebook from Databricks Workspace.

docs/resources/mlflow_model.md

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,6 @@ The following resources are often used in the same context:
4343
* [End to end workspace management](../guides/workspace-management.md) guide.
4444
* [databricks_directory](directory.md) to manage directories in [Databricks Workpace](https://docs.databricks.com/workspace/workspace-objects.html).
4545
* [databricks_mlflow_experiment](mlflow_experiment.md) to manage [MLflow experiments](https://docs.databricks.com/data/data-sources/mlflow-experiment.html) in Databricks.
46-
* [databricks_mlflow_model](mlflow_model.md) to create [MLflow models](https://docs.databricks.com/applications/mlflow/models.html) in Databricks.
4746
* [databricks_notebook](notebook.md) to manage [Databricks Notebooks](https://docs.databricks.com/notebooks/index.html).
4847
* [databricks_notebook](../data-sources/notebook.md) data to export a notebook from Databricks Workspace.
4948
* [databricks_repo](repo.md) to manage [Databricks Repos](https://docs.databricks.com/repos.html).

mlflow/resource_model.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ func (a ModelsAPI) Update(m *Model) error {
5858
return a.client.Patch(a.context, "/mlflow/registered-models/update", m)
5959
}
6060

61-
// Delete removes the model by it's name
61+
// Delete removes the model by its name
6262
func (a ModelsAPI) Delete(name string) error {
6363
return a.client.Delete(a.context, "/mlflow/registered-models/delete", map[string]string{
6464
"name": name,

mlflow/resource_webhook.go

Lines changed: 148 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,148 @@
1+
package mlflow
2+
3+
import (
4+
"context"
5+
"fmt"
6+
7+
"github.com/databrickslabs/terraform-provider-databricks/common"
8+
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
9+
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation"
10+
)
11+
12+
type HttpUrlSpec struct {
13+
URL string `json:"url"`
14+
EnableSslVerification bool `json:"enable_ssl_verification" tf:"default:true"`
15+
Secret string `json:"string,omitempty"`
16+
Authorization string `json:"authorization,omitempty" tf:"sensitive"`
17+
}
18+
19+
type JobSpec struct {
20+
JobID string `json:"job_id"`
21+
AccessToken string `json:"access_token" tf:"sensitive"`
22+
WorkspaceURL string `json:"workspace_url,omitempty"`
23+
}
24+
25+
type Webhook struct {
26+
ID string `json:"id" tf:"computed"`
27+
Events []string `json:"events"`
28+
Status string `json:"status,omitempty" tf:"default:ACTIVE"`
29+
Description string `json:"description,omitempty"`
30+
ModelName string `json:"model_name,omitempty" tf:"force_new"`
31+
HttpUrlSpec *HttpUrlSpec `json:"http_url_spec,omitempty"` // TODO: add diff suppressor?
32+
JobSpec *JobSpec `json:"job_spec,omitempty"` // TODO: add diff suppressor?
33+
}
34+
35+
type webhookListResponse struct {
36+
Webhooks []Webhook `json:"webhooks"`
37+
}
38+
39+
type webhookApiResponse struct {
40+
Webhook Webhook `json:"webhook"`
41+
}
42+
43+
type WebhooksAPI struct {
44+
client *common.DatabricksClient
45+
context context.Context
46+
}
47+
48+
func NewWebhooksAPI(ctx context.Context, m interface{}) WebhooksAPI {
49+
return WebhooksAPI{m.(*common.DatabricksClient), ctx}
50+
}
51+
52+
func (a WebhooksAPI) Create(m Webhook) (string, error) {
53+
var r webhookApiResponse
54+
err := a.client.Post(a.context, "/mlflow/registry-webhooks/create", &m, &r)
55+
return r.Webhook.ID, err
56+
}
57+
58+
func (a WebhooksAPI) Read(ID string) (Webhook, error) {
59+
var m webhookListResponse
60+
err := a.client.Get(a.context, "/mlflow/registry-webhooks/list", nil, &m)
61+
if err != nil {
62+
return Webhook{}, fmt.Errorf("error reading list of webhooks: %w", err)
63+
}
64+
for _, wh := range m.Webhooks {
65+
if wh.ID == ID {
66+
return wh, nil
67+
}
68+
}
69+
return Webhook{}, fmt.Errorf("Webhook with ID %s isn't found", ID)
70+
}
71+
72+
// Update the webhook entity
73+
func (a WebhooksAPI) Update(id string, m Webhook) error {
74+
// Update API doesn't allow to change Model name field after creation.
75+
m.ModelName = ""
76+
m.ID = id
77+
return a.client.Patch(a.context, "/mlflow/registry-webhooks/update", m)
78+
}
79+
80+
// Delete removes the webhook by its ID
81+
func (a WebhooksAPI) Delete(ID string) error {
82+
return a.client.Delete(a.context, "/mlflow/registry-webhooks/delete", map[string]string{
83+
"id": ID,
84+
})
85+
}
86+
87+
func ResourceMLFlowWebhook() *schema.Resource {
88+
s := common.StructToSchema(
89+
Webhook{},
90+
func(m map[string]*schema.Schema) map[string]*schema.Schema {
91+
m["status"].ValidateFunc = validation.StringInSlice([]string{"ACTIVE", "TEST_MODE", "DISABLED"}, true)
92+
// TODO: do we need a validation for Events?
93+
delete(m, "id")
94+
if p, err := common.SchemaPath(m, "http_url_spec", "enable_ssl_verification"); err == nil {
95+
p.Required = false
96+
p.Optional = true
97+
}
98+
if p, err := common.SchemaPath(m, "http_url_spec", "url"); err == nil {
99+
p.ValidateFunc = validation.IsURLWithHTTPS
100+
}
101+
m["http_url_spec"].ConflictsWith = []string{"job_spec"}
102+
m["job_spec"].ConflictsWith = []string{"http_url_spec"}
103+
104+
return m
105+
})
106+
107+
return common.Resource{
108+
Create: func(ctx context.Context, d *schema.ResourceData, c *common.DatabricksClient) error {
109+
var m Webhook
110+
common.DataToStructPointer(d, s, &m)
111+
if m.HttpUrlSpec == nil && m.JobSpec == nil {
112+
return fmt.Errorf("at least one of http_url_spec or job_spec should be specified")
113+
}
114+
115+
hookID, err := NewWebhooksAPI(ctx, c).Create(m)
116+
if err != nil {
117+
return fmt.Errorf("error creating a webhook: %w", err)
118+
}
119+
d.SetId(hookID)
120+
return nil
121+
},
122+
Read: func(ctx context.Context, d *schema.ResourceData, c *common.DatabricksClient) error {
123+
var mOrig Webhook
124+
common.DataToStructPointer(d, s, &mOrig)
125+
m, err := NewWebhooksAPI(ctx, c).Read(d.Id())
126+
if err != nil {
127+
return err
128+
}
129+
// We need to preserve original values as API doesn't return sensitive values
130+
if mOrig.JobSpec != nil && m.JobSpec != nil {
131+
m.JobSpec.AccessToken = mOrig.JobSpec.AccessToken
132+
}
133+
if mOrig.HttpUrlSpec != nil && m.HttpUrlSpec != nil {
134+
m.HttpUrlSpec.Authorization = mOrig.HttpUrlSpec.Authorization
135+
}
136+
return common.StructToData(m, s, d)
137+
},
138+
Update: func(ctx context.Context, d *schema.ResourceData, c *common.DatabricksClient) error {
139+
var m Webhook
140+
common.DataToStructPointer(d, s, &m)
141+
return NewWebhooksAPI(ctx, c).Update(d.Id(), m)
142+
},
143+
Delete: func(ctx context.Context, d *schema.ResourceData, c *common.DatabricksClient) error {
144+
return NewWebhooksAPI(ctx, c).Delete(d.Id())
145+
},
146+
Schema: s,
147+
}.ToResource()
148+
}

0 commit comments

Comments
 (0)