providers/google: support optionial uuid naming for Instance Template (#6604)
Auto-generating an Instance Template name (or just its suffix) allows the create_before_destroy lifecycle option to function correctly on the Instance Template resource. This in turn allows Instance Group Managers to be updated without being destroyed.
This commit is contained in:
parent
2620a78c49
commit
55742acf12
|
@ -2,6 +2,8 @@ package google
|
|||
|
||||
import (
|
||||
"fmt"
|
||||
"reflect"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"google.golang.org/api/compute/v1"
|
||||
|
@ -79,6 +81,37 @@ func TestAccInstanceGroupManager_update(t *testing.T) {
|
|||
})
|
||||
}
|
||||
|
||||
func TestAccInstanceGroupManager_updateLifecycle(t *testing.T) {
|
||||
var manager compute.InstanceGroupManager
|
||||
|
||||
tag1 := "tag1"
|
||||
tag2 := "tag2"
|
||||
igm := fmt.Sprintf("igm-test-%s", acctest.RandString(10))
|
||||
|
||||
resource.Test(t, resource.TestCase{
|
||||
PreCheck: func() { testAccPreCheck(t) },
|
||||
Providers: testAccProviders,
|
||||
CheckDestroy: testAccCheckInstanceGroupManagerDestroy,
|
||||
Steps: []resource.TestStep{
|
||||
resource.TestStep{
|
||||
Config: testAccInstanceGroupManager_updateLifecycle(tag1, igm),
|
||||
Check: resource.ComposeTestCheckFunc(
|
||||
testAccCheckInstanceGroupManagerExists(
|
||||
"google_compute_instance_group_manager.igm-update", &manager),
|
||||
),
|
||||
},
|
||||
resource.TestStep{
|
||||
Config: testAccInstanceGroupManager_updateLifecycle(tag2, igm),
|
||||
Check: resource.ComposeTestCheckFunc(
|
||||
testAccCheckInstanceGroupManagerExists(
|
||||
"google_compute_instance_group_manager.igm-update", &manager),
|
||||
testAccCheckInstanceGroupManagerTemplateTags(
|
||||
"google_compute_instance_group_manager.igm-update", []string{tag2}),
|
||||
),
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
||||
func testAccCheckInstanceGroupManagerDestroy(s *terraform.State) error {
|
||||
config := testAccProvider.Meta().(*Config)
|
||||
|
||||
|
@ -201,6 +234,40 @@ func testAccCheckInstanceGroupManagerNamedPorts(n string, np map[string]int64, i
|
|||
}
|
||||
}
|
||||
|
||||
func testAccCheckInstanceGroupManagerTemplateTags(n string, tags []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.clientCompute.InstanceGroupManagers.Get(
|
||||
config.Project, rs.Primary.Attributes["zone"], rs.Primary.ID).Do()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// check that the instance template updated
|
||||
instanceTemplate, err := config.clientCompute.InstanceTemplates.Get(
|
||||
config.Project, resourceSplitter(manager.InstanceTemplate)).Do()
|
||||
if err != nil {
|
||||
return fmt.Errorf("Error reading instance template: %s", err)
|
||||
}
|
||||
|
||||
if !reflect.DeepEqual(instanceTemplate.Properties.Tags.Items, tags) {
|
||||
return fmt.Errorf("instance template not updated")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func testAccInstanceGroupManager_basic(template, target, igm1, igm2 string) string {
|
||||
return fmt.Sprintf(`
|
||||
resource "google_compute_instance_template" "igm-basic" {
|
||||
|
@ -380,3 +447,49 @@ func testAccInstanceGroupManager_update2(template1, target, template2, igm strin
|
|||
}
|
||||
}`, template1, target, template2, igm)
|
||||
}
|
||||
|
||||
func testAccInstanceGroupManager_updateLifecycle(tag, igm string) string {
|
||||
return fmt.Sprintf(`
|
||||
resource "google_compute_instance_template" "igm-update" {
|
||||
machine_type = "n1-standard-1"
|
||||
can_ip_forward = false
|
||||
tags = ["%s"]
|
||||
|
||||
disk {
|
||||
source_image = "debian-cloud/debian-7-wheezy-v20160301"
|
||||
auto_delete = true
|
||||
boot = true
|
||||
}
|
||||
|
||||
network_interface {
|
||||
network = "default"
|
||||
}
|
||||
|
||||
service_account {
|
||||
scopes = ["userinfo-email", "compute-ro", "storage-ro"]
|
||||
}
|
||||
|
||||
lifecycle {
|
||||
create_before_destroy = true
|
||||
}
|
||||
}
|
||||
|
||||
resource "google_compute_instance_group_manager" "igm-update" {
|
||||
description = "Terraform test instance group manager"
|
||||
name = "%s"
|
||||
instance_template = "${google_compute_instance_template.igm-update.self_link}"
|
||||
base_instance_name = "igm-update"
|
||||
zone = "us-central1-c"
|
||||
target_size = 2
|
||||
named_port {
|
||||
name = "customhttp"
|
||||
port = 8080
|
||||
}
|
||||
}`, tag, igm)
|
||||
}
|
||||
|
||||
func resourceSplitter(resource string) string {
|
||||
splits := strings.Split(resource, "/")
|
||||
|
||||
return splits[len(splits)-1]
|
||||
}
|
||||
|
|
|
@ -4,6 +4,7 @@ import (
|
|||
"fmt"
|
||||
"log"
|
||||
|
||||
"github.com/hashicorp/terraform/helper/resource"
|
||||
"github.com/hashicorp/terraform/helper/schema"
|
||||
"google.golang.org/api/compute/v1"
|
||||
"google.golang.org/api/googleapi"
|
||||
|
@ -16,6 +17,38 @@ func resourceComputeInstanceTemplate() *schema.Resource {
|
|||
Delete: resourceComputeInstanceTemplateDelete,
|
||||
|
||||
Schema: map[string]*schema.Schema{
|
||||
"name": &schema.Schema{
|
||||
Type: schema.TypeString,
|
||||
Optional: true,
|
||||
Computed: true,
|
||||
ForceNew: true,
|
||||
ConflictsWith: []string{"name_prefix"},
|
||||
ValidateFunc: func(v interface{}, k string) (ws []string, errors []error) {
|
||||
// https://cloud.google.com/compute/docs/reference/latest/instanceTemplates#resource
|
||||
value := v.(string)
|
||||
if len(value) > 63 {
|
||||
errors = append(errors, fmt.Errorf(
|
||||
"%q cannot be longer than 63 characters", k))
|
||||
}
|
||||
return
|
||||
},
|
||||
},
|
||||
|
||||
"name_prefix": &schema.Schema{
|
||||
Type: schema.TypeString,
|
||||
Optional: true,
|
||||
ForceNew: true,
|
||||
ValidateFunc: func(v interface{}, k string) (ws []string, errors []error) {
|
||||
// https://cloud.google.com/compute/docs/reference/latest/instanceTemplates#resource
|
||||
// uuid is 26 characters, limit the prefix to 37.
|
||||
value := v.(string)
|
||||
if len(value) > 37 {
|
||||
errors = append(errors, fmt.Errorf(
|
||||
"%q cannot be longer than 37 characters, name is limited to 63", k))
|
||||
}
|
||||
return
|
||||
},
|
||||
},
|
||||
"disk": &schema.Schema{
|
||||
Type: schema.TypeList,
|
||||
Required: true,
|
||||
|
@ -98,12 +131,6 @@ func resourceComputeInstanceTemplate() *schema.Resource {
|
|||
ForceNew: true,
|
||||
},
|
||||
|
||||
"name": &schema.Schema{
|
||||
Type: schema.TypeString,
|
||||
Required: true,
|
||||
ForceNew: true,
|
||||
},
|
||||
|
||||
"automatic_restart": &schema.Schema{
|
||||
Type: schema.TypeBool,
|
||||
Optional: true,
|
||||
|
@ -512,10 +539,18 @@ func resourceComputeInstanceTemplateCreate(d *schema.ResourceData, meta interfac
|
|||
|
||||
instanceProperties.Tags = resourceInstanceTags(d)
|
||||
|
||||
var itName string
|
||||
if v, ok := d.GetOk("name"); ok {
|
||||
itName = v.(string)
|
||||
} else if v, ok := d.GetOk("name_prefix"); ok {
|
||||
itName = resource.PrefixedUniqueId(v.(string))
|
||||
} else {
|
||||
itName = resource.UniqueId()
|
||||
}
|
||||
instanceTemplate := compute.InstanceTemplate{
|
||||
Description: d.Get("description").(string),
|
||||
Properties: instanceProperties,
|
||||
Name: d.Get("name").(string),
|
||||
Name: itName,
|
||||
}
|
||||
|
||||
op, err := config.clientCompute.InstanceTemplates.Insert(
|
||||
|
@ -567,7 +602,7 @@ func resourceComputeInstanceTemplateRead(d *schema.ResourceData, meta interface{
|
|||
d.Set("tags_fingerprint", instanceTemplate.Properties.Tags.Fingerprint)
|
||||
}
|
||||
d.Set("self_link", instanceTemplate.SelfLink)
|
||||
|
||||
d.Set("name", instanceTemplate.Name)
|
||||
return nil
|
||||
}
|
||||
|
||||
|
|
|
@ -58,6 +58,51 @@ resource "google_compute_instance_template" "foobar" {
|
|||
}
|
||||
```
|
||||
|
||||
## Using with Instance Group Manager
|
||||
|
||||
Instance Templates cannot be updated after creation with the Google
|
||||
Cloud Platform API. In order to update an Instance Template, Terraform will
|
||||
destroy the existing resource and create a replacement. In order to effectively
|
||||
use an Instance Template resource with an [Instance Group Manager resource][1],
|
||||
it's recommended to specify `create_before_destroy` in a [lifecycle][2] block.
|
||||
Either omit the Instance Template `name` attribute, or specify a partial name
|
||||
with `name_prefix`. Example:
|
||||
|
||||
```
|
||||
resource "google_compute_instance_template" "instance_template" {
|
||||
name_prefix = "instance-template-"
|
||||
machine_type = "n1-standard-1"
|
||||
region = "us-central1"
|
||||
|
||||
// boot disk
|
||||
disk {
|
||||
...
|
||||
}
|
||||
|
||||
// networking
|
||||
network_interface {
|
||||
...
|
||||
}
|
||||
|
||||
lifecycle {
|
||||
create_before_destroy = true
|
||||
}
|
||||
}
|
||||
|
||||
resource "google_compute_instance_group_manager" "instance_group_manager" {
|
||||
name = "instance-group-manager"
|
||||
instance_template = "${google_compute_instance_template.instance_template.self_link}"
|
||||
base_instance_name = "instance-group-manager"
|
||||
zone = "us-central1-f"
|
||||
target_size = "1"
|
||||
}
|
||||
```
|
||||
|
||||
With this setup Terraform generates a unique name for your Instance
|
||||
Template and can then update the Instance Group manager without conflict before
|
||||
destroying the previous Instance Template.
|
||||
|
||||
|
||||
## Argument Reference
|
||||
|
||||
Note that changing any field for this resource forces a new resource to be created.
|
||||
|
@ -70,9 +115,12 @@ The following arguments are supported:
|
|||
|
||||
* `machine_type` - (Required) The machine type to create.
|
||||
|
||||
* `name` - (Required) A unique name for the resource, required by GCE.
|
||||
|
||||
- - -
|
||||
* `name` - (Optional) The name of the instance template. If you leave
|
||||
this blank, Terraform will auto-generate a unique name.
|
||||
|
||||
* `name_prefix` - (Optional) Creates a unique name beginning with the specified
|
||||
prefix. Conflicts with `name`.
|
||||
|
||||
* `can_ip_forward` - (Optional) Whether to allow sending and receiving of
|
||||
packets with non-matching source or destination IPs. This defaults to false.
|
||||
|
@ -191,3 +239,6 @@ exported:
|
|||
* `self_link` - The URI of the created resource.
|
||||
|
||||
* `tags_fingerprint` - The unique fingerprint of the tags.
|
||||
|
||||
[1]: /docs/providers/google/r/compute_instance_group_manager.html
|
||||
[2]: /docs/configuration/resources.html#lifecycle
|
||||
|
|
Loading…
Reference in New Issue