Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Instance group wait for status #3283

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions .changelog/4783.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
```release-note:enhancement
compute: added support for output-only `status` field on `google_compute_instance_group_manager` and `google_compute_region_instance_group_manager`
```
```release-note:enhancement
compute: added support for `wait_for_instances_status` on `google_compute_instance_group_manager` and `google_compute_region_instance_group_manager`
```
121 changes: 119 additions & 2 deletions google-beta/resource_compute_instance_group_manager.go
Original file line number Diff line number Diff line change
Expand Up @@ -269,6 +269,13 @@ func resourceComputeInstanceGroupManager() *schema.Resource {
Default: false,
Description: `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.`,
},
"wait_for_instances_status": {
Type: schema.TypeString,
Optional: true,
Default: "STABLE",
ValidateFunc: validation.StringInSlice([]string{"STABLE", "UPDATED"}, false),
Description: `When used with wait_for_instances specifies the status to wait for. When STABLE is specified this resource will wait until the instances are stable before returning. When UPDATED is set, it will wait for the version target to be reached and any per instance configs to be effective as well as all instances to be stable before returning.`,
},
"stateful_disk": {
Type: schema.TypeSet,
Optional: true,
Expand All @@ -295,6 +302,63 @@ func resourceComputeInstanceGroupManager() *schema.Resource {
Type: schema.TypeString,
Computed: true,
},
"status": {
Type: schema.TypeList,
Computed: true,
Description: `The status of this managed instance group.`,
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
"is_stable": {
Type: schema.TypeBool,
Computed: true,
Description: `A bit indicating whether the managed instance group is in a stable state. A stable state means that: none of the instances in the managed instance group is currently undergoing any type of change (for example, creation, restart, or deletion); no future changes are scheduled for instances in the managed instance group; and the managed instance group itself is not being modified.`,
},

"version_target": {
Type: schema.TypeList,
Computed: true,
Description: `A status of consistency of Instances' versions with their target version specified by version field on Instance Group Manager.`,
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
"is_reached": {
Type: schema.TypeBool,
Computed: true,
Description: `A bit indicating whether version target has been reached in this managed instance group, i.e. all instances are in their target version. Instances' target version are specified by version field on Instance Group Manager.`,
},
},
},
},
"stateful": {
Type: schema.TypeList,
Computed: true,
Description: `Stateful status of the given Instance Group Manager.`,
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
"has_stateful_config": {
Type: schema.TypeBool,
Computed: true,
Description: `A bit indicating whether the managed instance group has stateful configuration, that is, if you have configured any items in a stateful policy or in per-instance configs. The group might report that it has no stateful config even when there is still some preserved state on a managed instance, for example, if you have deleted all PICs but not yet applied those deletions.`,
},
"per_instance_configs": {
Type: schema.TypeList,
Computed: true,
Description: `Status of per-instance configs on the instance.`,
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
"all_effective": {
Type: schema.TypeBool,
Computed: true,
Description: `A bit indicating if all of the group's per-instance configs (listed in the output of a listPerInstanceConfigs API call) have status EFFECTIVE or there are no per-instance-configs.`,
},
},
},
},
},
},
},
},
},
},
},
UseJSONNumber: true,
}
Expand Down Expand Up @@ -554,12 +618,23 @@ func resourceComputeInstanceGroupManagerRead(d *schema.ResourceData, meta interf
if err = d.Set("update_policy", flattenUpdatePolicy(manager.UpdatePolicy)); err != nil {
return fmt.Errorf("Error setting update_policy in state: %s", err.Error())
}
if err = d.Set("status", flattenStatus(manager.Status)); err != nil {
return fmt.Errorf("Error setting status in state: %s", err.Error())
}

// If unset in state set to default value
if d.Get("wait_for_instances_status").(string) == "" {
if err = d.Set("wait_for_instances_status", "STABLE"); err != nil {
return fmt.Errorf("Error setting wait_for_instances_status in state: %s", err.Error())
}
}

if d.Get("wait_for_instances").(bool) {
waitForUpdates := d.Get("wait_for_instances_status").(string) == "UPDATED"
conf := resource.StateChangeConf{
Pending: []string{"creating", "error"},
Pending: []string{"creating", "error", "updating per instance configs", "reaching version target"},
Target: []string{"created"},
Refresh: waitForInstancesRefreshFunc(getManager, d, meta),
Refresh: waitForInstancesRefreshFunc(getManager, waitForUpdates, d, meta),
Timeout: d.Timeout(schema.TimeoutCreate),
}
_, err := conf.WaitForState()
Expand Down Expand Up @@ -905,10 +980,52 @@ func flattenUpdatePolicy(updatePolicy *computeBeta.InstanceGroupManagerUpdatePol
return results
}

func flattenStatus(status *computeBeta.InstanceGroupManagerStatus) []map[string]interface{} {
results := []map[string]interface{}{}
data := map[string]interface{}{
"is_stable": status.IsStable,
"stateful": flattenStatusStateful(status.Stateful),
"version_target": flattenStatusVersionTarget(status.VersionTarget),
}
results = append(results, data)
return results
}

func flattenStatusStateful(stateful *computeBeta.InstanceGroupManagerStatusStateful) []map[string]interface{} {
results := []map[string]interface{}{}
data := map[string]interface{}{
"has_stateful_config": stateful.HasStatefulConfig,
"per_instance_configs": flattenStatusStatefulConfigs(stateful.PerInstanceConfigs),
}
results = append(results, data)
return results
}

func flattenStatusStatefulConfigs(statefulConfigs *computeBeta.InstanceGroupManagerStatusStatefulPerInstanceConfigs) []map[string]interface{} {
results := []map[string]interface{}{}
data := map[string]interface{}{
"all_effective": statefulConfigs.AllEffective,
}
results = append(results, data)
return results
}

func flattenStatusVersionTarget(versionTarget *computeBeta.InstanceGroupManagerStatusVersionTarget) []map[string]interface{} {
results := []map[string]interface{}{}
data := map[string]interface{}{
"is_reached": versionTarget.IsReached,
}
results = append(results, data)
return results
}

func resourceInstanceGroupManagerStateImporter(d *schema.ResourceData, meta interface{}) ([]*schema.ResourceData, error) {
if err := d.Set("wait_for_instances", false); err != nil {
return nil, fmt.Errorf("Error setting wait_for_instances: %s", err)
}
if err := d.Set("wait_for_instances_status", "STABLE"); err != nil {
return nil, fmt.Errorf("Error setting wait_for_instances_status: %s", err)
}
config := meta.(*Config)
if err := parseImportId([]string{"projects/(?P<project>[^/]+)/zones/(?P<zone>[^/]+)/instanceGroupManagers/(?P<name>[^/]+)", "(?P<project>[^/]+)/(?P<zone>[^/]+)/(?P<name>[^/]+)", "(?P<project>[^/]+)/(?P<name>[^/]+)", "(?P<name>[^/]+)"}, d, config); err != nil {
return nil, err
Expand Down
159 changes: 159 additions & 0 deletions google-beta/resource_compute_instance_group_manager_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -373,6 +373,41 @@ func TestAccInstanceGroupManager_stateful(t *testing.T) {
})
}

func TestAccInstanceGroupManager_waitForStatus(t *testing.T) {
t.Parallel()

template := fmt.Sprintf("tf-test-igm-%s", randString(t, 10))
target := fmt.Sprintf("tf-test-igm-%s", randString(t, 10))
igm := fmt.Sprintf("tf-test-igm-%s", randString(t, 10))
perInstanceConfig := fmt.Sprintf("tf-test-config-%s", randString(t, 10))

vcrTest(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
CheckDestroy: testAccCheckInstanceGroupManagerDestroyProducer(t),
Steps: []resource.TestStep{
{
Config: testAccInstanceGroupManager_waitForStatus(template, target, igm, perInstanceConfig),
},
{
ResourceName: "google_compute_instance_group_manager.igm-basic",
ImportState: true,
ImportStateVerify: true,
ImportStateVerifyIgnore: []string{"status", "wait_for_instances_status", "wait_for_instances"},
},
{
Config: testAccInstanceGroupManager_waitForStatusUpdated(template, target, igm, perInstanceConfig),
},
{
ResourceName: "google_compute_instance_group_manager.igm-basic",
ImportState: true,
ImportStateVerify: true,
ImportStateVerifyIgnore: []string{"status", "wait_for_instances_status", "wait_for_instances"},
},
},
})
}

func testAccCheckInstanceGroupManagerDestroyProducer(t *testing.T) func(s *terraform.State) error {
return func(s *terraform.State) error {
config := googleProviderConfig(t)
Expand Down Expand Up @@ -1423,3 +1458,127 @@ resource "google_compute_http_health_check" "zero" {
}
`, template, target, igm, hck)
}

func testAccInstanceGroupManager_waitForStatus(template, target, igm, perInstanceConfig string) string {
return fmt.Sprintf(`
data "google_compute_image" "my_image" {
family = "debian-9"
project = "debian-cloud"
}

resource "google_compute_instance_template" "igm-basic" {
name = "%s"
machine_type = "e2-medium"
can_ip_forward = false
tags = ["foo", "bar"]
disk {
source_image = data.google_compute_image.my_image.self_link
auto_delete = true
boot = true
device_name = "my-stateful-disk"
}

network_interface {
network = "default"
}

service_account {
scopes = ["userinfo-email", "compute-ro", "storage-ro"]
}
}

resource "google_compute_target_pool" "igm-basic" {
description = "Resource created for Terraform acceptance testing"
name = "%s"
session_affinity = "CLIENT_IP_PROTO"
}

resource "google_compute_instance_group_manager" "igm-basic" {
description = "Terraform test instance group manager"
name = "%s"
version {
instance_template = google_compute_instance_template.igm-basic.self_link
name = "prod"
}
target_pools = [google_compute_target_pool.igm-basic.self_link]
base_instance_name = "igm-basic"
zone = "us-central1-c"
wait_for_instances = true
wait_for_instances_status = "STABLE"
}

resource "google_compute_per_instance_config" "per-instance" {
instance_group_manager = google_compute_instance_group_manager.igm-basic.name
zone = "us-central1-c"
name = "%s"
remove_instance_state_on_destroy = true
preserved_state {
metadata = {
foo = "bar"
}
}
}
`, template, target, igm, perInstanceConfig)
}

func testAccInstanceGroupManager_waitForStatusUpdated(template, target, igm, perInstanceConfig string) string {
return fmt.Sprintf(`
data "google_compute_image" "my_image" {
family = "debian-9"
project = "debian-cloud"
}

resource "google_compute_instance_template" "igm-basic" {
name = "%s"
machine_type = "e2-medium"
can_ip_forward = false
tags = ["foo", "bar"]
disk {
source_image = data.google_compute_image.my_image.self_link
auto_delete = true
boot = true
device_name = "my-stateful-disk"
}

network_interface {
network = "default"
}

service_account {
scopes = ["userinfo-email", "compute-ro", "storage-ro"]
}
}

resource "google_compute_target_pool" "igm-basic" {
description = "Resource created for Terraform acceptance testing"
name = "%s"
session_affinity = "CLIENT_IP_PROTO"
}

resource "google_compute_instance_group_manager" "igm-basic" {
description = "Terraform test instance group manager"
name = "%s"
version {
instance_template = google_compute_instance_template.igm-basic.self_link
name = "prod"
}
target_pools = [google_compute_target_pool.igm-basic.self_link]
base_instance_name = "igm-basic"
zone = "us-central1-c"
wait_for_instances = true
wait_for_instances_status = "UPDATED"
}

resource "google_compute_per_instance_config" "per-instance" {
instance_group_manager = google_compute_instance_group_manager.igm-basic.name
zone = "us-central1-c"
name = "%s"
remove_instance_state_on_destroy = true
preserved_state {
metadata = {
foo = "baz"
}
}
}
`, template, target, igm, perInstanceConfig)
}
Loading