github.com/btobolaski/terraform@v0.6.4-0.20150928030114-0c3f2a915c02/builtin/providers/google/resource_compute_target_pool.go (about)

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