-
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?
Conversation
get-status
for notebooks in Read after Create/Update operations7ecd15c
to
2ddb757
Compare
This PR adds the ability for a resource to specify that it may not need to call `Read` after `Create` and `Update` operations so we can avoid performing another API call(s). The resource may implement `CanSkipReadAfterCreateAndUpdate` function that can decide if the `Read` operation should be skipped. I decided to move common part from #4173 to make it easier to review
## Changes <!-- Summary of your changes that are easy to understand --> This PR adds the ability for a resource to specify that it may not need to call `Read` after `Create` and `Update` operations so we can avoid performing another API call(s). The resource may implement `CanSkipReadAfterCreateAndUpdate` function that can decide if the `Read` operation should be skipped. I decided to move common part from #4173 to make it easier to review ## 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` - [x] relevant acceptance tests are passing - [ ] using Go SDK
It was found that the import API returns `object_id` as a result of its execution, so we don't really need to call get-status to fill all attributes. This should help when we import a large number of notebooks, i.e., when applying exported resources. This also changes `format` and `language` attributes to `optional,computed` to avoid having issues with defaults, and suppress diff.
2ddb757
to
e80cda6
Compare
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.
I think this PR is doing a bit more than what the title suggests, especially now that #4190 lets us skip calling Read, and this PR is using that. I'm missing some specific Notebook context, so it's tough for me to fully judge if the change is correct. My review is mainly focused on the tests and code, and I don't see any issues there. @mgyucht, do you think you should have another look at it as well?
Co-authored-by: Omer Lachish <[email protected]>
If integration tests don't run automatically, an authorized user can run them manually by following the instructions below: Trigger: Inputs:
Checks will be approved automatically on success. |
Test Details: go/deco-tests/12159341397 |
@rauchy I've added more context to the PR description |
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.
I might be misunderstanding parts of this, as databricks_notebook
is a pretty complex resource, but I think there are some concerns we need to address before merging. LMK what you think.
oldFormat := d.Get("format").(string) | ||
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 comment
The reason will be displayed to describe this comment to others. Learn more.
// check if `source` is set, and if it is, use file exension to determine format | |
// check if `source` is set, and if it is, use file extension to determine format |
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 comment
The reason will be displayed to describe this comment to others. Learn more.
To clarify, we are doing this in the Read()
method for resource import? We should set these fields correctly on create/update definitely. IIUC we won't have access to the source
field during import, so there will be a diff anyways after importing a notebook, and this would be set in the update anyways. So I'm wondering why we need to set these in Read()
at all.
} | ||
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 comment
The 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 if
block would be skipped). It's marked as deprecated
and should always be notebook, I guess?
@@ -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 comment
The 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 if
since it is always true.
if createNotebook.Format == "" && createNotebook.Language != "" { | ||
createNotebook.Format = "SOURCE" | ||
d.Set("format", createNotebook.Format) | ||
} |
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.
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?
"source": "acceptance/testdata/tf-test-jupyter.ipynb", | ||
"format": "JUPYTER", |
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.
I don't think these will normally be part of the resource state during read for a newly imported resource. What is this test meant to cover?
@@ -119,22 +155,8 @@ func TestResourceNotebookCreate_DirectoryExist(t *testing.T) { | |||
Overwrite: true, | |||
Format: "SOURCE", | |||
}, | |||
}, | |||
{ |
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.
Very nice that we save multiple API calls here!
@@ -0,0 +1,42 @@ | |||
{ |
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.
// 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 comment
The reason will be displayed to describe this comment to others. Learn more.
👍 thank you for correcting this
} | ||
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 comment
The 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.
Changes
It's based on #4190.
It was found that the
import
API returnsobject_id
as a result of its execution, so we don't really need to call theRead
operation that will callget-status
to fill all attributes (when we create a notebook using theSOURCE
format).The other formats (
DBC
andJUPYTER
) will still call theRead
to fill in the missing parameters, such aslanguage
.It also changed the
language
tooptional,computed
so we can safely set the value and don't trigger the diff.This should help when we import a large number of notebooks, i.e., when applying exported resources.
Tests
make test
run locallyrelevant change indocs/
folderinternal/acceptance
using Go SDK