From 28efae534ad5594f7b641c1aa580df82b1e27910 Mon Sep 17 00:00:00 2001 From: Dana Hoffman Date: Tue, 20 Mar 2018 14:20:43 -0700 Subject: [PATCH] Add wait_for_instances field to IGM and self_link option to the IG data source (#1222) * Add wait_for_instances field to IGM and self_link option to the IG data source * don't be clever with errors --- ...ta_source_google_compute_instance_group.go | 50 +++++++++++------ ...urce_google_compute_instance_group_test.go | 54 ++++++++++++++++++- google/field_helpers.go | 4 ++ ...resource_compute_instance_group_manager.go | 53 ++++++++++++++---- ...e_compute_region_instance_group_manager.go | 17 +++--- ...oogle_compute_instance_group.html.markdown | 11 ++-- ...mpute_instance_group_manager.html.markdown | 4 ++ ...egion_instance_group_manager.html.markdown | 4 ++ 8 files changed, 155 insertions(+), 42 deletions(-) diff --git a/google/data_source_google_compute_instance_group.go b/google/data_source_google_compute_instance_group.go index 929976ccae2..a786b9b2d17 100644 --- a/google/data_source_google_compute_instance_group.go +++ b/google/data_source_google_compute_instance_group.go @@ -1,6 +1,7 @@ package google import ( + "errors" "fmt" "github.com/hashicorp/terraform/helper/schema" @@ -11,14 +12,23 @@ func dataSourceGoogleComputeInstanceGroup() *schema.Resource { Read: dataSourceComputeInstanceGroupRead, Schema: map[string]*schema.Schema{ "name": { - Type: schema.TypeString, - Required: true, + Type: schema.TypeString, + Optional: true, + ConflictsWith: []string{"self_link"}, + }, + + "self_link": { + Type: schema.TypeString, + Optional: true, + Computed: true, + ConflictsWith: []string{"name", "zone"}, }, "zone": { - Type: schema.TypeString, - Optional: true, - Computed: true, + Type: schema.TypeString, + Optional: true, + Computed: true, + ConflictsWith: []string{"self_link"}, }, "project": { @@ -62,11 +72,6 @@ func dataSourceGoogleComputeInstanceGroup() *schema.Resource { Computed: true, }, - "self_link": { - Type: schema.TypeString, - Computed: true, - }, - "size": { Type: schema.TypeInt, Computed: true, @@ -76,14 +81,25 @@ func dataSourceGoogleComputeInstanceGroup() *schema.Resource { } func dataSourceComputeInstanceGroupRead(d *schema.ResourceData, meta interface{}) error { - - zone, err := getZone(d, meta.(*Config)) - if err != nil { - return err + config := meta.(*Config) + if name, ok := d.GetOk("name"); ok { + zone, err := getZone(d, config) + if err != nil { + return err + } + d.SetId(fmt.Sprintf("%s/%s", zone, name.(string))) + } else if selfLink, ok := d.GetOk("self_link"); ok { + parsed, err := ParseInstanceGroupFieldValue(selfLink.(string), d, config) + if err != nil { + return err + } + d.Set("name", parsed.Name) + d.Set("zone", parsed.Zone) + d.Set("project", parsed.Project) + d.SetId(fmt.Sprintf("%s/%s", parsed.Zone, parsed.Name)) + } else { + return errors.New("Must provide either `self_link` or `zone/name`") } - name := d.Get("name").(string) - - d.SetId(fmt.Sprintf("%s/%s", zone, name)) return resourceComputeInstanceGroupRead(d, meta) } diff --git a/google/data_source_google_compute_instance_group_test.go b/google/data_source_google_compute_instance_group_test.go index 01e6a2bd006..97e66ca729e 100644 --- a/google/data_source_google_compute_instance_group_test.go +++ b/google/data_source_google_compute_instance_group_test.go @@ -48,18 +48,35 @@ func TestAccDataSourceGoogleComputeInstanceGroup_withNamedPort(t *testing.T) { }) } +func TestAccDataSourceGoogleComputeInstanceGroup_fromIGM(t *testing.T) { + t.Parallel() + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + Steps: []resource.TestStep{ + { + Config: testAccCheckDataSourceGoogleComputeInstanceGroup_fromIGM(), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr("data.google_compute_instance_group.test", "instances.#", "10"), + ), + }, + }, + }) +} + func testAccCheckDataSourceGoogleComputeInstanceGroup(dataSourceName string) resource.TestCheckFunc { return func(s *terraform.State) error { dsFullName := "data.google_compute_instance_group.test" rsFullName := "google_compute_instance_group.test" ds, ok := s.RootModule().Resources[dsFullName] if !ok { - return fmt.Errorf("cant' find resource called %s in state", dsFullName) + return fmt.Errorf("cant' find data source called %s in state", dsFullName) } rs, ok := s.RootModule().Resources[rsFullName] if !ok { - return fmt.Errorf("can't find data source called %s in state", rsFullName) + return fmt.Errorf("can't find resource called %s in state", rsFullName) } dsAttrs := ds.Primary.Attributes @@ -259,3 +276,36 @@ data "google_compute_instance_group" "test" { } `, acctest.RandString(10), acctest.RandString(10)) } + +func testAccCheckDataSourceGoogleComputeInstanceGroup_fromIGM() string { + return fmt.Sprintf(` +resource "google_compute_instance_template" "igm-basic" { + name = "%s" + machine_type = "n1-standard-1" + + disk { + source_image = "debian-cloud/debian-8-jessie-v20160803" + auto_delete = true + boot = true + } + + network_interface { + network = "default" + } +} + +resource "google_compute_instance_group_manager" "igm" { + name = "%s" + instance_template = "${google_compute_instance_template.igm-basic.self_link}" + base_instance_name = "igm" + zone = "us-central1-a" + target_size = 10 + + wait_for_instances = true +} + +data "google_compute_instance_group" "test" { + self_link = "${google_compute_instance_group_manager.igm.instance_group}" +} +`, acctest.RandomWithPrefix("test-igm"), acctest.RandomWithPrefix("test-igm")) +} diff --git a/google/field_helpers.go b/google/field_helpers.go index 45a5cd91390..6b165e52c45 100644 --- a/google/field_helpers.go +++ b/google/field_helpers.go @@ -58,6 +58,10 @@ func ParseMachineTypesFieldValue(machineType string, d TerraformResourceData, co return parseZonalFieldValue("machineTypes", machineType, "project", "zone", d, config, false) } +func ParseInstanceGroupFieldValue(instanceGroup string, d TerraformResourceData, config *Config) (*ZonalFieldValue, error) { + return parseZonalFieldValue("instanceGroups", instanceGroup, "project", "zone", d, config, false) +} + // ------------------------------------------------------------ // Base helpers used to create helpers for specific fields. // ------------------------------------------------------------ diff --git a/google/resource_compute_instance_group_manager.go b/google/resource_compute_instance_group_manager.go index f220bbb29a5..47a77d73846 100644 --- a/google/resource_compute_instance_group_manager.go +++ b/google/resource_compute_instance_group_manager.go @@ -6,6 +6,7 @@ import ( "strings" "time" + "github.com/hashicorp/terraform/helper/resource" "github.com/hashicorp/terraform/helper/schema" "github.com/hashicorp/terraform/helper/validation" @@ -197,6 +198,11 @@ func resourceComputeInstanceGroupManager() *schema.Resource { }, }, }, + "wait_for_instances": &schema.Schema{ + Type: schema.TypeBool, + Optional: true, + Default: true, + }, }, } } @@ -312,18 +318,18 @@ func flattenNamedPortsBeta(namedPorts []*computeBeta.NamedPort) []map[string]int } -func resourceComputeInstanceGroupManagerRead(d *schema.ResourceData, meta interface{}) error { +func getManager(d *schema.ResourceData, meta interface{}) (*computeBeta.InstanceGroupManager, error) { computeApiVersion := getComputeApiVersion(d, InstanceGroupManagerBaseApiVersion, InstanceGroupManagerVersionedFeatures) config := meta.(*Config) project, err := getProject(d, config) if err != nil { - return err + return nil, err } region, err := getRegion(d, config) if err != nil { - return err + return nil, err } manager := &computeBeta.InstanceGroupManager{} @@ -339,7 +345,7 @@ func resourceComputeInstanceGroupManagerRead(d *schema.ResourceData, meta interf v1Manager, e = config.clientCompute.InstanceGroupManagers.Get(project, zone, d.Id()).Do() if e != nil { - return handleNotFoundError(e, d, fmt.Sprintf("Instance Group Manager %q", d.Get("name").(string))) + return nil, handleNotFoundError(e, d, fmt.Sprintf("Instance Group Manager %q", d.Get("name").(string))) } } else { // If the resource was imported, the only info we have is the ID. Try to find the resource @@ -348,7 +354,7 @@ func resourceComputeInstanceGroupManagerRead(d *schema.ResourceData, meta interf resource, e = getZonalResourceFromRegion(getInstanceGroupManager, region, config.clientCompute, project) if e != nil { - return e + return nil, e } v1Manager = resource.(*compute.InstanceGroupManager) @@ -359,12 +365,12 @@ func resourceComputeInstanceGroupManagerRead(d *schema.ResourceData, meta interf // The resource doesn't exist anymore d.SetId("") - return nil + return nil, nil } err = Convert(v1Manager, manager) if err != nil { - return err + return nil, err } case v0beta: @@ -378,7 +384,7 @@ func resourceComputeInstanceGroupManagerRead(d *schema.ResourceData, meta interf v0betaManager, e = config.clientComputeBeta.InstanceGroupManagers.Get(project, zone, d.Id()).Do() if e != nil { - return handleNotFoundError(e, d, fmt.Sprintf("Instance Group Manager %q", d.Get("name").(string))) + return nil, handleNotFoundError(e, d, fmt.Sprintf("Instance Group Manager %q", d.Get("name").(string))) } } else { // If the resource was imported, the only info we have is the ID. Try to find the resource @@ -386,7 +392,7 @@ func resourceComputeInstanceGroupManagerRead(d *schema.ResourceData, meta interf var resource interface{} resource, e = getZonalBetaResourceFromRegion(getInstanceGroupManager, region, config.clientComputeBeta, project) if e != nil { - return e + return nil, e } v0betaManager = resource.(*computeBeta.InstanceGroupManager) @@ -397,11 +403,25 @@ func resourceComputeInstanceGroupManagerRead(d *schema.ResourceData, meta interf // The resource doesn't exist anymore d.SetId("") - return nil + return nil, nil } manager = v0betaManager } + return manager, nil +} + +func resourceComputeInstanceGroupManagerRead(d *schema.ResourceData, meta interface{}) error { + config := meta.(*Config) + project, err := getProject(d, config) + if err != nil { + return err + } + + manager, err := getManager(d, meta) + if err != nil { + return err + } d.Set("base_instance_name", manager.BaseInstanceName) d.Set("instance_template", manager.InstanceTemplate) @@ -422,6 +442,19 @@ func resourceComputeInstanceGroupManagerRead(d *schema.ResourceData, meta interf d.Set("update_strategy", update_strategy.(string)) d.Set("auto_healing_policies", flattenAutoHealingPolicies(manager.AutoHealingPolicies)) + if d.Get("wait_for_instances").(bool) { + conf := resource.StateChangeConf{ + Pending: []string{"creating", "error"}, + Target: []string{"created"}, + Refresh: waitForInstancesRefreshFunc(getManager, d, meta), + Timeout: d.Timeout(schema.TimeoutCreate), + } + _, err := conf.WaitForState() + if err != nil { + return err + } + } + return nil } diff --git a/google/resource_compute_region_instance_group_manager.go b/google/resource_compute_region_instance_group_manager.go index 5acb90fd695..68b0e379cd2 100644 --- a/google/resource_compute_region_instance_group_manager.go +++ b/google/resource_compute_region_instance_group_manager.go @@ -219,7 +219,7 @@ func resourceComputeRegionInstanceGroupManagerCreate(d *schema.ResourceData, met type getInstanceManagerFunc func(*schema.ResourceData, interface{}) (*computeBeta.InstanceGroupManager, error) -func getManager(d *schema.ResourceData, meta interface{}) (*computeBeta.InstanceGroupManager, error) { +func getRegionalManager(d *schema.ResourceData, meta interface{}) (*computeBeta.InstanceGroupManager, error) { computeApiVersion := getComputeApiVersion(d, RegionInstanceGroupManagerBaseApiVersion, RegionInstanceGroupManagerVersionedFeatures) config := meta.(*Config) @@ -257,17 +257,17 @@ func waitForInstancesRefreshFunc(f getInstanceManagerFunc, d *schema.ResourceDat log.Printf("[WARNING] Error in fetching manager while waiting for instances to come up: %s\n", err) return nil, "error", err } - if creatingCount := m.CurrentActions.Creating + m.CurrentActions.CreatingWithoutRetries; creatingCount > 0 { - return creatingCount, "creating", nil + if done := m.CurrentActions.None; done < m.TargetSize { + return done, "creating", nil } else { - return creatingCount, "created", nil + return done, "created", nil } } } func resourceComputeRegionInstanceGroupManagerRead(d *schema.ResourceData, meta interface{}) error { config := meta.(*Config) - manager, err := getManager(d, meta) + manager, err := getRegionalManager(d, meta) if err != nil { return err } @@ -298,12 +298,13 @@ func resourceComputeRegionInstanceGroupManagerRead(d *schema.ResourceData, meta conf := resource.StateChangeConf{ Pending: []string{"creating", "error"}, Target: []string{"created"}, - Refresh: waitForInstancesRefreshFunc(getManager, d, meta), + Refresh: waitForInstancesRefreshFunc(getRegionalManager, d, meta), Timeout: d.Timeout(schema.TimeoutCreate), } _, err := conf.WaitForState() - // If err is nil, success. - return err + if err != nil { + return err + } } return nil diff --git a/website/docs/d/google_compute_instance_group.html.markdown b/website/docs/d/google_compute_instance_group.html.markdown index cada8f87d59..5cb34a714a1 100644 --- a/website/docs/d/google_compute_instance_group.html.markdown +++ b/website/docs/d/google_compute_instance_group.html.markdown @@ -23,15 +23,16 @@ data "google_compute_instance_group" "all" { The following arguments are supported: -* `name` - (Required) The name of the instance group. - -* `zone` - (Required) The zone of the instance group. - -- - - +* `name` - (Optional) The name of the instance group. Either `name` or `self_link` must be provided. * `project` - (Optional) The ID of the project in which the resource belongs. If it is not provided, the provider project is used. +* `self_link` - (Optional) The self link of the instance group. Either `name` or `self_link` must be provided. + +* `zone` - (Optional) The zone of the instance group. If referencing the instance group by name + and `zone` is not provided, the provider zone is used. + ## Attributes Reference The following arguments are exported: diff --git a/website/docs/r/compute_instance_group_manager.html.markdown b/website/docs/r/compute_instance_group_manager.html.markdown index 97967511f18..15c0b2095fe 100644 --- a/website/docs/r/compute_instance_group_manager.html.markdown +++ b/website/docs/r/compute_instance_group_manager.html.markdown @@ -101,6 +101,10 @@ The following arguments are supported: instances in the group are added. Updating the target pools attribute does not affect existing instances. +* `wait_for_instances` - (Optional) Whether to wait for all instances to be created/updated before + returning. Note that if this is set to true and the operation does not succeed, Terraform will + continue trying until it times out. + --- * `auto_healing_policies` - (Optional, [Beta](/docs/providers/google/index.html#beta-features)) The autohealing policies for this managed instance diff --git a/website/docs/r/compute_region_instance_group_manager.html.markdown b/website/docs/r/compute_region_instance_group_manager.html.markdown index ff1d5f64060..0ba202bb1dd 100644 --- a/website/docs/r/compute_region_instance_group_manager.html.markdown +++ b/website/docs/r/compute_region_instance_group_manager.html.markdown @@ -95,6 +95,10 @@ The following arguments are supported: instances in the group are added. Updating the target pools attribute does not affect existing instances. +* `wait_for_instances` - (Optional) Whether to wait for all instances to be created/updated before + returning. Note that if this is set to true and the operation does not succeed, Terraform will + continue trying until it times out. + --- * `auto_healing_policies` - (Optional, [Beta](/docs/providers/google/index.html#beta-features)) The autohealing policies for this managed instance