github.com/koding/terraform@v0.6.4-0.20170608090606-5d7e0339779d/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 ) 11 12 func resourceComputeTargetPool() *schema.Resource { 13 return &schema.Resource{ 14 Create: resourceComputeTargetPoolCreate, 15 Read: resourceComputeTargetPoolRead, 16 Delete: resourceComputeTargetPoolDelete, 17 Update: resourceComputeTargetPoolUpdate, 18 Importer: &schema.ResourceImporter{ 19 State: schema.ImportStatePassthrough, 20 }, 21 22 Schema: map[string]*schema.Schema{ 23 "name": { 24 Type: schema.TypeString, 25 Required: true, 26 ForceNew: true, 27 }, 28 29 "backup_pool": { 30 Type: schema.TypeString, 31 Optional: true, 32 ForceNew: false, 33 }, 34 35 "description": { 36 Type: schema.TypeString, 37 Optional: true, 38 ForceNew: true, 39 }, 40 41 "failover_ratio": { 42 Type: schema.TypeFloat, 43 Optional: true, 44 ForceNew: true, 45 }, 46 47 "health_checks": { 48 Type: schema.TypeList, 49 Optional: true, 50 ForceNew: false, 51 Elem: &schema.Schema{Type: schema.TypeString}, 52 }, 53 54 "instances": { 55 Type: schema.TypeList, 56 Optional: true, 57 Computed: true, 58 ForceNew: false, 59 Elem: &schema.Schema{Type: schema.TypeString}, 60 }, 61 62 "project": { 63 Type: schema.TypeString, 64 Optional: true, 65 ForceNew: true, 66 Computed: true, 67 }, 68 69 "region": { 70 Type: schema.TypeString, 71 Optional: true, 72 ForceNew: true, 73 Computed: true, 74 }, 75 76 "self_link": { 77 Type: schema.TypeString, 78 Computed: true, 79 }, 80 81 "session_affinity": { 82 Type: schema.TypeString, 83 Optional: true, 84 ForceNew: true, 85 Default: "NONE", 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 return handleNotFoundError(err, d, fmt.Sprintf("Target Pool %q", d.Get("name").(string))) 395 } 396 397 regionUrl := strings.Split(tpool.Region, "/") 398 d.Set("self_link", tpool.SelfLink) 399 d.Set("backup_pool", tpool.BackupPool) 400 d.Set("description", tpool.Description) 401 d.Set("failover_ratio", tpool.FailoverRatio) 402 if tpool.HealthChecks != nil { 403 d.Set("health_checks", convertHealthChecksFromUrls(tpool.HealthChecks)) 404 } else { 405 d.Set("health_checks", nil) 406 } 407 if tpool.Instances != nil { 408 d.Set("instances", convertInstancesFromUrls(tpool.Instances)) 409 } else { 410 d.Set("instances", nil) 411 } 412 d.Set("name", tpool.Name) 413 d.Set("region", regionUrl[len(regionUrl)-1]) 414 d.Set("session_affinity", tpool.SessionAffinity) 415 d.Set("project", project) 416 return nil 417 } 418 419 func resourceComputeTargetPoolDelete(d *schema.ResourceData, meta interface{}) error { 420 config := meta.(*Config) 421 422 region, err := getRegion(d, config) 423 if err != nil { 424 return err 425 } 426 427 project, err := getProject(d, config) 428 if err != nil { 429 return err 430 } 431 432 // Delete the TargetPool 433 op, err := config.clientCompute.TargetPools.Delete( 434 project, region, d.Id()).Do() 435 if err != nil { 436 return fmt.Errorf("Error deleting TargetPool: %s", err) 437 } 438 439 err = computeOperationWaitRegion(config, op, project, region, "Deleting Target Pool") 440 if err != nil { 441 return err 442 } 443 d.SetId("") 444 return nil 445 }