github.com/danp/terraform@v0.9.5-0.20170426144147-39d740081351/builtin/providers/google/resource_compute_instance_group_manager.go (about) 1 package google 2 3 import ( 4 "fmt" 5 "log" 6 "strings" 7 "time" 8 9 "github.com/hashicorp/terraform/helper/schema" 10 "google.golang.org/api/compute/v1" 11 ) 12 13 func resourceComputeInstanceGroupManager() *schema.Resource { 14 return &schema.Resource{ 15 Create: resourceComputeInstanceGroupManagerCreate, 16 Read: resourceComputeInstanceGroupManagerRead, 17 Update: resourceComputeInstanceGroupManagerUpdate, 18 Delete: resourceComputeInstanceGroupManagerDelete, 19 Importer: &schema.ResourceImporter{ 20 State: schema.ImportStatePassthrough, 21 }, 22 23 Schema: map[string]*schema.Schema{ 24 "base_instance_name": &schema.Schema{ 25 Type: schema.TypeString, 26 Required: true, 27 ForceNew: true, 28 }, 29 30 "instance_template": &schema.Schema{ 31 Type: schema.TypeString, 32 Required: true, 33 }, 34 35 "name": &schema.Schema{ 36 Type: schema.TypeString, 37 Required: true, 38 ForceNew: true, 39 }, 40 41 "zone": &schema.Schema{ 42 Type: schema.TypeString, 43 Required: true, 44 ForceNew: true, 45 }, 46 47 "description": &schema.Schema{ 48 Type: schema.TypeString, 49 Optional: true, 50 ForceNew: true, 51 }, 52 53 "fingerprint": &schema.Schema{ 54 Type: schema.TypeString, 55 Computed: true, 56 }, 57 58 "instance_group": &schema.Schema{ 59 Type: schema.TypeString, 60 Computed: true, 61 }, 62 63 "named_port": &schema.Schema{ 64 Type: schema.TypeList, 65 Optional: true, 66 Elem: &schema.Resource{ 67 Schema: map[string]*schema.Schema{ 68 "name": &schema.Schema{ 69 Type: schema.TypeString, 70 Required: true, 71 }, 72 73 "port": &schema.Schema{ 74 Type: schema.TypeInt, 75 Required: true, 76 }, 77 }, 78 }, 79 }, 80 81 "project": &schema.Schema{ 82 Type: schema.TypeString, 83 Optional: true, 84 ForceNew: true, 85 Computed: true, 86 }, 87 88 "self_link": &schema.Schema{ 89 Type: schema.TypeString, 90 Computed: true, 91 }, 92 93 "update_strategy": &schema.Schema{ 94 Type: schema.TypeString, 95 Optional: true, 96 Default: "RESTART", 97 }, 98 99 "target_pools": &schema.Schema{ 100 Type: schema.TypeSet, 101 Optional: true, 102 Elem: &schema.Schema{Type: schema.TypeString}, 103 Set: schema.HashString, 104 }, 105 106 "target_size": &schema.Schema{ 107 Type: schema.TypeInt, 108 Computed: true, 109 Optional: true, 110 }, 111 }, 112 } 113 } 114 115 func getNamedPorts(nps []interface{}) []*compute.NamedPort { 116 namedPorts := make([]*compute.NamedPort, 0, len(nps)) 117 for _, v := range nps { 118 np := v.(map[string]interface{}) 119 namedPorts = append(namedPorts, &compute.NamedPort{ 120 Name: np["name"].(string), 121 Port: int64(np["port"].(int)), 122 }) 123 } 124 return namedPorts 125 } 126 127 func resourceComputeInstanceGroupManagerCreate(d *schema.ResourceData, meta interface{}) error { 128 config := meta.(*Config) 129 130 project, err := getProject(d, config) 131 if err != nil { 132 return err 133 } 134 135 // Get group size, default to 1 if not given 136 var target_size int64 = 1 137 if v, ok := d.GetOk("target_size"); ok { 138 target_size = int64(v.(int)) 139 } 140 141 // Build the parameter 142 manager := &compute.InstanceGroupManager{ 143 Name: d.Get("name").(string), 144 BaseInstanceName: d.Get("base_instance_name").(string), 145 InstanceTemplate: d.Get("instance_template").(string), 146 TargetSize: target_size, 147 } 148 149 // Set optional fields 150 if v, ok := d.GetOk("description"); ok { 151 manager.Description = v.(string) 152 } 153 154 if v, ok := d.GetOk("named_port"); ok { 155 manager.NamedPorts = getNamedPorts(v.([]interface{})) 156 } 157 158 if attr := d.Get("target_pools").(*schema.Set); attr.Len() > 0 { 159 var s []string 160 for _, v := range attr.List() { 161 s = append(s, v.(string)) 162 } 163 manager.TargetPools = s 164 } 165 166 updateStrategy := d.Get("update_strategy").(string) 167 if !(updateStrategy == "NONE" || updateStrategy == "RESTART") { 168 return fmt.Errorf("Update strategy must be \"NONE\" or \"RESTART\"") 169 } 170 171 log.Printf("[DEBUG] InstanceGroupManager insert request: %#v", manager) 172 op, err := config.clientCompute.InstanceGroupManagers.Insert( 173 project, d.Get("zone").(string), manager).Do() 174 if err != nil { 175 return fmt.Errorf("Error creating InstanceGroupManager: %s", err) 176 } 177 178 // It probably maybe worked, so store the ID now 179 d.SetId(manager.Name) 180 181 // Wait for the operation to complete 182 err = computeOperationWaitZone(config, op, project, d.Get("zone").(string), "Creating InstanceGroupManager") 183 if err != nil { 184 return err 185 } 186 187 return resourceComputeInstanceGroupManagerRead(d, meta) 188 } 189 190 func flattenNamedPorts(namedPorts []*compute.NamedPort) []map[string]interface{} { 191 result := make([]map[string]interface{}, 0, len(namedPorts)) 192 for _, namedPort := range namedPorts { 193 namedPortMap := make(map[string]interface{}) 194 namedPortMap["name"] = namedPort.Name 195 namedPortMap["port"] = namedPort.Port 196 result = append(result, namedPortMap) 197 } 198 return result 199 200 } 201 202 func resourceComputeInstanceGroupManagerRead(d *schema.ResourceData, meta interface{}) error { 203 config := meta.(*Config) 204 205 project, err := getProject(d, config) 206 if err != nil { 207 return err 208 } 209 210 region, err := getRegion(d, config) 211 if err != nil { 212 return err 213 } 214 215 getInstanceGroupManager := func(zone string) (interface{}, error) { 216 return config.clientCompute.InstanceGroupManagers.Get(project, zone, d.Id()).Do() 217 } 218 219 var manager *compute.InstanceGroupManager 220 var e error 221 if zone, ok := d.GetOk("zone"); ok { 222 manager, e = config.clientCompute.InstanceGroupManagers.Get(project, zone.(string), d.Id()).Do() 223 224 if e != nil { 225 return e 226 } 227 } else { 228 // If the resource was imported, the only info we have is the ID. Try to find the resource 229 // by searching in the region of the project. 230 var resource interface{} 231 resource, e = getZonalResourceFromRegion(getInstanceGroupManager, region, config.clientCompute, project) 232 233 if e != nil { 234 return e 235 } 236 237 manager = resource.(*compute.InstanceGroupManager) 238 } 239 240 if manager == nil { 241 log.Printf("[WARN] Removing Instance Group Manager %q because it's gone", d.Get("name").(string)) 242 // The resource doesn't exist anymore 243 d.SetId("") 244 return nil 245 } 246 247 zoneUrl := strings.Split(manager.Zone, "/") 248 d.Set("base_instance_name", manager.BaseInstanceName) 249 d.Set("instance_template", manager.InstanceTemplate) 250 d.Set("name", manager.Name) 251 d.Set("zone", zoneUrl[len(zoneUrl)-1]) 252 d.Set("description", manager.Description) 253 d.Set("project", project) 254 d.Set("target_size", manager.TargetSize) 255 d.Set("target_pools", manager.TargetPools) 256 d.Set("named_port", flattenNamedPorts(manager.NamedPorts)) 257 d.Set("fingerprint", manager.Fingerprint) 258 d.Set("instance_group", manager.InstanceGroup) 259 d.Set("target_size", manager.TargetSize) 260 d.Set("self_link", manager.SelfLink) 261 update_strategy, ok := d.GetOk("update_strategy") 262 if !ok { 263 update_strategy = "RESTART" 264 } 265 d.Set("update_strategy", update_strategy.(string)) 266 267 return nil 268 } 269 func resourceComputeInstanceGroupManagerUpdate(d *schema.ResourceData, meta interface{}) error { 270 config := meta.(*Config) 271 272 project, err := getProject(d, config) 273 if err != nil { 274 return err 275 } 276 277 d.Partial(true) 278 279 // If target_pools changes then update 280 if d.HasChange("target_pools") { 281 var targetPools []string 282 if attr := d.Get("target_pools").(*schema.Set); attr.Len() > 0 { 283 for _, v := range attr.List() { 284 targetPools = append(targetPools, v.(string)) 285 } 286 } 287 288 // Build the parameter 289 setTargetPools := &compute.InstanceGroupManagersSetTargetPoolsRequest{ 290 Fingerprint: d.Get("fingerprint").(string), 291 TargetPools: targetPools, 292 } 293 294 op, err := config.clientCompute.InstanceGroupManagers.SetTargetPools( 295 project, d.Get("zone").(string), d.Id(), setTargetPools).Do() 296 if err != nil { 297 return fmt.Errorf("Error updating InstanceGroupManager: %s", err) 298 } 299 300 // Wait for the operation to complete 301 err = computeOperationWaitZone(config, op, project, d.Get("zone").(string), "Updating InstanceGroupManager") 302 if err != nil { 303 return err 304 } 305 306 d.SetPartial("target_pools") 307 } 308 309 // If instance_template changes then update 310 if d.HasChange("instance_template") { 311 // Build the parameter 312 setInstanceTemplate := &compute.InstanceGroupManagersSetInstanceTemplateRequest{ 313 InstanceTemplate: d.Get("instance_template").(string), 314 } 315 316 op, err := config.clientCompute.InstanceGroupManagers.SetInstanceTemplate( 317 project, d.Get("zone").(string), d.Id(), setInstanceTemplate).Do() 318 if err != nil { 319 return fmt.Errorf("Error updating InstanceGroupManager: %s", err) 320 } 321 322 // Wait for the operation to complete 323 err = computeOperationWaitZone(config, op, project, d.Get("zone").(string), "Updating InstanceGroupManager") 324 if err != nil { 325 return err 326 } 327 328 if d.Get("update_strategy").(string) == "RESTART" { 329 managedInstances, err := config.clientCompute.InstanceGroupManagers.ListManagedInstances( 330 project, d.Get("zone").(string), d.Id()).Do() 331 332 managedInstanceCount := len(managedInstances.ManagedInstances) 333 instances := make([]string, managedInstanceCount) 334 for i, v := range managedInstances.ManagedInstances { 335 instances[i] = v.Instance 336 } 337 338 recreateInstances := &compute.InstanceGroupManagersRecreateInstancesRequest{ 339 Instances: instances, 340 } 341 342 op, err = config.clientCompute.InstanceGroupManagers.RecreateInstances( 343 project, d.Get("zone").(string), d.Id(), recreateInstances).Do() 344 345 if err != nil { 346 return fmt.Errorf("Error restarting instance group managers instances: %s", err) 347 } 348 349 // Wait for the operation to complete 350 err = computeOperationWaitZoneTime(config, op, project, d.Get("zone").(string), 351 managedInstanceCount*4, "Restarting InstanceGroupManagers instances") 352 if err != nil { 353 return err 354 } 355 } 356 357 d.SetPartial("instance_template") 358 } 359 360 // If named_port changes then update: 361 if d.HasChange("named_port") { 362 363 // Build the parameters for a "SetNamedPorts" request: 364 namedPorts := getNamedPorts(d.Get("named_port").([]interface{})) 365 setNamedPorts := &compute.InstanceGroupsSetNamedPortsRequest{ 366 NamedPorts: namedPorts, 367 } 368 369 // Make the request: 370 op, err := config.clientCompute.InstanceGroups.SetNamedPorts( 371 project, d.Get("zone").(string), d.Id(), setNamedPorts).Do() 372 if err != nil { 373 return fmt.Errorf("Error updating InstanceGroupManager: %s", err) 374 } 375 376 // Wait for the operation to complete: 377 err = computeOperationWaitZone(config, op, project, d.Get("zone").(string), "Updating InstanceGroupManager") 378 if err != nil { 379 return err 380 } 381 382 d.SetPartial("named_port") 383 } 384 385 // If size changes trigger a resize 386 if d.HasChange("target_size") { 387 if v, ok := d.GetOk("target_size"); ok { 388 // Only do anything if the new size is set 389 target_size := int64(v.(int)) 390 391 op, err := config.clientCompute.InstanceGroupManagers.Resize( 392 project, d.Get("zone").(string), d.Id(), target_size).Do() 393 if err != nil { 394 return fmt.Errorf("Error updating InstanceGroupManager: %s", err) 395 } 396 397 // Wait for the operation to complete 398 err = computeOperationWaitZone(config, op, project, d.Get("zone").(string), "Updating InstanceGroupManager") 399 if err != nil { 400 return err 401 } 402 } 403 404 d.SetPartial("target_size") 405 } 406 407 d.Partial(false) 408 409 return resourceComputeInstanceGroupManagerRead(d, meta) 410 } 411 412 func resourceComputeInstanceGroupManagerDelete(d *schema.ResourceData, meta interface{}) error { 413 config := meta.(*Config) 414 415 project, err := getProject(d, config) 416 if err != nil { 417 return err 418 } 419 420 zone := d.Get("zone").(string) 421 op, err := config.clientCompute.InstanceGroupManagers.Delete(project, zone, d.Id()).Do() 422 attempt := 0 423 for err != nil && attempt < 20 { 424 attempt++ 425 time.Sleep(2000 * time.Millisecond) 426 op, err = config.clientCompute.InstanceGroupManagers.Delete(project, zone, d.Id()).Do() 427 } 428 if err != nil { 429 return fmt.Errorf("Error deleting instance group manager: %s", err) 430 } 431 432 currentSize := int64(d.Get("target_size").(int)) 433 434 // Wait for the operation to complete 435 err = computeOperationWaitZone(config, op, project, d.Get("zone").(string), "Deleting InstanceGroupManager") 436 437 for err != nil && currentSize > 0 { 438 if !strings.Contains(err.Error(), "timeout") { 439 return err 440 } 441 442 instanceGroup, err := config.clientCompute.InstanceGroups.Get( 443 project, d.Get("zone").(string), d.Id()).Do() 444 445 if err != nil { 446 return fmt.Errorf("Error getting instance group size: %s", err) 447 } 448 449 if instanceGroup.Size >= currentSize { 450 return fmt.Errorf("Error, instance group isn't shrinking during delete") 451 } 452 453 log.Printf("[INFO] timeout occured, but instance group is shrinking (%d < %d)", instanceGroup.Size, currentSize) 454 455 currentSize = instanceGroup.Size 456 457 err = computeOperationWaitZone(config, op, project, d.Get("zone").(string), "Deleting InstanceGroupManager") 458 } 459 460 d.SetId("") 461 return nil 462 }