github.com/danp/terraform@v0.9.5-0.20170426144147-39d740081351/builtin/providers/google/resource_compute_instance_group_manager.go (about)

     1  package google
     2  
     3  import (
     4  	"fmt"
     5  	"log"
     6  	"strings"
     7  	"time"
     8  
     9  	"github.com/hashicorp/terraform/helper/schema"
    10  	"google.golang.org/api/compute/v1"
    11  )
    12  
    13  func resourceComputeInstanceGroupManager() *schema.Resource {
    14  	return &schema.Resource{
    15  		Create: resourceComputeInstanceGroupManagerCreate,
    16  		Read:   resourceComputeInstanceGroupManagerRead,
    17  		Update: resourceComputeInstanceGroupManagerUpdate,
    18  		Delete: resourceComputeInstanceGroupManagerDelete,
    19  		Importer: &schema.ResourceImporter{
    20  			State: schema.ImportStatePassthrough,
    21  		},
    22  
    23  		Schema: map[string]*schema.Schema{
    24  			"base_instance_name": &schema.Schema{
    25  				Type:     schema.TypeString,
    26  				Required: true,
    27  				ForceNew: true,
    28  			},
    29  
    30  			"instance_template": &schema.Schema{
    31  				Type:     schema.TypeString,
    32  				Required: true,
    33  			},
    34  
    35  			"name": &schema.Schema{
    36  				Type:     schema.TypeString,
    37  				Required: true,
    38  				ForceNew: true,
    39  			},
    40  
    41  			"zone": &schema.Schema{
    42  				Type:     schema.TypeString,
    43  				Required: true,
    44  				ForceNew: true,
    45  			},
    46  
    47  			"description": &schema.Schema{
    48  				Type:     schema.TypeString,
    49  				Optional: true,
    50  				ForceNew: true,
    51  			},
    52  
    53  			"fingerprint": &schema.Schema{
    54  				Type:     schema.TypeString,
    55  				Computed: true,
    56  			},
    57  
    58  			"instance_group": &schema.Schema{
    59  				Type:     schema.TypeString,
    60  				Computed: true,
    61  			},
    62  
    63  			"named_port": &schema.Schema{
    64  				Type:     schema.TypeList,
    65  				Optional: true,
    66  				Elem: &schema.Resource{
    67  					Schema: map[string]*schema.Schema{
    68  						"name": &schema.Schema{
    69  							Type:     schema.TypeString,
    70  							Required: true,
    71  						},
    72  
    73  						"port": &schema.Schema{
    74  							Type:     schema.TypeInt,
    75  							Required: true,
    76  						},
    77  					},
    78  				},
    79  			},
    80  
    81  			"project": &schema.Schema{
    82  				Type:     schema.TypeString,
    83  				Optional: true,
    84  				ForceNew: true,
    85  				Computed: true,
    86  			},
    87  
    88  			"self_link": &schema.Schema{
    89  				Type:     schema.TypeString,
    90  				Computed: true,
    91  			},
    92  
    93  			"update_strategy": &schema.Schema{
    94  				Type:     schema.TypeString,
    95  				Optional: true,
    96  				Default:  "RESTART",
    97  			},
    98  
    99  			"target_pools": &schema.Schema{
   100  				Type:     schema.TypeSet,
   101  				Optional: true,
   102  				Elem:     &schema.Schema{Type: schema.TypeString},
   103  				Set:      schema.HashString,
   104  			},
   105  
   106  			"target_size": &schema.Schema{
   107  				Type:     schema.TypeInt,
   108  				Computed: true,
   109  				Optional: true,
   110  			},
   111  		},
   112  	}
   113  }
   114  
   115  func getNamedPorts(nps []interface{}) []*compute.NamedPort {
   116  	namedPorts := make([]*compute.NamedPort, 0, len(nps))
   117  	for _, v := range nps {
   118  		np := v.(map[string]interface{})
   119  		namedPorts = append(namedPorts, &compute.NamedPort{
   120  			Name: np["name"].(string),
   121  			Port: int64(np["port"].(int)),
   122  		})
   123  	}
   124  	return namedPorts
   125  }
   126  
   127  func resourceComputeInstanceGroupManagerCreate(d *schema.ResourceData, meta interface{}) error {
   128  	config := meta.(*Config)
   129  
   130  	project, err := getProject(d, config)
   131  	if err != nil {
   132  		return err
   133  	}
   134  
   135  	// Get group size, default to 1 if not given
   136  	var target_size int64 = 1
   137  	if v, ok := d.GetOk("target_size"); ok {
   138  		target_size = int64(v.(int))
   139  	}
   140  
   141  	// Build the parameter
   142  	manager := &compute.InstanceGroupManager{
   143  		Name:             d.Get("name").(string),
   144  		BaseInstanceName: d.Get("base_instance_name").(string),
   145  		InstanceTemplate: d.Get("instance_template").(string),
   146  		TargetSize:       target_size,
   147  	}
   148  
   149  	// Set optional fields
   150  	if v, ok := d.GetOk("description"); ok {
   151  		manager.Description = v.(string)
   152  	}
   153  
   154  	if v, ok := d.GetOk("named_port"); ok {
   155  		manager.NamedPorts = getNamedPorts(v.([]interface{}))
   156  	}
   157  
   158  	if attr := d.Get("target_pools").(*schema.Set); attr.Len() > 0 {
   159  		var s []string
   160  		for _, v := range attr.List() {
   161  			s = append(s, v.(string))
   162  		}
   163  		manager.TargetPools = s
   164  	}
   165  
   166  	updateStrategy := d.Get("update_strategy").(string)
   167  	if !(updateStrategy == "NONE" || updateStrategy == "RESTART") {
   168  		return fmt.Errorf("Update strategy must be \"NONE\" or \"RESTART\"")
   169  	}
   170  
   171  	log.Printf("[DEBUG] InstanceGroupManager insert request: %#v", manager)
   172  	op, err := config.clientCompute.InstanceGroupManagers.Insert(
   173  		project, d.Get("zone").(string), manager).Do()
   174  	if err != nil {
   175  		return fmt.Errorf("Error creating InstanceGroupManager: %s", err)
   176  	}
   177  
   178  	// It probably maybe worked, so store the ID now
   179  	d.SetId(manager.Name)
   180  
   181  	// Wait for the operation to complete
   182  	err = computeOperationWaitZone(config, op, project, d.Get("zone").(string), "Creating InstanceGroupManager")
   183  	if err != nil {
   184  		return err
   185  	}
   186  
   187  	return resourceComputeInstanceGroupManagerRead(d, meta)
   188  }
   189  
   190  func flattenNamedPorts(namedPorts []*compute.NamedPort) []map[string]interface{} {
   191  	result := make([]map[string]interface{}, 0, len(namedPorts))
   192  	for _, namedPort := range namedPorts {
   193  		namedPortMap := make(map[string]interface{})
   194  		namedPortMap["name"] = namedPort.Name
   195  		namedPortMap["port"] = namedPort.Port
   196  		result = append(result, namedPortMap)
   197  	}
   198  	return result
   199  
   200  }
   201  
   202  func resourceComputeInstanceGroupManagerRead(d *schema.ResourceData, meta interface{}) error {
   203  	config := meta.(*Config)
   204  
   205  	project, err := getProject(d, config)
   206  	if err != nil {
   207  		return err
   208  	}
   209  
   210  	region, err := getRegion(d, config)
   211  	if err != nil {
   212  		return err
   213  	}
   214  
   215  	getInstanceGroupManager := func(zone string) (interface{}, error) {
   216  		return config.clientCompute.InstanceGroupManagers.Get(project, zone, d.Id()).Do()
   217  	}
   218  
   219  	var manager *compute.InstanceGroupManager
   220  	var e error
   221  	if zone, ok := d.GetOk("zone"); ok {
   222  		manager, e = config.clientCompute.InstanceGroupManagers.Get(project, zone.(string), d.Id()).Do()
   223  
   224  		if e != nil {
   225  			return e
   226  		}
   227  	} else {
   228  		// If the resource was imported, the only info we have is the ID. Try to find the resource
   229  		// by searching in the region of the project.
   230  		var resource interface{}
   231  		resource, e = getZonalResourceFromRegion(getInstanceGroupManager, region, config.clientCompute, project)
   232  
   233  		if e != nil {
   234  			return e
   235  		}
   236  
   237  		manager = resource.(*compute.InstanceGroupManager)
   238  	}
   239  
   240  	if manager == nil {
   241  		log.Printf("[WARN] Removing Instance Group Manager %q because it's gone", d.Get("name").(string))
   242  		// The resource doesn't exist anymore
   243  		d.SetId("")
   244  		return nil
   245  	}
   246  
   247  	zoneUrl := strings.Split(manager.Zone, "/")
   248  	d.Set("base_instance_name", manager.BaseInstanceName)
   249  	d.Set("instance_template", manager.InstanceTemplate)
   250  	d.Set("name", manager.Name)
   251  	d.Set("zone", zoneUrl[len(zoneUrl)-1])
   252  	d.Set("description", manager.Description)
   253  	d.Set("project", project)
   254  	d.Set("target_size", manager.TargetSize)
   255  	d.Set("target_pools", manager.TargetPools)
   256  	d.Set("named_port", flattenNamedPorts(manager.NamedPorts))
   257  	d.Set("fingerprint", manager.Fingerprint)
   258  	d.Set("instance_group", manager.InstanceGroup)
   259  	d.Set("target_size", manager.TargetSize)
   260  	d.Set("self_link", manager.SelfLink)
   261  	update_strategy, ok := d.GetOk("update_strategy")
   262  	if !ok {
   263  		update_strategy = "RESTART"
   264  	}
   265  	d.Set("update_strategy", update_strategy.(string))
   266  
   267  	return nil
   268  }
   269  func resourceComputeInstanceGroupManagerUpdate(d *schema.ResourceData, meta interface{}) error {
   270  	config := meta.(*Config)
   271  
   272  	project, err := getProject(d, config)
   273  	if err != nil {
   274  		return err
   275  	}
   276  
   277  	d.Partial(true)
   278  
   279  	// If target_pools changes then update
   280  	if d.HasChange("target_pools") {
   281  		var targetPools []string
   282  		if attr := d.Get("target_pools").(*schema.Set); attr.Len() > 0 {
   283  			for _, v := range attr.List() {
   284  				targetPools = append(targetPools, v.(string))
   285  			}
   286  		}
   287  
   288  		// Build the parameter
   289  		setTargetPools := &compute.InstanceGroupManagersSetTargetPoolsRequest{
   290  			Fingerprint: d.Get("fingerprint").(string),
   291  			TargetPools: targetPools,
   292  		}
   293  
   294  		op, err := config.clientCompute.InstanceGroupManagers.SetTargetPools(
   295  			project, d.Get("zone").(string), d.Id(), setTargetPools).Do()
   296  		if err != nil {
   297  			return fmt.Errorf("Error updating InstanceGroupManager: %s", err)
   298  		}
   299  
   300  		// Wait for the operation to complete
   301  		err = computeOperationWaitZone(config, op, project, d.Get("zone").(string), "Updating InstanceGroupManager")
   302  		if err != nil {
   303  			return err
   304  		}
   305  
   306  		d.SetPartial("target_pools")
   307  	}
   308  
   309  	// If instance_template changes then update
   310  	if d.HasChange("instance_template") {
   311  		// Build the parameter
   312  		setInstanceTemplate := &compute.InstanceGroupManagersSetInstanceTemplateRequest{
   313  			InstanceTemplate: d.Get("instance_template").(string),
   314  		}
   315  
   316  		op, err := config.clientCompute.InstanceGroupManagers.SetInstanceTemplate(
   317  			project, d.Get("zone").(string), d.Id(), setInstanceTemplate).Do()
   318  		if err != nil {
   319  			return fmt.Errorf("Error updating InstanceGroupManager: %s", err)
   320  		}
   321  
   322  		// Wait for the operation to complete
   323  		err = computeOperationWaitZone(config, op, project, d.Get("zone").(string), "Updating InstanceGroupManager")
   324  		if err != nil {
   325  			return err
   326  		}
   327  
   328  		if d.Get("update_strategy").(string) == "RESTART" {
   329  			managedInstances, err := config.clientCompute.InstanceGroupManagers.ListManagedInstances(
   330  				project, d.Get("zone").(string), d.Id()).Do()
   331  
   332  			managedInstanceCount := len(managedInstances.ManagedInstances)
   333  			instances := make([]string, managedInstanceCount)
   334  			for i, v := range managedInstances.ManagedInstances {
   335  				instances[i] = v.Instance
   336  			}
   337  
   338  			recreateInstances := &compute.InstanceGroupManagersRecreateInstancesRequest{
   339  				Instances: instances,
   340  			}
   341  
   342  			op, err = config.clientCompute.InstanceGroupManagers.RecreateInstances(
   343  				project, d.Get("zone").(string), d.Id(), recreateInstances).Do()
   344  
   345  			if err != nil {
   346  				return fmt.Errorf("Error restarting instance group managers instances: %s", err)
   347  			}
   348  
   349  			// Wait for the operation to complete
   350  			err = computeOperationWaitZoneTime(config, op, project, d.Get("zone").(string),
   351  				managedInstanceCount*4, "Restarting InstanceGroupManagers instances")
   352  			if err != nil {
   353  				return err
   354  			}
   355  		}
   356  
   357  		d.SetPartial("instance_template")
   358  	}
   359  
   360  	// If named_port changes then update:
   361  	if d.HasChange("named_port") {
   362  
   363  		// Build the parameters for a "SetNamedPorts" request:
   364  		namedPorts := getNamedPorts(d.Get("named_port").([]interface{}))
   365  		setNamedPorts := &compute.InstanceGroupsSetNamedPortsRequest{
   366  			NamedPorts: namedPorts,
   367  		}
   368  
   369  		// Make the request:
   370  		op, err := config.clientCompute.InstanceGroups.SetNamedPorts(
   371  			project, d.Get("zone").(string), d.Id(), setNamedPorts).Do()
   372  		if err != nil {
   373  			return fmt.Errorf("Error updating InstanceGroupManager: %s", err)
   374  		}
   375  
   376  		// Wait for the operation to complete:
   377  		err = computeOperationWaitZone(config, op, project, d.Get("zone").(string), "Updating InstanceGroupManager")
   378  		if err != nil {
   379  			return err
   380  		}
   381  
   382  		d.SetPartial("named_port")
   383  	}
   384  
   385  	// If size changes trigger a resize
   386  	if d.HasChange("target_size") {
   387  		if v, ok := d.GetOk("target_size"); ok {
   388  			// Only do anything if the new size is set
   389  			target_size := int64(v.(int))
   390  
   391  			op, err := config.clientCompute.InstanceGroupManagers.Resize(
   392  				project, d.Get("zone").(string), d.Id(), target_size).Do()
   393  			if err != nil {
   394  				return fmt.Errorf("Error updating InstanceGroupManager: %s", err)
   395  			}
   396  
   397  			// Wait for the operation to complete
   398  			err = computeOperationWaitZone(config, op, project, d.Get("zone").(string), "Updating InstanceGroupManager")
   399  			if err != nil {
   400  				return err
   401  			}
   402  		}
   403  
   404  		d.SetPartial("target_size")
   405  	}
   406  
   407  	d.Partial(false)
   408  
   409  	return resourceComputeInstanceGroupManagerRead(d, meta)
   410  }
   411  
   412  func resourceComputeInstanceGroupManagerDelete(d *schema.ResourceData, meta interface{}) error {
   413  	config := meta.(*Config)
   414  
   415  	project, err := getProject(d, config)
   416  	if err != nil {
   417  		return err
   418  	}
   419  
   420  	zone := d.Get("zone").(string)
   421  	op, err := config.clientCompute.InstanceGroupManagers.Delete(project, zone, d.Id()).Do()
   422  	attempt := 0
   423  	for err != nil && attempt < 20 {
   424  		attempt++
   425  		time.Sleep(2000 * time.Millisecond)
   426  		op, err = config.clientCompute.InstanceGroupManagers.Delete(project, zone, d.Id()).Do()
   427  	}
   428  	if err != nil {
   429  		return fmt.Errorf("Error deleting instance group manager: %s", err)
   430  	}
   431  
   432  	currentSize := int64(d.Get("target_size").(int))
   433  
   434  	// Wait for the operation to complete
   435  	err = computeOperationWaitZone(config, op, project, d.Get("zone").(string), "Deleting InstanceGroupManager")
   436  
   437  	for err != nil && currentSize > 0 {
   438  		if !strings.Contains(err.Error(), "timeout") {
   439  			return err
   440  		}
   441  
   442  		instanceGroup, err := config.clientCompute.InstanceGroups.Get(
   443  			project, d.Get("zone").(string), d.Id()).Do()
   444  
   445  		if err != nil {
   446  			return fmt.Errorf("Error getting instance group size: %s", err)
   447  		}
   448  
   449  		if instanceGroup.Size >= currentSize {
   450  			return fmt.Errorf("Error, instance group isn't shrinking during delete")
   451  		}
   452  
   453  		log.Printf("[INFO] timeout occured, but instance group is shrinking (%d < %d)", instanceGroup.Size, currentSize)
   454  
   455  		currentSize = instanceGroup.Size
   456  
   457  		err = computeOperationWaitZone(config, op, project, d.Get("zone").(string), "Deleting InstanceGroupManager")
   458  	}
   459  
   460  	d.SetId("")
   461  	return nil
   462  }