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