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