mirror of
https://github.com/letic/terraform-provider-google.git
synced 2024-07-05 17:52:38 +00:00
Implement multiple versions for regional instance groups (#1809)
This commit is contained in:
parent
0a87092bd9
commit
7da529f3c1
|
@ -456,10 +456,10 @@ func resourceComputeInstanceGroupManagerRead(d *schema.ResourceData, meta interf
|
|||
return nil
|
||||
}
|
||||
|
||||
// Updates an instance group manager by applying an update strategy (REPLACE, RESTART) respecting a rolling update policy (availability settings,
|
||||
// interval between updates, and particularly, the type of update PROACTIVE or OPPORTUNISTIC because updates performed by API are considered
|
||||
// OPPORTUNISTIC by default)
|
||||
func performUpdate(config *Config, id string, updateStrategy string, rollingUpdatePolicy *computeBeta.InstanceGroupManagerUpdatePolicy, versions []*computeBeta.InstanceGroupManagerVersion, project string, zone string) error {
|
||||
// Updates an instance group manager by applying the update strategy (REPLACE, RESTART)
|
||||
// and rolling update policy (PROACTIVE, OPPORTUNISTIC). Updates performed by API
|
||||
// are OPPORTUNISTIC by default.
|
||||
func performZoneUpdate(config *Config, id string, updateStrategy string, rollingUpdatePolicy *computeBeta.InstanceGroupManagerUpdatePolicy, versions []*computeBeta.InstanceGroupManagerVersion, project string, zone string) error {
|
||||
if updateStrategy == "RESTART" {
|
||||
managedInstances, err := config.clientComputeBeta.InstanceGroupManagers.ListManagedInstances(project, zone, id).Do()
|
||||
if err != nil {
|
||||
|
@ -646,7 +646,7 @@ func resourceComputeInstanceGroupManagerUpdate(d *schema.ResourceData, meta inte
|
|||
|
||||
updateStrategy := d.Get("update_strategy").(string)
|
||||
rollingUpdatePolicy := expandUpdatePolicy(d.Get("rolling_update_policy").([]interface{}))
|
||||
err = performUpdate(config, d.Id(), updateStrategy, rollingUpdatePolicy, nil, project, zone)
|
||||
err = performZoneUpdate(config, d.Id(), updateStrategy, rollingUpdatePolicy, nil, project, zone)
|
||||
d.SetPartial("instance_template")
|
||||
}
|
||||
|
||||
|
@ -655,7 +655,7 @@ func resourceComputeInstanceGroupManagerUpdate(d *schema.ResourceData, meta inte
|
|||
updateStrategy := d.Get("update_strategy").(string)
|
||||
rollingUpdatePolicy := expandUpdatePolicy(d.Get("rolling_update_policy").([]interface{}))
|
||||
versions := expandVersions(d.Get("version").([]interface{}))
|
||||
err = performUpdate(config, d.Id(), updateStrategy, rollingUpdatePolicy, versions, project, zone)
|
||||
err = performZoneUpdate(config, d.Id(), updateStrategy, rollingUpdatePolicy, versions, project, zone)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
|
|
@ -38,10 +38,50 @@ func resourceComputeRegionInstanceGroupManager() *schema.Resource {
|
|||
|
||||
"instance_template": &schema.Schema{
|
||||
Type: schema.TypeString,
|
||||
Required: true,
|
||||
Optional: true,
|
||||
DiffSuppressFunc: compareSelfLinkRelativePaths,
|
||||
},
|
||||
|
||||
"version": &schema.Schema{
|
||||
Type: schema.TypeList,
|
||||
Optional: true,
|
||||
Computed: true,
|
||||
Elem: &schema.Resource{
|
||||
Schema: map[string]*schema.Schema{
|
||||
"name": &schema.Schema{
|
||||
Type: schema.TypeString,
|
||||
Required: true,
|
||||
},
|
||||
|
||||
"instance_template": &schema.Schema{
|
||||
Type: schema.TypeString,
|
||||
Required: true,
|
||||
DiffSuppressFunc: compareSelfLinkRelativePaths,
|
||||
},
|
||||
|
||||
"target_size": &schema.Schema{
|
||||
Type: schema.TypeList,
|
||||
Optional: true,
|
||||
MaxItems: 1,
|
||||
Elem: &schema.Resource{
|
||||
Schema: map[string]*schema.Schema{
|
||||
"fixed": &schema.Schema{
|
||||
Type: schema.TypeInt,
|
||||
Optional: true,
|
||||
},
|
||||
|
||||
"percent": &schema.Schema{
|
||||
Type: schema.TypeInt,
|
||||
Optional: true,
|
||||
ValidateFunc: validation.IntBetween(0, 100),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
"name": &schema.Schema{
|
||||
Type: schema.TypeString,
|
||||
Required: true,
|
||||
|
@ -242,6 +282,7 @@ func resourceComputeRegionInstanceGroupManagerCreate(d *schema.ResourceData, met
|
|||
NamedPorts: getNamedPortsBeta(d.Get("named_port").([]interface{})),
|
||||
TargetPools: convertStringSet(d.Get("target_pools").(*schema.Set)),
|
||||
AutoHealingPolicies: expandAutoHealingPolicies(d.Get("auto_healing_policies").([]interface{})),
|
||||
Versions: expandVersions(d.Get("version").([]interface{})),
|
||||
DistributionPolicy: expandDistributionPolicy(d.Get("distribution_policy_zones").(*schema.Set)),
|
||||
// Force send TargetSize to allow size of 0.
|
||||
ForceSendFields: []string{"TargetSize"},
|
||||
|
@ -315,6 +356,9 @@ func resourceComputeRegionInstanceGroupManagerRead(d *schema.ResourceData, meta
|
|||
|
||||
d.Set("base_instance_name", manager.BaseInstanceName)
|
||||
d.Set("instance_template", manager.InstanceTemplate)
|
||||
if err := d.Set("version", flattenVersions(manager.Versions)); err != nil {
|
||||
return err
|
||||
}
|
||||
d.Set("name", manager.Name)
|
||||
d.Set("region", GetResourceNameFromSelfLink(manager.Region))
|
||||
d.Set("description", manager.Description)
|
||||
|
@ -329,6 +373,11 @@ func resourceComputeRegionInstanceGroupManagerRead(d *schema.ResourceData, meta
|
|||
return err
|
||||
}
|
||||
d.Set("self_link", ConvertSelfLinkToV1(manager.SelfLink))
|
||||
update_strategy, ok := d.GetOk("update_strategy")
|
||||
if !ok {
|
||||
update_strategy = "NONE"
|
||||
}
|
||||
d.Set("update_strategy", update_strategy.(string))
|
||||
|
||||
if d.Get("wait_for_instances").(bool) {
|
||||
conf := resource.StateChangeConf{
|
||||
|
@ -346,6 +395,63 @@ func resourceComputeRegionInstanceGroupManagerRead(d *schema.ResourceData, meta
|
|||
return nil
|
||||
}
|
||||
|
||||
// Updates an instance group manager by applying the update strategy (REPLACE, RESTART)
|
||||
// and rolling update policy (PROACTIVE, OPPORTUNISTIC). Updates performed by API
|
||||
// are OPPORTUNISTIC by default.
|
||||
func performRegionUpdate(config *Config, id string, updateStrategy string, rollingUpdatePolicy *computeBeta.InstanceGroupManagerUpdatePolicy, versions []*computeBeta.InstanceGroupManagerVersion, project string, region string) error {
|
||||
if updateStrategy == "RESTART" {
|
||||
managedInstances, err := config.clientComputeBeta.RegionInstanceGroupManagers.ListManagedInstances(project, region, id).Do()
|
||||
if err != nil {
|
||||
return fmt.Errorf("Error getting region instance group managers instances: %s", err)
|
||||
}
|
||||
|
||||
managedInstanceCount := len(managedInstances.ManagedInstances)
|
||||
instances := make([]string, managedInstanceCount)
|
||||
for i, v := range managedInstances.ManagedInstances {
|
||||
instances[i] = v.Instance
|
||||
}
|
||||
|
||||
recreateInstances := &computeBeta.RegionInstanceGroupManagersRecreateRequest{
|
||||
Instances: instances,
|
||||
}
|
||||
|
||||
op, err := config.clientComputeBeta.RegionInstanceGroupManagers.RecreateInstances(project, region, id, recreateInstances).Do()
|
||||
if err != nil {
|
||||
return fmt.Errorf("Error restarting region instance group managers instances: %s", err)
|
||||
}
|
||||
|
||||
// Wait for the operation to complete
|
||||
err = computeSharedOperationWaitTime(config.clientCompute, op, project, managedInstanceCount*4, "Restarting RegionInstanceGroupManagers instances")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if updateStrategy == "ROLLING_UPDATE" {
|
||||
// UpdatePolicy is set for InstanceGroupManager on update only, because it is only relevant for `Patch` calls.
|
||||
// Other tools(gcloud and UI) capable of executing the same `ROLLING UPDATE` call
|
||||
// expect those values to be provided by user as part of the call
|
||||
// or provide their own defaults without respecting what was previously set on UpdateManager.
|
||||
// To follow the same logic, we provide policy values on relevant update change only.
|
||||
manager := &computeBeta.InstanceGroupManager{
|
||||
UpdatePolicy: rollingUpdatePolicy,
|
||||
Versions: versions,
|
||||
}
|
||||
|
||||
op, err := config.clientComputeBeta.RegionInstanceGroupManagers.Patch(project, region, id, manager).Do()
|
||||
if err != nil {
|
||||
return fmt.Errorf("Error updating region managed group instances: %s", err)
|
||||
}
|
||||
|
||||
err = computeSharedOperationWait(config.clientCompute, op, project, "Updating region managed group instances")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func resourceComputeRegionInstanceGroupManagerUpdate(d *schema.ResourceData, meta interface{}) error {
|
||||
config := meta.(*Config)
|
||||
|
||||
|
@ -406,29 +512,23 @@ func resourceComputeRegionInstanceGroupManagerUpdate(d *schema.ResourceData, met
|
|||
return err
|
||||
}
|
||||
|
||||
if d.Get("update_strategy").(string) == "ROLLING_UPDATE" {
|
||||
// UpdatePolicy is set for InstanceGroupManager on update only, because it is only relevant for `Patch` calls.
|
||||
// Other tools(gcloud and UI) capable of executing the same `ROLLING UPDATE` call
|
||||
// expect those values to be provided by user as part of the call
|
||||
// or provide their own defaults without respecting what was previously set on UpdateManager.
|
||||
// To follow the same logic, we provide policy values on relevant update change only.
|
||||
manager := &computeBeta.InstanceGroupManager{
|
||||
UpdatePolicy: expandUpdatePolicy(d.Get("rolling_update_policy").([]interface{})),
|
||||
}
|
||||
updateStrategy := d.Get("update_strategy").(string)
|
||||
rollingUpdatePolicy := expandUpdatePolicy(d.Get("rolling_update_policy").([]interface{}))
|
||||
err = performRegionUpdate(config, d.Id(), updateStrategy, rollingUpdatePolicy, nil, project, region)
|
||||
d.SetPartial("instance_template")
|
||||
}
|
||||
|
||||
op, err = config.clientComputeBeta.RegionInstanceGroupManagers.Patch(
|
||||
project, region, d.Id(), manager).Do()
|
||||
if err != nil {
|
||||
return fmt.Errorf("Error updating managed group instances: %s", err)
|
||||
}
|
||||
|
||||
err = computeSharedOperationWait(config.clientCompute, op, project, "Updating managed group instances")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// If version changes then update
|
||||
if d.HasChange("version") {
|
||||
updateStrategy := d.Get("update_strategy").(string)
|
||||
rollingUpdatePolicy := expandUpdatePolicy(d.Get("rolling_update_policy").([]interface{}))
|
||||
versions := expandVersions(d.Get("version").([]interface{}))
|
||||
err = performRegionUpdate(config, d.Id(), updateStrategy, rollingUpdatePolicy, versions, project, region)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
d.SetPartial("instance_template")
|
||||
d.SetPartial("version")
|
||||
}
|
||||
|
||||
if d.HasChange("named_port") {
|
||||
|
|
|
@ -236,6 +236,7 @@ func TestAccRegionInstanceGroupManager_rollingUpdatePolicy(t *testing.T) {
|
|||
},
|
||||
})
|
||||
}
|
||||
|
||||
func TestAccRegionInstanceGroupManager_separateRegions(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
|
@ -262,6 +263,36 @@ func TestAccRegionInstanceGroupManager_separateRegions(t *testing.T) {
|
|||
})
|
||||
}
|
||||
|
||||
func TestAccRegionInstanceGroupManager_versions(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
var manager computeBeta.InstanceGroupManager
|
||||
|
||||
primaryTemplate := fmt.Sprintf("igm-test-%s", acctest.RandString(10))
|
||||
canaryTemplate := fmt.Sprintf("igm-test-%s", acctest.RandString(10))
|
||||
igm := fmt.Sprintf("igm-test-%s", acctest.RandString(10))
|
||||
|
||||
resource.Test(t, resource.TestCase{
|
||||
PreCheck: func() { testAccPreCheck(t) },
|
||||
Providers: testAccProviders,
|
||||
CheckDestroy: testAccCheckRegionInstanceGroupManagerDestroy,
|
||||
Steps: []resource.TestStep{
|
||||
resource.TestStep{
|
||||
Config: testAccRegionInstanceGroupManager_versions(primaryTemplate, canaryTemplate, igm),
|
||||
Check: resource.ComposeTestCheckFunc(
|
||||
testAccCheckRegionInstanceGroupManagerBetaExists("google_compute_region_instance_group_manager.igm-basic", &manager),
|
||||
testAccCheckRegionInstanceGroupManagerVersions("google_compute_region_instance_group_manager.igm-basic", primaryTemplate, canaryTemplate),
|
||||
),
|
||||
},
|
||||
resource.TestStep{
|
||||
ResourceName: "google_compute_region_instance_group_manager.igm-basic",
|
||||
ImportState: true,
|
||||
ImportStateVerify: true,
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
func TestAccRegionInstanceGroupManager_autoHealingPolicies(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
|
@ -477,6 +508,42 @@ func testAccCheckRegionInstanceGroupManagerNamedPorts(n string, np map[string]in
|
|||
}
|
||||
}
|
||||
|
||||
func testAccCheckRegionInstanceGroupManagerVersions(n string, primaryTemplate string, canaryTemplate string) resource.TestCheckFunc {
|
||||
return func(s *terraform.State) error {
|
||||
rs, ok := s.RootModule().Resources[n]
|
||||
if !ok {
|
||||
return fmt.Errorf("Not found: %s", n)
|
||||
}
|
||||
|
||||
if rs.Primary.ID == "" {
|
||||
return fmt.Errorf("No ID is set")
|
||||
}
|
||||
|
||||
config := testAccProvider.Meta().(*Config)
|
||||
|
||||
manager, err := config.clientComputeBeta.RegionInstanceGroupManagers.Get(config.Project, rs.Primary.Attributes["region"], rs.Primary.ID).Do()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if len(manager.Versions) != 2 {
|
||||
return fmt.Errorf("Expected # of versions to be 2, got %d", len(manager.Versions))
|
||||
}
|
||||
|
||||
primaryVersion := manager.Versions[0]
|
||||
if !strings.Contains(primaryVersion.InstanceTemplate, primaryTemplate) {
|
||||
return fmt.Errorf("Expected string \"%s\" to appear in \"%s\"", primaryTemplate, primaryVersion.InstanceTemplate)
|
||||
}
|
||||
|
||||
canaryVersion := manager.Versions[1]
|
||||
if !strings.Contains(canaryVersion.InstanceTemplate, canaryTemplate) {
|
||||
return fmt.Errorf("Expected string \"%s\" to appear in \"%s\"", canaryTemplate, canaryVersion.InstanceTemplate)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func testAccCheckRegionInstanceGroupManagerAutoHealingPolicies(n, hck string, initialDelaySec int64) resource.TestCheckFunc {
|
||||
return func(s *terraform.State) error {
|
||||
rs, ok := s.RootModule().Resources[n]
|
||||
|
@ -954,6 +1021,72 @@ resource "google_compute_http_health_check" "zero" {
|
|||
}
|
||||
`, template, target, igm, hck)
|
||||
}
|
||||
func testAccRegionInstanceGroupManager_versions(primaryTemplate string, canaryTemplate string, igm string) string {
|
||||
return fmt.Sprintf(`
|
||||
resource "google_compute_instance_template" "igm-primary" {
|
||||
name = "%s"
|
||||
machine_type = "n1-standard-1"
|
||||
can_ip_forward = false
|
||||
tags = ["foo", "bar"]
|
||||
disk {
|
||||
source_image = "debian-cloud/debian-8-jessie-v20160803"
|
||||
auto_delete = true
|
||||
boot = true
|
||||
}
|
||||
network_interface {
|
||||
network = "default"
|
||||
}
|
||||
metadata {
|
||||
foo = "bar"
|
||||
}
|
||||
service_account {
|
||||
scopes = ["userinfo-email", "compute-ro", "storage-ro"]
|
||||
}
|
||||
}
|
||||
|
||||
resource "google_compute_instance_template" "igm-canary" {
|
||||
name = "%s"
|
||||
machine_type = "n1-standard-1"
|
||||
can_ip_forward = false
|
||||
tags = ["foo", "bar"]
|
||||
disk {
|
||||
source_image = "debian-cloud/debian-8-jessie-v20160803"
|
||||
auto_delete = true
|
||||
boot = true
|
||||
}
|
||||
network_interface {
|
||||
network = "default"
|
||||
}
|
||||
metadata {
|
||||
foo = "bar"
|
||||
}
|
||||
service_account {
|
||||
scopes = ["userinfo-email", "compute-ro", "storage-ro"]
|
||||
}
|
||||
}
|
||||
|
||||
resource "google_compute_region_instance_group_manager" "igm-basic" {
|
||||
description = "Terraform test region instance group manager"
|
||||
name = "%s"
|
||||
base_instance_name = "igm-basic"
|
||||
region = "us-central1"
|
||||
target_size = 2
|
||||
|
||||
version {
|
||||
name = "primary"
|
||||
instance_template = "${google_compute_instance_template.igm-primary.self_link}"
|
||||
}
|
||||
|
||||
version {
|
||||
name = "canary"
|
||||
instance_template = "${google_compute_instance_template.igm-canary.self_link}"
|
||||
target_size {
|
||||
fixed = 1
|
||||
}
|
||||
}
|
||||
}
|
||||
`, primaryTemplate, canaryTemplate, igm)
|
||||
}
|
||||
|
||||
func testAccRegionInstanceGroupManager_distributionPolicy(template, igm string, zones []string) string {
|
||||
return fmt.Sprintf(`
|
||||
|
|
|
@ -15,7 +15,7 @@ and [API](https://cloud.google.com/compute/docs/reference/latest/regionInstanceG
|
|||
|
||||
~> **Note:** Use [google_compute_instance_group_manager](/docs/providers/google/r/compute_instance_group_manager.html) to create a single-zone instance group manager.
|
||||
|
||||
## Example Usage
|
||||
## Example Usage with top level instance template
|
||||
|
||||
```hcl
|
||||
resource "google_compute_health_check" "autohealing" {
|
||||
|
@ -55,6 +55,30 @@ resource "google_compute_region_instance_group_manager" "appserver" {
|
|||
|
||||
```
|
||||
|
||||
## Example Usage with multiple Versions
|
||||
```hcl
|
||||
resource "google_compute_region_instance_group_manager" "appserver" {
|
||||
name = "appserver-igm"
|
||||
|
||||
base_instance_name = "app"
|
||||
update_strategy = "NONE"
|
||||
region = "us-central1"
|
||||
|
||||
target_size = 5
|
||||
|
||||
version {
|
||||
instance_template = "${google_compute_instance_template.appserver.self_link}"
|
||||
}
|
||||
|
||||
version {
|
||||
instance_template = "${google_compute_instance_template.appserver-canary.self_link}"
|
||||
target_size {
|
||||
fixed = 1
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Argument Reference
|
||||
|
||||
The following arguments are supported:
|
||||
|
@ -66,8 +90,15 @@ The following arguments are supported:
|
|||
appending a hyphen and a random four-character string to the base instance
|
||||
name.
|
||||
|
||||
* `instance_template` - (Required) The full URL to an instance template from
|
||||
which all new instances will be created.
|
||||
* `instance_template` - (Optional) The full URL to an instance template from
|
||||
which all new instances will be created. Conflicts with `version` (see [documentation](https://cloud.google.com/compute/docs/instance-groups/updating-managed-instance-groups#relationship_between_instancetemplate_properties_for_a_managed_instance_group))
|
||||
|
||||
* `version` - (Optional) Application versions managed by this instance group. Each
|
||||
version deals with a specific instance template, allowing canary release scenarios.
|
||||
Conflicts with `instance_template`. Structure is documented below. Beware that
|
||||
exactly one version must not specify a target size. It means that versions with
|
||||
a target size will respect the setting, and the one without target size will
|
||||
be applied to all remaining Instances (top level target_size - each version target_size).
|
||||
|
||||
* `name` - (Required) The name of the instance group manager. Must be 1-63
|
||||
characters long and comply with
|
||||
|
@ -89,8 +120,8 @@ The following arguments are supported:
|
|||
|
||||
* `update_strategy` - (Optional, Default `"NONE"`) If the `instance_template`
|
||||
resource is modified, a value of `"NONE"` will prevent any of the managed
|
||||
instances from being restarted by Terraform. A value of `"ROLLING_UPDATE"`
|
||||
is supported as [Beta feature]. A value of `"ROLLING_UPDATE"` requires
|
||||
instances from being restarted by Terraform. A value of `"ROLLING_UPDATE"`
|
||||
is supported as [Beta feature]. A value of `"ROLLING_UPDATE"` requires
|
||||
`rolling_update_policy` block to be set
|
||||
|
||||
* `target_size` - (Optional) The target number of running instances for this managed
|
||||
|
@ -157,6 +188,40 @@ The **auto_healing_policies** block supports:
|
|||
* `initial_delay_sec` - (Required) The number of seconds that the managed instance group waits before
|
||||
it applies autohealing policies to new instances or recently recreated instances. Between 0 and 3600.
|
||||
|
||||
The **version** block supports:
|
||||
|
||||
```hcl
|
||||
version {
|
||||
name = "appserver-canary"
|
||||
instance_template = "${google_compute_instance_template.appserver-canary.self_link}"
|
||||
target_size {
|
||||
fixed = 1
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
```hcl
|
||||
version {
|
||||
name = "appserver-canary"
|
||||
instance_template = "${google_compute_instance_template.appserver-canary.self_link}"
|
||||
target_size {
|
||||
percent = 20
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
* `name` - (Required) - Version name.
|
||||
|
||||
* `instance_template` - (Required) - The full URL to an instance template from which all new instances of this version will be created.
|
||||
|
||||
* `target_size` - (Optional) - The number of instances calculated as a fixed number or a percentage depending on the settings. Structure is documented below.
|
||||
|
||||
The **target_size** block supports:
|
||||
|
||||
* `fixed` - (Optional), The number of instances which are managed for this version. Conflicts with `percent`.
|
||||
|
||||
* `percent` - (Optional), The number of instances (calculated as percentage) which are managed for this version. Conflicts with `fixed`.
|
||||
|
||||
## Attributes Reference
|
||||
|
||||
In addition to the arguments listed above, the following computed attributes are
|
||||
|
|
Loading…
Reference in New Issue
Block a user