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 }