github.com/ndarilek/terraform@v0.3.8-0.20150320140257-d3135c1b2bac/builtin/providers/google/resource_compute_target_pool.go (about) 1 package google 2 3 import ( 4 "fmt" 5 "log" 6 "strings" 7 "time" 8 9 "code.google.com/p/google-api-go-client/compute/v1" 10 "code.google.com/p/google-api-go-client/googleapi" 11 "github.com/hashicorp/terraform/helper/schema" 12 ) 13 14 func resourceComputeTargetPool() *schema.Resource { 15 return &schema.Resource{ 16 Create: resourceComputeTargetPoolCreate, 17 Read: resourceComputeTargetPoolRead, 18 Delete: resourceComputeTargetPoolDelete, 19 Update: resourceComputeTargetPoolUpdate, 20 21 Schema: map[string]*schema.Schema{ 22 "backup_pool": &schema.Schema{ 23 Type: schema.TypeString, 24 Optional: true, 25 ForceNew: false, 26 }, 27 28 "description": &schema.Schema{ 29 Type: schema.TypeString, 30 Optional: true, 31 ForceNew: true, 32 }, 33 34 "failover_ratio": &schema.Schema{ 35 Type: schema.TypeFloat, 36 Optional: true, 37 ForceNew: true, 38 }, 39 40 "health_checks": &schema.Schema{ 41 Type: schema.TypeList, 42 Optional: true, 43 ForceNew: false, 44 Elem: &schema.Schema{Type: schema.TypeString}, 45 }, 46 47 "instances": &schema.Schema{ 48 Type: schema.TypeList, 49 Optional: true, 50 ForceNew: false, 51 Elem: &schema.Schema{Type: schema.TypeString}, 52 }, 53 54 "name": &schema.Schema{ 55 Type: schema.TypeString, 56 Required: true, 57 ForceNew: true, 58 }, 59 60 "self_link": &schema.Schema{ 61 Type: schema.TypeString, 62 Computed: true, 63 }, 64 65 "session_affinity": &schema.Schema{ 66 Type: schema.TypeString, 67 Optional: true, 68 ForceNew: true, 69 }, 70 }, 71 } 72 } 73 74 func convertStringArr(ifaceArr []interface{}) []string { 75 arr := make([]string, len(ifaceArr)) 76 for i, v := range ifaceArr { 77 arr[i] = v.(string) 78 } 79 return arr 80 } 81 82 func waitOp(config *Config, op *compute.Operation, 83 resource string, action string) (*compute.Operation, error) { 84 85 w := &OperationWaiter{ 86 Service: config.clientCompute, 87 Op: op, 88 Region: config.Region, 89 Project: config.Project, 90 Type: OperationWaitRegion, 91 } 92 state := w.Conf() 93 state.Timeout = 2 * time.Minute 94 state.MinTimeout = 1 * time.Second 95 opRaw, err := state.WaitForState() 96 if err != nil { 97 return nil, fmt.Errorf("Error waiting for %s to %s: %s", resource, action, err) 98 } 99 return opRaw.(*compute.Operation), nil 100 } 101 102 // Healthchecks need to exist before being referred to from the target pool. 103 func convertHealthChecks(config *Config, 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(config.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 convertInstances(config *Config, 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 config.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 hchkUrls, err := convertHealthChecks( 141 config, convertStringArr(d.Get("health_checks").([]interface{}))) 142 if err != nil { 143 return err 144 } 145 146 instanceUrls, err := convertInstances( 147 config, convertStringArr(d.Get("instances").([]interface{}))) 148 if err != nil { 149 return err 150 } 151 152 // Build the parameter 153 tpool := &compute.TargetPool{ 154 BackupPool: d.Get("backup_pool").(string), 155 Description: d.Get("description").(string), 156 HealthChecks: hchkUrls, 157 Instances: instanceUrls, 158 Name: d.Get("name").(string), 159 SessionAffinity: d.Get("session_affinity").(string), 160 } 161 if d.Get("failover_ratio") != nil { 162 tpool.FailoverRatio = d.Get("failover_ratio").(float64) 163 } 164 log.Printf("[DEBUG] TargetPool insert request: %#v", tpool) 165 op, err := config.clientCompute.TargetPools.Insert( 166 config.Project, config.Region, tpool).Do() 167 if err != nil { 168 return fmt.Errorf("Error creating TargetPool: %s", err) 169 } 170 171 // It probably maybe worked, so store the ID now 172 d.SetId(tpool.Name) 173 174 op, err = waitOp(config, op, "TargetPool", "create") 175 if err != nil { 176 return err 177 } 178 if op.Error != nil { 179 // The resource didn't actually create 180 d.SetId("") 181 // Return the error 182 return OperationError(*op.Error) 183 } 184 185 return resourceComputeTargetPoolRead(d, meta) 186 } 187 188 func calcAddRemove(from []string, to []string) ([]string, []string) { 189 add := make([]string, 0) 190 remove := make([]string, 0) 191 for _, u := range to { 192 found := false 193 for _, v := range from { 194 if u == v { 195 found = true 196 break 197 } 198 } 199 if !found { 200 add = append(add, u) 201 } 202 } 203 for _, u := range from { 204 found := false 205 for _, v := range to { 206 if u == v { 207 found = true 208 break 209 } 210 } 211 if !found { 212 remove = append(remove, u) 213 } 214 } 215 return add, remove 216 } 217 218 func resourceComputeTargetPoolUpdate(d *schema.ResourceData, meta interface{}) error { 219 config := meta.(*Config) 220 221 d.Partial(true) 222 223 if d.HasChange("health_checks") { 224 225 from_, to_ := d.GetChange("health_checks") 226 from := convertStringArr(from_.([]interface{})) 227 to := convertStringArr(to_.([]interface{})) 228 fromUrls, err := convertHealthChecks(config, from) 229 if err != nil { 230 return err 231 } 232 toUrls, err := convertHealthChecks(config, to) 233 if err != nil { 234 return err 235 } 236 add, remove := calcAddRemove(fromUrls, toUrls) 237 238 removeReq := &compute.TargetPoolsRemoveHealthCheckRequest{ 239 HealthChecks: make([]*compute.HealthCheckReference, len(remove)), 240 } 241 for i, v := range remove { 242 removeReq.HealthChecks[i] = &compute.HealthCheckReference{HealthCheck: v} 243 } 244 op, err := config.clientCompute.TargetPools.RemoveHealthCheck( 245 config.Project, config.Region, d.Id(), removeReq).Do() 246 if err != nil { 247 return fmt.Errorf("Error updating health_check: %s", err) 248 } 249 op, err = waitOp(config, op, "TargetPool", "removing HealthChecks") 250 if err != nil { 251 return err 252 } 253 if op.Error != nil { 254 return OperationError(*op.Error) 255 } 256 257 addReq := &compute.TargetPoolsAddHealthCheckRequest{ 258 HealthChecks: make([]*compute.HealthCheckReference, len(add)), 259 } 260 for i, v := range add { 261 addReq.HealthChecks[i] = &compute.HealthCheckReference{HealthCheck: v} 262 } 263 op, err = config.clientCompute.TargetPools.AddHealthCheck( 264 config.Project, config.Region, d.Id(), addReq).Do() 265 if err != nil { 266 return fmt.Errorf("Error updating health_check: %s", err) 267 } 268 op, err = waitOp(config, op, "TargetPool", "adding HealthChecks") 269 if err != nil { 270 return err 271 } 272 if op.Error != nil { 273 return OperationError(*op.Error) 274 } 275 276 d.SetPartial("health_checks") 277 } 278 279 if d.HasChange("instances") { 280 281 from_, to_ := d.GetChange("instances") 282 from := convertStringArr(from_.([]interface{})) 283 to := convertStringArr(to_.([]interface{})) 284 fromUrls, err := convertInstances(config, from) 285 if err != nil { 286 return err 287 } 288 toUrls, err := convertInstances(config, to) 289 if err != nil { 290 return err 291 } 292 add, remove := calcAddRemove(fromUrls, toUrls) 293 294 addReq := &compute.TargetPoolsAddInstanceRequest{ 295 Instances: make([]*compute.InstanceReference, len(add)), 296 } 297 for i, v := range add { 298 addReq.Instances[i] = &compute.InstanceReference{Instance: v} 299 } 300 op, err := config.clientCompute.TargetPools.AddInstance( 301 config.Project, config.Region, d.Id(), addReq).Do() 302 if err != nil { 303 return fmt.Errorf("Error updating instances: %s", err) 304 } 305 op, err = waitOp(config, op, "TargetPool", "adding instances") 306 if err != nil { 307 return err 308 } 309 if op.Error != nil { 310 return OperationError(*op.Error) 311 } 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 config.Project, config.Region, d.Id(), removeReq).Do() 321 if err != nil { 322 return fmt.Errorf("Error updating instances: %s", err) 323 } 324 op, err = waitOp(config, op, "TargetPool", "removing instances") 325 if err != nil { 326 return err 327 } 328 if op.Error != nil { 329 return OperationError(*op.Error) 330 } 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 config.Project, config.Region, d.Id(), tref).Do() 342 if err != nil { 343 return fmt.Errorf("Error updating backup_pool: %s", err) 344 } 345 346 op, err = waitOp(config, op, "TargetPool", "updating backup_pool") 347 if err != nil { 348 return err 349 } 350 if op.Error != nil { 351 return OperationError(*op.Error) 352 } 353 354 d.SetPartial("backup_pool") 355 } 356 357 d.Partial(false) 358 359 return resourceComputeTargetPoolRead(d, meta) 360 } 361 362 func resourceComputeTargetPoolRead(d *schema.ResourceData, meta interface{}) error { 363 config := meta.(*Config) 364 365 tpool, err := config.clientCompute.TargetPools.Get( 366 config.Project, config.Region, d.Id()).Do() 367 if err != nil { 368 if gerr, ok := err.(*googleapi.Error); ok && gerr.Code == 404 { 369 // The resource doesn't exist anymore 370 d.SetId("") 371 372 return nil 373 } 374 375 return fmt.Errorf("Error reading TargetPool: %s", err) 376 } 377 378 d.Set("self_link", tpool.SelfLink) 379 380 return nil 381 } 382 383 func resourceComputeTargetPoolDelete(d *schema.ResourceData, meta interface{}) error { 384 config := meta.(*Config) 385 386 // Delete the TargetPool 387 op, err := config.clientCompute.TargetPools.Delete( 388 config.Project, config.Region, d.Id()).Do() 389 if err != nil { 390 return fmt.Errorf("Error deleting TargetPool: %s", err) 391 } 392 393 op, err = waitOp(config, op, "TargetPool", "delete") 394 if err != nil { 395 return err 396 } 397 if op.Error != nil { 398 return OperationError(*op.Error) 399 } 400 401 d.SetId("") 402 return nil 403 }