github.com/ndarilek/terraform@v0.3.8-0.20150320140257-d3135c1b2bac/builtin/providers/google/resource_compute_target_pool.go (about)

     1  package google
     2  
     3  import (
     4  	"fmt"
     5  	"log"
     6  	"strings"
     7  	"time"
     8  
     9  	"code.google.com/p/google-api-go-client/compute/v1"
    10  	"code.google.com/p/google-api-go-client/googleapi"
    11  	"github.com/hashicorp/terraform/helper/schema"
    12  )
    13  
    14  func resourceComputeTargetPool() *schema.Resource {
    15  	return &schema.Resource{
    16  		Create: resourceComputeTargetPoolCreate,
    17  		Read:   resourceComputeTargetPoolRead,
    18  		Delete: resourceComputeTargetPoolDelete,
    19  		Update: resourceComputeTargetPoolUpdate,
    20  
    21  		Schema: map[string]*schema.Schema{
    22  			"backup_pool": &schema.Schema{
    23  				Type:     schema.TypeString,
    24  				Optional: true,
    25  				ForceNew: false,
    26  			},
    27  
    28  			"description": &schema.Schema{
    29  				Type:     schema.TypeString,
    30  				Optional: true,
    31  				ForceNew: true,
    32  			},
    33  
    34  			"failover_ratio": &schema.Schema{
    35  				Type:     schema.TypeFloat,
    36  				Optional: true,
    37  				ForceNew: true,
    38  			},
    39  
    40  			"health_checks": &schema.Schema{
    41  				Type:     schema.TypeList,
    42  				Optional: true,
    43  				ForceNew: false,
    44  				Elem:     &schema.Schema{Type: schema.TypeString},
    45  			},
    46  
    47  			"instances": &schema.Schema{
    48  				Type:     schema.TypeList,
    49  				Optional: true,
    50  				ForceNew: false,
    51  				Elem:     &schema.Schema{Type: schema.TypeString},
    52  			},
    53  
    54  			"name": &schema.Schema{
    55  				Type:     schema.TypeString,
    56  				Required: true,
    57  				ForceNew: true,
    58  			},
    59  
    60  			"self_link": &schema.Schema{
    61  				Type:     schema.TypeString,
    62  				Computed: true,
    63  			},
    64  
    65  			"session_affinity": &schema.Schema{
    66  				Type:     schema.TypeString,
    67  				Optional: true,
    68  				ForceNew: true,
    69  			},
    70  		},
    71  	}
    72  }
    73  
    74  func convertStringArr(ifaceArr []interface{}) []string {
    75  	arr := make([]string, len(ifaceArr))
    76  	for i, v := range ifaceArr {
    77  		arr[i] = v.(string)
    78  	}
    79  	return arr
    80  }
    81  
    82  func waitOp(config *Config, op *compute.Operation,
    83  	resource string, action string) (*compute.Operation, error) {
    84  
    85  	w := &OperationWaiter{
    86  		Service: config.clientCompute,
    87  		Op:      op,
    88  		Region:  config.Region,
    89  		Project: config.Project,
    90  		Type:    OperationWaitRegion,
    91  	}
    92  	state := w.Conf()
    93  	state.Timeout = 2 * time.Minute
    94  	state.MinTimeout = 1 * time.Second
    95  	opRaw, err := state.WaitForState()
    96  	if err != nil {
    97  		return nil, fmt.Errorf("Error waiting for %s to %s: %s", resource, action, err)
    98  	}
    99  	return opRaw.(*compute.Operation), nil
   100  }
   101  
   102  // Healthchecks need to exist before being referred to from the target pool.
   103  func convertHealthChecks(config *Config, names []string) ([]string, error) {
   104  	urls := make([]string, len(names))
   105  	for i, name := range names {
   106  		// Look up the healthcheck
   107  		res, err := config.clientCompute.HttpHealthChecks.Get(config.Project, name).Do()
   108  		if err != nil {
   109  			return nil, fmt.Errorf("Error reading HealthCheck: %s", err)
   110  		}
   111  		urls[i] = res.SelfLink
   112  	}
   113  	return urls, nil
   114  }
   115  
   116  // Instances do not need to exist yet, so we simply generate URLs.
   117  // Instances can be full URLS or zone/name
   118  func convertInstances(config *Config, names []string) ([]string, error) {
   119  	urls := make([]string, len(names))
   120  	for i, name := range names {
   121  		if strings.HasPrefix(name, "https://www.googleapis.com/compute/v1/") {
   122  			urls[i] = name
   123  		} else {
   124  			splitName := strings.Split(name, "/")
   125  			if len(splitName) != 2 {
   126  				return nil, fmt.Errorf("Invalid instance name, require URL or zone/name: %s", name)
   127  			} else {
   128  				urls[i] = fmt.Sprintf(
   129  					"https://www.googleapis.com/compute/v1/projects/%s/zones/%s/instances/%s",
   130  					config.Project, splitName[0], splitName[1])
   131  			}
   132  		}
   133  	}
   134  	return urls, nil
   135  }
   136  
   137  func resourceComputeTargetPoolCreate(d *schema.ResourceData, meta interface{}) error {
   138  	config := meta.(*Config)
   139  
   140  	hchkUrls, err := convertHealthChecks(
   141  		config, convertStringArr(d.Get("health_checks").([]interface{})))
   142  	if err != nil {
   143  		return err
   144  	}
   145  
   146  	instanceUrls, err := convertInstances(
   147  		config, convertStringArr(d.Get("instances").([]interface{})))
   148  	if err != nil {
   149  		return err
   150  	}
   151  
   152  	// Build the parameter
   153  	tpool := &compute.TargetPool{
   154  		BackupPool:      d.Get("backup_pool").(string),
   155  		Description:     d.Get("description").(string),
   156  		HealthChecks:    hchkUrls,
   157  		Instances:       instanceUrls,
   158  		Name:            d.Get("name").(string),
   159  		SessionAffinity: d.Get("session_affinity").(string),
   160  	}
   161  	if d.Get("failover_ratio") != nil {
   162  		tpool.FailoverRatio = d.Get("failover_ratio").(float64)
   163  	}
   164  	log.Printf("[DEBUG] TargetPool insert request: %#v", tpool)
   165  	op, err := config.clientCompute.TargetPools.Insert(
   166  		config.Project, config.Region, tpool).Do()
   167  	if err != nil {
   168  		return fmt.Errorf("Error creating TargetPool: %s", err)
   169  	}
   170  
   171  	// It probably maybe worked, so store the ID now
   172  	d.SetId(tpool.Name)
   173  
   174  	op, err = waitOp(config, op, "TargetPool", "create")
   175  	if err != nil {
   176  		return err
   177  	}
   178  	if op.Error != nil {
   179  		// The resource didn't actually create
   180  		d.SetId("")
   181  		// Return the error
   182  		return OperationError(*op.Error)
   183  	}
   184  
   185  	return resourceComputeTargetPoolRead(d, meta)
   186  }
   187  
   188  func calcAddRemove(from []string, to []string) ([]string, []string) {
   189  	add := make([]string, 0)
   190  	remove := make([]string, 0)
   191  	for _, u := range to {
   192  		found := false
   193  		for _, v := range from {
   194  			if u == v {
   195  				found = true
   196  				break
   197  			}
   198  		}
   199  		if !found {
   200  			add = append(add, u)
   201  		}
   202  	}
   203  	for _, u := range from {
   204  		found := false
   205  		for _, v := range to {
   206  			if u == v {
   207  				found = true
   208  				break
   209  			}
   210  		}
   211  		if !found {
   212  			remove = append(remove, u)
   213  		}
   214  	}
   215  	return add, remove
   216  }
   217  
   218  func resourceComputeTargetPoolUpdate(d *schema.ResourceData, meta interface{}) error {
   219  	config := meta.(*Config)
   220  
   221  	d.Partial(true)
   222  
   223  	if d.HasChange("health_checks") {
   224  
   225  		from_, to_ := d.GetChange("health_checks")
   226  		from := convertStringArr(from_.([]interface{}))
   227  		to := convertStringArr(to_.([]interface{}))
   228  		fromUrls, err := convertHealthChecks(config, from)
   229  		if err != nil {
   230  			return err
   231  		}
   232  		toUrls, err := convertHealthChecks(config, to)
   233  		if err != nil {
   234  			return err
   235  		}
   236  		add, remove := calcAddRemove(fromUrls, toUrls)
   237  
   238  		removeReq := &compute.TargetPoolsRemoveHealthCheckRequest{
   239  			HealthChecks: make([]*compute.HealthCheckReference, len(remove)),
   240  		}
   241  		for i, v := range remove {
   242  			removeReq.HealthChecks[i] = &compute.HealthCheckReference{HealthCheck: v}
   243  		}
   244  		op, err := config.clientCompute.TargetPools.RemoveHealthCheck(
   245  			config.Project, config.Region, d.Id(), removeReq).Do()
   246  		if err != nil {
   247  			return fmt.Errorf("Error updating health_check: %s", err)
   248  		}
   249  		op, err = waitOp(config, op, "TargetPool", "removing HealthChecks")
   250  		if err != nil {
   251  			return err
   252  		}
   253  		if op.Error != nil {
   254  			return OperationError(*op.Error)
   255  		}
   256  
   257  		addReq := &compute.TargetPoolsAddHealthCheckRequest{
   258  			HealthChecks: make([]*compute.HealthCheckReference, len(add)),
   259  		}
   260  		for i, v := range add {
   261  			addReq.HealthChecks[i] = &compute.HealthCheckReference{HealthCheck: v}
   262  		}
   263  		op, err = config.clientCompute.TargetPools.AddHealthCheck(
   264  			config.Project, config.Region, d.Id(), addReq).Do()
   265  		if err != nil {
   266  			return fmt.Errorf("Error updating health_check: %s", err)
   267  		}
   268  		op, err = waitOp(config, op, "TargetPool", "adding HealthChecks")
   269  		if err != nil {
   270  			return err
   271  		}
   272  		if op.Error != nil {
   273  			return OperationError(*op.Error)
   274  		}
   275  
   276  		d.SetPartial("health_checks")
   277  	}
   278  
   279  	if d.HasChange("instances") {
   280  
   281  		from_, to_ := d.GetChange("instances")
   282  		from := convertStringArr(from_.([]interface{}))
   283  		to := convertStringArr(to_.([]interface{}))
   284  		fromUrls, err := convertInstances(config, from)
   285  		if err != nil {
   286  			return err
   287  		}
   288  		toUrls, err := convertInstances(config, to)
   289  		if err != nil {
   290  			return err
   291  		}
   292  		add, remove := calcAddRemove(fromUrls, toUrls)
   293  
   294  		addReq := &compute.TargetPoolsAddInstanceRequest{
   295  			Instances: make([]*compute.InstanceReference, len(add)),
   296  		}
   297  		for i, v := range add {
   298  			addReq.Instances[i] = &compute.InstanceReference{Instance: v}
   299  		}
   300  		op, err := config.clientCompute.TargetPools.AddInstance(
   301  			config.Project, config.Region, d.Id(), addReq).Do()
   302  		if err != nil {
   303  			return fmt.Errorf("Error updating instances: %s", err)
   304  		}
   305  		op, err = waitOp(config, op, "TargetPool", "adding instances")
   306  		if err != nil {
   307  			return err
   308  		}
   309  		if op.Error != nil {
   310  			return OperationError(*op.Error)
   311  		}
   312  
   313  		removeReq := &compute.TargetPoolsRemoveInstanceRequest{
   314  			Instances: make([]*compute.InstanceReference, len(remove)),
   315  		}
   316  		for i, v := range remove {
   317  			removeReq.Instances[i] = &compute.InstanceReference{Instance: v}
   318  		}
   319  		op, err = config.clientCompute.TargetPools.RemoveInstance(
   320  			config.Project, config.Region, d.Id(), removeReq).Do()
   321  		if err != nil {
   322  			return fmt.Errorf("Error updating instances: %s", err)
   323  		}
   324  		op, err = waitOp(config, op, "TargetPool", "removing instances")
   325  		if err != nil {
   326  			return err
   327  		}
   328  		if op.Error != nil {
   329  			return OperationError(*op.Error)
   330  		}
   331  
   332  		d.SetPartial("instances")
   333  	}
   334  
   335  	if d.HasChange("backup_pool") {
   336  		bpool_name := d.Get("backup_pool").(string)
   337  		tref := &compute.TargetReference{
   338  			Target: bpool_name,
   339  		}
   340  		op, err := config.clientCompute.TargetPools.SetBackup(
   341  			config.Project, config.Region, d.Id(), tref).Do()
   342  		if err != nil {
   343  			return fmt.Errorf("Error updating backup_pool: %s", err)
   344  		}
   345  
   346  		op, err = waitOp(config, op, "TargetPool", "updating backup_pool")
   347  		if err != nil {
   348  			return err
   349  		}
   350  		if op.Error != nil {
   351  			return OperationError(*op.Error)
   352  		}
   353  
   354  		d.SetPartial("backup_pool")
   355  	}
   356  
   357  	d.Partial(false)
   358  
   359  	return resourceComputeTargetPoolRead(d, meta)
   360  }
   361  
   362  func resourceComputeTargetPoolRead(d *schema.ResourceData, meta interface{}) error {
   363  	config := meta.(*Config)
   364  
   365  	tpool, err := config.clientCompute.TargetPools.Get(
   366  		config.Project, config.Region, d.Id()).Do()
   367  	if err != nil {
   368  		if gerr, ok := err.(*googleapi.Error); ok && gerr.Code == 404 {
   369  			// The resource doesn't exist anymore
   370  			d.SetId("")
   371  
   372  			return nil
   373  		}
   374  
   375  		return fmt.Errorf("Error reading TargetPool: %s", err)
   376  	}
   377  
   378  	d.Set("self_link", tpool.SelfLink)
   379  
   380  	return nil
   381  }
   382  
   383  func resourceComputeTargetPoolDelete(d *schema.ResourceData, meta interface{}) error {
   384  	config := meta.(*Config)
   385  
   386  	// Delete the TargetPool
   387  	op, err := config.clientCompute.TargetPools.Delete(
   388  		config.Project, config.Region, d.Id()).Do()
   389  	if err != nil {
   390  		return fmt.Errorf("Error deleting TargetPool: %s", err)
   391  	}
   392  
   393  	op, err = waitOp(config, op, "TargetPool", "delete")
   394  	if err != nil {
   395  		return err
   396  	}
   397  	if op.Error != nil {
   398  		return OperationError(*op.Error)
   399  	}
   400  
   401  	d.SetId("")
   402  	return nil
   403  }