From 76926a1bed15abe6b1df95298e181e93f9b17e1b Mon Sep 17 00:00:00 2001 From: norbjd Date: Fri, 1 Nov 2019 12:39:18 +0100 Subject: [PATCH 01/11] Add status argument (RUNNING/TERMINATED) to google_compute_instance - defaults to RUNNING - cannot create instance with status TERMINATED - when updating, wait until the instance reach the expected status --- google/resource_compute_instance.go | 137 +++++++-- google/resource_compute_instance_test.go | 281 ++++++++++++++++++ .../datasource_compute_instance.html.markdown | 2 + website/docs/r/compute_instance.html.markdown | 3 + 4 files changed, 401 insertions(+), 22 deletions(-) diff --git a/google/resource_compute_instance.go b/google/resource_compute_instance.go index 0eb67fba119..01d4765a1fa 100644 --- a/google/resource_compute_instance.go +++ b/google/resource_compute_instance.go @@ -12,6 +12,7 @@ import ( "github.com/hashicorp/errwrap" "github.com/hashicorp/terraform-plugin-sdk/helper/customdiff" + "github.com/hashicorp/terraform-plugin-sdk/helper/resource" "github.com/hashicorp/terraform-plugin-sdk/helper/schema" "github.com/hashicorp/terraform-plugin-sdk/helper/validation" "github.com/mitchellh/hashstructure" @@ -563,6 +564,13 @@ func resourceComputeInstance() *schema.Resource { }, }, + "status": { + Type: schema.TypeString, + Optional: true, + Default: "RUNNING", + ValidateFunc: validation.StringInSlice([]string{"RUNNING", "TERMINATED"}, false), + }, + "tags": { Type: schema.TypeSet, Optional: true, @@ -718,6 +726,10 @@ func expandComputeInstance(project string, d *schema.ResourceData, config *Confi return nil, fmt.Errorf("Error creating guest accelerators: %s", err) } + if d.Get("status") != "RUNNING" { + return nil, fmt.Errorf("On creation, status can only be RUNNING") + } + // Create the instance information return &computeBeta.Instance{ CanIpForward: d.Get("can_ip_forward").(bool), @@ -969,11 +981,61 @@ func resourceComputeInstanceRead(d *schema.ResourceData, meta interface{}) error d.Set("name", instance.Name) d.Set("description", instance.Description) d.Set("hostname", instance.Hostname) + waitUntilInstanceHaveTheExpectedStatus(config, d) + d.Set("status", instance.Status) d.SetId(instance.Name) return nil } +func getAllStatusExcept(exceptedStatus string) []string { + allStatus := []string{ + "PROVISIONING", + "REPAIRING", + "RUNNING", + "STAGING", + "STOPPED", + "STOPPING", + "SUSPENDED", + "SUSPENDING", + "TERMINATED", + } + for i, status := range allStatus { + if exceptedStatus == status { + return append(allStatus[:i], allStatus[i+1:]...) + } + } + return nil +} + +func waitUntilInstanceHaveTheExpectedStatus(config *Config, d *schema.ResourceData) error { + expectedStatus := d.Get("status").(string) + stateRefreshFunc := func() (interface{}, string, error) { + instance, err := getInstance(config, d) + if err != nil || instance == nil { + log.Printf("Error on InstanceStateRefresh: %s", err) + return nil, "", err + } + return instance.Id, instance.Status, nil + } + stateChangeConf := resource.StateChangeConf{ + Delay: 5 * time.Second, + Pending: getAllStatusExcept(expectedStatus), + Refresh: stateRefreshFunc, + Target: []string{expectedStatus}, + Timeout: 120 * time.Second, + MinTimeout: 5 * time.Second, + } + _, err := stateChangeConf.WaitForState() + + if err != nil { + return fmt.Errorf( + "Error waiting for instance to reach status %s: %s", expectedStatus, err) + } + + return nil +} + func resourceComputeInstanceUpdate(d *schema.ResourceData, meta interface{}) error { config := meta.(*Config) @@ -1101,6 +1163,31 @@ func resourceComputeInstanceUpdate(d *schema.ResourceData, meta interface{}) err d.SetPartial("scheduling") } + if d.HasChange("status") { + var op *compute.Operation + expectedStatus := d.Get("status").(string) + if expectedStatus == "RUNNING" { + op, err = config.clientCompute.Instances.Start(project, zone, instance.Name).Do() + if err != nil { + return err + } + } else if expectedStatus == "TERMINATED" { + op, err = config.clientCompute.Instances.Stop(project, zone, instance.Name).Do() + if err != nil { + return err + } + } else { + return errors.New(fmt.Sprintf( + "Unknown status : %s", expectedStatus, + )) + } + opErr := computeOperationWaitTime(config.clientCompute, op, project, "updating status", int(d.Timeout(schema.TimeoutUpdate).Minutes())) + if opErr != nil { + return opErr + } + d.SetPartial("status") + } + networkInterfacesCount := d.Get("network_interface.#").(int) // Sanity check if networkInterfacesCount != len(instance.NetworkInterfaces) { @@ -1337,18 +1424,22 @@ func resourceComputeInstanceUpdate(d *schema.ResourceData, meta interface{}) err // Attributes which can only be changed if the instance is stopped if scopesChange || d.HasChange("service_account.0.email") || d.HasChange("machine_type") || d.HasChange("min_cpu_platform") || d.HasChange("enable_display") { - if !d.Get("allow_stopping_for_update").(bool) { - return fmt.Errorf("Changing the machine_type, min_cpu_platform, service_account, or enable display on an instance requires stopping it. " + - "To acknowledge this, please set allow_stopping_for_update = true in your config.") - } - op, err := config.clientCompute.Instances.Stop(project, zone, instance.Name).Do() - if err != nil { - return errwrap.Wrapf("Error stopping instance: {{err}}", err) - } + status := d.Get("status").(string) - opErr := computeOperationWaitTime(config.clientCompute, op, project, "stopping instance", int(d.Timeout(schema.TimeoutUpdate).Minutes())) - if opErr != nil { - return opErr + if status != "TERMINATED" { + if !d.Get("allow_stopping_for_update").(bool) { + return fmt.Errorf("Changing the machine_type, min_cpu_platform, service_account, or enable display on an instance requires stopping it. " + + "To acknowledge this, please set allow_stopping_for_update = true in your config.") + } + op, err := config.clientCompute.Instances.Stop(project, zone, instance.Name).Do() + if err != nil { + return errwrap.Wrapf("Error stopping instance: {{err}}", err) + } + + opErr := computeOperationWaitTime(config.clientCompute, op, project, "stopping instance", int(d.Timeout(schema.TimeoutUpdate).Minutes())) + if opErr != nil { + return opErr + } } if d.HasChange("machine_type") { @@ -1359,7 +1450,7 @@ func resourceComputeInstanceUpdate(d *schema.ResourceData, meta interface{}) err req := &compute.InstancesSetMachineTypeRequest{ MachineType: mt.RelativeLink(), } - op, err = config.clientCompute.Instances.SetMachineType(project, zone, instance.Name, req).Do() + op, err := config.clientCompute.Instances.SetMachineType(project, zone, instance.Name, req).Do() if err != nil { return err } @@ -1381,7 +1472,7 @@ func resourceComputeInstanceUpdate(d *schema.ResourceData, meta interface{}) err req := &compute.InstancesSetMinCpuPlatformRequest{ MinCpuPlatform: minCpuPlatform.(string), } - op, err = config.clientCompute.Instances.SetMinCpuPlatform(project, zone, instance.Name, req).Do() + op, err := config.clientCompute.Instances.SetMinCpuPlatform(project, zone, instance.Name, req).Do() if err != nil { return err } @@ -1400,7 +1491,7 @@ func resourceComputeInstanceUpdate(d *schema.ResourceData, meta interface{}) err req.Email = saMap["email"].(string) req.Scopes = canonicalizeServiceScopes(convertStringSet(saMap["scopes"].(*schema.Set))) } - op, err = config.clientCompute.Instances.SetServiceAccount(project, zone, instance.Name, req).Do() + op, err := config.clientCompute.Instances.SetServiceAccount(project, zone, instance.Name, req).Do() if err != nil { return err } @@ -1416,7 +1507,7 @@ func resourceComputeInstanceUpdate(d *schema.ResourceData, meta interface{}) err EnableDisplay: d.Get("enable_display").(bool), ForceSendFields: []string{"EnableDisplay"}, } - op, err = config.clientCompute.Instances.UpdateDisplayDevice(project, zone, instance.Name, req).Do() + op, err := config.clientCompute.Instances.UpdateDisplayDevice(project, zone, instance.Name, req).Do() if err != nil { return fmt.Errorf("Error updating display device: %s", err) } @@ -1427,14 +1518,16 @@ func resourceComputeInstanceUpdate(d *schema.ResourceData, meta interface{}) err d.SetPartial("enable_display") } - op, err = config.clientCompute.Instances.Start(project, zone, instance.Name).Do() - if err != nil { - return errwrap.Wrapf("Error starting instance: {{err}}", err) - } + if status != "TERMINATED" { + op, err := config.clientCompute.Instances.Start(project, zone, instance.Name).Do() + if err != nil { + return errwrap.Wrapf("Error starting instance: {{err}}", err) + } - opErr = computeOperationWaitTime(config.clientCompute, op, project, "starting instance", int(d.Timeout(schema.TimeoutUpdate).Minutes())) - if opErr != nil { - return opErr + opErr := computeOperationWaitTime(config.clientCompute, op, project, "starting instance", int(d.Timeout(schema.TimeoutUpdate).Minutes())) + if opErr != nil { + return opErr + } } } diff --git a/google/resource_compute_instance_test.go b/google/resource_compute_instance_test.go index 778a6660ac9..c9d4a674549 100644 --- a/google/resource_compute_instance_test.go +++ b/google/resource_compute_instance_test.go @@ -2,6 +2,7 @@ package google import ( "fmt" + "regexp" "strconv" "strings" "testing" @@ -1223,6 +1224,116 @@ func TestAccComputeInstance_enableDisplay(t *testing.T) { }) } +func TestAccComputeInstance_statusTerminatedUpdateFields(t *testing.T) { + t.Parallel() + + var instance compute.Instance + var instanceName = fmt.Sprintf("instance-test-%s", acctest.RandString(10)) + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckComputeInstanceDestroy, + Steps: []resource.TestStep{ + { + Config: testAccComputeInstance_statusRunning(instanceName), + Check: resource.ComposeTestCheckFunc( + testAccCheckComputeInstanceExists( + "google_compute_instance.foobar", &instance), + testAccCheckComputeInstanceHasStatusRunning(&instance), + ), + }, + { + Config: testAccComputeInstance_statusTerminated(instanceName), + Check: resource.ComposeTestCheckFunc( + testAccCheckComputeInstanceExists( + "google_compute_instance.foobar", &instance), + testAccCheckComputeInstanceHasStatusTerminated(&instance), + ), + }, + { + Config: testAccComputeInstance_statusTerminatedUpdate(instanceName), + Check: resource.ComposeTestCheckFunc( + testAccCheckComputeInstanceExists( + "google_compute_instance.foobar", &instance), + testAccCheckComputeInstanceMetadata( + &instance, "bar", "baz"), + testAccCheckComputeInstanceLabel(&instance, "only_me", "nothing_else"), + testAccCheckComputeInstanceTag(&instance, "baz"), + testAccCheckComputeInstanceAccessConfig(&instance), + testAccCheckComputeInstanceHasStatusTerminated(&instance), + ), + }, + { + Config: testAccComputeInstance_statusTerminatedUpdateRequiringStopping(instanceName), + Check: resource.ComposeTestCheckFunc( + testAccCheckComputeInstanceExists( + "google_compute_instance.foobar", &instance), + testAccCheckComputeInstanceHasMachineType(&instance, "n1-standard-2"), + testAccCheckComputeInstanceHasStatusTerminated(&instance), + ), + }, + }, + }) +} + +func TestAccComputeInstance_statusTerminatedToRunning(t *testing.T) { + t.Parallel() + + var instance compute.Instance + var instanceName = fmt.Sprintf("instance-test-%s", acctest.RandString(10)) + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckComputeInstanceDestroy, + Steps: []resource.TestStep{ + { + Config: testAccComputeInstance_statusRunning(instanceName), + Check: resource.ComposeTestCheckFunc( + testAccCheckComputeInstanceExists( + "google_compute_instance.foobar", &instance), + testAccCheckComputeInstanceHasStatusRunning(&instance), + ), + }, + { + Config: testAccComputeInstance_statusTerminated(instanceName), + Check: resource.ComposeTestCheckFunc( + testAccCheckComputeInstanceExists( + "google_compute_instance.foobar", &instance), + testAccCheckComputeInstanceHasStatusTerminated(&instance), + ), + }, + { + Config: testAccComputeInstance_statusRunning(instanceName), + Check: resource.ComposeTestCheckFunc( + testAccCheckComputeInstanceExists( + "google_compute_instance.foobar", &instance), + testAccCheckComputeInstanceHasStatusRunning(&instance), + ), + }, + }, + }) +} + +func TestAccComputeInstance_statusTerminatedOnCreation(t *testing.T) { + t.Parallel() + + var instanceName = fmt.Sprintf("instance-test-%s", acctest.RandString(10)) + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckComputeInstanceDestroy, + Steps: []resource.TestStep{ + { + Config: testAccComputeInstance_statusTerminated(instanceName), + ExpectError: regexp.MustCompile("On creation, status can only be RUNNING"), + }, + }, + }) +} + func testAccCheckComputeInstanceUpdateMachineType(n string) resource.TestCheckFunc { return func(s *terraform.State) error { rs, ok := s.RootModule().Resources[n] @@ -1744,6 +1855,16 @@ func testAccCheckComputeInstanceHasMinCpuPlatform(instance *compute.Instance, mi } } +func testAccCheckComputeInstanceHasMachineType(instance *compute.Instance, machineType string) resource.TestCheckFunc { + return func(s *terraform.State) error { + if !strings.HasSuffix(instance.MachineType, "machineTypes/"+machineType) { + return fmt.Errorf("Wrong machine type: expected to end with %s, got %s", machineType, instance.MachineType) + } + + return nil + } +} + func testAccCheckComputeInstanceHasAliasIpRange(instance *compute.Instance, subnetworkRangeName, iPCidrRange string) resource.TestCheckFunc { return func(s *terraform.State) error { for _, networkInterface := range instance.NetworkInterfaces { @@ -1809,6 +1930,24 @@ func testAccCheckComputeInstanceLacksShieldedVmConfig(instance *computeBeta.Inst } } +func testAccCheckComputeInstanceHasStatusRunning(instance *compute.Instance) resource.TestCheckFunc { + return func(s *terraform.State) error { + if instance.Status != "RUNNING" { + return fmt.Errorf("Instance is not RUNNING, state: %s", instance.Status) + } + return nil + } +} + +func testAccCheckComputeInstanceHasStatusTerminated(instance *compute.Instance) resource.TestCheckFunc { + return func(s *terraform.State) error { + if instance.Status != "TERMINATED" { + return fmt.Errorf("Instance is not TERMINATED, state: %s", instance.Status) + } + return nil + } +} + func testAccComputeInstance_basic(instance string) string { return fmt.Sprintf(` data "google_compute_image" "my_image" { @@ -3718,3 +3857,145 @@ resource "google_compute_instance" "foobar" { } `, instance) } + +func testAccComputeInstance_statusRunning(instance string) string { + return fmt.Sprintf(` +data "google_compute_image" "my_image" { + family = "debian-9" + project = "debian-cloud" +} + +resource "google_compute_instance" "foobar" { + name = "%s" + machine_type = "n1-standard-1" + zone = "us-central1-a" + can_ip_forward = false + tags = ["foo", "bar"] + + boot_disk { + initialize_params{ + image = "${data.google_compute_image.my_image.self_link}" + } + } + + network_interface { + network = "default" + } + + status = "RUNNING" + + metadata = { + foo = "bar" + } +} +`, instance) +} + +func testAccComputeInstance_statusTerminated(instance string) string { + return fmt.Sprintf(` +data "google_compute_image" "my_image" { + family = "debian-9" + project = "debian-cloud" +} + +resource "google_compute_instance" "foobar" { + name = "%s" + machine_type = "n1-standard-1" + zone = "us-central1-a" + can_ip_forward = false + tags = ["foo", "bar"] + + boot_disk { + initialize_params{ + image = "${data.google_compute_image.my_image.self_link}" + } + } + + network_interface { + network = "default" + } + + status = "TERMINATED" + + metadata = { + foo = "bar" + } +} +`, instance) +} + +func testAccComputeInstance_statusTerminatedUpdate(instance string) string { + return fmt.Sprintf(` +data "google_compute_image" "my_image" { + family = "debian-9" + project = "debian-cloud" +} + +resource "google_compute_instance" "foobar" { + name = "%s" + machine_type = "n1-standard-1" + zone = "us-central1-a" + can_ip_forward = false + tags = ["baz"] + + boot_disk { + initialize_params{ + image = "${data.google_compute_image.my_image.self_link}" + } + } + + network_interface { + network = "default" + access_config { } + } + + status = "TERMINATED" + + metadata = { + bar = "baz" + } + + labels = { + only_me = "nothing_else" + } +} +`, instance) +} + +func testAccComputeInstance_statusTerminatedUpdateRequiringStopping(instance string) string { + return fmt.Sprintf(` +data "google_compute_image" "my_image" { + family = "debian-9" + project = "debian-cloud" +} + +resource "google_compute_instance" "foobar" { + name = "%s" + machine_type = "n1-standard-2" + zone = "us-central1-a" + can_ip_forward = false + tags = ["baz"] + + boot_disk { + initialize_params{ + image = "${data.google_compute_image.my_image.self_link}" + } + } + + network_interface { + network = "default" + access_config { } + } + + status = "TERMINATED" + + metadata = { + bar = "baz" + } + + labels = { + only_me = "nothing_else" + } +} +`, instance) +} diff --git a/website/docs/d/datasource_compute_instance.html.markdown b/website/docs/d/datasource_compute_instance.html.markdown index 6fad0aedd94..ebb5152b646 100644 --- a/website/docs/d/datasource_compute_instance.html.markdown +++ b/website/docs/d/datasource_compute_instance.html.markdown @@ -71,6 +71,8 @@ The following arguments are supported: * `service_account` - The service account to attach to the instance. Structure is documented below. +* `status` - The status of the instance. + * `tags` - The list of tags attached to the instance. * `instance_id` - The server-assigned unique identifier of this instance. diff --git a/website/docs/r/compute_instance.html.markdown b/website/docs/r/compute_instance.html.markdown index 9ab8a516ff2..e34b3a7ab03 100644 --- a/website/docs/r/compute_instance.html.markdown +++ b/website/docs/r/compute_instance.html.markdown @@ -133,6 +133,9 @@ The following arguments are supported: Structure is documented below. **Note**: [`allow_stopping_for_update`](#allow_stopping_for_update) must be set to true in order to update this field. +* `status` - (Optional) Status of the instance. + Either "RUNNING" or "TERMINATED", defaults to "RUNNING" + * `tags` - (Optional) A list of tags to attach to the instance. * `shielded_instance_config` - (Optional) Enable [Shielded VM](https://cloud.google.com/security/shielded-cloud/shielded-vm) on this instance. Shielded VM provides verifiable integrity to prevent against malware and rootkits. Defaults to disabled. Structure is documented below. From b30b483f3a85388218ff5facbf933d5874b7ce91 Mon Sep 17 00:00:00 2001 From: norbjd Date: Fri, 1 Nov 2019 21:03:18 +0100 Subject: [PATCH 02/11] Fix lint issues --- google/resource_compute_instance.go | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/google/resource_compute_instance.go b/google/resource_compute_instance.go index 01d4765a1fa..977142f7dfa 100644 --- a/google/resource_compute_instance.go +++ b/google/resource_compute_instance.go @@ -981,7 +981,10 @@ func resourceComputeInstanceRead(d *schema.ResourceData, meta interface{}) error d.Set("name", instance.Name) d.Set("description", instance.Description) d.Set("hostname", instance.Hostname) - waitUntilInstanceHaveTheExpectedStatus(config, d) + err = waitUntilInstanceHaveTheExpectedStatus(config, d) + if err != nil { + return fmt.Errorf("Error waiting for status: %s", err) + } d.Set("status", instance.Status) d.SetId(instance.Name) @@ -1177,9 +1180,7 @@ func resourceComputeInstanceUpdate(d *schema.ResourceData, meta interface{}) err return err } } else { - return errors.New(fmt.Sprintf( - "Unknown status : %s", expectedStatus, - )) + return fmt.Errorf("Unknown status : %s", expectedStatus) } opErr := computeOperationWaitTime(config.clientCompute, op, project, "updating status", int(d.Timeout(schema.TimeoutUpdate).Minutes())) if opErr != nil { From cab4af50fa1dd1d373a5989839e32c6bffd88825 Mon Sep 17 00:00:00 2001 From: norbjd Date: Thu, 5 Dec 2019 12:51:07 +0100 Subject: [PATCH 03/11] Move waitUntilInstanceHaveTheExpectedStatus call to resourceComputeInstanceCreate --- google/resource_compute_instance.go | 105 ++++++++++++++-------------- 1 file changed, 53 insertions(+), 52 deletions(-) diff --git a/google/resource_compute_instance.go b/google/resource_compute_instance.go index 977142f7dfa..a043efd959b 100644 --- a/google/resource_compute_instance.go +++ b/google/resource_compute_instance.go @@ -753,6 +753,54 @@ func expandComputeInstance(project string, d *schema.ResourceData, config *Confi }, nil } +func getAllStatusExcept(exceptedStatus string) []string { + allStatus := []string{ + "PROVISIONING", + "REPAIRING", + "RUNNING", + "STAGING", + "STOPPED", + "STOPPING", + "SUSPENDED", + "SUSPENDING", + "TERMINATED", + } + for i, status := range allStatus { + if exceptedStatus == status { + return append(allStatus[:i], allStatus[i+1:]...) + } + } + return nil +} + +func waitUntilInstanceHaveTheExpectedStatus(config *Config, d *schema.ResourceData) error { + expectedStatus := d.Get("status").(string) + stateRefreshFunc := func() (interface{}, string, error) { + instance, err := getInstance(config, d) + if err != nil || instance == nil { + log.Printf("Error on InstanceStateRefresh: %s", err) + return nil, "", err + } + return instance.Id, instance.Status, nil + } + stateChangeConf := resource.StateChangeConf{ + Delay: 5 * time.Second, + Pending: getAllStatusExcept(expectedStatus), + Refresh: stateRefreshFunc, + Target: []string{expectedStatus}, + Timeout: 120 * time.Second, + MinTimeout: 5 * time.Second, + } + _, err := stateChangeConf.WaitForState() + + if err != nil { + return fmt.Errorf( + "Error waiting for instance to reach status %s: %s", expectedStatus, err) + } + + return nil +} + func resourceComputeInstanceCreate(d *schema.ResourceData, meta interface{}) error { config := meta.(*Config) @@ -798,6 +846,11 @@ func resourceComputeInstanceCreate(d *schema.ResourceData, meta interface{}) err return waitErr } + err = waitUntilInstanceHaveTheExpectedStatus(config, d) + if err != nil { + return fmt.Errorf("Error waiting for status: %s", err) + } + return resourceComputeInstanceRead(d, meta) } @@ -981,64 +1034,12 @@ func resourceComputeInstanceRead(d *schema.ResourceData, meta interface{}) error d.Set("name", instance.Name) d.Set("description", instance.Description) d.Set("hostname", instance.Hostname) - err = waitUntilInstanceHaveTheExpectedStatus(config, d) - if err != nil { - return fmt.Errorf("Error waiting for status: %s", err) - } d.Set("status", instance.Status) d.SetId(instance.Name) return nil } -func getAllStatusExcept(exceptedStatus string) []string { - allStatus := []string{ - "PROVISIONING", - "REPAIRING", - "RUNNING", - "STAGING", - "STOPPED", - "STOPPING", - "SUSPENDED", - "SUSPENDING", - "TERMINATED", - } - for i, status := range allStatus { - if exceptedStatus == status { - return append(allStatus[:i], allStatus[i+1:]...) - } - } - return nil -} - -func waitUntilInstanceHaveTheExpectedStatus(config *Config, d *schema.ResourceData) error { - expectedStatus := d.Get("status").(string) - stateRefreshFunc := func() (interface{}, string, error) { - instance, err := getInstance(config, d) - if err != nil || instance == nil { - log.Printf("Error on InstanceStateRefresh: %s", err) - return nil, "", err - } - return instance.Id, instance.Status, nil - } - stateChangeConf := resource.StateChangeConf{ - Delay: 5 * time.Second, - Pending: getAllStatusExcept(expectedStatus), - Refresh: stateRefreshFunc, - Target: []string{expectedStatus}, - Timeout: 120 * time.Second, - MinTimeout: 5 * time.Second, - } - _, err := stateChangeConf.WaitForState() - - if err != nil { - return fmt.Errorf( - "Error waiting for instance to reach status %s: %s", expectedStatus, err) - } - - return nil -} - func resourceComputeInstanceUpdate(d *schema.ResourceData, meta interface{}) error { config := meta.(*Config) From 8e0ebc2a8fabb975ef7246639ead5d17cc1354dd Mon Sep 17 00:00:00 2001 From: norbjd Date: Fri, 6 Dec 2019 09:55:46 +0100 Subject: [PATCH 04/11] Add acceptance test (non-regression) --- google/resource_compute_instance_test.go | 62 ++++++++++++++++++++++++ 1 file changed, 62 insertions(+) diff --git a/google/resource_compute_instance_test.go b/google/resource_compute_instance_test.go index c9d4a674549..971d5da3e5b 100644 --- a/google/resource_compute_instance_test.go +++ b/google/resource_compute_instance_test.go @@ -1334,6 +1334,42 @@ func TestAccComputeInstance_statusTerminatedOnCreation(t *testing.T) { }) } +func TestAccComputeInstance_statusTerminatedManuallyAndRestart(t *testing.T) { + t.Parallel() + + var instance compute.Instance + var instanceName = fmt.Sprintf("instance-test-%s", acctest.RandString(10)) + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckComputeInstanceDestroy, + Steps: []resource.TestStep{ + { + Config: testAccComputeInstance_basic(instanceName), + Check: resource.ComposeTestCheckFunc( + testAccCheckComputeInstanceExists( + "google_compute_instance.foobar", &instance), + testAccCheckComputeInstanceHasStatusRunning(&instance), + testAccCheckComputeInstanceStop("google_compute_instance.foobar"), + testAccCheckComputeInstanceExists( + "google_compute_instance.foobar", &instance), + testAccCheckComputeInstanceHasStatusTerminated(&instance), + ), + ExpectNonEmptyPlan: true, + }, + { + Config: testAccComputeInstance_basic(instanceName), + Check: resource.ComposeTestCheckFunc( + testAccCheckComputeInstanceExists( + "google_compute_instance.foobar", &instance), + testAccCheckComputeInstanceHasStatusRunning(&instance), + ), + }, + }, + }) +} + func testAccCheckComputeInstanceUpdateMachineType(n string) resource.TestCheckFunc { return func(s *terraform.State) error { rs, ok := s.RootModule().Resources[n] @@ -1948,6 +1984,32 @@ func testAccCheckComputeInstanceHasStatusTerminated(instance *compute.Instance) } } +func testAccCheckComputeInstanceStop(n string) resource.TestCheckFunc { + return func(s *terraform.State) error { + rs, ok := s.RootModule().Resources[n] + if !ok { + return fmt.Errorf("Not found: %s", n) + } + + if rs.Primary.ID == "" { + return fmt.Errorf("No ID is set") + } + + config := testAccProvider.Meta().(*Config) + + op, err := config.clientCompute.Instances.Stop(config.Project, rs.Primary.Attributes["zone"], rs.Primary.ID).Do() + if err != nil { + return fmt.Errorf("Could not stop instance: %s", err) + } + err = computeOperationWait(config.clientCompute, op, config.Project, "Waiting on stop") + if err != nil { + return fmt.Errorf("Could not stop instance: %s", err) + } + + return nil + } +} + func testAccComputeInstance_basic(instance string) string { return fmt.Sprintf(` data "google_compute_image" "my_image" { From 6f592334e93778c1b00e410e0f93b77149e91a2d Mon Sep 17 00:00:00 2001 From: norbjd Date: Sat, 8 Feb 2020 16:04:25 +0100 Subject: [PATCH 05/11] Review comments - rename status to desired_status - add CustomizeDiff to deny TERMINATED desired_status on instance creation - update acceptance tests --- google/resource_compute_instance.go | 162 ++++++++++++----------- google/resource_compute_instance_test.go | 153 ++++++++++++++++----- 2 files changed, 202 insertions(+), 113 deletions(-) diff --git a/google/resource_compute_instance.go b/google/resource_compute_instance.go index a043efd959b..8648b9f7be4 100644 --- a/google/resource_compute_instance.go +++ b/google/resource_compute_instance.go @@ -564,10 +564,9 @@ func resourceComputeInstance() *schema.Resource { }, }, - "status": { + "desired_status": { Type: schema.TypeString, Optional: true, - Default: "RUNNING", ValidateFunc: validation.StringInSlice([]string{"RUNNING", "TERMINATED"}, false), }, @@ -628,6 +627,7 @@ func resourceComputeInstance() *schema.Resource { }, suppressEmptyGuestAcceleratorDiff, ), + desiredStatusDiff, ), } } @@ -726,10 +726,6 @@ func expandComputeInstance(project string, d *schema.ResourceData, config *Confi return nil, fmt.Errorf("Error creating guest accelerators: %s", err) } - if d.Get("status") != "RUNNING" { - return nil, fmt.Errorf("On creation, status can only be RUNNING") - } - // Create the instance information return &computeBeta.Instance{ CanIpForward: d.Get("can_ip_forward").(bool), @@ -753,7 +749,8 @@ func expandComputeInstance(project string, d *schema.ResourceData, config *Confi }, nil } -func getAllStatusExcept(exceptedStatus string) []string { +// return all possible Compute instances status except the one passed as parameter +func getAllStatusBut(status string) []string { allStatus := []string{ "PROVISIONING", "REPAIRING", @@ -765,37 +762,40 @@ func getAllStatusExcept(exceptedStatus string) []string { "SUSPENDING", "TERMINATED", } - for i, status := range allStatus { - if exceptedStatus == status { + for i, s := range allStatus { + if status == s { return append(allStatus[:i], allStatus[i+1:]...) } } return nil } -func waitUntilInstanceHaveTheExpectedStatus(config *Config, d *schema.ResourceData) error { - expectedStatus := d.Get("status").(string) - stateRefreshFunc := func() (interface{}, string, error) { - instance, err := getInstance(config, d) - if err != nil || instance == nil { - log.Printf("Error on InstanceStateRefresh: %s", err) - return nil, "", err +func waitUntilInstanceHasDesiredStatus(config *Config, d *schema.ResourceData) error { + desiredStatus := d.Get("desired_status").(string) + + if desiredStatus != "" { + stateRefreshFunc := func() (interface{}, string, error) { + instance, err := getInstance(config, d) + if err != nil || instance == nil { + log.Printf("Error on InstanceStateRefresh: %s", err) + return nil, "", err + } + return instance.Id, instance.Status, nil } - return instance.Id, instance.Status, nil - } - stateChangeConf := resource.StateChangeConf{ - Delay: 5 * time.Second, - Pending: getAllStatusExcept(expectedStatus), - Refresh: stateRefreshFunc, - Target: []string{expectedStatus}, - Timeout: 120 * time.Second, - MinTimeout: 5 * time.Second, - } - _, err := stateChangeConf.WaitForState() + stateChangeConf := resource.StateChangeConf{ + Delay: 5 * time.Second, + Pending: getAllStatusBut(desiredStatus), + Refresh: stateRefreshFunc, + Target: []string{desiredStatus}, + Timeout: d.Timeout(schema.TimeoutUpdate), + MinTimeout: 2 * time.Second, + } + _, err := stateChangeConf.WaitForState() - if err != nil { - return fmt.Errorf( - "Error waiting for instance to reach status %s: %s", expectedStatus, err) + if err != nil { + return fmt.Errorf( + "Error waiting for instance to reach desired status %s: %s", desiredStatus, err) + } } return nil @@ -846,7 +846,7 @@ func resourceComputeInstanceCreate(d *schema.ResourceData, meta interface{}) err return waitErr } - err = waitUntilInstanceHaveTheExpectedStatus(config, d) + err = waitUntilInstanceHasDesiredStatus(config, d) if err != nil { return fmt.Errorf("Error waiting for status: %s", err) } @@ -1034,7 +1034,11 @@ func resourceComputeInstanceRead(d *schema.ResourceData, meta interface{}) error d.Set("name", instance.Name) d.Set("description", instance.Description) d.Set("hostname", instance.Hostname) - d.Set("status", instance.Status) + + if d.Get("desired_status") != "" { + d.Set("desired_status", instance.Status) + } + d.SetId(instance.Name) return nil @@ -1167,27 +1171,29 @@ func resourceComputeInstanceUpdate(d *schema.ResourceData, meta interface{}) err d.SetPartial("scheduling") } - if d.HasChange("status") { - var op *compute.Operation - expectedStatus := d.Get("status").(string) - if expectedStatus == "RUNNING" { - op, err = config.clientCompute.Instances.Start(project, zone, instance.Name).Do() - if err != nil { - return err + if d.HasChange("desired_status") { + desiredStatus := d.Get("desired_status").(string) + + if desiredStatus != "" { + var op *compute.Operation + + if desiredStatus == "RUNNING" { + op, err = config.clientCompute.Instances.Start(project, zone, instance.Name).Do() + if err != nil { + return err + } + } else if desiredStatus == "TERMINATED" { + op, err = config.clientCompute.Instances.Stop(project, zone, instance.Name).Do() + if err != nil { + return err + } } - } else if expectedStatus == "TERMINATED" { - op, err = config.clientCompute.Instances.Stop(project, zone, instance.Name).Do() - if err != nil { - return err + opErr := computeOperationWaitTime(config.clientCompute, op, project, "updating status", int(d.Timeout(schema.TimeoutUpdate).Minutes())) + if opErr != nil { + return opErr } - } else { - return fmt.Errorf("Unknown status : %s", expectedStatus) - } - opErr := computeOperationWaitTime(config.clientCompute, op, project, "updating status", int(d.Timeout(schema.TimeoutUpdate).Minutes())) - if opErr != nil { - return opErr } - d.SetPartial("status") + d.SetPartial("desired_status") } networkInterfacesCount := d.Get("network_interface.#").(int) @@ -1426,22 +1432,18 @@ func resourceComputeInstanceUpdate(d *schema.ResourceData, meta interface{}) err // Attributes which can only be changed if the instance is stopped if scopesChange || d.HasChange("service_account.0.email") || d.HasChange("machine_type") || d.HasChange("min_cpu_platform") || d.HasChange("enable_display") { - status := d.Get("status").(string) - - if status != "TERMINATED" { - if !d.Get("allow_stopping_for_update").(bool) { - return fmt.Errorf("Changing the machine_type, min_cpu_platform, service_account, or enable display on an instance requires stopping it. " + - "To acknowledge this, please set allow_stopping_for_update = true in your config.") - } - op, err := config.clientCompute.Instances.Stop(project, zone, instance.Name).Do() - if err != nil { - return errwrap.Wrapf("Error stopping instance: {{err}}", err) - } + if !d.Get("allow_stopping_for_update").(bool) { + return fmt.Errorf("Changing the machine_type, min_cpu_platform, service_account, or enable display on an instance requires stopping it. " + + "To acknowledge this, please set allow_stopping_for_update = true in your config.") + } + op, err := config.clientCompute.Instances.Stop(project, zone, instance.Name).Do() + if err != nil { + return errwrap.Wrapf("Error stopping instance: {{err}}", err) + } - opErr := computeOperationWaitTime(config.clientCompute, op, project, "stopping instance", int(d.Timeout(schema.TimeoutUpdate).Minutes())) - if opErr != nil { - return opErr - } + opErr := computeOperationWaitTime(config.clientCompute, op, project, "stopping instance", int(d.Timeout(schema.TimeoutUpdate).Minutes())) + if opErr != nil { + return opErr } if d.HasChange("machine_type") { @@ -1519,18 +1521,6 @@ func resourceComputeInstanceUpdate(d *schema.ResourceData, meta interface{}) err } d.SetPartial("enable_display") } - - if status != "TERMINATED" { - op, err := config.clientCompute.Instances.Start(project, zone, instance.Name).Do() - if err != nil { - return errwrap.Wrapf("Error starting instance: {{err}}", err) - } - - opErr := computeOperationWaitTime(config.clientCompute, op, project, "starting instance", int(d.Timeout(schema.TimeoutUpdate).Minutes())) - if opErr != nil { - return opErr - } - } } if d.HasChange("shielded_instance_config") { @@ -1678,6 +1668,24 @@ func suppressEmptyGuestAcceleratorDiff(d *schema.ResourceDiff, meta interface{}) return nil } +func desiredStatusDiff(diff *schema.ResourceDiff, meta interface{}) error { + // when creating an instance, name is not set + oldName, _ := diff.GetChange("name") + + if oldName == nil || oldName == "" { + _, newDesiredStatus := diff.GetChange("desired_status") + + if newDesiredStatus == nil || newDesiredStatus == "" { + return nil + } else if newDesiredStatus != "RUNNING" { + return fmt.Errorf("When creating an instance, desired_status can only accept RUNNING value") + } + return nil + } + + return nil +} + func resourceComputeInstanceDelete(d *schema.ResourceData, meta interface{}) error { config := meta.(*Config) diff --git a/google/resource_compute_instance_test.go b/google/resource_compute_instance_test.go index 971d5da3e5b..d8d7dbadcde 100644 --- a/google/resource_compute_instance_test.go +++ b/google/resource_compute_instance_test.go @@ -1224,7 +1224,7 @@ func TestAccComputeInstance_enableDisplay(t *testing.T) { }) } -func TestAccComputeInstance_statusTerminatedUpdateFields(t *testing.T) { +func TestAccComputeInstance_desiredStatusOnCreation(t *testing.T) { t.Parallel() var instance compute.Instance @@ -1236,40 +1236,52 @@ func TestAccComputeInstance_statusTerminatedUpdateFields(t *testing.T) { CheckDestroy: testAccCheckComputeInstanceDestroy, Steps: []resource.TestStep{ { - Config: testAccComputeInstance_statusRunning(instanceName), + Config: testAccComputeInstance_desiredStatusTerminated(instanceName), + ExpectError: regexp.MustCompile("When creating an instance, desired_status can only accept RUNNING value"), + }, + { + Config: testAccComputeInstance_desiredStatusRunning(instanceName), Check: resource.ComposeTestCheckFunc( testAccCheckComputeInstanceExists( "google_compute_instance.foobar", &instance), testAccCheckComputeInstanceHasStatusRunning(&instance), ), }, + }, + }) +} + +func TestAccComputeInstance_desiredStatusSetAfterCreation(t *testing.T) { + t.Parallel() + + var instance compute.Instance + var instanceName = fmt.Sprintf("instance-test-%s", acctest.RandString(10)) + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckComputeInstanceDestroy, + Steps: []resource.TestStep{ { - Config: testAccComputeInstance_statusTerminated(instanceName), + Config: testAccComputeInstance_desiredStatusNotSet(instanceName), Check: resource.ComposeTestCheckFunc( testAccCheckComputeInstanceExists( "google_compute_instance.foobar", &instance), - testAccCheckComputeInstanceHasStatusTerminated(&instance), ), }, { - Config: testAccComputeInstance_statusTerminatedUpdate(instanceName), + Config: testAccComputeInstance_desiredStatusRunning(instanceName), Check: resource.ComposeTestCheckFunc( testAccCheckComputeInstanceExists( "google_compute_instance.foobar", &instance), - testAccCheckComputeInstanceMetadata( - &instance, "bar", "baz"), - testAccCheckComputeInstanceLabel(&instance, "only_me", "nothing_else"), - testAccCheckComputeInstanceTag(&instance, "baz"), - testAccCheckComputeInstanceAccessConfig(&instance), - testAccCheckComputeInstanceHasStatusTerminated(&instance), + testAccCheckComputeInstanceHasStatusRunning(&instance), ), }, { - Config: testAccComputeInstance_statusTerminatedUpdateRequiringStopping(instanceName), + Config: testAccComputeInstance_desiredStatusTerminated(instanceName), Check: resource.ComposeTestCheckFunc( testAccCheckComputeInstanceExists( "google_compute_instance.foobar", &instance), - testAccCheckComputeInstanceHasMachineType(&instance, "n1-standard-2"), testAccCheckComputeInstanceHasStatusTerminated(&instance), ), }, @@ -1277,7 +1289,7 @@ func TestAccComputeInstance_statusTerminatedUpdateFields(t *testing.T) { }) } -func TestAccComputeInstance_statusTerminatedToRunning(t *testing.T) { +func TestAccComputeInstance_desiredStatusUnsetAfterCreation(t *testing.T) { t.Parallel() var instance compute.Instance @@ -1289,7 +1301,7 @@ func TestAccComputeInstance_statusTerminatedToRunning(t *testing.T) { CheckDestroy: testAccCheckComputeInstanceDestroy, Steps: []resource.TestStep{ { - Config: testAccComputeInstance_statusRunning(instanceName), + Config: testAccComputeInstance_desiredStatusRunning(instanceName), Check: resource.ComposeTestCheckFunc( testAccCheckComputeInstanceExists( "google_compute_instance.foobar", &instance), @@ -1297,7 +1309,7 @@ func TestAccComputeInstance_statusTerminatedToRunning(t *testing.T) { ), }, { - Config: testAccComputeInstance_statusTerminated(instanceName), + Config: testAccComputeInstance_desiredStatusTerminated(instanceName), Check: resource.ComposeTestCheckFunc( testAccCheckComputeInstanceExists( "google_compute_instance.foobar", &instance), @@ -1305,20 +1317,21 @@ func TestAccComputeInstance_statusTerminatedToRunning(t *testing.T) { ), }, { - Config: testAccComputeInstance_statusRunning(instanceName), + Config: testAccComputeInstance_desiredStatusNotSet(instanceName), Check: resource.ComposeTestCheckFunc( testAccCheckComputeInstanceExists( "google_compute_instance.foobar", &instance), - testAccCheckComputeInstanceHasStatusRunning(&instance), + testAccCheckComputeInstanceHasStatusTerminated(&instance), ), }, }, }) } -func TestAccComputeInstance_statusTerminatedOnCreation(t *testing.T) { +func TestAccComputeInstance_desiredStatusTerminatedToRunning(t *testing.T) { t.Parallel() + var instance compute.Instance var instanceName = fmt.Sprintf("instance-test-%s", acctest.RandString(10)) resource.Test(t, resource.TestCase{ @@ -1327,14 +1340,33 @@ func TestAccComputeInstance_statusTerminatedOnCreation(t *testing.T) { CheckDestroy: testAccCheckComputeInstanceDestroy, Steps: []resource.TestStep{ { - Config: testAccComputeInstance_statusTerminated(instanceName), - ExpectError: regexp.MustCompile("On creation, status can only be RUNNING"), + Config: testAccComputeInstance_desiredStatusNotSet(instanceName), + Check: resource.ComposeTestCheckFunc( + testAccCheckComputeInstanceExists( + "google_compute_instance.foobar", &instance), + ), + }, + { + Config: testAccComputeInstance_desiredStatusTerminated(instanceName), + Check: resource.ComposeTestCheckFunc( + testAccCheckComputeInstanceExists( + "google_compute_instance.foobar", &instance), + testAccCheckComputeInstanceHasStatusTerminated(&instance), + ), + }, + { + Config: testAccComputeInstance_desiredStatusRunning(instanceName), + Check: resource.ComposeTestCheckFunc( + testAccCheckComputeInstanceExists( + "google_compute_instance.foobar", &instance), + testAccCheckComputeInstanceHasStatusRunning(&instance), + ), }, }, }) } -func TestAccComputeInstance_statusTerminatedManuallyAndRestart(t *testing.T) { +func TestAccComputeInstance_desiredStatusTerminatedUpdateFields(t *testing.T) { t.Parallel() var instance compute.Instance @@ -1346,24 +1378,40 @@ func TestAccComputeInstance_statusTerminatedManuallyAndRestart(t *testing.T) { CheckDestroy: testAccCheckComputeInstanceDestroy, Steps: []resource.TestStep{ { - Config: testAccComputeInstance_basic(instanceName), + Config: testAccComputeInstance_desiredStatusNotSet(instanceName), Check: resource.ComposeTestCheckFunc( testAccCheckComputeInstanceExists( "google_compute_instance.foobar", &instance), - testAccCheckComputeInstanceHasStatusRunning(&instance), - testAccCheckComputeInstanceStop("google_compute_instance.foobar"), + ), + }, + { + Config: testAccComputeInstance_desiredStatusTerminated(instanceName), + Check: resource.ComposeTestCheckFunc( testAccCheckComputeInstanceExists( "google_compute_instance.foobar", &instance), testAccCheckComputeInstanceHasStatusTerminated(&instance), ), - ExpectNonEmptyPlan: true, }, { - Config: testAccComputeInstance_basic(instanceName), + Config: testAccComputeInstance_desiredStatusTerminatedUpdate(instanceName), Check: resource.ComposeTestCheckFunc( testAccCheckComputeInstanceExists( "google_compute_instance.foobar", &instance), - testAccCheckComputeInstanceHasStatusRunning(&instance), + testAccCheckComputeInstanceMetadata( + &instance, "bar", "baz"), + testAccCheckComputeInstanceLabel(&instance, "only_me", "nothing_else"), + testAccCheckComputeInstanceTag(&instance, "baz"), + testAccCheckComputeInstanceAccessConfig(&instance), + testAccCheckComputeInstanceHasStatusTerminated(&instance), + ), + }, + { + Config: testAccComputeInstance_desiredStatusTerminatedUpdateRequiringStopping(instanceName), + Check: resource.ComposeTestCheckFunc( + testAccCheckComputeInstanceExists( + "google_compute_instance.foobar", &instance), + testAccCheckComputeInstanceHasMachineType(&instance, "n1-standard-2"), + testAccCheckComputeInstanceHasStatusTerminated(&instance), ), }, }, @@ -3920,7 +3968,38 @@ resource "google_compute_instance" "foobar" { `, instance) } -func testAccComputeInstance_statusRunning(instance string) string { +func testAccComputeInstance_desiredStatusNotSet(instance string) string { + return fmt.Sprintf(` +data "google_compute_image" "my_image" { + family = "debian-9" + project = "debian-cloud" +} + +resource "google_compute_instance" "foobar" { + name = "%s" + machine_type = "n1-standard-1" + zone = "us-central1-a" + can_ip_forward = false + tags = ["foo", "bar"] + + boot_disk { + initialize_params{ + image = "${data.google_compute_image.my_image.self_link}" + } + } + + network_interface { + network = "default" + } + + metadata = { + foo = "bar" + } +} +`, instance) +} + +func testAccComputeInstance_desiredStatusRunning(instance string) string { return fmt.Sprintf(` data "google_compute_image" "my_image" { family = "debian-9" @@ -3944,7 +4023,7 @@ resource "google_compute_instance" "foobar" { network = "default" } - status = "RUNNING" + desired_status = "RUNNING" metadata = { foo = "bar" @@ -3953,7 +4032,7 @@ resource "google_compute_instance" "foobar" { `, instance) } -func testAccComputeInstance_statusTerminated(instance string) string { +func testAccComputeInstance_desiredStatusTerminated(instance string) string { return fmt.Sprintf(` data "google_compute_image" "my_image" { family = "debian-9" @@ -3977,7 +4056,7 @@ resource "google_compute_instance" "foobar" { network = "default" } - status = "TERMINATED" + desired_status = "TERMINATED" metadata = { foo = "bar" @@ -3986,7 +4065,7 @@ resource "google_compute_instance" "foobar" { `, instance) } -func testAccComputeInstance_statusTerminatedUpdate(instance string) string { +func testAccComputeInstance_desiredStatusTerminatedUpdate(instance string) string { return fmt.Sprintf(` data "google_compute_image" "my_image" { family = "debian-9" @@ -4011,7 +4090,7 @@ resource "google_compute_instance" "foobar" { access_config { } } - status = "TERMINATED" + desired_status = "TERMINATED" metadata = { bar = "baz" @@ -4024,7 +4103,7 @@ resource "google_compute_instance" "foobar" { `, instance) } -func testAccComputeInstance_statusTerminatedUpdateRequiringStopping(instance string) string { +func testAccComputeInstance_desiredStatusTerminatedUpdateRequiringStopping(instance string) string { return fmt.Sprintf(` data "google_compute_image" "my_image" { family = "debian-9" @@ -4049,7 +4128,7 @@ resource "google_compute_instance" "foobar" { access_config { } } - status = "TERMINATED" + desired_status = "TERMINATED" metadata = { bar = "baz" @@ -4058,6 +4137,8 @@ resource "google_compute_instance" "foobar" { labels = { only_me = "nothing_else" } + + allow_stopping_for_update = true } `, instance) } From da3d3a7d0389e4848abd3706fb1f9ddc1ba03c98 Mon Sep 17 00:00:00 2001 From: norbjd Date: Sat, 8 Feb 2020 16:15:24 +0100 Subject: [PATCH 06/11] Update documentation --- website/docs/d/datasource_compute_instance.html.markdown | 2 +- website/docs/r/compute_instance.html.markdown | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/website/docs/d/datasource_compute_instance.html.markdown b/website/docs/d/datasource_compute_instance.html.markdown index ebb5152b646..10723695382 100644 --- a/website/docs/d/datasource_compute_instance.html.markdown +++ b/website/docs/d/datasource_compute_instance.html.markdown @@ -71,7 +71,7 @@ The following arguments are supported: * `service_account` - The service account to attach to the instance. Structure is documented below. -* `status` - The status of the instance. +* `desired_status` - The desired status for this instance. * `tags` - The list of tags attached to the instance. diff --git a/website/docs/r/compute_instance.html.markdown b/website/docs/r/compute_instance.html.markdown index e34b3a7ab03..ab19e808603 100644 --- a/website/docs/r/compute_instance.html.markdown +++ b/website/docs/r/compute_instance.html.markdown @@ -133,8 +133,8 @@ The following arguments are supported: Structure is documented below. **Note**: [`allow_stopping_for_update`](#allow_stopping_for_update) must be set to true in order to update this field. -* `status` - (Optional) Status of the instance. - Either "RUNNING" or "TERMINATED", defaults to "RUNNING" +* `desired_status` - (Optional) Desired status of the instance. + Either "RUNNING" or "TERMINATED". * `tags` - (Optional) A list of tags to attach to the instance. From 82c3b3e2658f98d06e0032dc651419ef3ca74b75 Mon Sep 17 00:00:00 2001 From: norbjd Date: Sat, 8 Feb 2020 17:35:59 +0100 Subject: [PATCH 07/11] Remove unused method testAccCheckComputeInstanceStop --- google/resource_compute_instance_test.go | 26 ------------------------ 1 file changed, 26 deletions(-) diff --git a/google/resource_compute_instance_test.go b/google/resource_compute_instance_test.go index fb466d92d6c..aa8fb775751 100644 --- a/google/resource_compute_instance_test.go +++ b/google/resource_compute_instance_test.go @@ -2071,32 +2071,6 @@ func testAccCheckComputeInstanceHasStatusTerminated(instance *compute.Instance) } } -func testAccCheckComputeInstanceStop(n string) resource.TestCheckFunc { - return func(s *terraform.State) error { - rs, ok := s.RootModule().Resources[n] - if !ok { - return fmt.Errorf("Not found: %s", n) - } - - if rs.Primary.ID == "" { - return fmt.Errorf("No ID is set") - } - - config := testAccProvider.Meta().(*Config) - - op, err := config.clientCompute.Instances.Stop(config.Project, rs.Primary.Attributes["zone"], rs.Primary.ID).Do() - if err != nil { - return fmt.Errorf("Could not stop instance: %s", err) - } - err = computeOperationWait(config.clientCompute, op, config.Project, "Waiting on stop") - if err != nil { - return fmt.Errorf("Could not stop instance: %s", err) - } - - return nil - } -} - func testAccComputeInstance_basic(instance string) string { return fmt.Sprintf(` data "google_compute_image" "my_image" { From 1dcda6e0c949c3644bcb95d813a454aa54b1bcd5 Mon Sep 17 00:00:00 2001 From: norbjd Date: Sat, 8 Feb 2020 17:41:38 +0100 Subject: [PATCH 08/11] Remove useless changes caused by previous refacto --- google/resource_compute_instance.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/google/resource_compute_instance.go b/google/resource_compute_instance.go index 7dd0bb407ee..cc42fdd206d 100644 --- a/google/resource_compute_instance.go +++ b/google/resource_compute_instance.go @@ -1432,7 +1432,7 @@ func resourceComputeInstanceUpdate(d *schema.ResourceData, meta interface{}) err req := &compute.InstancesSetMachineTypeRequest{ MachineType: mt.RelativeLink(), } - op, err := config.clientCompute.Instances.SetMachineType(project, zone, instance.Name, req).Do() + op, err = config.clientCompute.Instances.SetMachineType(project, zone, instance.Name, req).Do() if err != nil { return err } @@ -1454,7 +1454,7 @@ func resourceComputeInstanceUpdate(d *schema.ResourceData, meta interface{}) err req := &compute.InstancesSetMinCpuPlatformRequest{ MinCpuPlatform: minCpuPlatform.(string), } - op, err := config.clientCompute.Instances.SetMinCpuPlatform(project, zone, instance.Name, req).Do() + op, err = config.clientCompute.Instances.SetMinCpuPlatform(project, zone, instance.Name, req).Do() if err != nil { return err } @@ -1473,7 +1473,7 @@ func resourceComputeInstanceUpdate(d *schema.ResourceData, meta interface{}) err req.Email = saMap["email"].(string) req.Scopes = canonicalizeServiceScopes(convertStringSet(saMap["scopes"].(*schema.Set))) } - op, err := config.clientCompute.Instances.SetServiceAccount(project, zone, instance.Name, req).Do() + op, err = config.clientCompute.Instances.SetServiceAccount(project, zone, instance.Name, req).Do() if err != nil { return err } @@ -1489,7 +1489,7 @@ func resourceComputeInstanceUpdate(d *schema.ResourceData, meta interface{}) err EnableDisplay: d.Get("enable_display").(bool), ForceSendFields: []string{"EnableDisplay"}, } - op, err := config.clientCompute.Instances.UpdateDisplayDevice(project, zone, instance.Name, req).Do() + op, err = config.clientCompute.Instances.UpdateDisplayDevice(project, zone, instance.Name, req).Do() if err != nil { return fmt.Errorf("Error updating display device: %s", err) } From 6adc156a6093314ec98418301c4659d00055793d Mon Sep 17 00:00:00 2001 From: norbjd Date: Sat, 15 Feb 2020 10:27:15 +0100 Subject: [PATCH 09/11] Fix review comments : refacto getAllStatusBut + comment desiredStatusDiff --- google/resource_compute_instance.go | 30 +++++++++++++++-------------- 1 file changed, 16 insertions(+), 14 deletions(-) diff --git a/google/resource_compute_instance.go b/google/resource_compute_instance.go index cc42fdd206d..72764dcc69d 100644 --- a/google/resource_compute_instance.go +++ b/google/resource_compute_instance.go @@ -723,25 +723,26 @@ func expandComputeInstance(project string, d *schema.ResourceData, config *Confi }, nil } +var computeInstanceStatus = []string{ + "PROVISIONING", + "REPAIRING", + "RUNNING", + "STAGING", + "STOPPED", + "STOPPING", + "SUSPENDED", + "SUSPENDING", + "TERMINATED", +} + // return all possible Compute instances status except the one passed as parameter func getAllStatusBut(status string) []string { - allStatus := []string{ - "PROVISIONING", - "REPAIRING", - "RUNNING", - "STAGING", - "STOPPED", - "STOPPING", - "SUSPENDED", - "SUSPENDING", - "TERMINATED", - } - for i, s := range allStatus { + for i, s := range computeInstanceStatus { if status == s { - return append(allStatus[:i], allStatus[i+1:]...) + return append(computeInstanceStatus[:i], computeInstanceStatus[i+1:]...) } } - return nil + return computeInstanceStatus } func waitUntilInstanceHasDesiredStatus(config *Config, d *schema.ResourceData) error { @@ -1678,6 +1679,7 @@ func suppressEmptyGuestAcceleratorDiff(d *schema.ResourceDiff, meta interface{}) return nil } +// return an error if the desired_status field is set to a value other than RUNNING on Create. func desiredStatusDiff(diff *schema.ResourceDiff, meta interface{}) error { // when creating an instance, name is not set oldName, _ := diff.GetChange("name") From c663de7bd141acd2dbb06c4346a5ac815066545e Mon Sep 17 00:00:00 2001 From: norbjd Date: Sat, 15 Feb 2020 14:37:00 +0100 Subject: [PATCH 10/11] Handle cases with desired_status and allow_stopping_for_update --- google/resource_compute_instance.go | 148 +++--- google/resource_compute_instance_test.go | 597 ++++++++++++++++++++++- 2 files changed, 669 insertions(+), 76 deletions(-) diff --git a/google/resource_compute_instance.go b/google/resource_compute_instance.go index 72764dcc69d..dd5a24ad3cd 100644 --- a/google/resource_compute_instance.go +++ b/google/resource_compute_instance.go @@ -1148,33 +1148,6 @@ func resourceComputeInstanceUpdate(d *schema.ResourceData, meta interface{}) err d.SetPartial("scheduling") } - if d.HasChange("desired_status") { - desiredStatus := d.Get("desired_status").(string) - - if desiredStatus != "" { - var op *compute.Operation - - if desiredStatus == "RUNNING" { - op, err = config.clientCompute.Instances.Start(project, zone, instance.Name).Do() - if err != nil { - return err - } - } else if desiredStatus == "TERMINATED" { - op, err = config.clientCompute.Instances.Stop(project, zone, instance.Name).Do() - if err != nil { - return err - } - } - opErr := computeOperationWaitTime( - config, op, project, "updating status", - int(d.Timeout(schema.TimeoutUpdate).Minutes())) - if opErr != nil { - return opErr - } - } - d.SetPartial("desired_status") - } - networkInterfacesCount := d.Get("network_interface.#").(int) // Sanity check if networkInterfacesCount != len(instance.NetworkInterfaces) { @@ -1409,20 +1382,56 @@ func resourceComputeInstanceUpdate(d *schema.ResourceData, meta interface{}) err d.SetPartial("deletion_protection") } - // Attributes which can only be changed if the instance is stopped - if scopesChange || d.HasChange("service_account.0.email") || d.HasChange("machine_type") || d.HasChange("min_cpu_platform") || d.HasChange("enable_display") { - if !d.Get("allow_stopping_for_update").(bool) { - return fmt.Errorf("Changing the machine_type, min_cpu_platform, service_account, or enable display on an instance requires stopping it. " + - "To acknowledge this, please set allow_stopping_for_update = true in your config.") + needToStopInstanceBeforeUpdating := scopesChange || d.HasChange("service_account.0.email") || d.HasChange("machine_type") || d.HasChange("min_cpu_platform") || d.HasChange("enable_display") + + if d.HasChange("desired_status") && !needToStopInstanceBeforeUpdating { + desiredStatus := d.Get("desired_status").(string) + + if desiredStatus != "" { + var op *compute.Operation + + if desiredStatus == "RUNNING" { + op, err = config.clientCompute.Instances.Start(project, zone, instance.Name).Do() + if err != nil { + return err + } + } else if desiredStatus == "TERMINATED" { + op, err = config.clientCompute.Instances.Stop(project, zone, instance.Name).Do() + if err != nil { + return err + } + } + opErr := computeOperationWaitTime( + config, op, project, "updating status", + int(d.Timeout(schema.TimeoutUpdate).Minutes())) + if opErr != nil { + return opErr + } } - op, err := config.clientCompute.Instances.Stop(project, zone, instance.Name).Do() - if err != nil { - return errwrap.Wrapf("Error stopping instance: {{err}}", err) + d.SetPartial("desired_status") + } + + // Attributes which can only be changed if the instance is stopped + if needToStopInstanceBeforeUpdating { + statusBeforeUpdate := instance.Status + desiredStatus := d.Get("desired_status").(string) + + if statusBeforeUpdate == "RUNNING" && desiredStatus != "TERMINATED" && !d.Get("allow_stopping_for_update").(bool) { + return fmt.Errorf("Changing the machine_type, min_cpu_platform, service_account, or enable display on a started instance requires stopping it. " + + "To acknowledge this, please set allow_stopping_for_update = true in your config. " + + "You can also stop it by setting desired_status = \"TERMINATED\", but the instance will not be restarted after the update.") } - opErr := computeOperationWaitTime(config, op, project, "stopping instance", int(d.Timeout(schema.TimeoutUpdate).Minutes())) - if opErr != nil { - return opErr + if statusBeforeUpdate == "RUNNING" { + op, err := config.clientCompute.Instances.Stop(project, zone, instance.Name).Do() + if err != nil { + return errwrap.Wrapf("Error stopping instance: {{err}}", err) + } + + opErr := computeOperationWaitTime(config, op, project, "stopping instance", int(d.Timeout(schema.TimeoutUpdate).Minutes())) + if opErr != nil { + return opErr + } } if d.HasChange("machine_type") { @@ -1433,7 +1442,7 @@ func resourceComputeInstanceUpdate(d *schema.ResourceData, meta interface{}) err req := &compute.InstancesSetMachineTypeRequest{ MachineType: mt.RelativeLink(), } - op, err = config.clientCompute.Instances.SetMachineType(project, zone, instance.Name, req).Do() + op, err := config.clientCompute.Instances.SetMachineType(project, zone, instance.Name, req).Do() if err != nil { return err } @@ -1455,7 +1464,7 @@ func resourceComputeInstanceUpdate(d *schema.ResourceData, meta interface{}) err req := &compute.InstancesSetMinCpuPlatformRequest{ MinCpuPlatform: minCpuPlatform.(string), } - op, err = config.clientCompute.Instances.SetMinCpuPlatform(project, zone, instance.Name, req).Do() + op, err := config.clientCompute.Instances.SetMinCpuPlatform(project, zone, instance.Name, req).Do() if err != nil { return err } @@ -1474,7 +1483,7 @@ func resourceComputeInstanceUpdate(d *schema.ResourceData, meta interface{}) err req.Email = saMap["email"].(string) req.Scopes = canonicalizeServiceScopes(convertStringSet(saMap["scopes"].(*schema.Set))) } - op, err = config.clientCompute.Instances.SetServiceAccount(project, zone, instance.Name, req).Do() + op, err := config.clientCompute.Instances.SetServiceAccount(project, zone, instance.Name, req).Do() if err != nil { return err } @@ -1490,7 +1499,7 @@ func resourceComputeInstanceUpdate(d *schema.ResourceData, meta interface{}) err EnableDisplay: d.Get("enable_display").(bool), ForceSendFields: []string{"EnableDisplay"}, } - op, err = config.clientCompute.Instances.UpdateDisplayDevice(project, zone, instance.Name, req).Do() + op, err := config.clientCompute.Instances.UpdateDisplayDevice(project, zone, instance.Name, req).Do() if err != nil { return fmt.Errorf("Error updating display device: %s", err) } @@ -1501,35 +1510,40 @@ func resourceComputeInstanceUpdate(d *schema.ResourceData, meta interface{}) err d.SetPartial("enable_display") } - // Retrieve instance from config to pull encryption keys if necessary - instanceFromConfig, err := expandComputeInstance(project, d, config) - if err != nil { - return err - } + if (statusBeforeUpdate == "RUNNING" && desiredStatus != "TERMINATED") || + (statusBeforeUpdate == "TERMINATED" && desiredStatus == "RUNNING") { + // Retrieve instance from config to pull encryption keys if necessary + instanceFromConfig, err := expandComputeInstance(project, d, config) + if err != nil { + return err + } - var encrypted []*compute.CustomerEncryptionKeyProtectedDisk - for _, disk := range instanceFromConfig.Disks { - if disk.DiskEncryptionKey != nil { - key := compute.CustomerEncryptionKey{RawKey: disk.DiskEncryptionKey.RawKey, KmsKeyName: disk.DiskEncryptionKey.KmsKeyName} - eDisk := compute.CustomerEncryptionKeyProtectedDisk{Source: disk.Source, DiskEncryptionKey: &key} - encrypted = append(encrypted, &eDisk) + var encrypted []*compute.CustomerEncryptionKeyProtectedDisk + for _, disk := range instanceFromConfig.Disks { + if disk.DiskEncryptionKey != nil { + key := compute.CustomerEncryptionKey{RawKey: disk.DiskEncryptionKey.RawKey, KmsKeyName: disk.DiskEncryptionKey.KmsKeyName} + eDisk := compute.CustomerEncryptionKeyProtectedDisk{Source: disk.Source, DiskEncryptionKey: &key} + encrypted = append(encrypted, &eDisk) + } } - } - if len(encrypted) > 0 { - request := compute.InstancesStartWithEncryptionKeyRequest{Disks: encrypted} - op, err = config.clientCompute.Instances.StartWithEncryptionKey(project, zone, instance.Name, &request).Do() - } else { - op, err = config.clientCompute.Instances.Start(project, zone, instance.Name).Do() - } - if err != nil { - return errwrap.Wrapf("Error starting instance: {{err}}", err) - } + var op *compute.Operation - opErr = computeOperationWaitTime(config, op, project, - "starting instance", int(d.Timeout(schema.TimeoutUpdate).Minutes())) - if opErr != nil { - return opErr + if len(encrypted) > 0 { + request := compute.InstancesStartWithEncryptionKeyRequest{Disks: encrypted} + op, err = config.clientCompute.Instances.StartWithEncryptionKey(project, zone, instance.Name, &request).Do() + } else { + op, err = config.clientCompute.Instances.Start(project, zone, instance.Name).Do() + } + if err != nil { + return errwrap.Wrapf("Error starting instance: {{err}}", err) + } + + opErr := computeOperationWaitTime(config, op, project, + "starting instance", int(d.Timeout(schema.TimeoutUpdate).Minutes())) + if opErr != nil { + return opErr + } } } diff --git a/google/resource_compute_instance_test.go b/google/resource_compute_instance_test.go index aa8fb775751..21a421457b2 100644 --- a/google/resource_compute_instance_test.go +++ b/google/resource_compute_instance_test.go @@ -1440,19 +1440,439 @@ func TestAccComputeInstance_desiredStatusTerminatedUpdateFields(t *testing.T) { &instance, "bar", "baz"), testAccCheckComputeInstanceLabel(&instance, "only_me", "nothing_else"), testAccCheckComputeInstanceTag(&instance, "baz"), - testAccCheckComputeInstanceAccessConfig(&instance), + testAccCheckComputeInstanceHasStatusTerminated(&instance), + ), + }, + }, + }) +} + +func TestAccComputeInstance_updateRunning_desiredStatusNotSet_allowStoppingForUpdate(t *testing.T) { + t.Parallel() + + var instance compute.Instance + var instanceName = fmt.Sprintf("instance-test-%s", acctest.RandString(10)) + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckComputeInstanceDestroy, + Steps: []resource.TestStep{ + { + Config: testAccComputeInstance_desiredStatusNotSet(instanceName), + Check: resource.ComposeTestCheckFunc( + testAccCheckComputeInstanceExists( + "google_compute_instance.foobar", &instance), + testAccCheckComputeInstanceHasStatusRunning(&instance), + ), + }, + { + Config: testAccComputeInstance_updateRequiringStopping_desiredStatusNotSet_allowStoppingForUpdate(instanceName), + Check: resource.ComposeTestCheckFunc( + testAccCheckComputeInstanceExists( + "google_compute_instance.foobar", &instance), + testAccCheckComputeInstanceHasMachineType(&instance, "n1-standard-2"), + testAccCheckComputeInstanceHasStatusRunning(&instance), + ), + }, + }, + }) +} + +func TestAccComputeInstance_updateRunning_desiredStatusRunning_allowStoppingForUpdate(t *testing.T) { + t.Parallel() + + var instance compute.Instance + var instanceName = fmt.Sprintf("instance-test-%s", acctest.RandString(10)) + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckComputeInstanceDestroy, + Steps: []resource.TestStep{ + { + Config: testAccComputeInstance_desiredStatusNotSet(instanceName), + Check: resource.ComposeTestCheckFunc( + testAccCheckComputeInstanceExists( + "google_compute_instance.foobar", &instance), + testAccCheckComputeInstanceHasStatusRunning(&instance), + ), + }, + { + Config: testAccComputeInstance_updateRequiringStopping_desiredStatusRunning_allowStoppingForUpdate(instanceName), + Check: resource.ComposeTestCheckFunc( + testAccCheckComputeInstanceExists( + "google_compute_instance.foobar", &instance), + testAccCheckComputeInstanceHasMachineType(&instance, "n1-standard-2"), + testAccCheckComputeInstanceHasStatusRunning(&instance), + ), + }, + }, + }) +} + +func TestAccComputeInstance_updateRunning_desiredStatusNotSet_notAllowStoppingForUpdate(t *testing.T) { + t.Parallel() + + var instance compute.Instance + var instanceName = fmt.Sprintf("instance-test-%s", acctest.RandString(10)) + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckComputeInstanceDestroy, + Steps: []resource.TestStep{ + { + Config: testAccComputeInstance_desiredStatusNotSet(instanceName), + Check: resource.ComposeTestCheckFunc( + testAccCheckComputeInstanceExists( + "google_compute_instance.foobar", &instance), + testAccCheckComputeInstanceHasStatusRunning(&instance), + ), + }, + { + Config: testAccComputeInstance_updateRequiringStopping_desiredStatusNotSet_notAllowStoppingForUpdate(instanceName), + ExpectError: regexp.MustCompile("Changing the machine_type, min_cpu_platform, service_account, " + + "or enable display on a started instance requires stopping it. To acknowledge this, please set " + + "allow_stopping_for_update = true in your config. " + + "You can also stop it by setting desired_status = \"TERMINATED\", but the instance will not " + + "be restarted after the update."), + }, + }, + }) +} + +func TestAccComputeInstance_updateRunning_desiredStatusRunning_notAllowStoppingForUpdate(t *testing.T) { + t.Parallel() + + var instance compute.Instance + var instanceName = fmt.Sprintf("instance-test-%s", acctest.RandString(10)) + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckComputeInstanceDestroy, + Steps: []resource.TestStep{ + { + Config: testAccComputeInstance_desiredStatusNotSet(instanceName), + Check: resource.ComposeTestCheckFunc( + testAccCheckComputeInstanceExists( + "google_compute_instance.foobar", &instance), + testAccCheckComputeInstanceHasStatusRunning(&instance), + ), + }, + { + Config: testAccComputeInstance_updateRequiringStopping_desiredStatusRunning_notAllowStoppingForUpdate(instanceName), + ExpectError: regexp.MustCompile("Changing the machine_type, min_cpu_platform, service_account, " + + "or enable display on a started instance requires stopping it. To acknowledge this, please set " + + "allow_stopping_for_update = true in your config. " + + "You can also stop it by setting desired_status = \"TERMINATED\", but the instance will not " + + "be restarted after the update."), + }, + }, + }) +} + +func TestAccComputeInstance_updateRunning_desiredStatusTerminated_allowStoppingForUpdate(t *testing.T) { + t.Parallel() + + var instance compute.Instance + var instanceName = fmt.Sprintf("instance-test-%s", acctest.RandString(10)) + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckComputeInstanceDestroy, + Steps: []resource.TestStep{ + { + Config: testAccComputeInstance_desiredStatusNotSet(instanceName), + Check: resource.ComposeTestCheckFunc( + testAccCheckComputeInstanceExists( + "google_compute_instance.foobar", &instance), + testAccCheckComputeInstanceHasStatusRunning(&instance), + ), + }, + { + Config: testAccComputeInstance_updateRequiringStopping_desiredStatusTerminated_allowStoppingForUpdate(instanceName), + Check: resource.ComposeTestCheckFunc( + testAccCheckComputeInstanceExists( + "google_compute_instance.foobar", &instance), + testAccCheckComputeInstanceHasMachineType(&instance, "n1-standard-2"), + testAccCheckComputeInstanceHasStatusTerminated(&instance), + ), + }, + }, + }) +} + +func TestAccComputeInstance_updateRunning_desiredStatusTerminated_notAllowStoppingForUpdate(t *testing.T) { + t.Parallel() + + var instance compute.Instance + var instanceName = fmt.Sprintf("instance-test-%s", acctest.RandString(10)) + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckComputeInstanceDestroy, + Steps: []resource.TestStep{ + { + Config: testAccComputeInstance_desiredStatusNotSet(instanceName), + Check: resource.ComposeTestCheckFunc( + testAccCheckComputeInstanceExists( + "google_compute_instance.foobar", &instance), + testAccCheckComputeInstanceHasStatusRunning(&instance), + ), + }, + { + Config: testAccComputeInstance_updateRequiringStopping_desiredStatusTerminated_notAllowStoppingForUpdate(instanceName), + Check: resource.ComposeTestCheckFunc( + testAccCheckComputeInstanceExists( + "google_compute_instance.foobar", &instance), + testAccCheckComputeInstanceHasMachineType(&instance, "n1-standard-2"), + testAccCheckComputeInstanceHasStatusTerminated(&instance), + ), + }, + }, + }) +} + +func TestAccComputeInstance_updateTerminated_desiredStatusNotSet_allowStoppingForUpdate(t *testing.T) { + t.Parallel() + + var instance compute.Instance + var instanceName = fmt.Sprintf("instance-test-%s", acctest.RandString(10)) + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckComputeInstanceDestroy, + Steps: []resource.TestStep{ + { + Config: testAccComputeInstance_desiredStatusNotSet(instanceName), + Check: resource.ComposeTestCheckFunc( + testAccCheckComputeInstanceExists( + "google_compute_instance.foobar", &instance), + testAccCheckComputeInstanceHasStatusRunning(&instance), + ), + }, + { + Config: testAccComputeInstance_desiredStatusTerminated(instanceName), + Check: resource.ComposeTestCheckFunc( + testAccCheckComputeInstanceExists( + "google_compute_instance.foobar", &instance), + testAccCheckComputeInstanceHasStatusTerminated(&instance), + ), + }, + { + Config: testAccComputeInstance_updateRequiringStopping_desiredStatusNotSet_allowStoppingForUpdate(instanceName), + Check: resource.ComposeTestCheckFunc( + testAccCheckComputeInstanceExists( + "google_compute_instance.foobar", &instance), + testAccCheckComputeInstanceHasMachineType(&instance, "n1-standard-2"), + testAccCheckComputeInstanceHasStatusTerminated(&instance), + ), + }, + }, + }) +} + +func TestAccComputeInstance_updateTerminated_desiredStatusTerminated_allowStoppingForUpdate(t *testing.T) { + t.Parallel() + + var instance compute.Instance + var instanceName = fmt.Sprintf("instance-test-%s", acctest.RandString(10)) + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckComputeInstanceDestroy, + Steps: []resource.TestStep{ + { + Config: testAccComputeInstance_desiredStatusNotSet(instanceName), + Check: resource.ComposeTestCheckFunc( + testAccCheckComputeInstanceExists( + "google_compute_instance.foobar", &instance), + testAccCheckComputeInstanceHasStatusRunning(&instance), + ), + }, + { + Config: testAccComputeInstance_desiredStatusTerminated(instanceName), + Check: resource.ComposeTestCheckFunc( + testAccCheckComputeInstanceExists( + "google_compute_instance.foobar", &instance), + testAccCheckComputeInstanceHasStatusTerminated(&instance), + ), + }, + { + Config: testAccComputeInstance_updateRequiringStopping_desiredStatusTerminated_allowStoppingForUpdate(instanceName), + Check: resource.ComposeTestCheckFunc( + testAccCheckComputeInstanceExists( + "google_compute_instance.foobar", &instance), + testAccCheckComputeInstanceHasMachineType(&instance, "n1-standard-2"), + testAccCheckComputeInstanceHasStatusTerminated(&instance), + ), + }, + }, + }) +} + +func TestAccComputeInstance_updateTerminated_desiredStatusNotSet_notAllowStoppingForUpdate(t *testing.T) { + t.Parallel() + + var instance compute.Instance + var instanceName = fmt.Sprintf("instance-test-%s", acctest.RandString(10)) + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckComputeInstanceDestroy, + Steps: []resource.TestStep{ + { + Config: testAccComputeInstance_desiredStatusNotSet(instanceName), + Check: resource.ComposeTestCheckFunc( + testAccCheckComputeInstanceExists( + "google_compute_instance.foobar", &instance), + testAccCheckComputeInstanceHasStatusRunning(&instance), + ), + }, + { + Config: testAccComputeInstance_desiredStatusTerminated(instanceName), + Check: resource.ComposeTestCheckFunc( + testAccCheckComputeInstanceExists( + "google_compute_instance.foobar", &instance), + testAccCheckComputeInstanceHasStatusTerminated(&instance), + ), + }, + { + Config: testAccComputeInstance_updateRequiringStopping_desiredStatusNotSet_notAllowStoppingForUpdate(instanceName), + Check: resource.ComposeTestCheckFunc( + testAccCheckComputeInstanceExists( + "google_compute_instance.foobar", &instance), + testAccCheckComputeInstanceHasMachineType(&instance, "n1-standard-2"), + testAccCheckComputeInstanceHasStatusTerminated(&instance), + ), + }, + }, + }) +} + +func TestAccComputeInstance_updateTerminated_desiredStatusTerminated_notAllowStoppingForUpdate(t *testing.T) { + t.Parallel() + + var instance compute.Instance + var instanceName = fmt.Sprintf("instance-test-%s", acctest.RandString(10)) + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckComputeInstanceDestroy, + Steps: []resource.TestStep{ + { + Config: testAccComputeInstance_desiredStatusNotSet(instanceName), + Check: resource.ComposeTestCheckFunc( + testAccCheckComputeInstanceExists( + "google_compute_instance.foobar", &instance), + testAccCheckComputeInstanceHasStatusRunning(&instance), + ), + }, + { + Config: testAccComputeInstance_desiredStatusTerminated(instanceName), + Check: resource.ComposeTestCheckFunc( + testAccCheckComputeInstanceExists( + "google_compute_instance.foobar", &instance), + testAccCheckComputeInstanceHasStatusTerminated(&instance), + ), + }, + { + Config: testAccComputeInstance_updateRequiringStopping_desiredStatusTerminated_notAllowStoppingForUpdate(instanceName), + Check: resource.ComposeTestCheckFunc( + testAccCheckComputeInstanceExists( + "google_compute_instance.foobar", &instance), + testAccCheckComputeInstanceHasMachineType(&instance, "n1-standard-2"), + testAccCheckComputeInstanceHasStatusTerminated(&instance), + ), + }, + }, + }) +} + +func TestAccComputeInstance_updateTerminated_desiredStatusRunning_allowStoppingForUpdate(t *testing.T) { + t.Parallel() + + var instance compute.Instance + var instanceName = fmt.Sprintf("instance-test-%s", acctest.RandString(10)) + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckComputeInstanceDestroy, + Steps: []resource.TestStep{ + { + Config: testAccComputeInstance_desiredStatusNotSet(instanceName), + Check: resource.ComposeTestCheckFunc( + testAccCheckComputeInstanceExists( + "google_compute_instance.foobar", &instance), + testAccCheckComputeInstanceHasStatusRunning(&instance), + ), + }, + { + Config: testAccComputeInstance_desiredStatusTerminated(instanceName), + Check: resource.ComposeTestCheckFunc( + testAccCheckComputeInstanceExists( + "google_compute_instance.foobar", &instance), testAccCheckComputeInstanceHasStatusTerminated(&instance), ), }, { - Config: testAccComputeInstance_desiredStatusTerminatedUpdateRequiringStopping(instanceName), + Config: testAccComputeInstance_updateRequiringStopping_desiredStatusRunning_allowStoppingForUpdate(instanceName), Check: resource.ComposeTestCheckFunc( testAccCheckComputeInstanceExists( "google_compute_instance.foobar", &instance), testAccCheckComputeInstanceHasMachineType(&instance, "n1-standard-2"), + testAccCheckComputeInstanceHasStatusRunning(&instance), + ), + }, + }, + }) +} + +func TestAccComputeInstance_updateTerminated_desiredStatusRunning_notAllowStoppingForUpdate(t *testing.T) { + t.Parallel() + + var instance compute.Instance + var instanceName = fmt.Sprintf("instance-test-%s", acctest.RandString(10)) + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckComputeInstanceDestroy, + Steps: []resource.TestStep{ + { + Config: testAccComputeInstance_desiredStatusNotSet(instanceName), + Check: resource.ComposeTestCheckFunc( + testAccCheckComputeInstanceExists( + "google_compute_instance.foobar", &instance), + testAccCheckComputeInstanceHasStatusRunning(&instance), + ), + }, + { + Config: testAccComputeInstance_desiredStatusTerminated(instanceName), + Check: resource.ComposeTestCheckFunc( + testAccCheckComputeInstanceExists( + "google_compute_instance.foobar", &instance), testAccCheckComputeInstanceHasStatusTerminated(&instance), ), }, + { + Config: testAccComputeInstance_updateRequiringStopping_desiredStatusRunning_notAllowStoppingForUpdate(instanceName), + Check: resource.ComposeTestCheckFunc( + testAccCheckComputeInstanceExists( + "google_compute_instance.foobar", &instance), + testAccCheckComputeInstanceHasMachineType(&instance, "n1-standard-2"), + testAccCheckComputeInstanceHasStatusRunning(&instance), + ), + }, }, }) } @@ -4224,7 +4644,6 @@ resource "google_compute_instance" "foobar" { network_interface { network = "default" - access_config { } } desired_status = "TERMINATED" @@ -4240,7 +4659,7 @@ resource "google_compute_instance" "foobar" { `, instance) } -func testAccComputeInstance_desiredStatusTerminatedUpdateRequiringStopping(instance string) string { +func testAccComputeInstance_updateRequiringStopping_desiredStatusNotSet_notAllowStoppingForUpdate(instance string) string { return fmt.Sprintf(` data "google_compute_image" "my_image" { family = "debian-9" @@ -4252,7 +4671,139 @@ resource "google_compute_instance" "foobar" { machine_type = "n1-standard-2" zone = "us-central1-a" can_ip_forward = false - tags = ["baz"] + tags = ["foo", "bar"] + + boot_disk { + initialize_params{ + image = "${data.google_compute_image.my_image.self_link}" + } + } + + network_interface { + network = "default" + } + + metadata = { + foo = "bar" + } +} +`, instance) +} + +func testAccComputeInstance_updateRequiringStopping_desiredStatusNotSet_allowStoppingForUpdate(instance string) string { + return fmt.Sprintf(` +data "google_compute_image" "my_image" { + family = "debian-9" + project = "debian-cloud" +} + +resource "google_compute_instance" "foobar" { + name = "%s" + machine_type = "n1-standard-2" + zone = "us-central1-a" + can_ip_forward = false + tags = ["foo", "bar"] + + boot_disk { + initialize_params{ + image = "${data.google_compute_image.my_image.self_link}" + } + } + + network_interface { + network = "default" + } + + metadata = { + foo = "bar" + } + + allow_stopping_for_update = true +} +`, instance) +} + +func testAccComputeInstance_updateRequiringStopping_desiredStatusRunning_notAllowStoppingForUpdate(instance string) string { + return fmt.Sprintf(` +data "google_compute_image" "my_image" { + family = "debian-9" + project = "debian-cloud" +} + +resource "google_compute_instance" "foobar" { + name = "%s" + machine_type = "n1-standard-2" + zone = "us-central1-a" + can_ip_forward = false + tags = ["foo", "bar"] + + boot_disk { + initialize_params{ + image = "${data.google_compute_image.my_image.self_link}" + } + } + + network_interface { + network = "default" + } + + desired_status = "RUNNING" + + metadata = { + foo = "bar" + } +} +`, instance) +} + +func testAccComputeInstance_updateRequiringStopping_desiredStatusRunning_allowStoppingForUpdate(instance string) string { + return fmt.Sprintf(` +data "google_compute_image" "my_image" { + family = "debian-9" + project = "debian-cloud" +} + +resource "google_compute_instance" "foobar" { + name = "%s" + machine_type = "n1-standard-2" + zone = "us-central1-a" + can_ip_forward = false + tags = ["foo", "bar"] + + boot_disk { + initialize_params{ + image = "${data.google_compute_image.my_image.self_link}" + } + } + + network_interface { + network = "default" + } + + desired_status = "RUNNING" + + metadata = { + foo = "bar" + } + + allow_stopping_for_update = true +} +`, instance) +} + +func testAccComputeInstance_updateRequiringStopping_desiredStatusTerminated_notAllowStoppingForUpdate(instance string) string { + return fmt.Sprintf(` +data "google_compute_image" "my_image" { + family = "debian-9" + project = "debian-cloud" +} + +resource "google_compute_instance" "foobar" { + name = "%s" + machine_type = "n1-standard-2" + zone = "us-central1-a" + can_ip_forward = false + tags = ["foo", "bar"] boot_disk { initialize_params{ @@ -4262,17 +4813,45 @@ resource "google_compute_instance" "foobar" { network_interface { network = "default" - access_config { } } desired_status = "TERMINATED" metadata = { - bar = "baz" + foo = "bar" } +} +`, instance) +} - labels = { - only_me = "nothing_else" +func testAccComputeInstance_updateRequiringStopping_desiredStatusTerminated_allowStoppingForUpdate(instance string) string { + return fmt.Sprintf(` +data "google_compute_image" "my_image" { + family = "debian-9" + project = "debian-cloud" +} + +resource "google_compute_instance" "foobar" { + name = "%s" + machine_type = "n1-standard-2" + zone = "us-central1-a" + can_ip_forward = false + tags = ["foo", "bar"] + + boot_disk { + initialize_params{ + image = "${data.google_compute_image.my_image.self_link}" + } + } + + network_interface { + network = "default" + } + + desired_status = "TERMINATED" + + metadata = { + foo = "bar" } allow_stopping_for_update = true From 7f4c8785b11d3ab0a474d0ac9c41b4d523977fc1 Mon Sep 17 00:00:00 2001 From: norbjd Date: Thu, 20 Feb 2020 12:51:50 +0100 Subject: [PATCH 11/11] Fix review comments (refacto) - extract common method startInstanceOperation to start instance (with or without pulling encryption keys) - refacto tests : reuse configs + extract common helper method --- google/resource_compute_instance.go | 75 ++- google/resource_compute_instance_test.go | 534 +++--------------- .../datasource_compute_instance.html.markdown | 2 - 3 files changed, 140 insertions(+), 471 deletions(-) diff --git a/google/resource_compute_instance.go b/google/resource_compute_instance.go index dd5a24ad3cd..6759aecd3b4 100644 --- a/google/resource_compute_instance.go +++ b/google/resource_compute_instance.go @@ -1391,9 +1391,9 @@ func resourceComputeInstanceUpdate(d *schema.ResourceData, meta interface{}) err var op *compute.Operation if desiredStatus == "RUNNING" { - op, err = config.clientCompute.Instances.Start(project, zone, instance.Name).Do() + op, err = startInstanceOperation(d, config) if err != nil { - return err + return errwrap.Wrapf("Error starting instance: {{err}}", err) } } else if desiredStatus == "TERMINATED" { op, err = config.clientCompute.Instances.Stop(project, zone, instance.Name).Do() @@ -1422,7 +1422,7 @@ func resourceComputeInstanceUpdate(d *schema.ResourceData, meta interface{}) err "You can also stop it by setting desired_status = \"TERMINATED\", but the instance will not be restarted after the update.") } - if statusBeforeUpdate == "RUNNING" { + if statusBeforeUpdate != "TERMINATED" { op, err := config.clientCompute.Instances.Stop(project, zone, instance.Name).Do() if err != nil { return errwrap.Wrapf("Error stopping instance: {{err}}", err) @@ -1512,29 +1512,7 @@ func resourceComputeInstanceUpdate(d *schema.ResourceData, meta interface{}) err if (statusBeforeUpdate == "RUNNING" && desiredStatus != "TERMINATED") || (statusBeforeUpdate == "TERMINATED" && desiredStatus == "RUNNING") { - // Retrieve instance from config to pull encryption keys if necessary - instanceFromConfig, err := expandComputeInstance(project, d, config) - if err != nil { - return err - } - - var encrypted []*compute.CustomerEncryptionKeyProtectedDisk - for _, disk := range instanceFromConfig.Disks { - if disk.DiskEncryptionKey != nil { - key := compute.CustomerEncryptionKey{RawKey: disk.DiskEncryptionKey.RawKey, KmsKeyName: disk.DiskEncryptionKey.KmsKeyName} - eDisk := compute.CustomerEncryptionKeyProtectedDisk{Source: disk.Source, DiskEncryptionKey: &key} - encrypted = append(encrypted, &eDisk) - } - } - - var op *compute.Operation - - if len(encrypted) > 0 { - request := compute.InstancesStartWithEncryptionKeyRequest{Disks: encrypted} - op, err = config.clientCompute.Instances.StartWithEncryptionKey(project, zone, instance.Name, &request).Do() - } else { - op, err = config.clientCompute.Instances.Start(project, zone, instance.Name).Do() - } + op, err := startInstanceOperation(d, config) if err != nil { return errwrap.Wrapf("Error starting instance: {{err}}", err) } @@ -1570,6 +1548,51 @@ func resourceComputeInstanceUpdate(d *schema.ResourceData, meta interface{}) err return resourceComputeInstanceRead(d, meta) } +func startInstanceOperation(d *schema.ResourceData, config *Config) (*compute.Operation, error) { + project, err := getProject(d, config) + if err != nil { + return nil, err + } + + zone, err := getZone(d, config) + if err != nil { + return nil, err + } + + // Use beta api directly in order to read network_interface.fingerprint without having to put it in the schema. + // Change back to getInstance(config, d) once updating alias ips is GA. + instance, err := config.clientComputeBeta.Instances.Get(project, zone, d.Get("name").(string)).Do() + if err != nil { + return nil, handleNotFoundError(err, d, fmt.Sprintf("Instance %s", instance.Name)) + } + + // Retrieve instance from config to pull encryption keys if necessary + instanceFromConfig, err := expandComputeInstance(project, d, config) + if err != nil { + return nil, err + } + + var encrypted []*compute.CustomerEncryptionKeyProtectedDisk + for _, disk := range instanceFromConfig.Disks { + if disk.DiskEncryptionKey != nil { + key := compute.CustomerEncryptionKey{RawKey: disk.DiskEncryptionKey.RawKey, KmsKeyName: disk.DiskEncryptionKey.KmsKeyName} + eDisk := compute.CustomerEncryptionKeyProtectedDisk{Source: disk.Source, DiskEncryptionKey: &key} + encrypted = append(encrypted, &eDisk) + } + } + + var op *compute.Operation + + if len(encrypted) > 0 { + request := compute.InstancesStartWithEncryptionKeyRequest{Disks: encrypted} + op, err = config.clientCompute.Instances.StartWithEncryptionKey(project, zone, instance.Name, &request).Do() + } else { + op, err = config.clientCompute.Instances.Start(project, zone, instance.Name).Do() + } + + return op, err +} + func expandAttachedDisk(diskConfig map[string]interface{}, d *schema.ResourceData, meta interface{}) (*computeBeta.AttachedDisk, error) { config := meta.(*Config) diff --git a/google/resource_compute_instance_test.go b/google/resource_compute_instance_test.go index 21a421457b2..78fb37790d7 100644 --- a/google/resource_compute_instance_test.go +++ b/google/resource_compute_instance_test.go @@ -1275,22 +1275,22 @@ func TestAccComputeInstance_desiredStatusOnCreation(t *testing.T) { CheckDestroy: testAccCheckComputeInstanceDestroy, Steps: []resource.TestStep{ { - Config: testAccComputeInstance_desiredStatusTerminated(instanceName), + Config: testAccComputeInstance_machineType_desiredStatus_allowStoppingForUpdate(instanceName, "n1-standard-1", "TERMINATED", false), ExpectError: regexp.MustCompile("When creating an instance, desired_status can only accept RUNNING value"), }, { - Config: testAccComputeInstance_desiredStatusRunning(instanceName), + Config: testAccComputeInstance_machineType_desiredStatus_allowStoppingForUpdate(instanceName, "n1-standard-1", "RUNNING", false), Check: resource.ComposeTestCheckFunc( testAccCheckComputeInstanceExists( "google_compute_instance.foobar", &instance), - testAccCheckComputeInstanceHasStatusRunning(&instance), + testAccCheckComputeInstanceHasStatus(&instance, "RUNNING"), ), }, }, }) } -func TestAccComputeInstance_desiredStatusSetAfterCreation(t *testing.T) { +func TestAccComputeInstance_desiredStatusUpdateBasic(t *testing.T) { t.Parallel() var instance compute.Instance @@ -1302,103 +1302,42 @@ func TestAccComputeInstance_desiredStatusSetAfterCreation(t *testing.T) { CheckDestroy: testAccCheckComputeInstanceDestroy, Steps: []resource.TestStep{ { - Config: testAccComputeInstance_desiredStatusNotSet(instanceName), - Check: resource.ComposeTestCheckFunc( - testAccCheckComputeInstanceExists( - "google_compute_instance.foobar", &instance), - ), - }, - { - Config: testAccComputeInstance_desiredStatusRunning(instanceName), - Check: resource.ComposeTestCheckFunc( - testAccCheckComputeInstanceExists( - "google_compute_instance.foobar", &instance), - testAccCheckComputeInstanceHasStatusRunning(&instance), - ), - }, - { - Config: testAccComputeInstance_desiredStatusTerminated(instanceName), - Check: resource.ComposeTestCheckFunc( - testAccCheckComputeInstanceExists( - "google_compute_instance.foobar", &instance), - testAccCheckComputeInstanceHasStatusTerminated(&instance), - ), - }, - }, - }) -} - -func TestAccComputeInstance_desiredStatusUnsetAfterCreation(t *testing.T) { - t.Parallel() - - var instance compute.Instance - var instanceName = fmt.Sprintf("instance-test-%s", acctest.RandString(10)) - - resource.Test(t, resource.TestCase{ - PreCheck: func() { testAccPreCheck(t) }, - Providers: testAccProviders, - CheckDestroy: testAccCheckComputeInstanceDestroy, - Steps: []resource.TestStep{ - { - Config: testAccComputeInstance_desiredStatusRunning(instanceName), - Check: resource.ComposeTestCheckFunc( - testAccCheckComputeInstanceExists( - "google_compute_instance.foobar", &instance), - testAccCheckComputeInstanceHasStatusRunning(&instance), - ), - }, - { - Config: testAccComputeInstance_desiredStatusTerminated(instanceName), + Config: testAccComputeInstance_basic2(instanceName), Check: resource.ComposeTestCheckFunc( testAccCheckComputeInstanceExists( "google_compute_instance.foobar", &instance), - testAccCheckComputeInstanceHasStatusTerminated(&instance), ), }, { - Config: testAccComputeInstance_desiredStatusNotSet(instanceName), + Config: testAccComputeInstance_machineType_desiredStatus_allowStoppingForUpdate(instanceName, "n1-standard-1", "RUNNING", false), Check: resource.ComposeTestCheckFunc( testAccCheckComputeInstanceExists( "google_compute_instance.foobar", &instance), - testAccCheckComputeInstanceHasStatusTerminated(&instance), + testAccCheckComputeInstanceHasStatus(&instance, "RUNNING"), ), }, - }, - }) -} - -func TestAccComputeInstance_desiredStatusTerminatedToRunning(t *testing.T) { - t.Parallel() - - var instance compute.Instance - var instanceName = fmt.Sprintf("instance-test-%s", acctest.RandString(10)) - - resource.Test(t, resource.TestCase{ - PreCheck: func() { testAccPreCheck(t) }, - Providers: testAccProviders, - CheckDestroy: testAccCheckComputeInstanceDestroy, - Steps: []resource.TestStep{ { - Config: testAccComputeInstance_desiredStatusNotSet(instanceName), + Config: testAccComputeInstance_machineType_desiredStatus_allowStoppingForUpdate(instanceName, "n1-standard-1", "TERMINATED", false), Check: resource.ComposeTestCheckFunc( testAccCheckComputeInstanceExists( "google_compute_instance.foobar", &instance), + testAccCheckComputeInstanceHasStatus(&instance, "TERMINATED"), ), }, { - Config: testAccComputeInstance_desiredStatusTerminated(instanceName), + Config: testAccComputeInstance_basic2(instanceName), Check: resource.ComposeTestCheckFunc( testAccCheckComputeInstanceExists( "google_compute_instance.foobar", &instance), - testAccCheckComputeInstanceHasStatusTerminated(&instance), + testAccCheckComputeInstanceHasStatus(&instance, "TERMINATED"), ), }, { - Config: testAccComputeInstance_desiredStatusRunning(instanceName), + Config: testAccComputeInstance_machineType_desiredStatus_allowStoppingForUpdate(instanceName, "n1-standard-1", "RUNNING", false), Check: resource.ComposeTestCheckFunc( testAccCheckComputeInstanceExists( "google_compute_instance.foobar", &instance), - testAccCheckComputeInstanceHasStatusRunning(&instance), + testAccCheckComputeInstanceHasStatus(&instance, "RUNNING"), ), }, }, @@ -1417,18 +1356,18 @@ func TestAccComputeInstance_desiredStatusTerminatedUpdateFields(t *testing.T) { CheckDestroy: testAccCheckComputeInstanceDestroy, Steps: []resource.TestStep{ { - Config: testAccComputeInstance_desiredStatusNotSet(instanceName), + Config: testAccComputeInstance_basic2(instanceName), Check: resource.ComposeTestCheckFunc( testAccCheckComputeInstanceExists( "google_compute_instance.foobar", &instance), ), }, { - Config: testAccComputeInstance_desiredStatusTerminated(instanceName), + Config: testAccComputeInstance_machineType_desiredStatus_allowStoppingForUpdate(instanceName, "n1-standard-1", "TERMINATED", false), Check: resource.ComposeTestCheckFunc( testAccCheckComputeInstanceExists( "google_compute_instance.foobar", &instance), - testAccCheckComputeInstanceHasStatusTerminated(&instance), + testAccCheckComputeInstanceHasStatus(&instance, "TERMINATED"), ), }, { @@ -1440,39 +1379,7 @@ func TestAccComputeInstance_desiredStatusTerminatedUpdateFields(t *testing.T) { &instance, "bar", "baz"), testAccCheckComputeInstanceLabel(&instance, "only_me", "nothing_else"), testAccCheckComputeInstanceTag(&instance, "baz"), - testAccCheckComputeInstanceHasStatusTerminated(&instance), - ), - }, - }, - }) -} - -func TestAccComputeInstance_updateRunning_desiredStatusNotSet_allowStoppingForUpdate(t *testing.T) { - t.Parallel() - - var instance compute.Instance - var instanceName = fmt.Sprintf("instance-test-%s", acctest.RandString(10)) - - resource.Test(t, resource.TestCase{ - PreCheck: func() { testAccPreCheck(t) }, - Providers: testAccProviders, - CheckDestroy: testAccCheckComputeInstanceDestroy, - Steps: []resource.TestStep{ - { - Config: testAccComputeInstance_desiredStatusNotSet(instanceName), - Check: resource.ComposeTestCheckFunc( - testAccCheckComputeInstanceExists( - "google_compute_instance.foobar", &instance), - testAccCheckComputeInstanceHasStatusRunning(&instance), - ), - }, - { - Config: testAccComputeInstance_updateRequiringStopping_desiredStatusNotSet_allowStoppingForUpdate(instanceName), - Check: resource.ComposeTestCheckFunc( - testAccCheckComputeInstanceExists( - "google_compute_instance.foobar", &instance), - testAccCheckComputeInstanceHasMachineType(&instance, "n1-standard-2"), - testAccCheckComputeInstanceHasStatusRunning(&instance), + testAccCheckComputeInstanceHasStatus(&instance, "TERMINATED"), ), }, }, @@ -1491,20 +1398,20 @@ func TestAccComputeInstance_updateRunning_desiredStatusRunning_allowStoppingForU CheckDestroy: testAccCheckComputeInstanceDestroy, Steps: []resource.TestStep{ { - Config: testAccComputeInstance_desiredStatusNotSet(instanceName), + Config: testAccComputeInstance_basic2(instanceName), Check: resource.ComposeTestCheckFunc( testAccCheckComputeInstanceExists( "google_compute_instance.foobar", &instance), - testAccCheckComputeInstanceHasStatusRunning(&instance), + testAccCheckComputeInstanceHasStatus(&instance, "RUNNING"), ), }, { - Config: testAccComputeInstance_updateRequiringStopping_desiredStatusRunning_allowStoppingForUpdate(instanceName), + Config: testAccComputeInstance_machineType_desiredStatus_allowStoppingForUpdate(instanceName, "n1-standard-2", "RUNNING", true), Check: resource.ComposeTestCheckFunc( testAccCheckComputeInstanceExists( "google_compute_instance.foobar", &instance), testAccCheckComputeInstanceHasMachineType(&instance, "n1-standard-2"), - testAccCheckComputeInstanceHasStatusRunning(&instance), + testAccCheckComputeInstanceHasStatus(&instance, "RUNNING"), ), }, }, @@ -1523,15 +1430,15 @@ func TestAccComputeInstance_updateRunning_desiredStatusNotSet_notAllowStoppingFo CheckDestroy: testAccCheckComputeInstanceDestroy, Steps: []resource.TestStep{ { - Config: testAccComputeInstance_desiredStatusNotSet(instanceName), + Config: testAccComputeInstance_basic2(instanceName), Check: resource.ComposeTestCheckFunc( testAccCheckComputeInstanceExists( "google_compute_instance.foobar", &instance), - testAccCheckComputeInstanceHasStatusRunning(&instance), + testAccCheckComputeInstanceHasStatus(&instance, "RUNNING"), ), }, { - Config: testAccComputeInstance_updateRequiringStopping_desiredStatusNotSet_notAllowStoppingForUpdate(instanceName), + Config: testAccComputeInstance_machineType_desiredStatus_allowStoppingForUpdate(instanceName, "n1-standard-2", "", false), ExpectError: regexp.MustCompile("Changing the machine_type, min_cpu_platform, service_account, " + "or enable display on a started instance requires stopping it. To acknowledge this, please set " + "allow_stopping_for_update = true in your config. " + @@ -1554,15 +1461,15 @@ func TestAccComputeInstance_updateRunning_desiredStatusRunning_notAllowStoppingF CheckDestroy: testAccCheckComputeInstanceDestroy, Steps: []resource.TestStep{ { - Config: testAccComputeInstance_desiredStatusNotSet(instanceName), + Config: testAccComputeInstance_basic2(instanceName), Check: resource.ComposeTestCheckFunc( testAccCheckComputeInstanceExists( "google_compute_instance.foobar", &instance), - testAccCheckComputeInstanceHasStatusRunning(&instance), + testAccCheckComputeInstanceHasStatus(&instance, "RUNNING"), ), }, { - Config: testAccComputeInstance_updateRequiringStopping_desiredStatusRunning_notAllowStoppingForUpdate(instanceName), + Config: testAccComputeInstance_machineType_desiredStatus_allowStoppingForUpdate(instanceName, "n1-standard-2", "RUNNING", false), ExpectError: regexp.MustCompile("Changing the machine_type, min_cpu_platform, service_account, " + "or enable display on a started instance requires stopping it. To acknowledge this, please set " + "allow_stopping_for_update = true in your config. " + @@ -1585,20 +1492,20 @@ func TestAccComputeInstance_updateRunning_desiredStatusTerminated_allowStoppingF CheckDestroy: testAccCheckComputeInstanceDestroy, Steps: []resource.TestStep{ { - Config: testAccComputeInstance_desiredStatusNotSet(instanceName), + Config: testAccComputeInstance_basic2(instanceName), Check: resource.ComposeTestCheckFunc( testAccCheckComputeInstanceExists( "google_compute_instance.foobar", &instance), - testAccCheckComputeInstanceHasStatusRunning(&instance), + testAccCheckComputeInstanceHasStatus(&instance, "RUNNING"), ), }, { - Config: testAccComputeInstance_updateRequiringStopping_desiredStatusTerminated_allowStoppingForUpdate(instanceName), + Config: testAccComputeInstance_machineType_desiredStatus_allowStoppingForUpdate(instanceName, "n1-standard-2", "TERMINATED", true), Check: resource.ComposeTestCheckFunc( testAccCheckComputeInstanceExists( "google_compute_instance.foobar", &instance), testAccCheckComputeInstanceHasMachineType(&instance, "n1-standard-2"), - testAccCheckComputeInstanceHasStatusTerminated(&instance), + testAccCheckComputeInstanceHasStatus(&instance, "TERMINATED"), ), }, }, @@ -1617,20 +1524,20 @@ func TestAccComputeInstance_updateRunning_desiredStatusTerminated_notAllowStoppi CheckDestroy: testAccCheckComputeInstanceDestroy, Steps: []resource.TestStep{ { - Config: testAccComputeInstance_desiredStatusNotSet(instanceName), + Config: testAccComputeInstance_basic2(instanceName), Check: resource.ComposeTestCheckFunc( testAccCheckComputeInstanceExists( "google_compute_instance.foobar", &instance), - testAccCheckComputeInstanceHasStatusRunning(&instance), + testAccCheckComputeInstanceHasStatus(&instance, "RUNNING"), ), }, { - Config: testAccComputeInstance_updateRequiringStopping_desiredStatusTerminated_notAllowStoppingForUpdate(instanceName), + Config: testAccComputeInstance_machineType_desiredStatus_allowStoppingForUpdate(instanceName, "n1-standard-2", "TERMINATED", false), Check: resource.ComposeTestCheckFunc( testAccCheckComputeInstanceExists( "google_compute_instance.foobar", &instance), testAccCheckComputeInstanceHasMachineType(&instance, "n1-standard-2"), - testAccCheckComputeInstanceHasStatusTerminated(&instance), + testAccCheckComputeInstanceHasStatus(&instance, "TERMINATED"), ), }, }, @@ -1649,28 +1556,28 @@ func TestAccComputeInstance_updateTerminated_desiredStatusNotSet_allowStoppingFo CheckDestroy: testAccCheckComputeInstanceDestroy, Steps: []resource.TestStep{ { - Config: testAccComputeInstance_desiredStatusNotSet(instanceName), + Config: testAccComputeInstance_basic2(instanceName), Check: resource.ComposeTestCheckFunc( testAccCheckComputeInstanceExists( "google_compute_instance.foobar", &instance), - testAccCheckComputeInstanceHasStatusRunning(&instance), + testAccCheckComputeInstanceHasStatus(&instance, "RUNNING"), ), }, { - Config: testAccComputeInstance_desiredStatusTerminated(instanceName), + Config: testAccComputeInstance_machineType_desiredStatus_allowStoppingForUpdate(instanceName, "n1-standard-1", "TERMINATED", false), Check: resource.ComposeTestCheckFunc( testAccCheckComputeInstanceExists( "google_compute_instance.foobar", &instance), - testAccCheckComputeInstanceHasStatusTerminated(&instance), + testAccCheckComputeInstanceHasStatus(&instance, "TERMINATED"), ), }, { - Config: testAccComputeInstance_updateRequiringStopping_desiredStatusNotSet_allowStoppingForUpdate(instanceName), + Config: testAccComputeInstance_machineType_desiredStatus_allowStoppingForUpdate(instanceName, "n1-standard-2", "", true), Check: resource.ComposeTestCheckFunc( testAccCheckComputeInstanceExists( "google_compute_instance.foobar", &instance), testAccCheckComputeInstanceHasMachineType(&instance, "n1-standard-2"), - testAccCheckComputeInstanceHasStatusTerminated(&instance), + testAccCheckComputeInstanceHasStatus(&instance, "TERMINATED"), ), }, }, @@ -1689,28 +1596,28 @@ func TestAccComputeInstance_updateTerminated_desiredStatusTerminated_allowStoppi CheckDestroy: testAccCheckComputeInstanceDestroy, Steps: []resource.TestStep{ { - Config: testAccComputeInstance_desiredStatusNotSet(instanceName), + Config: testAccComputeInstance_basic2(instanceName), Check: resource.ComposeTestCheckFunc( testAccCheckComputeInstanceExists( "google_compute_instance.foobar", &instance), - testAccCheckComputeInstanceHasStatusRunning(&instance), + testAccCheckComputeInstanceHasStatus(&instance, "RUNNING"), ), }, { - Config: testAccComputeInstance_desiredStatusTerminated(instanceName), + Config: testAccComputeInstance_machineType_desiredStatus_allowStoppingForUpdate(instanceName, "n1-standard-1", "TERMINATED", false), Check: resource.ComposeTestCheckFunc( testAccCheckComputeInstanceExists( "google_compute_instance.foobar", &instance), - testAccCheckComputeInstanceHasStatusTerminated(&instance), + testAccCheckComputeInstanceHasStatus(&instance, "TERMINATED"), ), }, { - Config: testAccComputeInstance_updateRequiringStopping_desiredStatusTerminated_allowStoppingForUpdate(instanceName), + Config: testAccComputeInstance_machineType_desiredStatus_allowStoppingForUpdate(instanceName, "n1-standard-2", "TERMINATED", true), Check: resource.ComposeTestCheckFunc( testAccCheckComputeInstanceExists( "google_compute_instance.foobar", &instance), testAccCheckComputeInstanceHasMachineType(&instance, "n1-standard-2"), - testAccCheckComputeInstanceHasStatusTerminated(&instance), + testAccCheckComputeInstanceHasStatus(&instance, "TERMINATED"), ), }, }, @@ -1729,28 +1636,28 @@ func TestAccComputeInstance_updateTerminated_desiredStatusNotSet_notAllowStoppin CheckDestroy: testAccCheckComputeInstanceDestroy, Steps: []resource.TestStep{ { - Config: testAccComputeInstance_desiredStatusNotSet(instanceName), + Config: testAccComputeInstance_basic2(instanceName), Check: resource.ComposeTestCheckFunc( testAccCheckComputeInstanceExists( "google_compute_instance.foobar", &instance), - testAccCheckComputeInstanceHasStatusRunning(&instance), + testAccCheckComputeInstanceHasStatus(&instance, "RUNNING"), ), }, { - Config: testAccComputeInstance_desiredStatusTerminated(instanceName), + Config: testAccComputeInstance_machineType_desiredStatus_allowStoppingForUpdate(instanceName, "n1-standard-1", "TERMINATED", false), Check: resource.ComposeTestCheckFunc( testAccCheckComputeInstanceExists( "google_compute_instance.foobar", &instance), - testAccCheckComputeInstanceHasStatusTerminated(&instance), + testAccCheckComputeInstanceHasStatus(&instance, "TERMINATED"), ), }, { - Config: testAccComputeInstance_updateRequiringStopping_desiredStatusNotSet_notAllowStoppingForUpdate(instanceName), + Config: testAccComputeInstance_machineType_desiredStatus_allowStoppingForUpdate(instanceName, "n1-standard-2", "", false), Check: resource.ComposeTestCheckFunc( testAccCheckComputeInstanceExists( "google_compute_instance.foobar", &instance), testAccCheckComputeInstanceHasMachineType(&instance, "n1-standard-2"), - testAccCheckComputeInstanceHasStatusTerminated(&instance), + testAccCheckComputeInstanceHasStatus(&instance, "TERMINATED"), ), }, }, @@ -1769,28 +1676,28 @@ func TestAccComputeInstance_updateTerminated_desiredStatusTerminated_notAllowSto CheckDestroy: testAccCheckComputeInstanceDestroy, Steps: []resource.TestStep{ { - Config: testAccComputeInstance_desiredStatusNotSet(instanceName), + Config: testAccComputeInstance_basic2(instanceName), Check: resource.ComposeTestCheckFunc( testAccCheckComputeInstanceExists( "google_compute_instance.foobar", &instance), - testAccCheckComputeInstanceHasStatusRunning(&instance), + testAccCheckComputeInstanceHasStatus(&instance, "RUNNING"), ), }, { - Config: testAccComputeInstance_desiredStatusTerminated(instanceName), + Config: testAccComputeInstance_machineType_desiredStatus_allowStoppingForUpdate(instanceName, "n1-standard-1", "TERMINATED", false), Check: resource.ComposeTestCheckFunc( testAccCheckComputeInstanceExists( "google_compute_instance.foobar", &instance), - testAccCheckComputeInstanceHasStatusTerminated(&instance), + testAccCheckComputeInstanceHasStatus(&instance, "TERMINATED"), ), }, { - Config: testAccComputeInstance_updateRequiringStopping_desiredStatusTerminated_notAllowStoppingForUpdate(instanceName), + Config: testAccComputeInstance_machineType_desiredStatus_allowStoppingForUpdate(instanceName, "n1-standard-2", "TERMINATED", false), Check: resource.ComposeTestCheckFunc( testAccCheckComputeInstanceExists( "google_compute_instance.foobar", &instance), testAccCheckComputeInstanceHasMachineType(&instance, "n1-standard-2"), - testAccCheckComputeInstanceHasStatusTerminated(&instance), + testAccCheckComputeInstanceHasStatus(&instance, "TERMINATED"), ), }, }, @@ -1809,28 +1716,28 @@ func TestAccComputeInstance_updateTerminated_desiredStatusRunning_allowStoppingF CheckDestroy: testAccCheckComputeInstanceDestroy, Steps: []resource.TestStep{ { - Config: testAccComputeInstance_desiredStatusNotSet(instanceName), + Config: testAccComputeInstance_basic2(instanceName), Check: resource.ComposeTestCheckFunc( testAccCheckComputeInstanceExists( "google_compute_instance.foobar", &instance), - testAccCheckComputeInstanceHasStatusRunning(&instance), + testAccCheckComputeInstanceHasStatus(&instance, "RUNNING"), ), }, { - Config: testAccComputeInstance_desiredStatusTerminated(instanceName), + Config: testAccComputeInstance_machineType_desiredStatus_allowStoppingForUpdate(instanceName, "n1-standard-1", "TERMINATED", false), Check: resource.ComposeTestCheckFunc( testAccCheckComputeInstanceExists( "google_compute_instance.foobar", &instance), - testAccCheckComputeInstanceHasStatusTerminated(&instance), + testAccCheckComputeInstanceHasStatus(&instance, "TERMINATED"), ), }, { - Config: testAccComputeInstance_updateRequiringStopping_desiredStatusRunning_allowStoppingForUpdate(instanceName), + Config: testAccComputeInstance_machineType_desiredStatus_allowStoppingForUpdate(instanceName, "n1-standard-2", "RUNNING", true), Check: resource.ComposeTestCheckFunc( testAccCheckComputeInstanceExists( "google_compute_instance.foobar", &instance), testAccCheckComputeInstanceHasMachineType(&instance, "n1-standard-2"), - testAccCheckComputeInstanceHasStatusRunning(&instance), + testAccCheckComputeInstanceHasStatus(&instance, "RUNNING"), ), }, }, @@ -1849,28 +1756,28 @@ func TestAccComputeInstance_updateTerminated_desiredStatusRunning_notAllowStoppi CheckDestroy: testAccCheckComputeInstanceDestroy, Steps: []resource.TestStep{ { - Config: testAccComputeInstance_desiredStatusNotSet(instanceName), + Config: testAccComputeInstance_basic2(instanceName), Check: resource.ComposeTestCheckFunc( testAccCheckComputeInstanceExists( "google_compute_instance.foobar", &instance), - testAccCheckComputeInstanceHasStatusRunning(&instance), + testAccCheckComputeInstanceHasStatus(&instance, "RUNNING"), ), }, { - Config: testAccComputeInstance_desiredStatusTerminated(instanceName), + Config: testAccComputeInstance_machineType_desiredStatus_allowStoppingForUpdate(instanceName, "n1-standard-1", "TERMINATED", false), Check: resource.ComposeTestCheckFunc( testAccCheckComputeInstanceExists( "google_compute_instance.foobar", &instance), - testAccCheckComputeInstanceHasStatusTerminated(&instance), + testAccCheckComputeInstanceHasStatus(&instance, "TERMINATED"), ), }, { - Config: testAccComputeInstance_updateRequiringStopping_desiredStatusRunning_notAllowStoppingForUpdate(instanceName), + Config: testAccComputeInstance_machineType_desiredStatus_allowStoppingForUpdate(instanceName, "n1-standard-2", "RUNNING", false), Check: resource.ComposeTestCheckFunc( testAccCheckComputeInstanceExists( "google_compute_instance.foobar", &instance), testAccCheckComputeInstanceHasMachineType(&instance, "n1-standard-2"), - testAccCheckComputeInstanceHasStatusRunning(&instance), + testAccCheckComputeInstanceHasStatus(&instance, "RUNNING"), ), }, }, @@ -2400,8 +2307,9 @@ func testAccCheckComputeInstanceHasMinCpuPlatform(instance *compute.Instance, mi func testAccCheckComputeInstanceHasMachineType(instance *compute.Instance, machineType string) resource.TestCheckFunc { return func(s *terraform.State) error { - if !strings.HasSuffix(instance.MachineType, "machineTypes/"+machineType) { - return fmt.Errorf("Wrong machine type: expected to end with %s, got %s", machineType, instance.MachineType) + instanceMachineType := GetResourceNameFromSelfLink(instance.MachineType) + if instanceMachineType != machineType { + return fmt.Errorf("Wrong machine type: expected %s, got %s", machineType, instanceMachineType) } return nil @@ -2473,19 +2381,10 @@ func testAccCheckComputeInstanceLacksShieldedVmConfig(instance *computeBeta.Inst } } -func testAccCheckComputeInstanceHasStatusRunning(instance *compute.Instance) resource.TestCheckFunc { - return func(s *terraform.State) error { - if instance.Status != "RUNNING" { - return fmt.Errorf("Instance is not RUNNING, state: %s", instance.Status) - } - return nil - } -} - -func testAccCheckComputeInstanceHasStatusTerminated(instance *compute.Instance) resource.TestCheckFunc { +func testAccCheckComputeInstanceHasStatus(instance *compute.Instance, status string) resource.TestCheckFunc { return func(s *terraform.State) error { - if instance.Status != "TERMINATED" { - return fmt.Errorf("Instance is not TERMINATED, state: %s", instance.Status) + if instance.Status != status { + return fmt.Errorf("Instance has not status %s, status: %s", status, instance.Status) } return nil } @@ -4525,38 +4424,18 @@ resource "google_compute_instance" "foobar" { `, instance) } -func testAccComputeInstance_desiredStatusNotSet(instance string) string { - return fmt.Sprintf(` -data "google_compute_image" "my_image" { - family = "debian-9" - project = "debian-cloud" -} - -resource "google_compute_instance" "foobar" { - name = "%s" - machine_type = "n1-standard-1" - zone = "us-central1-a" - can_ip_forward = false - tags = ["foo", "bar"] - - boot_disk { - initialize_params{ - image = "${data.google_compute_image.my_image.self_link}" - } +func testAccComputeInstance_machineType_desiredStatus_allowStoppingForUpdate( + instance, machineType, desiredStatus string, + allowStoppingForUpdate bool, +) string { + desiredStatusConfigSection := "" + if desiredStatus != "" { + desiredStatusConfigSection = fmt.Sprintf( + "desired_status = \"%s\"", + desiredStatus, + ) } - network_interface { - network = "default" - } - - metadata = { - foo = "bar" - } -} -`, instance) -} - -func testAccComputeInstance_desiredStatusRunning(instance string) string { return fmt.Sprintf(` data "google_compute_image" "my_image" { family = "debian-9" @@ -4565,7 +4444,7 @@ data "google_compute_image" "my_image" { resource "google_compute_instance" "foobar" { name = "%s" - machine_type = "n1-standard-1" + machine_type = "%s" zone = "us-central1-a" can_ip_forward = false tags = ["foo", "bar"] @@ -4580,46 +4459,15 @@ resource "google_compute_instance" "foobar" { network = "default" } - desired_status = "RUNNING" + %s metadata = { foo = "bar" } -} -`, instance) -} - -func testAccComputeInstance_desiredStatusTerminated(instance string) string { - return fmt.Sprintf(` -data "google_compute_image" "my_image" { - family = "debian-9" - project = "debian-cloud" -} - -resource "google_compute_instance" "foobar" { - name = "%s" - machine_type = "n1-standard-1" - zone = "us-central1-a" - can_ip_forward = false - tags = ["foo", "bar"] - - boot_disk { - initialize_params{ - image = "${data.google_compute_image.my_image.self_link}" - } - } - network_interface { - network = "default" - } - - desired_status = "TERMINATED" - - metadata = { - foo = "bar" - } + allow_stopping_for_update = %t } -`, instance) +`, instance, machineType, desiredStatusConfigSection, allowStoppingForUpdate) } func testAccComputeInstance_desiredStatusTerminatedUpdate(instance string) string { @@ -4658,203 +4506,3 @@ resource "google_compute_instance" "foobar" { } `, instance) } - -func testAccComputeInstance_updateRequiringStopping_desiredStatusNotSet_notAllowStoppingForUpdate(instance string) string { - return fmt.Sprintf(` -data "google_compute_image" "my_image" { - family = "debian-9" - project = "debian-cloud" -} - -resource "google_compute_instance" "foobar" { - name = "%s" - machine_type = "n1-standard-2" - zone = "us-central1-a" - can_ip_forward = false - tags = ["foo", "bar"] - - boot_disk { - initialize_params{ - image = "${data.google_compute_image.my_image.self_link}" - } - } - - network_interface { - network = "default" - } - - metadata = { - foo = "bar" - } -} -`, instance) -} - -func testAccComputeInstance_updateRequiringStopping_desiredStatusNotSet_allowStoppingForUpdate(instance string) string { - return fmt.Sprintf(` -data "google_compute_image" "my_image" { - family = "debian-9" - project = "debian-cloud" -} - -resource "google_compute_instance" "foobar" { - name = "%s" - machine_type = "n1-standard-2" - zone = "us-central1-a" - can_ip_forward = false - tags = ["foo", "bar"] - - boot_disk { - initialize_params{ - image = "${data.google_compute_image.my_image.self_link}" - } - } - - network_interface { - network = "default" - } - - metadata = { - foo = "bar" - } - - allow_stopping_for_update = true -} -`, instance) -} - -func testAccComputeInstance_updateRequiringStopping_desiredStatusRunning_notAllowStoppingForUpdate(instance string) string { - return fmt.Sprintf(` -data "google_compute_image" "my_image" { - family = "debian-9" - project = "debian-cloud" -} - -resource "google_compute_instance" "foobar" { - name = "%s" - machine_type = "n1-standard-2" - zone = "us-central1-a" - can_ip_forward = false - tags = ["foo", "bar"] - - boot_disk { - initialize_params{ - image = "${data.google_compute_image.my_image.self_link}" - } - } - - network_interface { - network = "default" - } - - desired_status = "RUNNING" - - metadata = { - foo = "bar" - } -} -`, instance) -} - -func testAccComputeInstance_updateRequiringStopping_desiredStatusRunning_allowStoppingForUpdate(instance string) string { - return fmt.Sprintf(` -data "google_compute_image" "my_image" { - family = "debian-9" - project = "debian-cloud" -} - -resource "google_compute_instance" "foobar" { - name = "%s" - machine_type = "n1-standard-2" - zone = "us-central1-a" - can_ip_forward = false - tags = ["foo", "bar"] - - boot_disk { - initialize_params{ - image = "${data.google_compute_image.my_image.self_link}" - } - } - - network_interface { - network = "default" - } - - desired_status = "RUNNING" - - metadata = { - foo = "bar" - } - - allow_stopping_for_update = true -} -`, instance) -} - -func testAccComputeInstance_updateRequiringStopping_desiredStatusTerminated_notAllowStoppingForUpdate(instance string) string { - return fmt.Sprintf(` -data "google_compute_image" "my_image" { - family = "debian-9" - project = "debian-cloud" -} - -resource "google_compute_instance" "foobar" { - name = "%s" - machine_type = "n1-standard-2" - zone = "us-central1-a" - can_ip_forward = false - tags = ["foo", "bar"] - - boot_disk { - initialize_params{ - image = "${data.google_compute_image.my_image.self_link}" - } - } - - network_interface { - network = "default" - } - - desired_status = "TERMINATED" - - metadata = { - foo = "bar" - } -} -`, instance) -} - -func testAccComputeInstance_updateRequiringStopping_desiredStatusTerminated_allowStoppingForUpdate(instance string) string { - return fmt.Sprintf(` -data "google_compute_image" "my_image" { - family = "debian-9" - project = "debian-cloud" -} - -resource "google_compute_instance" "foobar" { - name = "%s" - machine_type = "n1-standard-2" - zone = "us-central1-a" - can_ip_forward = false - tags = ["foo", "bar"] - - boot_disk { - initialize_params{ - image = "${data.google_compute_image.my_image.self_link}" - } - } - - network_interface { - network = "default" - } - - desired_status = "TERMINATED" - - metadata = { - foo = "bar" - } - - allow_stopping_for_update = true -} -`, instance) -} diff --git a/website/docs/d/datasource_compute_instance.html.markdown b/website/docs/d/datasource_compute_instance.html.markdown index dbfb280f026..45898b94404 100644 --- a/website/docs/d/datasource_compute_instance.html.markdown +++ b/website/docs/d/datasource_compute_instance.html.markdown @@ -72,8 +72,6 @@ The following arguments are supported: * `service_account` - The service account to attach to the instance. Structure is documented below. -* `desired_status` - The desired status for this instance. - * `tags` - The list of tags attached to the instance. * `instance_id` - The server-assigned unique identifier of this instance.