From 32bf0df2d02df5ed89bab4941e7a7e9915bc88e8 Mon Sep 17 00:00:00 2001 From: Dana Hoffman Date: Wed, 28 Jun 2017 15:43:58 -0700 Subject: [PATCH] Add `scratch_disk` property to `google_compute_instance` and deprecate `disk` (#123) * Add scratch_disk property to google_compute_instance * docs for scratch_disk * limit scope of scratchDisks array by using bool, test formatting * add slash back to disk check --- google/resource_compute_instance.go | 71 +++++++++++++++- google/resource_compute_instance_test.go | 80 ++++++++++++++++++- website/docs/r/compute_instance.html.markdown | 16 +++- 3 files changed, 160 insertions(+), 7 deletions(-) diff --git a/google/resource_compute_instance.go b/google/resource_compute_instance.go index 9176f8e6..23f3868a 100644 --- a/google/resource_compute_instance.go +++ b/google/resource_compute_instance.go @@ -107,10 +107,27 @@ func resourceComputeInstance() *schema.Resource { }, }, - "disk": &schema.Schema{ + "scratch_disk": &schema.Schema{ Type: schema.TypeList, Optional: true, ForceNew: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "interface": &schema.Schema{ + Type: schema.TypeString, + Optional: true, + Default: "SCSI", + ValidateFunc: validation.StringInSlice([]string{"SCSI", "NVME"}, false), + }, + }, + }, + }, + + "disk": &schema.Schema{ + Type: schema.TypeList, + Optional: true, + ForceNew: true, + Deprecated: "Use boot_disk, scratch_disk, and attached_disk instead", Elem: &schema.Resource{ Schema: map[string]*schema.Schema{ // TODO(mitchellh): one of image or disk is required @@ -499,6 +516,15 @@ func resourceComputeInstanceCreate(d *schema.ResourceData, meta interface{}) err disks = append(disks, bootDisk) } + var hasScratchDisk bool + if _, hasScratchDisk := d.GetOk("scratch_disk"); hasScratchDisk { + scratchDisks, err := expandScratchDisks(d, config, zone) + if err != nil { + return err + } + disks = append(disks, scratchDisks...) + } + disksCount := d.Get("disk.#").(int) attachedDisksCount := d.Get("attached_disk.#").(int) @@ -545,6 +571,9 @@ func resourceComputeInstanceCreate(d *schema.ResourceData, meta interface{}) err if v, ok := d.GetOk(prefix + ".scratch"); ok { if v.(bool) { + if hasScratchDisk { + return fmt.Errorf("Cannot set scratch disks using both `scratch_disk` and `disk` properties") + } disk.Type = "SCRATCH" } } @@ -960,11 +989,12 @@ func resourceComputeInstanceRead(d *schema.ResourceData, meta interface{}) error disksCount := d.Get("disk.#").(int) attachedDisksCount := d.Get("attached_disk.#").(int) + scratchDisksCount := d.Get("scratch_disk.#").(int) if _, ok := d.GetOk("boot_disk"); ok { disksCount++ } - if expectedDisks := disksCount + attachedDisksCount; len(instance.Disks) != expectedDisks { + if expectedDisks := disksCount + attachedDisksCount + scratchDisksCount; len(instance.Disks) != expectedDisks { return fmt.Errorf("Expected %d disks, API returned %d", expectedDisks, len(instance.Disks)) } @@ -975,13 +1005,20 @@ func resourceComputeInstanceRead(d *schema.ResourceData, meta interface{}) error dIndex := 0 adIndex := 0 + sIndex := 0 disks := make([]map[string]interface{}, 0, disksCount) attachedDisks := make([]map[string]interface{}, 0, attachedDisksCount) + scratchDisks := make([]map[string]interface{}, 0, scratchDisksCount) for _, disk := range instance.Disks { if _, ok := d.GetOk("boot_disk"); ok && disk.Boot { // This disk is a boot disk and there is a boot disk set in the config, therefore // this is the boot disk set in the config. d.Set("boot_disk", flattenBootDisk(d, disk)) + } else if _, ok := d.GetOk("scratch_disk"); ok && disk.Type == "SCRATCH" { + // This disk is a scratch disk and there are scratch disks set in the config, therefore + // this is a scratch disk set in the config. + scratchDisks = append(scratchDisks, flattenScratchDisk(disk)) + sIndex++ } else if _, ok := attachedDiskSources[disk.Source]; !ok { di := map[string]interface{}{ "disk": d.Get(fmt.Sprintf("disk.%d.disk", dIndex)), @@ -1013,6 +1050,7 @@ func resourceComputeInstanceRead(d *schema.ResourceData, meta interface{}) error } d.Set("disk", disks) d.Set("attached_disk", attachedDisks) + d.Set("scratch_disk", scratchDisks) d.Set("self_link", instance.SelfLink) d.SetId(instance.Name) @@ -1371,3 +1409,32 @@ func flattenBootDisk(d *schema.ResourceData, disk *compute.AttachedDisk) []map[s return []map[string]interface{}{result} } + +func expandScratchDisks(d *schema.ResourceData, config *Config, zone *compute.Zone) ([]*compute.AttachedDisk, error) { + diskType, err := readDiskType(config, zone, "local-ssd") + if err != nil { + return nil, fmt.Errorf("Error loading disk type 'local-ssd': %s", err) + } + + n := d.Get("scratch_disk.#").(int) + scratchDisks := make([]*compute.AttachedDisk, 0, n) + for i := 0; i < n; i++ { + scratchDisks = append(scratchDisks, &compute.AttachedDisk{ + AutoDelete: true, + Type: "SCRATCH", + Interface: d.Get(fmt.Sprintf("scratch_disk.%d.interface", i)).(string), + InitializeParams: &compute.AttachedDiskInitializeParams{ + DiskType: diskType.SelfLink, + }, + }) + } + + return scratchDisks, nil +} + +func flattenScratchDisk(disk *compute.AttachedDisk) map[string]interface{} { + result := map[string]interface{}{ + "interface": disk.Interface, + } + return result +} diff --git a/google/resource_compute_instance_test.go b/google/resource_compute_instance_test.go index c173379b..5decac93 100644 --- a/google/resource_compute_instance_test.go +++ b/google/resource_compute_instance_test.go @@ -347,6 +347,27 @@ func TestAccComputeInstance_local_ssd(t *testing.T) { }) } +func TestAccComputeInstance_scratchDisk(t *testing.T) { + 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{ + resource.TestStep{ + Config: testAccComputeInstance_scratchDisk(instanceName), + Check: resource.ComposeTestCheckFunc( + testAccCheckComputeInstanceExists( + "google_compute_instance.scratch", &instance), + testAccCheckComputeInstanceScratchDisk(&instance, []string{"NVME", "SCSI"}), + ), + }, + }, + }) +} + func TestAccComputeInstance_update_deprecated_network(t *testing.T) { var instance compute.Instance var instanceName = fmt.Sprintf("instance-test-%s", acctest.RandString(10)) @@ -794,7 +815,7 @@ func testAccCheckComputeInstanceDisk(instance *compute.Instance, source string, } for _, disk := range instance.Disks { - if strings.HasSuffix(disk.Source, source) && disk.AutoDelete == delete && disk.Boot == boot { + if strings.HasSuffix(disk.Source, "/"+source) && disk.AutoDelete == delete && disk.Boot == boot { return nil } } @@ -821,6 +842,34 @@ func testAccCheckComputeInstanceBootDisk(instance *compute.Instance, source stri } } +func testAccCheckComputeInstanceScratchDisk(instance *compute.Instance, interfaces []string) resource.TestCheckFunc { + return func(s *terraform.State) error { + if instance.Disks == nil { + return fmt.Errorf("no disks") + } + + i := 0 + for _, disk := range instance.Disks { + if disk.Type == "SCRATCH" { + if i >= len(interfaces) { + return fmt.Errorf("Expected %d scratch disks, found more", len(interfaces)) + } + if disk.Interface != interfaces[i] { + return fmt.Errorf("Mismatched interface on scratch disk #%d, expected: %q, found: %q", + i, interfaces[i], disk.Interface) + } + i++ + } + } + + if i != len(interfaces) { + return fmt.Errorf("Expected %d scratch disks, found %d", len(interfaces), i) + } + + return nil + } +} + func testAccCheckComputeInstanceDiskEncryptionKey(n string, instance *compute.Instance) resource.TestCheckFunc { return func(s *terraform.State) error { rs, ok := s.RootModule().Resources[n] @@ -1380,6 +1429,35 @@ resource "google_compute_instance" "local-ssd" { `, instance) } +func testAccComputeInstance_scratchDisk(instance string) string { + return fmt.Sprintf(` +resource "google_compute_instance" "scratch" { + name = "%s" + machine_type = "n1-standard-1" + zone = "us-central1-a" + + boot_disk { + initialize_params { + image = "debian-8-jessie-v20160803" + } + } + + scratch_disk { + interface = "NVME" + } + + scratch_disk { + interface = "SCSI" + } + + network_interface { + network = "default" + } + +} +`, instance) +} + func testAccComputeInstance_service_account(instance 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 f58150d8..884f3e1b 100644 --- a/website/docs/r/compute_instance.html.markdown +++ b/website/docs/r/compute_instance.html.markdown @@ -76,6 +76,9 @@ The following arguments are supported: - - - +* `scratch_disk` - (Optional) Scratch disks to attach to the instance. This can be + specified multiple times for multiple scratch disks. Structure is documented below. + * `can_ip_forward` - (Optional) Whether to allow sending and receiving of packets with non-matching source or destination IPs. This defaults to false. @@ -85,9 +88,6 @@ The following arguments are supported: * `description` - (Optional) A brief description of this resource. -* `disk` - (Optional) Disks to attach to the instance. This can be specified - multiple times for multiple disks. Structure is documented below. - * `labels` - (Optional) A set of key/value label pairs to assign to the instance. * `metadata` - (Optional) Metadata key/value pairs to make available from @@ -112,6 +112,9 @@ The following arguments are supported: --- +* `disk` - (DEPRECATED) Disks to attach to the instance. This can be specified + multiple times for multiple disks. Structure is documented below. + * `network` - (DEPRECATED) Networks to attach to the instance. This can be specified multiple times for multiple networks. Structure is documented below. @@ -151,7 +154,12 @@ The `initialize_params` block supports: `global/images/family/{family}`, `family/{family}`, `{project}/{family}`, `{project}/{image}`, `{family}`, or `{image}`. -The `disk` block supports: (Note that either disk or image is required, unless +The `scratch_disk` block supports: + +* `interface` - (Optional) The disk interface to use for attaching this disk; either SCSI or NVME. + Defaults to SCSI. + +(DEPRECATED) The `disk` block supports: (Note that either disk or image is required, unless the type is "local-ssd", in which case scratch must be true). * `disk` - The name of the existing disk (such as those managed by