terraform/builtin/providers/google/resource_compute_target_poo...

366 lines
9.3 KiB
Go

package google
import (
"fmt"
"log"
"strings"
"github.com/hashicorp/terraform/helper/schema"
"google.golang.org/api/compute/v1"
"google.golang.org/api/googleapi"
)
func resourceComputeTargetPool() *schema.Resource {
return &schema.Resource{
Create: resourceComputeTargetPoolCreate,
Read: resourceComputeTargetPoolRead,
Delete: resourceComputeTargetPoolDelete,
Update: resourceComputeTargetPoolUpdate,
Schema: map[string]*schema.Schema{
"backup_pool": &schema.Schema{
Type: schema.TypeString,
Optional: true,
ForceNew: false,
},
"description": &schema.Schema{
Type: schema.TypeString,
Optional: true,
ForceNew: true,
},
"failover_ratio": &schema.Schema{
Type: schema.TypeFloat,
Optional: true,
ForceNew: true,
},
"health_checks": &schema.Schema{
Type: schema.TypeList,
Optional: true,
ForceNew: false,
Elem: &schema.Schema{Type: schema.TypeString},
},
"instances": &schema.Schema{
Type: schema.TypeList,
Optional: true,
ForceNew: false,
Elem: &schema.Schema{Type: schema.TypeString},
},
"name": &schema.Schema{
Type: schema.TypeString,
Required: true,
ForceNew: true,
},
"self_link": &schema.Schema{
Type: schema.TypeString,
Computed: true,
},
"session_affinity": &schema.Schema{
Type: schema.TypeString,
Optional: true,
ForceNew: true,
},
"region": &schema.Schema{
Type: schema.TypeString,
Optional: true,
ForceNew: true,
},
},
}
}
func convertStringArr(ifaceArr []interface{}) []string {
arr := make([]string, len(ifaceArr))
for i, v := range ifaceArr {
arr[i] = v.(string)
}
return arr
}
// Healthchecks need to exist before being referred to from the target pool.
func convertHealthChecks(config *Config, names []string) ([]string, error) {
urls := make([]string, len(names))
for i, name := range names {
// Look up the healthcheck
res, err := config.clientCompute.HttpHealthChecks.Get(config.Project, name).Do()
if err != nil {
return nil, fmt.Errorf("Error reading HealthCheck: %s", err)
}
urls[i] = res.SelfLink
}
return urls, nil
}
// Instances do not need to exist yet, so we simply generate URLs.
// Instances can be full URLS or zone/name
func convertInstances(config *Config, names []string) ([]string, error) {
urls := make([]string, len(names))
for i, name := range names {
if strings.HasPrefix(name, "https://www.googleapis.com/compute/v1/") {
urls[i] = name
} else {
splitName := strings.Split(name, "/")
if len(splitName) != 2 {
return nil, fmt.Errorf("Invalid instance name, require URL or zone/name: %s", name)
} else {
urls[i] = fmt.Sprintf(
"https://www.googleapis.com/compute/v1/projects/%s/zones/%s/instances/%s",
config.Project, splitName[0], splitName[1])
}
}
}
return urls, nil
}
func resourceComputeTargetPoolCreate(d *schema.ResourceData, meta interface{}) error {
config := meta.(*Config)
region := getOptionalRegion(d, config)
hchkUrls, err := convertHealthChecks(
config, convertStringArr(d.Get("health_checks").([]interface{})))
if err != nil {
return err
}
instanceUrls, err := convertInstances(
config, convertStringArr(d.Get("instances").([]interface{})))
if err != nil {
return err
}
// Build the parameter
tpool := &compute.TargetPool{
BackupPool: d.Get("backup_pool").(string),
Description: d.Get("description").(string),
HealthChecks: hchkUrls,
Instances: instanceUrls,
Name: d.Get("name").(string),
SessionAffinity: d.Get("session_affinity").(string),
}
if d.Get("failover_ratio") != nil {
tpool.FailoverRatio = d.Get("failover_ratio").(float64)
}
log.Printf("[DEBUG] TargetPool insert request: %#v", tpool)
op, err := config.clientCompute.TargetPools.Insert(
config.Project, region, tpool).Do()
if err != nil {
return fmt.Errorf("Error creating TargetPool: %s", err)
}
// It probably maybe worked, so store the ID now
d.SetId(tpool.Name)
err = computeOperationWaitRegion(config, op, region, "Creating Target Pool")
if err != nil {
return err
}
return resourceComputeTargetPoolRead(d, meta)
}
func calcAddRemove(from []string, to []string) ([]string, []string) {
add := make([]string, 0)
remove := make([]string, 0)
for _, u := range to {
found := false
for _, v := range from {
if u == v {
found = true
break
}
}
if !found {
add = append(add, u)
}
}
for _, u := range from {
found := false
for _, v := range to {
if u == v {
found = true
break
}
}
if !found {
remove = append(remove, u)
}
}
return add, remove
}
func resourceComputeTargetPoolUpdate(d *schema.ResourceData, meta interface{}) error {
config := meta.(*Config)
region := getOptionalRegion(d, config)
d.Partial(true)
if d.HasChange("health_checks") {
from_, to_ := d.GetChange("health_checks")
from := convertStringArr(from_.([]interface{}))
to := convertStringArr(to_.([]interface{}))
fromUrls, err := convertHealthChecks(config, from)
if err != nil {
return err
}
toUrls, err := convertHealthChecks(config, to)
if err != nil {
return err
}
add, remove := calcAddRemove(fromUrls, toUrls)
removeReq := &compute.TargetPoolsRemoveHealthCheckRequest{
HealthChecks: make([]*compute.HealthCheckReference, len(remove)),
}
for i, v := range remove {
removeReq.HealthChecks[i] = &compute.HealthCheckReference{HealthCheck: v}
}
op, err := config.clientCompute.TargetPools.RemoveHealthCheck(
config.Project, region, d.Id(), removeReq).Do()
if err != nil {
return fmt.Errorf("Error updating health_check: %s", err)
}
err = computeOperationWaitRegion(config, op, region, "Updating Target Pool")
if err != nil {
return err
}
addReq := &compute.TargetPoolsAddHealthCheckRequest{
HealthChecks: make([]*compute.HealthCheckReference, len(add)),
}
for i, v := range add {
addReq.HealthChecks[i] = &compute.HealthCheckReference{HealthCheck: v}
}
op, err = config.clientCompute.TargetPools.AddHealthCheck(
config.Project, region, d.Id(), addReq).Do()
if err != nil {
return fmt.Errorf("Error updating health_check: %s", err)
}
err = computeOperationWaitRegion(config, op, region, "Updating Target Pool")
if err != nil {
return err
}
d.SetPartial("health_checks")
}
if d.HasChange("instances") {
from_, to_ := d.GetChange("instances")
from := convertStringArr(from_.([]interface{}))
to := convertStringArr(to_.([]interface{}))
fromUrls, err := convertInstances(config, from)
if err != nil {
return err
}
toUrls, err := convertInstances(config, to)
if err != nil {
return err
}
add, remove := calcAddRemove(fromUrls, toUrls)
addReq := &compute.TargetPoolsAddInstanceRequest{
Instances: make([]*compute.InstanceReference, len(add)),
}
for i, v := range add {
addReq.Instances[i] = &compute.InstanceReference{Instance: v}
}
op, err := config.clientCompute.TargetPools.AddInstance(
config.Project, region, d.Id(), addReq).Do()
if err != nil {
return fmt.Errorf("Error updating instances: %s", err)
}
err = computeOperationWaitRegion(config, op, region, "Updating Target Pool")
if err != nil {
return err
}
removeReq := &compute.TargetPoolsRemoveInstanceRequest{
Instances: make([]*compute.InstanceReference, len(remove)),
}
for i, v := range remove {
removeReq.Instances[i] = &compute.InstanceReference{Instance: v}
}
op, err = config.clientCompute.TargetPools.RemoveInstance(
config.Project, region, d.Id(), removeReq).Do()
if err != nil {
return fmt.Errorf("Error updating instances: %s", err)
}
err = computeOperationWaitRegion(config, op, region, "Updating Target Pool")
if err != nil {
return err
}
d.SetPartial("instances")
}
if d.HasChange("backup_pool") {
bpool_name := d.Get("backup_pool").(string)
tref := &compute.TargetReference{
Target: bpool_name,
}
op, err := config.clientCompute.TargetPools.SetBackup(
config.Project, region, d.Id(), tref).Do()
if err != nil {
return fmt.Errorf("Error updating backup_pool: %s", err)
}
err = computeOperationWaitRegion(config, op, region, "Updating Target Pool")
if err != nil {
return err
}
d.SetPartial("backup_pool")
}
d.Partial(false)
return resourceComputeTargetPoolRead(d, meta)
}
func resourceComputeTargetPoolRead(d *schema.ResourceData, meta interface{}) error {
config := meta.(*Config)
region := getOptionalRegion(d, config)
tpool, err := config.clientCompute.TargetPools.Get(
config.Project, region, d.Id()).Do()
if err != nil {
if gerr, ok := err.(*googleapi.Error); ok && gerr.Code == 404 {
log.Printf("[WARN] Removing Target Pool %q because it's gone", d.Get("name").(string))
// The resource doesn't exist anymore
d.SetId("")
return nil
}
return fmt.Errorf("Error reading TargetPool: %s", err)
}
d.Set("self_link", tpool.SelfLink)
return nil
}
func resourceComputeTargetPoolDelete(d *schema.ResourceData, meta interface{}) error {
config := meta.(*Config)
region := getOptionalRegion(d, config)
// Delete the TargetPool
op, err := config.clientCompute.TargetPools.Delete(
config.Project, region, d.Id()).Do()
if err != nil {
return fmt.Errorf("Error deleting TargetPool: %s", err)
}
err = computeOperationWaitRegion(config, op, region, "Deleting Target Pool")
if err != nil {
return err
}
d.SetId("")
return nil
}