github.com/recobe182/terraform@v0.8.5-0.20170117231232-49ab22a935b7/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  	resource, err := getZonalResourceFromRegion(getInstanceGroupManager, region, config.clientCompute, project)
   220  	if err != nil {
   221  		return err
   222  	}
   223  	if resource == nil {
   224  		log.Printf("[WARN] Removing Instance Group Manager %q because it's gone", d.Get("name").(string))
   225  		// The resource doesn't exist anymore
   226  		d.SetId("")
   227  		return nil
   228  	}
   229  	manager := resource.(*compute.InstanceGroupManager)
   230  
   231  	zoneUrl := strings.Split(manager.Zone, "/")
   232  	d.Set("base_instance_name", manager.BaseInstanceName)
   233  	d.Set("instance_template", manager.InstanceTemplate)
   234  	d.Set("name", manager.Name)
   235  	d.Set("zone", zoneUrl[len(zoneUrl)-1])
   236  	d.Set("description", manager.Description)
   237  	d.Set("project", project)
   238  	d.Set("target_size", manager.TargetSize)
   239  	d.Set("target_pools", manager.TargetPools)
   240  	d.Set("named_port", flattenNamedPorts(manager.NamedPorts))
   241  	d.Set("fingerprint", manager.Fingerprint)
   242  	d.Set("instance_group", manager.InstanceGroup)
   243  	d.Set("target_size", manager.TargetSize)
   244  	d.Set("self_link", manager.SelfLink)
   245  	update_strategy, ok := d.GetOk("update_strategy")
   246  	if !ok {
   247  		update_strategy = "RESTART"
   248  	}
   249  	d.Set("update_strategy", update_strategy.(string))
   250  
   251  	return nil
   252  }
   253  func resourceComputeInstanceGroupManagerUpdate(d *schema.ResourceData, meta interface{}) error {
   254  	config := meta.(*Config)
   255  
   256  	project, err := getProject(d, config)
   257  	if err != nil {
   258  		return err
   259  	}
   260  
   261  	d.Partial(true)
   262  
   263  	// If target_pools changes then update
   264  	if d.HasChange("target_pools") {
   265  		var targetPools []string
   266  		if attr := d.Get("target_pools").(*schema.Set); attr.Len() > 0 {
   267  			for _, v := range attr.List() {
   268  				targetPools = append(targetPools, v.(string))
   269  			}
   270  		}
   271  
   272  		// Build the parameter
   273  		setTargetPools := &compute.InstanceGroupManagersSetTargetPoolsRequest{
   274  			Fingerprint: d.Get("fingerprint").(string),
   275  			TargetPools: targetPools,
   276  		}
   277  
   278  		op, err := config.clientCompute.InstanceGroupManagers.SetTargetPools(
   279  			project, d.Get("zone").(string), d.Id(), setTargetPools).Do()
   280  		if err != nil {
   281  			return fmt.Errorf("Error updating InstanceGroupManager: %s", err)
   282  		}
   283  
   284  		// Wait for the operation to complete
   285  		err = computeOperationWaitZone(config, op, project, d.Get("zone").(string), "Updating InstanceGroupManager")
   286  		if err != nil {
   287  			return err
   288  		}
   289  
   290  		d.SetPartial("target_pools")
   291  	}
   292  
   293  	// If instance_template changes then update
   294  	if d.HasChange("instance_template") {
   295  		// Build the parameter
   296  		setInstanceTemplate := &compute.InstanceGroupManagersSetInstanceTemplateRequest{
   297  			InstanceTemplate: d.Get("instance_template").(string),
   298  		}
   299  
   300  		op, err := config.clientCompute.InstanceGroupManagers.SetInstanceTemplate(
   301  			project, d.Get("zone").(string), d.Id(), setInstanceTemplate).Do()
   302  		if err != nil {
   303  			return fmt.Errorf("Error updating InstanceGroupManager: %s", err)
   304  		}
   305  
   306  		// Wait for the operation to complete
   307  		err = computeOperationWaitZone(config, op, project, d.Get("zone").(string), "Updating InstanceGroupManager")
   308  		if err != nil {
   309  			return err
   310  		}
   311  
   312  		if d.Get("update_strategy").(string) == "RESTART" {
   313  			managedInstances, err := config.clientCompute.InstanceGroupManagers.ListManagedInstances(
   314  				project, d.Get("zone").(string), d.Id()).Do()
   315  
   316  			managedInstanceCount := len(managedInstances.ManagedInstances)
   317  			instances := make([]string, managedInstanceCount)
   318  			for i, v := range managedInstances.ManagedInstances {
   319  				instances[i] = v.Instance
   320  			}
   321  
   322  			recreateInstances := &compute.InstanceGroupManagersRecreateInstancesRequest{
   323  				Instances: instances,
   324  			}
   325  
   326  			op, err = config.clientCompute.InstanceGroupManagers.RecreateInstances(
   327  				project, d.Get("zone").(string), d.Id(), recreateInstances).Do()
   328  
   329  			if err != nil {
   330  				return fmt.Errorf("Error restarting instance group managers instances: %s", err)
   331  			}
   332  
   333  			// Wait for the operation to complete
   334  			err = computeOperationWaitZoneTime(config, op, project, d.Get("zone").(string),
   335  				managedInstanceCount*4, "Restarting InstanceGroupManagers instances")
   336  			if err != nil {
   337  				return err
   338  			}
   339  		}
   340  
   341  		d.SetPartial("instance_template")
   342  	}
   343  
   344  	// If named_port changes then update:
   345  	if d.HasChange("named_port") {
   346  
   347  		// Build the parameters for a "SetNamedPorts" request:
   348  		namedPorts := getNamedPorts(d.Get("named_port").([]interface{}))
   349  		setNamedPorts := &compute.InstanceGroupsSetNamedPortsRequest{
   350  			NamedPorts: namedPorts,
   351  		}
   352  
   353  		// Make the request:
   354  		op, err := config.clientCompute.InstanceGroups.SetNamedPorts(
   355  			project, d.Get("zone").(string), d.Id(), setNamedPorts).Do()
   356  		if err != nil {
   357  			return fmt.Errorf("Error updating InstanceGroupManager: %s", err)
   358  		}
   359  
   360  		// Wait for the operation to complete:
   361  		err = computeOperationWaitZone(config, op, project, d.Get("zone").(string), "Updating InstanceGroupManager")
   362  		if err != nil {
   363  			return err
   364  		}
   365  
   366  		d.SetPartial("named_port")
   367  	}
   368  
   369  	// If size changes trigger a resize
   370  	if d.HasChange("target_size") {
   371  		if v, ok := d.GetOk("target_size"); ok {
   372  			// Only do anything if the new size is set
   373  			target_size := int64(v.(int))
   374  
   375  			op, err := config.clientCompute.InstanceGroupManagers.Resize(
   376  				project, d.Get("zone").(string), d.Id(), target_size).Do()
   377  			if err != nil {
   378  				return fmt.Errorf("Error updating InstanceGroupManager: %s", err)
   379  			}
   380  
   381  			// Wait for the operation to complete
   382  			err = computeOperationWaitZone(config, op, project, d.Get("zone").(string), "Updating InstanceGroupManager")
   383  			if err != nil {
   384  				return err
   385  			}
   386  		}
   387  
   388  		d.SetPartial("target_size")
   389  	}
   390  
   391  	d.Partial(false)
   392  
   393  	return resourceComputeInstanceGroupManagerRead(d, meta)
   394  }
   395  
   396  func resourceComputeInstanceGroupManagerDelete(d *schema.ResourceData, meta interface{}) error {
   397  	config := meta.(*Config)
   398  
   399  	project, err := getProject(d, config)
   400  	if err != nil {
   401  		return err
   402  	}
   403  
   404  	zone := d.Get("zone").(string)
   405  	op, err := config.clientCompute.InstanceGroupManagers.Delete(project, zone, d.Id()).Do()
   406  	attempt := 0
   407  	for err != nil && attempt < 20 {
   408  		attempt++
   409  		time.Sleep(2000 * time.Millisecond)
   410  		op, err = config.clientCompute.InstanceGroupManagers.Delete(project, zone, d.Id()).Do()
   411  	}
   412  	if err != nil {
   413  		return fmt.Errorf("Error deleting instance group manager: %s", err)
   414  	}
   415  
   416  	currentSize := int64(d.Get("target_size").(int))
   417  
   418  	// Wait for the operation to complete
   419  	err = computeOperationWaitZone(config, op, project, d.Get("zone").(string), "Deleting InstanceGroupManager")
   420  
   421  	for err != nil && currentSize > 0 {
   422  		if !strings.Contains(err.Error(), "timeout") {
   423  			return err
   424  		}
   425  
   426  		instanceGroup, err := config.clientCompute.InstanceGroups.Get(
   427  			project, d.Get("zone").(string), d.Id()).Do()
   428  
   429  		if err != nil {
   430  			return fmt.Errorf("Error getting instance group size: %s", err)
   431  		}
   432  
   433  		if instanceGroup.Size >= currentSize {
   434  			return fmt.Errorf("Error, instance group isn't shrinking during delete")
   435  		}
   436  
   437  		log.Printf("[INFO] timeout occured, but instance group is shrinking (%d < %d)", instanceGroup.Size, currentSize)
   438  
   439  		currentSize = instanceGroup.Size
   440  
   441  		err = computeOperationWaitZone(config, op, project, d.Get("zone").(string), "Deleting InstanceGroupManager")
   442  	}
   443  
   444  	d.SetId("")
   445  	return nil
   446  }