diff --git a/google/resource_compute_instance.go b/google/resource_compute_instance.go index a7a6b46a01c..992b0f93749 100644 --- a/google/resource_compute_instance.go +++ b/google/resource_compute_instance.go @@ -79,6 +79,7 @@ func resourceComputeInstance() *schema.Resource { "initialize_params": &schema.Schema{ Type: schema.TypeList, Optional: true, + Computed: true, ForceNew: true, MaxItems: 1, Elem: &schema.Resource{ @@ -86,6 +87,7 @@ func resourceComputeInstance() *schema.Resource { "size": &schema.Schema{ Type: schema.TypeInt, Optional: true, + Computed: true, ForceNew: true, ValidateFunc: validation.IntAtLeast(1), }, @@ -93,14 +95,17 @@ func resourceComputeInstance() *schema.Resource { "type": &schema.Schema{ Type: schema.TypeString, Optional: true, + Computed: true, ForceNew: true, ValidateFunc: validation.StringInSlice([]string{"pd-standard", "pd-ssd"}, false), }, "image": &schema.Schema{ - Type: schema.TypeString, - Optional: true, - ForceNew: true, + Type: schema.TypeString, + Optional: true, + Computed: true, + ForceNew: true, + DiffSuppressFunc: diskImageDiffSuppress, }, }, }, @@ -582,6 +587,20 @@ func getInstance(config *Config, d *schema.ResourceData) (*computeBeta.Instance, return instance, nil } +func getDisk(diskUri string, d *schema.ResourceData, config *Config) (*compute.Disk, error) { + source, err := ParseDiskFieldValue(diskUri, d, config) + if err != nil { + return nil, err + } + + disk, err := config.clientCompute.Disks.Get(source.Project, source.Zone, source.Name).Do() + if err != nil { + return nil, err + } + + return disk, err +} + func resourceComputeInstanceCreate(d *schema.ResourceData, meta interface{}) error { config := meta.(*Config) @@ -820,7 +839,7 @@ func resourceComputeInstanceRead(d *schema.ResourceData, meta interface{}) error extraAttachedDisks := []map[string]interface{}{} for _, disk := range instance.Disks { if disk.Boot { - d.Set("boot_disk", flattenBootDisk(d, disk)) + d.Set("boot_disk", flattenBootDisk(d, disk, config)) } else if disk.Type == "SCRATCH" { scratchDisks = append(scratchDisks, flattenScratchDisk(disk)) sIndex++ @@ -1309,7 +1328,7 @@ func expandBootDisk(d *schema.ResourceData, config *Config, zone *compute.Zone, return disk, nil } -func flattenBootDisk(d *schema.ResourceData, disk *computeBeta.AttachedDisk) []map[string]interface{} { +func flattenBootDisk(d *schema.ResourceData, disk *computeBeta.AttachedDisk, config *Config) []map[string]interface{} { result := map[string]interface{}{ "auto_delete": disk.AutoDelete, "device_name": disk.DeviceName, @@ -1318,15 +1337,30 @@ func flattenBootDisk(d *schema.ResourceData, disk *computeBeta.AttachedDisk) []m // originally specified to avoid diffs. "disk_encryption_key_raw": d.Get("boot_disk.0.disk_encryption_key_raw"), } + + diskDetails, err := getDisk(disk.Source, d, config) + if err != nil { + log.Printf("[WARN] Cannot retrieve boot disk details: %s", err) + + if _, ok := d.GetOk("boot_disk.0.initialize_params.#"); ok { + // If we can't read the disk details due to permission for instance, + // copy the initialize_params from what the user originally specified to avoid diffs. + m := d.Get("boot_disk.0.initialize_params") + result["initialize_params"] = m + } + } else { + result["initialize_params"] = []map[string]interface{}{{ + "type": GetResourceNameFromSelfLink(diskDetails.Type), + // If the config specifies a family name that doesn't match the image name, then + // the diff won't be properly suppressed. See DiffSuppressFunc for this field. + "image": diskDetails.SourceImage, + "size": diskDetails.SizeGb, + }} + } + if disk.DiskEncryptionKey != nil { result["disk_encryption_key_sha256"] = disk.DiskEncryptionKey.Sha256 } - if _, ok := d.GetOk("boot_disk.0.initialize_params.#"); ok { - // initialize_params is not returned from the API, so copy it from what the user - // originally specified to avoid diffs. - m := d.Get("boot_disk.0.initialize_params") - result["initialize_params"] = m - } return []map[string]interface{}{result} } diff --git a/google/resource_compute_instance_test.go b/google/resource_compute_instance_test.go index d542e3c0edd..52840a747fb 100644 --- a/google/resource_compute_instance_test.go +++ b/google/resource_compute_instance_test.go @@ -41,7 +41,7 @@ func TestAccComputeInstance_basic1(t *testing.T) { ResourceName: "google_compute_instance.foobar", ImportState: true, ImportStateId: fmt.Sprintf("%s/%s/%s", getTestProjectFromEnv(), "us-central1-a", instanceName), - ImportStateVerifyIgnore: []string{"boot_disk.0.initialize_params", "create_timeout"}, + ImportStateVerifyIgnore: []string{"create_timeout"}, }, }, }) @@ -253,10 +253,9 @@ func TestAccComputeInstance_attachedDisk(t *testing.T) { ), }, resource.TestStep{ - ResourceName: "google_compute_instance.foobar", - ImportState: true, - ImportStateId: fmt.Sprintf("%s/%s/%s", getTestProjectFromEnv(), "us-central1-a", instanceName), - ImportStateVerifyIgnore: []string{"boot_disk.0.initialize_params"}, + ResourceName: "google_compute_instance.foobar", + ImportState: true, + ImportStateId: fmt.Sprintf("%s/%s/%s", getTestProjectFromEnv(), "us-central1-a", instanceName), }, }, }) @@ -283,10 +282,9 @@ func TestAccComputeInstance_attachedDisk_sourceUrl(t *testing.T) { ), }, resource.TestStep{ - ResourceName: "google_compute_instance.foobar", - ImportState: true, - ImportStateId: fmt.Sprintf("%s/%s/%s", getTestProjectFromEnv(), "us-central1-a", instanceName), - ImportStateVerifyIgnore: []string{"boot_disk.0.initialize_params"}, + ResourceName: "google_compute_instance.foobar", + ImportState: true, + ImportStateId: fmt.Sprintf("%s/%s/%s", getTestProjectFromEnv(), "us-central1-a", instanceName), }, }, }) @@ -395,10 +393,9 @@ func TestAccComputeInstance_bootDisk_sourceUrl(t *testing.T) { ), }, resource.TestStep{ - ResourceName: "google_compute_instance.foobar", - ImportState: true, - ImportStateId: fmt.Sprintf("%s/%s/%s", getTestProjectFromEnv(), "us-central1-a", instanceName), - ImportStateVerifyIgnore: []string{"boot_disk.0.initialize_params"}, + ResourceName: "google_compute_instance.foobar", + ImportState: true, + ImportStateId: fmt.Sprintf("%s/%s/%s", getTestProjectFromEnv(), "us-central1-a", instanceName), }, }, }) @@ -448,10 +445,9 @@ func TestAccComputeInstance_scratchDisk(t *testing.T) { ), }, resource.TestStep{ - ResourceName: "google_compute_instance.foobar", - ImportState: true, - ImportStateId: fmt.Sprintf("%s/%s/%s", getTestProjectFromEnv(), "us-central1-a", instanceName), - ImportStateVerifyIgnore: []string{"boot_disk.0.initialize_params"}, + ResourceName: "google_compute_instance.foobar", + ImportState: true, + ImportStateId: fmt.Sprintf("%s/%s/%s", getTestProjectFromEnv(), "us-central1-a", instanceName), }, }, }) @@ -547,10 +543,9 @@ func TestAccComputeInstance_service_account(t *testing.T) { ), }, resource.TestStep{ - ResourceName: "google_compute_instance.foobar", - ImportState: true, - ImportStateId: fmt.Sprintf("%s/%s/%s", getTestProjectFromEnv(), "us-central1-a", instanceName), - ImportStateVerifyIgnore: []string{"boot_disk.0.initialize_params"}, + ResourceName: "google_compute_instance.foobar", + ImportState: true, + ImportStateId: fmt.Sprintf("%s/%s/%s", getTestProjectFromEnv(), "us-central1-a", instanceName), }, }, }) @@ -575,10 +570,9 @@ func TestAccComputeInstance_scheduling(t *testing.T) { ), }, resource.TestStep{ - ResourceName: "google_compute_instance.foobar", - ImportState: true, - ImportStateId: fmt.Sprintf("%s/%s/%s", getTestProjectFromEnv(), "us-central1-a", instanceName), - ImportStateVerifyIgnore: []string{"boot_disk.0.initialize_params"}, + ResourceName: "google_compute_instance.foobar", + ImportState: true, + ImportStateId: fmt.Sprintf("%s/%s/%s", getTestProjectFromEnv(), "us-central1-a", instanceName), }, }, }) @@ -604,10 +598,9 @@ func TestAccComputeInstance_subnet_auto(t *testing.T) { ), }, resource.TestStep{ - ResourceName: "google_compute_instance.foobar", - ImportState: true, - ImportStateId: fmt.Sprintf("%s/%s/%s", getTestProjectFromEnv(), "us-central1-a", instanceName), - ImportStateVerifyIgnore: []string{"boot_disk.0.initialize_params"}, + ResourceName: "google_compute_instance.foobar", + ImportState: true, + ImportStateId: fmt.Sprintf("%s/%s/%s", getTestProjectFromEnv(), "us-central1-a", instanceName), }, }, }) @@ -633,10 +626,9 @@ func TestAccComputeInstance_subnet_custom(t *testing.T) { ), }, resource.TestStep{ - ResourceName: "google_compute_instance.foobar", - ImportState: true, - ImportStateId: fmt.Sprintf("%s/%s/%s", getTestProjectFromEnv(), "us-central1-a", instanceName), - ImportStateVerifyIgnore: []string{"boot_disk.0.initialize_params"}, + ResourceName: "google_compute_instance.foobar", + ImportState: true, + ImportStateId: fmt.Sprintf("%s/%s/%s", getTestProjectFromEnv(), "us-central1-a", instanceName), }, }, }) @@ -721,7 +713,6 @@ func TestAccComputeInstance_private_image_family(t *testing.T) { var instance compute.Instance var instanceName = fmt.Sprintf("instance-test-%s", acctest.RandString(10)) var diskName = fmt.Sprintf("instance-testd-%s", acctest.RandString(10)) - var imageName = fmt.Sprintf("instance-testi-%s", acctest.RandString(10)) var familyName = fmt.Sprintf("instance-testf-%s", acctest.RandString(10)) resource.Test(t, resource.TestCase{ @@ -730,7 +721,7 @@ func TestAccComputeInstance_private_image_family(t *testing.T) { CheckDestroy: testAccCheckComputeInstanceDestroy, Steps: []resource.TestStep{ resource.TestStep{ - Config: testAccComputeInstance_private_image_family(diskName, imageName, familyName, instanceName), + Config: testAccComputeInstance_private_image_family(diskName, familyName, instanceName), Check: resource.ComposeTestCheckFunc( testAccCheckComputeInstanceExists( "google_compute_instance.foobar", &instance), @@ -763,7 +754,7 @@ func TestAccComputeInstance_forceChangeMachineTypeManually(t *testing.T) { ResourceName: "google_compute_instance.foobar", ImportState: true, ImportStateId: fmt.Sprintf("%s/%s/%s", getTestProjectFromEnv(), "us-central1-a", instanceName), - ImportStateVerifyIgnore: []string{"boot_disk.0.initialize_params", "create_timeout"}, + ImportStateVerifyIgnore: []string{"create_timeout"}, }, }, }) @@ -790,10 +781,9 @@ func TestAccComputeInstance_multiNic(t *testing.T) { ), }, resource.TestStep{ - ResourceName: "google_compute_instance.foobar", - ImportState: true, - ImportStateId: fmt.Sprintf("%s/%s/%s", getTestProjectFromEnv(), "us-central1-a", instanceName), - ImportStateVerifyIgnore: []string{"boot_disk.0.initialize_params"}, + ResourceName: "google_compute_instance.foobar", + ImportState: true, + ImportStateId: fmt.Sprintf("%s/%s/%s", getTestProjectFromEnv(), "us-central1-a", instanceName), }, }, }) @@ -818,10 +808,9 @@ func TestAccComputeInstance_guestAccelerator(t *testing.T) { ), }, resource.TestStep{ - ResourceName: "google_compute_instance.foobar", - ImportState: true, - ImportStateId: fmt.Sprintf("%s/%s/%s", getTestProjectFromEnv(), "us-east1-d", instanceName), - ImportStateVerifyIgnore: []string{"boot_disk.0.initialize_params"}, + ResourceName: "google_compute_instance.foobar", + ImportState: true, + ImportStateId: fmt.Sprintf("%s/%s/%s", getTestProjectFromEnv(), "us-east1-d", instanceName), }, }, }) @@ -847,10 +836,9 @@ func TestAccComputeInstance_minCpuPlatform(t *testing.T) { ), }, resource.TestStep{ - ResourceName: "google_compute_instance.foobar", - ImportState: true, - ImportStateId: fmt.Sprintf("%s/%s/%s", getTestProjectFromEnv(), "us-east1-d", instanceName), - ImportStateVerifyIgnore: []string{"boot_disk.0.initialize_params"}, + ResourceName: "google_compute_instance.foobar", + ImportState: true, + ImportStateId: fmt.Sprintf("%s/%s/%s", getTestProjectFromEnv(), "us-east1-d", instanceName), }, }, }) @@ -875,10 +863,9 @@ func TestAccComputeInstance_primaryAliasIpRange(t *testing.T) { ), }, resource.TestStep{ - ResourceName: "google_compute_instance.foobar", - ImportState: true, - ImportStateId: fmt.Sprintf("%s/%s/%s", getTestProjectFromEnv(), "us-east1-d", instanceName), - ImportStateVerifyIgnore: []string{"boot_disk.0.initialize_params"}, + ResourceName: "google_compute_instance.foobar", + ImportState: true, + ImportStateId: fmt.Sprintf("%s/%s/%s", getTestProjectFromEnv(), "us-east1-d", instanceName), }, }, }) @@ -903,10 +890,9 @@ func TestAccComputeInstance_secondaryAliasIpRange(t *testing.T) { ), }, resource.TestStep{ - ResourceName: "google_compute_instance.foobar", - ImportState: true, - ImportStateId: fmt.Sprintf("%s/%s/%s", getTestProjectFromEnv(), "us-east1-d", instanceName), - ImportStateVerifyIgnore: []string{"boot_disk.0.initialize_params"}, + ResourceName: "google_compute_instance.foobar", + ImportState: true, + ImportStateId: fmt.Sprintf("%s/%s/%s", getTestProjectFromEnv(), "us-east1-d", instanceName), }, }, }) @@ -2225,7 +2211,7 @@ resource "google_compute_instance" "foobar" { `, acctest.RandString(10), acctest.RandString(10), instance, address) } -func testAccComputeInstance_private_image_family(disk, image, family, instance string) string { +func testAccComputeInstance_private_image_family(disk, family, instance string) string { return fmt.Sprintf(` resource "google_compute_disk" "foobar" { name = "%s" @@ -2234,7 +2220,7 @@ resource "google_compute_disk" "foobar" { } resource "google_compute_image" "foobar" { - name = "%s" + name = "%s-1" source_disk = "${google_compute_disk.foobar.self_link}" family = "%s" } @@ -2258,7 +2244,7 @@ resource "google_compute_instance" "foobar" { foo = "bar" } } -`, disk, image, family, instance) +`, disk, family, family, instance) } func testAccComputeInstance_multiNic(instance, network, subnetwork string) string { diff --git a/website/docs/r/compute_instance.html.markdown b/website/docs/r/compute_instance.html.markdown index f582b88b51e..4965ea67449 100644 --- a/website/docs/r/compute_instance.html.markdown +++ b/website/docs/r/compute_instance.html.markdown @@ -144,7 +144,10 @@ The `initialize_params` block supports: one of: the image's `self_link`, `projects/{project}/global/images/{image}`, `projects/{project}/global/images/family/{family}`, `global/images/{image}`, `global/images/family/{family}`, `family/{family}`, `{project}/{family}`, - `{project}/{image}`, `{family}`, or `{image}`. + `{project}/{image}`, `{family}`, or `{image}`. If referred by family, the + images names must include the family name. For instance, the image + `centos-6-v20180104` includes its family name `centos-6`. These images can + be referred by family name here. The `scratch_disk` block supports: @@ -276,7 +279,7 @@ exported: ## Import -~> **Note:** The fields `boot_disk.0.initialize_params`, `boot_disk.0.disk_entryption_raw` and `attached_disk.*.disk_encryption_key_raw` cannot be imported automatically. The API doesn't return this information. If you are setting one of these fields in your config, you will need to update your state manually after importing the resource. +~> **Note:** The fields `boot_disk.0.disk_entryption_raw` and `attached_disk.*.disk_encryption_key_raw` cannot be imported automatically. The API doesn't return this information. If you are setting one of these fields in your config, you will need to update your state manually after importing the resource. Instances can be imported using the `project`, `zone` and `name`, e.g.