From 18072d2b0665b0182e8bcd677fc1d0ca75693507 Mon Sep 17 00:00:00 2001 From: bcreddy-gcp <123543489+bcreddy-gcp@users.noreply.github.com> Date: Wed, 14 Feb 2024 02:39:33 +0530 Subject: [PATCH] =?UTF-8?q?Make=20notebooks=20wait=20for=20active=20state?= =?UTF-8?q?=20and=20enable=20stopping/starting=20ins=E2=80=A6=20(#9971)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Make notebooks wait for active state and enable stopping/starting instances using the desired_state field * ignore update_time --- mmv1/products/notebooks/Instance.yaml | 22 +++++ .../terraform/constants/notebooks_instance.go | 53 ++++++++++++ .../notebook_instance_basic_stopped.tf.erb | 10 +++ .../examples/notebook_instance_full.tf.erb | 1 + .../post_create/notebooks_instance.go.erb | 13 +++ .../post_update/notebooks_instance.go.erb | 21 +++++ ...ource_notebooks_instance_state_test.go.erb | 85 +++++++++++++++++++ 7 files changed, 205 insertions(+) create mode 100644 mmv1/templates/terraform/examples/notebook_instance_basic_stopped.tf.erb create mode 100644 mmv1/templates/terraform/post_create/notebooks_instance.go.erb create mode 100644 mmv1/templates/terraform/post_update/notebooks_instance.go.erb create mode 100644 mmv1/third_party/terraform/services/notebooks/resource_notebooks_instance_state_test.go.erb diff --git a/mmv1/products/notebooks/Instance.yaml b/mmv1/products/notebooks/Instance.yaml index b770e0489040..1e0ad5c49374 100644 --- a/mmv1/products/notebooks/Instance.yaml +++ b/mmv1/products/notebooks/Instance.yaml @@ -55,6 +55,17 @@ examples: region_override: 'us-west1-a' vars: instance_name: 'notebooks-instance' + - !ruby/object:Provider::Terraform::Examples + name: 'notebook_instance_basic_stopped' + primary_resource_id: 'instance' + primary_resource_name: "fmt.Sprintf(\"tf-test-notebooks-instance%s\", + context[\"\ + random_suffix\"])" + region_override: 'us-west1-a' + vars: + instance_name: 'notebooks-instance' + ignore_read_extra: + - 'desired_state' - !ruby/object:Provider::Terraform::Examples name: 'notebook_instance_basic_container' primary_resource_id: 'instance' @@ -87,9 +98,20 @@ examples: key_name: 'acctest.BootstrapKMSKeyInLocation(t, "global").CryptoKey.Name' test_env_vars: service_account: :SERVICE_ACCT +virtual_fields: + - !ruby/object:Api::Type::Enum + name: desired_state + description: | + Desired state of the Notebook Instance. Set this field to `ACTIVE` to start the Instance, and `STOPPED` to stop the Instance. + values: + - :ACTIVE + - :STOPPED + default_value: :ACTIVE custom_code: !ruby/object:Provider::Terraform::CustomCode constants: templates/terraform/constants/notebooks_instance.go update_encoder: templates/terraform/update_encoder/notebooks_instance.go + post_create: templates/terraform/post_create/notebooks_instance.go.erb + post_update: templates/terraform/post_update/notebooks_instance.go.erb state_upgraders: true schema_version: 1 parameters: diff --git a/mmv1/templates/terraform/constants/notebooks_instance.go b/mmv1/templates/terraform/constants/notebooks_instance.go index 5d376547fe83..230d2aece282 100644 --- a/mmv1/templates/terraform/constants/notebooks_instance.go +++ b/mmv1/templates/terraform/constants/notebooks_instance.go @@ -33,3 +33,56 @@ func NotebooksInstanceKmsDiffSuppress(_, old, new string, _ *schema.ResourceData } return false } + +<% unless compiler == "terraformgoogleconversion-codegen" -%> +// waitForNotebooksInstanceActive waits for an Notebook instance to become "ACTIVE" +func waitForNotebooksInstanceActive(d *schema.ResourceData, config *transport_tpg.Config, timeout time.Duration) error { + return resource.Retry(timeout, func() *resource.RetryError { + if err := resourceNotebooksInstanceRead(d, config); err != nil { + return resource.NonRetryableError(err) + } + + name := d.Get("name").(string) + state := d.Get("state").(string) + if state == "ACTIVE" { + log.Printf("[DEBUG] Notebook Instance %q has state %q.", name, state) + return nil + } else { + return resource.RetryableError(fmt.Errorf("Notebook Instance %q has state %q. Waiting for ACTIVE state", name, state)) + } + + }) +} +<% end -%> + +func modifyNotebooksInstanceState(config *transport_tpg.Config, d *schema.ResourceData, project string, billingProject string, userAgent string, state string) (map[string]interface{}, error) { + url, err := tpgresource.ReplaceVars(d, config, "{{NotebooksBasePath}}projects/{{project}}/locations/{{location}}/instances/{{name}}:"+state) + if err != nil { + return nil, err + } + + res, err := transport_tpg.SendRequest(transport_tpg.SendRequestOptions{ + Config: config, + Method: "POST", + Project: billingProject, + RawURL: url, + UserAgent: userAgent, + }) + if err != nil { + return nil, fmt.Errorf("Unable to %q google_notebooks_instance %q: %s", state, d.Id(), err) + } + return res, nil +} + +<% unless compiler == "terraformgoogleconversion-codegen" -%> +func waitForNotebooksOperation(config *transport_tpg.Config, d *schema.ResourceData, project string, billingProject string, userAgent string, response map[string]interface{}) error { + var opRes map[string]interface{} + err := NotebooksOperationWaitTimeWithResponse( + config, response, &opRes, project, "Modifying Notebook Instance state", userAgent, + d.Timeout(schema.TimeoutUpdate)) + if err != nil { + return err + } + return nil +} +<% end -%> diff --git a/mmv1/templates/terraform/examples/notebook_instance_basic_stopped.tf.erb b/mmv1/templates/terraform/examples/notebook_instance_basic_stopped.tf.erb new file mode 100644 index 000000000000..41130bcca3ef --- /dev/null +++ b/mmv1/templates/terraform/examples/notebook_instance_basic_stopped.tf.erb @@ -0,0 +1,10 @@ +resource "google_notebooks_instance" "<%= ctx[:primary_resource_id] %>" { + name = "<%= ctx[:vars]["instance_name"] %>" + location = "us-west1-a" + machine_type = "e2-medium" + vm_image { + project = "deeplearning-platform-release" + image_family = "tf-latest-cpu" + } + desired_state = "STOPPED" +} diff --git a/mmv1/templates/terraform/examples/notebook_instance_full.tf.erb b/mmv1/templates/terraform/examples/notebook_instance_full.tf.erb index 0137529b0d1a..31f2af55ca3c 100644 --- a/mmv1/templates/terraform/examples/notebook_instance_full.tf.erb +++ b/mmv1/templates/terraform/examples/notebook_instance_full.tf.erb @@ -36,6 +36,7 @@ resource "google_notebooks_instance" "<%= ctx[:primary_resource_id] %>" { ] disk_encryption = "CMEK" kms_key = "<%= ctx[:vars]['key_name'] %>" + desired_state = "ACTIVE" } data "google_compute_network" "my_network" { diff --git a/mmv1/templates/terraform/post_create/notebooks_instance.go.erb b/mmv1/templates/terraform/post_create/notebooks_instance.go.erb new file mode 100644 index 000000000000..a70d7cb313a5 --- /dev/null +++ b/mmv1/templates/terraform/post_create/notebooks_instance.go.erb @@ -0,0 +1,13 @@ +if err := waitForNotebooksInstanceActive(d, config, d.Timeout(schema.TimeoutCreate) - time.Minute); err != nil { + return fmt.Errorf("Notebook instance %q did not reach ACTIVE state: %q", d.Get("name").(string), err) +} + +if p, ok := d.GetOk("desired_state"); ok && p.(string) == "STOPPED" { + dRes, err := modifyNotebooksInstanceState(config, d, project, billingProject, userAgent, "stop") + if err != nil { + return err + } + if err := waitForNotebooksOperation(config, d, project, billingProject, userAgent, dRes); err != nil { + return fmt.Errorf("Error stopping Notebook Instance: %s", err) + } +} diff --git a/mmv1/templates/terraform/post_update/notebooks_instance.go.erb b/mmv1/templates/terraform/post_update/notebooks_instance.go.erb new file mode 100644 index 000000000000..819cff2d1dd2 --- /dev/null +++ b/mmv1/templates/terraform/post_update/notebooks_instance.go.erb @@ -0,0 +1,21 @@ +name := d.Get("name").(string) +state := d.Get("state").(string) +desired_state := d.Get("desired_state").(string) + +if state != desired_state { + verb := "start" + if desired_state == "STOPPED" { + verb = "stop" + } + pRes, err := modifyNotebooksInstanceState(config, d, project, billingProject, userAgent, verb) + if err != nil { + return err + } + + if err := waitForNotebooksOperation(config, d, project, billingProject, userAgent, pRes); err != nil { + return fmt.Errorf("Error waiting to modify Notebook Instance state: %s", err) + } + +} else { + log.Printf("[DEBUG] Notebook Instance %q has state %q.", name, state) +} diff --git a/mmv1/third_party/terraform/services/notebooks/resource_notebooks_instance_state_test.go.erb b/mmv1/third_party/terraform/services/notebooks/resource_notebooks_instance_state_test.go.erb new file mode 100644 index 000000000000..ff99d9c7cc31 --- /dev/null +++ b/mmv1/third_party/terraform/services/notebooks/resource_notebooks_instance_state_test.go.erb @@ -0,0 +1,85 @@ +<% autogen_exception -%> +package notebooks_test + +<% unless version == 'ga' %> +import ( + "fmt" + "testing" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" + "github.com/hashicorp/terraform-provider-google/google/acctest" +) + +func TestAccNotebooksInstance_state(t *testing.T) { + t.Parallel() + + prefix := fmt.Sprintf("%d", acctest.RandInt(t)) + name := fmt.Sprintf("tf-%s", prefix) + + acctest.VcrTest(t, resource.TestCase{ + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories(t), + Steps: []resource.TestStep{ + { + Config: testAccNotebooksInstance_basic_active(name), + }, + { + ResourceName: "google_notebooks_instance.test", + ImportState: true, + ImportStateVerify: true, + ExpectNonEmptyPlan: true, + ImportStateVerifyIgnore: []string{"container_image", "metadata", "vm_image","desired_state", "update_time"}, + }, + { + Config: testAccNotebooksInstance_basic_stopped(name), + }, + { + ResourceName: "google_notebooks_instance.test", + ImportState: true, + ImportStateVerify: true, + ExpectNonEmptyPlan: true, + ImportStateVerifyIgnore: []string{"container_image", "metadata", "vm_image","desired_state", "update_time"}, + }, + { + Config: testAccNotebooksInstance_basic_active(name), + }, + { + ResourceName: "google_notebooks_instance.test", + ImportState: true, + ImportStateVerify: true, + ExpectNonEmptyPlan: true, + ImportStateVerifyIgnore: []string{"container_image", "metadata", "vm_image","desired_state", "update_time"}, + }, + }, + }) +} + +func testAccNotebooksInstance_basic_active(name string) string { + return fmt.Sprintf(` +resource "google_notebooks_instance" "test" { + name = "%s" + location = "us-west1-a" + machine_type = "e2-medium" + vm_image { + project = "deeplearning-platform-release" + image_family = "tf-latest-cpu" + } + desired_state = "ACTIVE" +} +`, name) +} + +func testAccNotebooksInstance_basic_stopped(name string) string { + return fmt.Sprintf(` +resource "google_notebooks_instance" "test" { + name = "%s" + location = "us-west1-a" + machine_type = "e2-medium" + vm_image { + project = "deeplearning-platform-release" + image_family = "tf-latest-cpu" + } + desired_state = "STOPPED" +} +`, name) +} +<% end -%>