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 }