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