github.com/arvindram03/terraform@v0.3.7-0.20150212015210-408f838db36d/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  
   219  func resourceComputeTargetPoolUpdate(d *schema.ResourceData, meta interface{}) error {
   220  	config := meta.(*Config)
   221  
   222  	d.Partial(true)
   223  
   224  	if d.HasChange("health_checks") {
   225  
   226  		from_, to_ := d.GetChange("health_checks")
   227  		from := convertStringArr(from_.([]interface{}))
   228  		to := convertStringArr(to_.([]interface{}))
   229  		fromUrls, err := convertHealthChecks(config, from)
   230  		if err != nil {
   231  			return err
   232  		}
   233  		toUrls, err := convertHealthChecks(config, to)
   234  		if err != nil {
   235  			return err
   236  		}
   237  		add, remove := calcAddRemove(fromUrls, toUrls)
   238  
   239  		removeReq := &compute.TargetPoolsRemoveHealthCheckRequest{
   240  			HealthChecks: make([]*compute.HealthCheckReference, len(remove)),
   241  		}
   242  		for i, v := range remove {
   243  			removeReq.HealthChecks[i] = &compute.HealthCheckReference{HealthCheck: v}
   244  		}
   245  		op, err := config.clientCompute.TargetPools.RemoveHealthCheck(
   246  			config.Project, config.Region, d.Id(), removeReq).Do()
   247  		if err != nil {
   248  			return fmt.Errorf("Error updating health_check: %s", err)
   249  		}
   250  		op, err = waitOp(config, op, "TargetPool", "removing HealthChecks")
   251  		if err != nil {
   252  			return err
   253  		}
   254  		if op.Error != nil {
   255  			return OperationError(*op.Error)
   256  		}
   257  
   258  		addReq := &compute.TargetPoolsAddHealthCheckRequest{
   259  			HealthChecks: make([]*compute.HealthCheckReference, len(add)),
   260  		}
   261  		for i, v := range add {
   262  			addReq.HealthChecks[i] = &compute.HealthCheckReference{HealthCheck: v}
   263  		}
   264  		op, err = config.clientCompute.TargetPools.AddHealthCheck(
   265  			config.Project, config.Region, d.Id(), addReq).Do()
   266  		if err != nil {
   267  			return fmt.Errorf("Error updating health_check: %s", err)
   268  		}
   269  		op, err = waitOp(config, op, "TargetPool", "adding HealthChecks")
   270  		if err != nil {
   271  			return err
   272  		}
   273  		if op.Error != nil {
   274  			return OperationError(*op.Error)
   275  		}
   276  
   277  		d.SetPartial("health_checks")
   278  	}
   279  
   280  	if d.HasChange("instances") {
   281  
   282  		from_, to_ := d.GetChange("instances")
   283  		from := convertStringArr(from_.([]interface{}))
   284  		to := convertStringArr(to_.([]interface{}))
   285  		fromUrls, err := convertInstances(config, from)
   286  		if err != nil {
   287  			return err
   288  		}
   289  		toUrls, err := convertInstances(config, to)
   290  		if err != nil {
   291  			return err
   292  		}
   293  		add, remove := calcAddRemove(fromUrls, toUrls)
   294  
   295  		addReq := &compute.TargetPoolsAddInstanceRequest{
   296  			Instances: make([]*compute.InstanceReference, len(add)),
   297  		}
   298  		for i, v := range add {
   299  			addReq.Instances[i] = &compute.InstanceReference{Instance: v}
   300  		}
   301  		op, err := config.clientCompute.TargetPools.AddInstance(
   302  			config.Project, config.Region, d.Id(), addReq).Do()
   303  		if err != nil {
   304  			return fmt.Errorf("Error updating instances: %s", err)
   305  		}
   306  		op, err = waitOp(config, op, "TargetPool", "adding instances")
   307  		if err != nil {
   308  			return err
   309  		}
   310  		if op.Error != nil {
   311  			return OperationError(*op.Error)
   312  		}
   313  
   314  		removeReq := &compute.TargetPoolsRemoveInstanceRequest{
   315  			Instances: make([]*compute.InstanceReference, len(remove)),
   316  		}
   317  		for i, v := range remove {
   318  			removeReq.Instances[i] = &compute.InstanceReference{Instance: v}
   319  		}
   320  		op, err = config.clientCompute.TargetPools.RemoveInstance(
   321  			config.Project, config.Region, d.Id(), removeReq).Do()
   322  		if err != nil {
   323  			return fmt.Errorf("Error updating instances: %s", err)
   324  		}
   325  		op, err = waitOp(config, op, "TargetPool", "removing instances")
   326  		if err != nil {
   327  			return err
   328  		}
   329  		if op.Error != nil {
   330  			return OperationError(*op.Error)
   331  		}
   332  
   333  		d.SetPartial("instances")
   334  	}
   335  
   336  	if d.HasChange("backup_pool") {
   337  		bpool_name := d.Get("backup_pool").(string)
   338  		tref := &compute.TargetReference{
   339  			Target: bpool_name,
   340  		}
   341  		op, err := config.clientCompute.TargetPools.SetBackup(
   342  			config.Project, config.Region, d.Id(), tref).Do()
   343  		if err != nil {
   344  			return fmt.Errorf("Error updating backup_pool: %s", err)
   345  		}
   346  
   347  		op, err = waitOp(config, op, "TargetPool", "updating backup_pool")
   348  		if err != nil {
   349  			return err
   350  		}
   351  		if op.Error != nil {
   352  			return OperationError(*op.Error)
   353  		}
   354  
   355  		d.SetPartial("backup_pool")
   356  	}
   357  
   358  	d.Partial(false)
   359  
   360  	return resourceComputeTargetPoolRead(d, meta)
   361  }
   362  
   363  func resourceComputeTargetPoolRead(d *schema.ResourceData, meta interface{}) error {
   364  	config := meta.(*Config)
   365  
   366  	tpool, err := config.clientCompute.TargetPools.Get(
   367  		config.Project, config.Region, d.Id()).Do()
   368  	if err != nil {
   369  		if gerr, ok := err.(*googleapi.Error); ok && gerr.Code == 404 {
   370  			// The resource doesn't exist anymore
   371  			d.SetId("")
   372  
   373  			return nil
   374  		}
   375  
   376  		return fmt.Errorf("Error reading TargetPool: %s", err)
   377  	}
   378  
   379  	d.Set("self_link", tpool.SelfLink)
   380  
   381  	return nil
   382  }
   383  
   384  func resourceComputeTargetPoolDelete(d *schema.ResourceData, meta interface{}) error {
   385  	config := meta.(*Config)
   386  
   387  	// Delete the TargetPool
   388  	op, err := config.clientCompute.TargetPools.Delete(
   389  		config.Project, config.Region, d.Id()).Do()
   390  	if err != nil {
   391  		return fmt.Errorf("Error deleting TargetPool: %s", err)
   392  	}
   393  
   394  	op, err = waitOp(config, op, "TargetPool", "delete")
   395  	if err != nil {
   396  		return err
   397  	}
   398  	if op.Error != nil {
   399  		return OperationError(*op.Error)
   400  	}
   401  
   402  	d.SetId("")
   403  	return nil
   404  }