github.com/arvindram03/terraform@v0.3.7-0.20150212015210-408f838db36d/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 219 func resourceComputeTargetPoolUpdate(d *schema.ResourceData, meta interface{}) error { 220 config := meta.(*Config) 221 222 d.Partial(true) 223 224 if d.HasChange("health_checks") { 225 226 from_, to_ := d.GetChange("health_checks") 227 from := convertStringArr(from_.([]interface{})) 228 to := convertStringArr(to_.([]interface{})) 229 fromUrls, err := convertHealthChecks(config, from) 230 if err != nil { 231 return err 232 } 233 toUrls, err := convertHealthChecks(config, to) 234 if err != nil { 235 return err 236 } 237 add, remove := calcAddRemove(fromUrls, toUrls) 238 239 removeReq := &compute.TargetPoolsRemoveHealthCheckRequest{ 240 HealthChecks: make([]*compute.HealthCheckReference, len(remove)), 241 } 242 for i, v := range remove { 243 removeReq.HealthChecks[i] = &compute.HealthCheckReference{HealthCheck: v} 244 } 245 op, err := config.clientCompute.TargetPools.RemoveHealthCheck( 246 config.Project, config.Region, d.Id(), removeReq).Do() 247 if err != nil { 248 return fmt.Errorf("Error updating health_check: %s", err) 249 } 250 op, err = waitOp(config, op, "TargetPool", "removing HealthChecks") 251 if err != nil { 252 return err 253 } 254 if op.Error != nil { 255 return OperationError(*op.Error) 256 } 257 258 addReq := &compute.TargetPoolsAddHealthCheckRequest{ 259 HealthChecks: make([]*compute.HealthCheckReference, len(add)), 260 } 261 for i, v := range add { 262 addReq.HealthChecks[i] = &compute.HealthCheckReference{HealthCheck: v} 263 } 264 op, err = config.clientCompute.TargetPools.AddHealthCheck( 265 config.Project, config.Region, d.Id(), addReq).Do() 266 if err != nil { 267 return fmt.Errorf("Error updating health_check: %s", err) 268 } 269 op, err = waitOp(config, op, "TargetPool", "adding HealthChecks") 270 if err != nil { 271 return err 272 } 273 if op.Error != nil { 274 return OperationError(*op.Error) 275 } 276 277 d.SetPartial("health_checks") 278 } 279 280 if d.HasChange("instances") { 281 282 from_, to_ := d.GetChange("instances") 283 from := convertStringArr(from_.([]interface{})) 284 to := convertStringArr(to_.([]interface{})) 285 fromUrls, err := convertInstances(config, from) 286 if err != nil { 287 return err 288 } 289 toUrls, err := convertInstances(config, to) 290 if err != nil { 291 return err 292 } 293 add, remove := calcAddRemove(fromUrls, toUrls) 294 295 addReq := &compute.TargetPoolsAddInstanceRequest{ 296 Instances: make([]*compute.InstanceReference, len(add)), 297 } 298 for i, v := range add { 299 addReq.Instances[i] = &compute.InstanceReference{Instance: v} 300 } 301 op, err := config.clientCompute.TargetPools.AddInstance( 302 config.Project, config.Region, d.Id(), addReq).Do() 303 if err != nil { 304 return fmt.Errorf("Error updating instances: %s", err) 305 } 306 op, err = waitOp(config, op, "TargetPool", "adding instances") 307 if err != nil { 308 return err 309 } 310 if op.Error != nil { 311 return OperationError(*op.Error) 312 } 313 314 removeReq := &compute.TargetPoolsRemoveInstanceRequest{ 315 Instances: make([]*compute.InstanceReference, len(remove)), 316 } 317 for i, v := range remove { 318 removeReq.Instances[i] = &compute.InstanceReference{Instance: v} 319 } 320 op, err = config.clientCompute.TargetPools.RemoveInstance( 321 config.Project, config.Region, d.Id(), removeReq).Do() 322 if err != nil { 323 return fmt.Errorf("Error updating instances: %s", err) 324 } 325 op, err = waitOp(config, op, "TargetPool", "removing instances") 326 if err != nil { 327 return err 328 } 329 if op.Error != nil { 330 return OperationError(*op.Error) 331 } 332 333 d.SetPartial("instances") 334 } 335 336 if d.HasChange("backup_pool") { 337 bpool_name := d.Get("backup_pool").(string) 338 tref := &compute.TargetReference{ 339 Target: bpool_name, 340 } 341 op, err := config.clientCompute.TargetPools.SetBackup( 342 config.Project, config.Region, d.Id(), tref).Do() 343 if err != nil { 344 return fmt.Errorf("Error updating backup_pool: %s", err) 345 } 346 347 op, err = waitOp(config, op, "TargetPool", "updating backup_pool") 348 if err != nil { 349 return err 350 } 351 if op.Error != nil { 352 return OperationError(*op.Error) 353 } 354 355 d.SetPartial("backup_pool") 356 } 357 358 d.Partial(false) 359 360 return resourceComputeTargetPoolRead(d, meta) 361 } 362 363 func resourceComputeTargetPoolRead(d *schema.ResourceData, meta interface{}) error { 364 config := meta.(*Config) 365 366 tpool, err := config.clientCompute.TargetPools.Get( 367 config.Project, config.Region, d.Id()).Do() 368 if err != nil { 369 if gerr, ok := err.(*googleapi.Error); ok && gerr.Code == 404 { 370 // The resource doesn't exist anymore 371 d.SetId("") 372 373 return nil 374 } 375 376 return fmt.Errorf("Error reading TargetPool: %s", err) 377 } 378 379 d.Set("self_link", tpool.SelfLink) 380 381 return nil 382 } 383 384 func resourceComputeTargetPoolDelete(d *schema.ResourceData, meta interface{}) error { 385 config := meta.(*Config) 386 387 // Delete the TargetPool 388 op, err := config.clientCompute.TargetPools.Delete( 389 config.Project, config.Region, d.Id()).Do() 390 if err != nil { 391 return fmt.Errorf("Error deleting TargetPool: %s", err) 392 } 393 394 op, err = waitOp(config, op, "TargetPool", "delete") 395 if err != nil { 396 return err 397 } 398 if op.Error != nil { 399 return OperationError(*op.Error) 400 } 401 402 d.SetId("") 403 return nil 404 }