github.com/danp/terraform@v0.9.5-0.20170426144147-39d740081351/builtin/providers/google/resource_compute_instance_group.go (about) 1 package google 2 3 import ( 4 "fmt" 5 "log" 6 "strings" 7 8 "google.golang.org/api/compute/v1" 9 "google.golang.org/api/googleapi" 10 11 "github.com/hashicorp/terraform/helper/schema" 12 ) 13 14 func resourceComputeInstanceGroup() *schema.Resource { 15 return &schema.Resource{ 16 Create: resourceComputeInstanceGroupCreate, 17 Read: resourceComputeInstanceGroupRead, 18 Update: resourceComputeInstanceGroupUpdate, 19 Delete: resourceComputeInstanceGroupDelete, 20 21 SchemaVersion: 1, 22 23 Schema: map[string]*schema.Schema{ 24 "name": &schema.Schema{ 25 Type: schema.TypeString, 26 Required: true, 27 ForceNew: true, 28 }, 29 30 "zone": &schema.Schema{ 31 Type: schema.TypeString, 32 Required: true, 33 ForceNew: true, 34 }, 35 36 "description": &schema.Schema{ 37 Type: schema.TypeString, 38 Optional: true, 39 ForceNew: true, 40 }, 41 42 "instances": &schema.Schema{ 43 Type: schema.TypeSet, 44 Optional: true, 45 Elem: &schema.Schema{Type: schema.TypeString}, 46 Set: schema.HashString, 47 }, 48 49 "named_port": &schema.Schema{ 50 Type: schema.TypeList, 51 Optional: true, 52 Elem: &schema.Resource{ 53 Schema: map[string]*schema.Schema{ 54 "name": &schema.Schema{ 55 Type: schema.TypeString, 56 Required: true, 57 }, 58 59 "port": &schema.Schema{ 60 Type: schema.TypeInt, 61 Required: true, 62 }, 63 }, 64 }, 65 }, 66 67 "network": &schema.Schema{ 68 Type: schema.TypeString, 69 Computed: true, 70 }, 71 72 "project": &schema.Schema{ 73 Type: schema.TypeString, 74 Optional: true, 75 ForceNew: true, 76 }, 77 78 "self_link": &schema.Schema{ 79 Type: schema.TypeString, 80 Computed: true, 81 }, 82 83 "size": &schema.Schema{ 84 Type: schema.TypeInt, 85 Computed: true, 86 }, 87 }, 88 } 89 } 90 91 func getInstanceReferences(instanceUrls []string) (refs []*compute.InstanceReference) { 92 for _, v := range instanceUrls { 93 refs = append(refs, &compute.InstanceReference{ 94 Instance: v, 95 }) 96 } 97 return refs 98 } 99 100 func validInstanceURLs(instanceUrls []string) bool { 101 for _, v := range instanceUrls { 102 if !strings.HasPrefix(v, "https://www.googleapis.com/compute/v1/") { 103 return false 104 } 105 } 106 return true 107 } 108 109 func resourceComputeInstanceGroupCreate(d *schema.ResourceData, meta interface{}) error { 110 config := meta.(*Config) 111 112 project, err := getProject(d, config) 113 if err != nil { 114 return err 115 } 116 117 // Build the parameter 118 instanceGroup := &compute.InstanceGroup{ 119 Name: d.Get("name").(string), 120 } 121 122 // Set optional fields 123 if v, ok := d.GetOk("description"); ok { 124 instanceGroup.Description = v.(string) 125 } 126 127 if v, ok := d.GetOk("named_port"); ok { 128 instanceGroup.NamedPorts = getNamedPorts(v.([]interface{})) 129 } 130 131 log.Printf("[DEBUG] InstanceGroup insert request: %#v", instanceGroup) 132 op, err := config.clientCompute.InstanceGroups.Insert( 133 project, d.Get("zone").(string), instanceGroup).Do() 134 if err != nil { 135 return fmt.Errorf("Error creating InstanceGroup: %s", err) 136 } 137 138 // It probably maybe worked, so store the ID now 139 d.SetId(instanceGroup.Name) 140 141 // Wait for the operation to complete 142 err = computeOperationWaitZone(config, op, project, d.Get("zone").(string), "Creating InstanceGroup") 143 if err != nil { 144 return err 145 } 146 147 if v, ok := d.GetOk("instances"); ok { 148 instanceUrls := convertStringArr(v.(*schema.Set).List()) 149 if !validInstanceURLs(instanceUrls) { 150 return fmt.Errorf("Error invalid instance URLs: %v", instanceUrls) 151 } 152 153 addInstanceReq := &compute.InstanceGroupsAddInstancesRequest{ 154 Instances: getInstanceReferences(instanceUrls), 155 } 156 157 log.Printf("[DEBUG] InstanceGroup add instances request: %#v", addInstanceReq) 158 op, err := config.clientCompute.InstanceGroups.AddInstances( 159 project, d.Get("zone").(string), d.Id(), addInstanceReq).Do() 160 if err != nil { 161 return fmt.Errorf("Error adding instances to InstanceGroup: %s", err) 162 } 163 164 // Wait for the operation to complete 165 err = computeOperationWaitZone(config, op, project, d.Get("zone").(string), "Adding instances to InstanceGroup") 166 if err != nil { 167 return err 168 } 169 } 170 171 return resourceComputeInstanceGroupRead(d, meta) 172 } 173 174 func resourceComputeInstanceGroupRead(d *schema.ResourceData, meta interface{}) error { 175 config := meta.(*Config) 176 177 project, err := getProject(d, config) 178 if err != nil { 179 return err 180 } 181 182 // retreive instance group 183 instanceGroup, err := config.clientCompute.InstanceGroups.Get( 184 project, d.Get("zone").(string), d.Id()).Do() 185 if err != nil { 186 if gerr, ok := err.(*googleapi.Error); ok && gerr.Code == 404 { 187 // The resource doesn't exist anymore 188 d.SetId("") 189 190 return nil 191 } 192 193 return fmt.Errorf("Error reading InstanceGroup: %s", err) 194 } 195 196 // retreive instance group members 197 var memberUrls []string 198 members, err := config.clientCompute.InstanceGroups.ListInstances( 199 project, d.Get("zone").(string), d.Id(), &compute.InstanceGroupsListInstancesRequest{ 200 InstanceState: "ALL", 201 }).Do() 202 if err != nil { 203 if gerr, ok := err.(*googleapi.Error); ok && gerr.Code == 404 { 204 // The resource doesn't have any instances 205 d.Set("instances", nil) 206 } else { 207 // any other errors return them 208 return fmt.Errorf("Error reading InstanceGroup Members: %s", err) 209 } 210 } else { 211 for _, member := range members.Items { 212 memberUrls = append(memberUrls, member.Instance) 213 } 214 log.Printf("[DEBUG] InstanceGroup members: %v", memberUrls) 215 d.Set("instances", memberUrls) 216 } 217 218 // Set computed fields 219 d.Set("network", instanceGroup.Network) 220 d.Set("size", instanceGroup.Size) 221 d.Set("self_link", instanceGroup.SelfLink) 222 223 return nil 224 } 225 func resourceComputeInstanceGroupUpdate(d *schema.ResourceData, meta interface{}) error { 226 config := meta.(*Config) 227 228 project, err := getProject(d, config) 229 if err != nil { 230 return err 231 } 232 233 // refresh the state incase referenced instances have been removed earlier in the run 234 err = resourceComputeInstanceGroupRead(d, meta) 235 if err != nil { 236 return fmt.Errorf("Error reading InstanceGroup: %s", err) 237 } 238 239 d.Partial(true) 240 241 if d.HasChange("instances") { 242 // to-do check for no instances 243 from_, to_ := d.GetChange("instances") 244 245 from := convertStringArr(from_.(*schema.Set).List()) 246 to := convertStringArr(to_.(*schema.Set).List()) 247 248 if !validInstanceURLs(from) { 249 return fmt.Errorf("Error invalid instance URLs: %v", from) 250 } 251 if !validInstanceURLs(to) { 252 return fmt.Errorf("Error invalid instance URLs: %v", to) 253 } 254 255 add, remove := calcAddRemove(from, to) 256 257 if len(remove) > 0 { 258 removeReq := &compute.InstanceGroupsRemoveInstancesRequest{ 259 Instances: getInstanceReferences(remove), 260 } 261 262 log.Printf("[DEBUG] InstanceGroup remove instances request: %#v", removeReq) 263 removeOp, err := config.clientCompute.InstanceGroups.RemoveInstances( 264 project, d.Get("zone").(string), d.Id(), removeReq).Do() 265 if err != nil { 266 return fmt.Errorf("Error removing instances from InstanceGroup: %s", err) 267 } 268 269 // Wait for the operation to complete 270 err = computeOperationWaitZone(config, removeOp, project, d.Get("zone").(string), "Updating InstanceGroup") 271 if err != nil { 272 return err 273 } 274 } 275 276 if len(add) > 0 { 277 278 addReq := &compute.InstanceGroupsAddInstancesRequest{ 279 Instances: getInstanceReferences(add), 280 } 281 282 log.Printf("[DEBUG] InstanceGroup adding instances request: %#v", addReq) 283 addOp, err := config.clientCompute.InstanceGroups.AddInstances( 284 project, d.Get("zone").(string), d.Id(), addReq).Do() 285 if err != nil { 286 return fmt.Errorf("Error adding instances from InstanceGroup: %s", err) 287 } 288 289 // Wait for the operation to complete 290 err = computeOperationWaitZone(config, addOp, project, d.Get("zone").(string), "Updating InstanceGroup") 291 if err != nil { 292 return err 293 } 294 } 295 296 d.SetPartial("instances") 297 } 298 299 if d.HasChange("named_port") { 300 namedPorts := getNamedPorts(d.Get("named_port").([]interface{})) 301 302 namedPortsReq := &compute.InstanceGroupsSetNamedPortsRequest{ 303 NamedPorts: namedPorts, 304 } 305 306 log.Printf("[DEBUG] InstanceGroup updating named ports request: %#v", namedPortsReq) 307 op, err := config.clientCompute.InstanceGroups.SetNamedPorts( 308 project, d.Get("zone").(string), d.Id(), namedPortsReq).Do() 309 if err != nil { 310 return fmt.Errorf("Error updating named ports for InstanceGroup: %s", err) 311 } 312 313 err = computeOperationWaitZone(config, op, project, d.Get("zone").(string), "Updating InstanceGroup") 314 if err != nil { 315 return err 316 } 317 d.SetPartial("named_port") 318 } 319 320 d.Partial(false) 321 322 return resourceComputeInstanceGroupRead(d, meta) 323 } 324 325 func resourceComputeInstanceGroupDelete(d *schema.ResourceData, meta interface{}) error { 326 config := meta.(*Config) 327 328 project, err := getProject(d, config) 329 if err != nil { 330 return err 331 } 332 333 zone := d.Get("zone").(string) 334 op, err := config.clientCompute.InstanceGroups.Delete(project, zone, d.Id()).Do() 335 if err != nil { 336 return fmt.Errorf("Error deleting InstanceGroup: %s", err) 337 } 338 339 err = computeOperationWaitZone(config, op, project, zone, "Deleting InstanceGroup") 340 if err != nil { 341 return err 342 } 343 344 d.SetId("") 345 return nil 346 }