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