Skip to content

Commit d1fae80

Browse files
Make google_notebooks_instance metadata field updatable (#7945) (#14650)
* Added update tests proving that metadata is not updatable * Test fixes * Made notebooks instance metadata field updatable * Added metadata to ImportStateVerifyIgnore for impacted tests * Removed non-updatable fields from update test * Temporarily made metadata field not updatable to prove that only these changes are required to make the tests pass * Revert "Temporarily made metadata field not updatable to prove that only these changes are required to make the tests pass" This reverts commit f22a9e4cd29c3e0d35c1888aca5280a1bef6c22b. Signed-off-by: Modular Magician <[email protected]>
1 parent e8f711c commit d1fae80

File tree

3 files changed

+215
-1
lines changed

3 files changed

+215
-1
lines changed

.changelog/7945.txt

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
```release-note:enhancement
2+
notebooks: added update-in-place support for `google_notebooks_instance.metadata` field
3+
```

google/resource_notebooks_instance.go

+62-1
Original file line numberDiff line numberDiff line change
@@ -220,7 +220,6 @@ An object containing a list of "key": value pairs. Example: { "name": "wrench",
220220
"metadata": {
221221
Type: schema.TypeMap,
222222
Optional: true,
223-
ForceNew: true,
224223
Description: `Custom metadata to apply to this instance.
225224
An object containing a list of "key": value pairs. Example: { "name": "wrench", "mass": "1.3kg", "count": "3" }.`,
226225
Elem: &schema.Schema{Type: schema.TypeString},
@@ -820,6 +819,11 @@ func resourceNotebooksInstanceUpdate(d *schema.ResourceData, meta interface{}) e
820819
obj["labels"] = labelsProp
821820
}
822821

822+
obj, err = resourceNotebooksInstanceUpdateEncoder(d, meta, obj)
823+
if err != nil {
824+
return err
825+
}
826+
823827
url, err := tpgresource.ReplaceVars(d, config, "{{NotebooksBasePath}}projects/{{project}}/locations/{{location}}/instances/{{name}}:setLabels")
824828
if err != nil {
825829
return err
@@ -852,6 +856,53 @@ func resourceNotebooksInstanceUpdate(d *schema.ResourceData, meta interface{}) e
852856
return err
853857
}
854858
}
859+
if d.HasChange("metadata") {
860+
obj := make(map[string]interface{})
861+
862+
metadataProp, err := expandNotebooksInstanceMetadata(d.Get("metadata"), d, config)
863+
if err != nil {
864+
return err
865+
} else if v, ok := d.GetOkExists("metadata"); !tpgresource.IsEmptyValue(reflect.ValueOf(v)) && (ok || !reflect.DeepEqual(v, metadataProp)) {
866+
obj["metadata"] = metadataProp
867+
}
868+
869+
obj, err = resourceNotebooksInstanceUpdateEncoder(d, meta, obj)
870+
if err != nil {
871+
return err
872+
}
873+
874+
url, err := tpgresource.ReplaceVars(d, config, "{{NotebooksBasePath}}projects/{{project}}/locations/{{location}}/instances/{{name}}:updateMetadataItems")
875+
if err != nil {
876+
return err
877+
}
878+
879+
// err == nil indicates that the billing_project value was found
880+
if bp, err := tpgresource.GetBillingProject(d, config); err == nil {
881+
billingProject = bp
882+
}
883+
884+
res, err := transport_tpg.SendRequest(transport_tpg.SendRequestOptions{
885+
Config: config,
886+
Method: "PATCH",
887+
Project: billingProject,
888+
RawURL: url,
889+
UserAgent: userAgent,
890+
Body: obj,
891+
Timeout: d.Timeout(schema.TimeoutUpdate),
892+
})
893+
if err != nil {
894+
return fmt.Errorf("Error updating Instance %q: %s", d.Id(), err)
895+
} else {
896+
log.Printf("[DEBUG] Finished updating Instance %q: %#v", d.Id(), res)
897+
}
898+
899+
err = NotebooksOperationWaitTime(
900+
config, res, project, "Updating Instance", userAgent,
901+
d.Timeout(schema.TimeoutUpdate))
902+
if err != nil {
903+
return err
904+
}
905+
}
855906

856907
d.Partial(false)
857908

@@ -1408,3 +1459,13 @@ func expandNotebooksInstanceContainerImageRepository(v interface{}, d tpgresourc
14081459
func expandNotebooksInstanceContainerImageTag(v interface{}, d tpgresource.TerraformResourceData, config *transport_tpg.Config) (interface{}, error) {
14091460
return v, nil
14101461
}
1462+
1463+
func resourceNotebooksInstanceUpdateEncoder(d *schema.ResourceData, meta interface{}, obj map[string]interface{}) (map[string]interface{}, error) {
1464+
// Update requests use "items" as the api name instead of "metadata"
1465+
// https://cloud.google.com/vertex-ai/docs/workbench/reference/rest/v1/projects.locations.instances/updateMetadataItems
1466+
if metadata, ok := obj["metadata"]; ok {
1467+
obj["items"] = metadata
1468+
delete(obj, "metadata")
1469+
}
1470+
return obj, nil
1471+
}
+150
Original file line numberDiff line numberDiff line change
@@ -1 +1,151 @@
11
package google
2+
3+
import (
4+
"fmt"
5+
"strconv"
6+
"testing"
7+
8+
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource"
9+
)
10+
11+
func TestAccNotebooksInstance_create_vm_image(t *testing.T) {
12+
t.Parallel()
13+
14+
prefix := fmt.Sprintf("%d", RandInt(t))
15+
name := fmt.Sprintf("tf-%s", prefix)
16+
17+
VcrTest(t, resource.TestCase{
18+
ProtoV5ProviderFactories: ProtoV5ProviderFactories(t),
19+
Steps: []resource.TestStep{
20+
{
21+
Config: testAccNotebooksInstance_create_vm_image(name),
22+
},
23+
{
24+
ResourceName: "google_notebooks_instance.test",
25+
ImportState: true,
26+
ImportStateVerify: true,
27+
ImportStateVerifyIgnore: []string{"vm_image", "metadata"},
28+
},
29+
},
30+
})
31+
}
32+
33+
func TestAccNotebooksInstance_update(t *testing.T) {
34+
context := map[string]interface{}{
35+
"random_suffix": RandString(t, 10),
36+
}
37+
38+
VcrTest(t, resource.TestCase{
39+
ProtoV5ProviderFactories: ProtoV5ProviderFactories(t),
40+
Steps: []resource.TestStep{
41+
{
42+
Config: testAccNotebooksInstance_basic(context),
43+
},
44+
{
45+
ResourceName: "google_notebooks_instance.instance",
46+
ImportState: true,
47+
ImportStateVerify: true,
48+
ImportStateVerifyIgnore: []string{"vm_image", "metadata"},
49+
},
50+
{
51+
Config: testAccNotebooksInstance_update(context, true),
52+
},
53+
{
54+
ResourceName: "google_notebooks_instance.instance",
55+
ImportState: true,
56+
ImportStateVerify: true,
57+
ImportStateVerifyIgnore: []string{"vm_image", "metadata"},
58+
},
59+
{
60+
Config: testAccNotebooksInstance_update(context, false),
61+
},
62+
{
63+
ResourceName: "google_notebooks_instance.instance",
64+
ImportState: true,
65+
ImportStateVerify: true,
66+
ImportStateVerifyIgnore: []string{"vm_image", "metadata"},
67+
},
68+
},
69+
})
70+
}
71+
72+
func testAccNotebooksInstance_create_vm_image(name string) string {
73+
return fmt.Sprintf(`
74+
75+
resource "google_notebooks_instance" "test" {
76+
name = "%s"
77+
location = "us-west1-a"
78+
machine_type = "e2-medium"
79+
metadata = {
80+
proxy-mode = "service_account"
81+
terraform = "true"
82+
}
83+
84+
nic_type = "VIRTIO_NET"
85+
86+
reservation_affinity {
87+
consume_reservation_type = "NO_RESERVATION"
88+
}
89+
90+
vm_image {
91+
project = "deeplearning-platform-release"
92+
image_family = "tf-latest-cpu"
93+
}
94+
}
95+
`, name)
96+
}
97+
98+
func testAccNotebooksInstance_basic(context map[string]interface{}) string {
99+
return Nprintf(`
100+
resource "google_notebooks_instance" "instance" {
101+
name = "tf-test-notebooks-instance%{random_suffix}"
102+
location = "us-central1-a"
103+
machine_type = "e2-medium"
104+
105+
vm_image {
106+
project = "deeplearning-platform-release"
107+
image_family = "tf-latest-cpu"
108+
}
109+
110+
metadata = {
111+
proxy-mode = "service_account"
112+
terraform = "true"
113+
}
114+
115+
lifecycle {
116+
prevent_destroy = true
117+
}
118+
}
119+
`, context)
120+
}
121+
122+
func testAccNotebooksInstance_update(context map[string]interface{}, preventDestroy bool) string {
123+
context["prevent_destroy"] = strconv.FormatBool(preventDestroy)
124+
125+
return Nprintf(`
126+
resource "google_notebooks_instance" "instance" {
127+
name = "tf-test-notebooks-instance%{random_suffix}"
128+
location = "us-central1-a"
129+
machine_type = "e2-medium"
130+
131+
vm_image {
132+
project = "deeplearning-platform-release"
133+
image_family = "tf-latest-cpu"
134+
}
135+
136+
metadata = {
137+
proxy-mode = "service_account"
138+
terraform = "true"
139+
notebook-upgrade-schedule = "0 * * * *"
140+
}
141+
142+
labels = {
143+
key = "value"
144+
}
145+
146+
lifecycle {
147+
prevent_destroy = %{prevent_destroy}
148+
}
149+
}
150+
`, context)
151+
}

0 commit comments

Comments
 (0)