diff --git a/google/field_helpers.go b/google/field_helpers.go index 0f1a00a9..93d567b8 100644 --- a/google/field_helpers.go +++ b/google/field_helpers.go @@ -25,6 +25,10 @@ func ParseSslCertificateFieldValue(sslCertificate string, d TerraformResourceDat return parseGlobalFieldValue("sslCertificates", sslCertificate, "project", d, config, false) } +func ParseDiskFieldValue(disk string, d TerraformResourceData, config *Config) (*ZonalFieldValue, error) { + return parseZonalFieldValue("disks", disk, "project", "zone", d, config, false) +} + // ------------------------------------------------------------ // Base helpers used to create helpers for specific fields. // ------------------------------------------------------------ diff --git a/google/resource_compute_instance.go b/google/resource_compute_instance.go index bcabb04f..43020134 100644 --- a/google/resource_compute_instance.go +++ b/google/resource_compute_instance.go @@ -108,11 +108,12 @@ func resourceComputeInstance() *schema.Resource { }, "source": &schema.Schema{ - Type: schema.TypeString, - Optional: true, - Computed: true, - ForceNew: true, - ConflictsWith: []string{"boot_disk.initialize_params"}, + Type: schema.TypeString, + Optional: true, + Computed: true, + ForceNew: true, + ConflictsWith: []string{"boot_disk.initialize_params"}, + DiffSuppressFunc: linkDiffSuppress, }, }, }, @@ -207,8 +208,9 @@ func resourceComputeInstance() *schema.Resource { Elem: &schema.Resource{ Schema: map[string]*schema.Schema{ "source": &schema.Schema{ - Type: schema.TypeString, - Required: true, + Type: schema.TypeString, + Required: true, + DiffSuppressFunc: linkDiffSuppress, }, "device_name": &schema.Schema{ @@ -622,8 +624,12 @@ func resourceComputeInstanceCreate(d *schema.ResourceData, meta interface{}) err for i := 0; i < attachedDisksCount; i++ { prefix := fmt.Sprintf("attached_disk.%d", i) + source, err := ParseDiskFieldValue(d.Get(prefix+".source").(string), d, config) + if err != nil { + return err + } disk := computeBeta.AttachedDisk{ - Source: d.Get(prefix + ".source").(string), + Source: source.RelativeLink(), AutoDelete: false, // Don't allow autodelete; let terraform handle disk deletion } @@ -914,7 +920,11 @@ func resourceComputeInstanceRead(d *schema.ResourceData, meta interface{}) error attachedDisksCount := d.Get("attached_disk.#").(int) attachedDiskSources := make(map[string]int, attachedDisksCount) for i := 0; i < attachedDisksCount; i++ { - attachedDiskSources[d.Get(fmt.Sprintf("attached_disk.%d.source", i)).(string)] = i + source, err := ParseDiskFieldValue(d.Get(fmt.Sprintf("attached_disk.%d.source", i)).(string), d, config) + if err != nil { + return err + } + attachedDiskSources[source.RelativeLink()] = i } sIndex := 0 @@ -928,7 +938,11 @@ func resourceComputeInstanceRead(d *schema.ResourceData, meta interface{}) error scratchDisks = append(scratchDisks, flattenScratchDisk(disk)) sIndex++ } else { - adIndex, inConfig := attachedDiskSources[disk.Source] + source, err := ParseDiskFieldValue(disk.Source, d, config) + if err != nil { + return err + } + adIndex, inConfig := attachedDiskSources[source.RelativeLink()] di := map[string]interface{}{ "source": disk.Source, "device_name": disk.DeviceName, @@ -1282,13 +1296,11 @@ func expandBootDisk(d *schema.ResourceData, config *Config, zone *compute.Zone, } if v, ok := d.GetOk("boot_disk.0.source"); ok { - diskName := v.(string) - diskData, err := config.clientCompute.Disks.Get( - project, zone.Name, diskName).Do() + source, err := ParseDiskFieldValue(v.(string), d, config) if err != nil { - return nil, fmt.Errorf("Error loading disk '%s': %s", diskName, err) + return nil, err } - disk.Source = diskData.SelfLink + disk.Source = source.RelativeLink() } if _, ok := d.GetOk("boot_disk.0.initialize_params"); ok { @@ -1322,11 +1334,10 @@ func expandBootDisk(d *schema.ResourceData, config *Config, zone *compute.Zone, } func flattenBootDisk(d *schema.ResourceData, disk *computeBeta.AttachedDisk) []map[string]interface{} { - sourceUrl := strings.Split(disk.Source, "/") result := map[string]interface{}{ "auto_delete": disk.AutoDelete, "device_name": disk.DeviceName, - "source": sourceUrl[len(sourceUrl)-1], + "source": disk.Source, // disk_encryption_key_raw is not returned from the API, so copy it from what the user // originally specified to avoid diffs. "disk_encryption_key_raw": d.Get("boot_disk.0.disk_encryption_key_raw"), diff --git a/google/resource_compute_instance_test.go b/google/resource_compute_instance_test.go index 4ac10973..131c37e7 100644 --- a/google/resource_compute_instance_test.go +++ b/google/resource_compute_instance_test.go @@ -251,6 +251,30 @@ func TestAccComputeInstance_attachedDisk(t *testing.T) { }) } +func TestAccComputeInstance_attachedDisk_sourceUrl(t *testing.T) { + t.Parallel() + + var instance compute.Instance + var instanceName = fmt.Sprintf("instance-test-%s", acctest.RandString(10)) + var diskName = fmt.Sprintf("instance-testd-%s", acctest.RandString(10)) + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckComputeInstanceDestroy, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testAccComputeInstance_attachedDisk_sourceUrl(diskName, instanceName), + Check: resource.ComposeTestCheckFunc( + testAccCheckComputeInstanceExists( + "google_compute_instance.foobar", &instance), + testAccCheckComputeInstanceDisk(&instance, diskName, false, false), + ), + }, + }, + }) +} + func TestAccComputeInstance_bootDisk_source(t *testing.T) { t.Parallel() @@ -275,6 +299,30 @@ func TestAccComputeInstance_bootDisk_source(t *testing.T) { }) } +func TestAccComputeInstance_bootDisk_sourceUrl(t *testing.T) { + t.Parallel() + + var instance compute.Instance + var instanceName = fmt.Sprintf("instance-test-%s", acctest.RandString(10)) + var diskName = fmt.Sprintf("instance-test-%s", acctest.RandString(10)) + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckComputeInstanceDestroy, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testAccComputeInstance_bootDisk_sourceUrl(diskName, instanceName), + Check: resource.ComposeTestCheckFunc( + testAccCheckComputeInstanceExists( + "google_compute_instance.foobar", &instance), + testAccCheckComputeInstanceBootDisk(&instance, diskName), + ), + }, + }, + }) +} + func TestAccComputeInstance_bootDisk_type(t *testing.T) { t.Parallel() @@ -1539,6 +1587,41 @@ resource "google_compute_disk" "foobar" { zone = "us-central1-a" } +resource "google_compute_instance" "foobar" { + name = "%s" + machine_type = "n1-standard-1" + zone = "us-central1-a" + + boot_disk { + initialize_params { + image = "debian-8-jessie-v20160803" + } + } + + attached_disk { + source = "${google_compute_disk.foobar.name}" + } + + network_interface { + network = "default" + } + + metadata { + foo = "bar" + } +} +`, disk, instance) +} + +func testAccComputeInstance_attachedDisk_sourceUrl(disk, instance string) string { + return fmt.Sprintf(` +resource "google_compute_disk" "foobar" { + name = "%s" + size = 10 + type = "pd-ssd" + zone = "us-central1-a" +} + resource "google_compute_instance" "foobar" { name = "%s" machine_type = "n1-standard-1" @@ -1589,6 +1672,30 @@ resource "google_compute_instance" "foobar" { `, disk, instance) } +func testAccComputeInstance_bootDisk_sourceUrl(disk, instance string) string { + return fmt.Sprintf(` +resource "google_compute_disk" "foobar" { + name = "%s" + zone = "us-central1-a" + image = "debian-8-jessie-v20160803" +} + +resource "google_compute_instance" "foobar" { + name = "%s" + machine_type = "n1-standard-1" + zone = "us-central1-a" + + boot_disk { + source = "${google_compute_disk.foobar.self_link}" + } + + network_interface { + network = "default" + } +} +`, disk, instance) +} + func testAccComputeInstance_bootDisk_type(instance string, diskType string) string { return fmt.Sprintf(` resource "google_compute_instance" "foobar" { diff --git a/website/docs/r/compute_instance.html.markdown b/website/docs/r/compute_instance.html.markdown index 9d27eb62..12129265 100644 --- a/website/docs/r/compute_instance.html.markdown +++ b/website/docs/r/compute_instance.html.markdown @@ -130,7 +130,7 @@ The `boot_disk` block supports: alongside the new instance. Either `initialize_params` or `source` must be set. Structure is documented below. -* `source` - (Optional) The name of the existing disk (such as those managed by +* `source` - (Optional) The name or self_link of the existing disk (such as those managed by `google_compute_disk`) to attach. The `initialize_params` block supports: @@ -153,7 +153,7 @@ The `scratch_disk` block supports: The `attached_disk` block supports: -* `source` - (Required) The self_link of the disk to attach to this instance. +* `source` - (Required) The name or self_link of the disk to attach to this instance. * `device_name` - (Optional) Name with which the attached disk will be accessible under `/dev/disk/by-id/`