github.com/koding/terraform@v0.6.4-0.20170608090606-5d7e0339779d/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": { 25 Type: schema.TypeString, 26 Required: true, 27 ForceNew: true, 28 }, 29 30 "zone": { 31 Type: schema.TypeString, 32 Required: true, 33 ForceNew: true, 34 }, 35 36 "description": { 37 Type: schema.TypeString, 38 Optional: true, 39 ForceNew: true, 40 }, 41 42 "instances": { 43 Type: schema.TypeSet, 44 Optional: true, 45 Computed: true, 46 Elem: &schema.Schema{Type: schema.TypeString}, 47 Set: schema.HashString, 48 }, 49 50 "named_port": { 51 Type: schema.TypeList, 52 Optional: true, 53 Elem: &schema.Resource{ 54 Schema: map[string]*schema.Schema{ 55 "name": { 56 Type: schema.TypeString, 57 Required: true, 58 }, 59 60 "port": { 61 Type: schema.TypeInt, 62 Required: true, 63 }, 64 }, 65 }, 66 }, 67 68 "network": { 69 Type: schema.TypeString, 70 Optional: true, 71 Computed: true, 72 ForceNew: true, 73 }, 74 75 "project": { 76 Type: schema.TypeString, 77 Optional: true, 78 ForceNew: true, 79 }, 80 81 "self_link": { 82 Type: schema.TypeString, 83 Computed: true, 84 }, 85 86 "size": { 87 Type: schema.TypeInt, 88 Computed: true, 89 }, 90 }, 91 } 92 } 93 94 func getInstanceReferences(instanceUrls []string) (refs []*compute.InstanceReference) { 95 for _, v := range instanceUrls { 96 refs = append(refs, &compute.InstanceReference{ 97 Instance: v, 98 }) 99 } 100 return refs 101 } 102 103 func validInstanceURLs(instanceUrls []string) bool { 104 for _, v := range instanceUrls { 105 if !strings.HasPrefix(v, "https://www.googleapis.com/compute/v1/") { 106 return false 107 } 108 } 109 return true 110 } 111 112 func resourceComputeInstanceGroupCreate(d *schema.ResourceData, meta interface{}) error { 113 config := meta.(*Config) 114 115 project, err := getProject(d, config) 116 if err != nil { 117 return err 118 } 119 120 // Build the parameter 121 instanceGroup := &compute.InstanceGroup{ 122 Name: d.Get("name").(string), 123 } 124 125 // Set optional fields 126 if v, ok := d.GetOk("description"); ok { 127 instanceGroup.Description = v.(string) 128 } 129 130 if v, ok := d.GetOk("named_port"); ok { 131 instanceGroup.NamedPorts = getNamedPorts(v.([]interface{})) 132 } 133 134 if v, ok := d.GetOk("network"); ok { 135 instanceGroup.Network = v.(string) 136 } 137 138 log.Printf("[DEBUG] InstanceGroup insert request: %#v", instanceGroup) 139 op, err := config.clientCompute.InstanceGroups.Insert( 140 project, d.Get("zone").(string), instanceGroup).Do() 141 if err != nil { 142 return fmt.Errorf("Error creating InstanceGroup: %s", err) 143 } 144 145 // It probably maybe worked, so store the ID now 146 d.SetId(instanceGroup.Name) 147 148 // Wait for the operation to complete 149 err = computeOperationWaitZone(config, op, project, d.Get("zone").(string), "Creating InstanceGroup") 150 if err != nil { 151 return err 152 } 153 154 if v, ok := d.GetOk("instances"); ok { 155 instanceUrls := convertStringArr(v.(*schema.Set).List()) 156 if !validInstanceURLs(instanceUrls) { 157 return fmt.Errorf("Error invalid instance URLs: %v", instanceUrls) 158 } 159 160 addInstanceReq := &compute.InstanceGroupsAddInstancesRequest{ 161 Instances: getInstanceReferences(instanceUrls), 162 } 163 164 log.Printf("[DEBUG] InstanceGroup add instances request: %#v", addInstanceReq) 165 op, err := config.clientCompute.InstanceGroups.AddInstances( 166 project, d.Get("zone").(string), d.Id(), addInstanceReq).Do() 167 if err != nil { 168 return fmt.Errorf("Error adding instances to InstanceGroup: %s", err) 169 } 170 171 // Wait for the operation to complete 172 err = computeOperationWaitZone(config, op, project, d.Get("zone").(string), "Adding instances to InstanceGroup") 173 if err != nil { 174 return err 175 } 176 } 177 178 return resourceComputeInstanceGroupRead(d, meta) 179 } 180 181 func resourceComputeInstanceGroupRead(d *schema.ResourceData, meta interface{}) error { 182 config := meta.(*Config) 183 184 project, err := getProject(d, config) 185 if err != nil { 186 return err 187 } 188 189 // retreive instance group 190 instanceGroup, err := config.clientCompute.InstanceGroups.Get( 191 project, d.Get("zone").(string), d.Id()).Do() 192 if err != nil { 193 return handleNotFoundError(err, d, fmt.Sprintf("Instance Group %q", d.Get("name").(string))) 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 }