-
Notifications
You must be signed in to change notification settings - Fork 397
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
[Feature] Skip calling Read after Create/Update operations for notebooks #4173
base: main
Are you sure you want to change the base?
Changes from 4 commits
e80cda6
7293ae2
fafd992
7a54068
7f1b4e7
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,42 @@ | ||
{ | ||
"cells": [ | ||
{ | ||
"cell_type": "code", | ||
"execution_count": 0, | ||
"metadata": { | ||
"application/vnd.databricks.v1+cell": { | ||
"cellMetadata": {}, | ||
"inputWidgets": {}, | ||
"nuid": "b921d27d-a2bd-4ec8-bc2d-92ccc095246f", | ||
"showTitle": false, | ||
"tableResultSettingsMap": {}, | ||
"title": "" | ||
} | ||
}, | ||
"outputs": [], | ||
"source": [ | ||
"print(\"Hello World\")" | ||
] | ||
} | ||
], | ||
"metadata": { | ||
"application/vnd.databricks.v1+notebook": { | ||
"dashboards": [], | ||
"environmentMetadata": { | ||
"base_environment": "", | ||
"client": "1" | ||
}, | ||
"language": "python", | ||
"notebookMetadata": { | ||
"pythonIndentUnit": 4 | ||
}, | ||
"notebookName": "1", | ||
"widgets": {} | ||
}, | ||
"language_info": { | ||
"name": "python" | ||
} | ||
}, | ||
"nbformat": 4, | ||
"nbformat_minor": 0 | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,60 @@ | ||
{ | ||
"cells": [ | ||
{ | ||
"cell_type": "code", | ||
"execution_count": 0, | ||
"metadata": { | ||
"application/vnd.databricks.v1+cell": { | ||
"cellMetadata": {}, | ||
"inputWidgets": {}, | ||
"nuid": "769d5d72-59f3-417b-9f8c-08785a9c4423", | ||
"showTitle": false, | ||
"tableResultSettingsMap": {}, | ||
"title": "" | ||
} | ||
}, | ||
"outputs": [], | ||
"source": [ | ||
"print(\"First Hello\")" | ||
] | ||
}, | ||
{ | ||
"cell_type": "code", | ||
"execution_count": 0, | ||
"metadata": { | ||
"application/vnd.databricks.v1+cell": { | ||
"cellMetadata": {}, | ||
"inputWidgets": {}, | ||
"nuid": "b921d27d-a2bd-4ec8-bc2d-92ccc095246f", | ||
"showTitle": false, | ||
"tableResultSettingsMap": {}, | ||
"title": "" | ||
} | ||
}, | ||
"outputs": [], | ||
"source": [ | ||
"print(\"Hello World\")" | ||
] | ||
} | ||
], | ||
"metadata": { | ||
"application/vnd.databricks.v1+notebook": { | ||
"dashboards": [], | ||
"environmentMetadata": { | ||
"base_environment": "", | ||
"client": "1" | ||
}, | ||
"language": "python", | ||
"notebookMetadata": { | ||
"pythonIndentUnit": 4 | ||
}, | ||
"notebookName": "1", | ||
"widgets": {} | ||
}, | ||
"language_info": { | ||
"name": "python" | ||
} | ||
}, | ||
"nbformat": 4, | ||
"nbformat_minor": 0 | ||
} |
Original file line number | Diff line number | Diff line change | ||||
---|---|---|---|---|---|---|
|
@@ -60,6 +60,10 @@ type ObjectStatus struct { | |||||
Size int64 `json:"size,omitempty"` | ||||||
} | ||||||
|
||||||
type ImportResponse struct { | ||||||
ObjectID int64 `json:"object_id,omitempty"` | ||||||
} | ||||||
|
||||||
// ExportPath contains the base64 content of the notebook | ||||||
type ExportPath struct { | ||||||
Content string `json:"content,omitempty"` | ||||||
|
@@ -98,12 +102,14 @@ type NotebooksAPI struct { | |||||
var mtx = &sync.Mutex{} | ||||||
|
||||||
// Create creates a notebook given the content and path | ||||||
func (a NotebooksAPI) Create(r ImportPath) error { | ||||||
func (a NotebooksAPI) Create(r ImportPath) (ImportResponse, error) { | ||||||
if r.Format == "DBC" { | ||||||
mtx.Lock() | ||||||
defer mtx.Unlock() | ||||||
} | ||||||
return a.client.Post(a.context, "/workspace/import", r, nil) | ||||||
var responce ImportResponse | ||||||
err := a.client.Post(a.context, "/workspace/import", r, &responce) | ||||||
return responce, err | ||||||
alexott marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||
} | ||||||
|
||||||
// Read returns the notebook metadata and not the contents | ||||||
|
@@ -203,31 +209,29 @@ func (a NotebooksAPI) Delete(path string, recursive bool) error { | |||||
}, nil) | ||||||
} | ||||||
|
||||||
func setComputedProperties(d *schema.ResourceData, c *common.DatabricksClient) { | ||||||
d.Set("url", c.FormatURL("#workspace", d.Id())) | ||||||
d.Set("workspace_path", "/Workspace"+d.Id()) | ||||||
} | ||||||
|
||||||
// ResourceNotebook manages notebooks | ||||||
func ResourceNotebook() common.Resource { | ||||||
s := FileContentSchema(map[string]*schema.Schema{ | ||||||
"language": { | ||||||
Type: schema.TypeString, | ||||||
Optional: true, | ||||||
Computed: true, // we need it because it will be filled by the provider or backend | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 👍 thank you for correcting this |
||||||
ValidateFunc: validation.StringInSlice([]string{ | ||||||
Scala, | ||||||
Python, | ||||||
R, | ||||||
SQL, | ||||||
}, false), | ||||||
DiffSuppressFunc: func(k, old, new string, d *schema.ResourceData) bool { | ||||||
source := d.Get("source").(string) | ||||||
if source == "" { | ||||||
return false | ||||||
} | ||||||
ext := strings.ToLower(filepath.Ext(source)) | ||||||
return old == extMap[ext].Language | ||||||
}, | ||||||
}, | ||||||
"format": { | ||||||
Type: schema.TypeString, | ||||||
Optional: true, | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. You don't need to change it in this PR, but is this really optional? From the docs, users can't specify this, and it seems like we always set it in Create based on the language/source rather than accepting the user-provided value. |
||||||
Default: "SOURCE", | ||||||
Computed: true, | ||||||
ValidateFunc: validation.StringInSlice([]string{ | ||||||
"SOURCE", | ||||||
"DBC", | ||||||
|
@@ -258,6 +262,9 @@ func ResourceNotebook() common.Resource { | |||||
return common.Resource{ | ||||||
Schema: s, | ||||||
SchemaVersion: 1, | ||||||
CanSkipReadAfterCreateAndUpdate: func(d *schema.ResourceData) bool { | ||||||
return d.Get("format").(string) == "SOURCE" | ||||||
}, | ||||||
Create: func(ctx context.Context, d *schema.ResourceData, c *common.DatabricksClient) error { | ||||||
content, err := ReadContent(d) | ||||||
if err != nil { | ||||||
|
@@ -272,6 +279,10 @@ func ResourceNotebook() common.Resource { | |||||
Path: path, | ||||||
Overwrite: true, | ||||||
} | ||||||
if createNotebook.Format == "" && createNotebook.Language != "" { | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can users specify format? It's not documented on the public docs: https://registry.terraform.io/providers/databricks/databricks/1.61.0/docs/resources/notebook. If not, we can remove the first branch of the |
||||||
createNotebook.Format = "SOURCE" | ||||||
d.Set("format", createNotebook.Format) | ||||||
} | ||||||
Comment on lines
+282
to
+285
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. To make this easier to follow, it seems like we should do something like the following: if createNotebook.Language == "" {
// Users must not specify `content_base64` without a language, so
// `source` must be set.
ext := strings.ToLower(filepath.Ext(d.Get("source").(string)))
createNotebook.Language = extMap[ext].Language
createNotebook.Format = extMap[ext].Format
// Overwrite cannot be used for Dbc format
createNotebook.Overwrite = extMap[ext].Overwrite
d.Set("language", createNotebook.Language)
} else {
createNotebook.Format = "SOURCE"
}
d.Set("format", createNotebook.Format) Is this logic correct? |
||||||
if createNotebook.Language == "" { | ||||||
// TODO: check what happens with empty source | ||||||
ext := strings.ToLower(filepath.Ext(d.Get("source").(string))) | ||||||
|
@@ -281,8 +292,9 @@ func ResourceNotebook() common.Resource { | |||||
createNotebook.Overwrite = extMap[ext].Overwrite | ||||||
// by default it's SOURCE, but for DBC we have to change it | ||||||
d.Set("format", createNotebook.Format) | ||||||
d.Set("language", createNotebook.Language) | ||||||
} | ||||||
err = notebooksAPI.Create(createNotebook) | ||||||
resp, err := notebooksAPI.Create(createNotebook) | ||||||
if err != nil { | ||||||
if isParentDoesntExistError(err) { | ||||||
parent := filepath.ToSlash(filepath.Dir(path)) | ||||||
|
@@ -291,16 +303,32 @@ func ResourceNotebook() common.Resource { | |||||
if err != nil { | ||||||
return err | ||||||
} | ||||||
err = notebooksAPI.Create(createNotebook) | ||||||
resp, err = notebooksAPI.Create(createNotebook) | ||||||
} | ||||||
if err != nil { | ||||||
return err | ||||||
} | ||||||
} | ||||||
if d.Get("object_type").(string) == "" { | ||||||
d.Set("object_type", Notebook) | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why do we need to know the object type here? Isn't it always notebook for a databricks_notebook? Or is it because this can be a DBC file which can contain other stuff as well? I am also missing when it would be set before (when this |
||||||
} | ||||||
d.Set("object_id", resp.ObjectID) | ||||||
d.SetId(path) | ||||||
setComputedProperties(d, c) | ||||||
return nil | ||||||
}, | ||||||
Read: func(ctx context.Context, d *schema.ResourceData, c *common.DatabricksClient) error { | ||||||
oldFormat := d.Get("format").(string) | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. To clarify, we are doing this in the |
||||||
if oldFormat == "" { | ||||||
source := d.Get("source").(string) | ||||||
// check if `source` is set, and if it is, use file exension to determine format | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
if source != "" { | ||||||
ext := strings.ToLower(filepath.Ext(source)) | ||||||
oldFormat = extMap[ext].Format | ||||||
} else { | ||||||
oldFormat = "SOURCE" | ||||||
} | ||||||
} | ||||||
w, err := c.WorkspaceClient() | ||||||
if err != nil { | ||||||
return err | ||||||
|
@@ -311,9 +339,12 @@ func ResourceNotebook() common.Resource { | |||||
if err != nil { | ||||||
return err | ||||||
} | ||||||
d.Set("url", c.FormatURL("#workspace", d.Id())) | ||||||
d.Set("workspace_path", "/Workspace"+objectStatus.Path) | ||||||
return common.StructToData(objectStatus, s, d) | ||||||
setComputedProperties(d, c) | ||||||
err = common.StructToData(objectStatus, s, d) | ||||||
if err != nil { | ||||||
return err | ||||||
} | ||||||
return d.Set("format", oldFormat) | ||||||
}, | ||||||
Update: func(ctx context.Context, d *schema.ResourceData, c *common.DatabricksClient) error { | ||||||
notebooksAPI := NewNotebooksAPI(ctx, c) | ||||||
|
@@ -322,25 +353,33 @@ func ResourceNotebook() common.Resource { | |||||
return err | ||||||
} | ||||||
format := d.Get("format").(string) | ||||||
var resp ImportResponse | ||||||
if format == "DBC" { | ||||||
// Overwrite cannot be used for source format when importing a folder | ||||||
err = notebooksAPI.Delete(d.Id(), true) | ||||||
if err != nil { | ||||||
return err | ||||||
} | ||||||
return notebooksAPI.Create(ImportPath{ | ||||||
resp, err = notebooksAPI.Create(ImportPath{ | ||||||
Content: base64.StdEncoding.EncodeToString(content), | ||||||
Format: format, | ||||||
Path: d.Id(), | ||||||
}) | ||||||
} else { | ||||||
resp, err = notebooksAPI.Create(ImportPath{ | ||||||
Content: base64.StdEncoding.EncodeToString(content), | ||||||
Language: d.Get("language").(string), | ||||||
Format: format, | ||||||
Overwrite: true, | ||||||
Path: d.Id(), | ||||||
}) | ||||||
} | ||||||
return notebooksAPI.Create(ImportPath{ | ||||||
Content: base64.StdEncoding.EncodeToString(content), | ||||||
Language: d.Get("language").(string), | ||||||
Format: format, | ||||||
Overwrite: true, | ||||||
Path: d.Id(), | ||||||
}) | ||||||
if err != nil { | ||||||
return err | ||||||
} | ||||||
d.Set("object_id", resp.ObjectID) | ||||||
setComputedProperties(d, c) | ||||||
return nil | ||||||
}, | ||||||
Delete: func(ctx context.Context, d *schema.ResourceData, c *common.DatabricksClient) error { | ||||||
objType := d.Get("object_type") | ||||||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you add a readme for the
.dbc
files? It's not clear what is in those.