github.com/danp/terraform@v0.9.5-0.20170426144147-39d740081351/builtin/providers/aws/resource_aws_emr_instance_group.go (about) 1 package aws 2 3 import ( 4 "errors" 5 "log" 6 "time" 7 8 "fmt" 9 10 "github.com/aws/aws-sdk-go/aws" 11 "github.com/aws/aws-sdk-go/service/emr" 12 "github.com/hashicorp/terraform/helper/resource" 13 "github.com/hashicorp/terraform/helper/schema" 14 ) 15 16 var emrInstanceGroupNotFound = errors.New("No matching EMR Instance Group") 17 18 func resourceAwsEMRInstanceGroup() *schema.Resource { 19 return &schema.Resource{ 20 Create: resourceAwsEMRInstanceGroupCreate, 21 Read: resourceAwsEMRInstanceGroupRead, 22 Update: resourceAwsEMRInstanceGroupUpdate, 23 Delete: resourceAwsEMRInstanceGroupDelete, 24 Schema: map[string]*schema.Schema{ 25 "cluster_id": { 26 Type: schema.TypeString, 27 Required: true, 28 ForceNew: true, 29 }, 30 "instance_type": { 31 Type: schema.TypeString, 32 Required: true, 33 ForceNew: true, 34 }, 35 "instance_count": { 36 Type: schema.TypeInt, 37 Optional: true, 38 Default: 0, 39 }, 40 "running_instance_count": { 41 Type: schema.TypeInt, 42 Computed: true, 43 }, 44 "status": { 45 Type: schema.TypeString, 46 Computed: true, 47 }, 48 "name": { 49 Type: schema.TypeString, 50 Optional: true, 51 ForceNew: true, 52 }, 53 "ebs_optimized": { 54 Type: schema.TypeBool, 55 Optional: true, 56 ForceNew: true, 57 }, 58 "ebs_config": { 59 Type: schema.TypeSet, 60 Optional: true, 61 ForceNew: true, 62 Elem: &schema.Resource{ 63 Schema: map[string]*schema.Schema{ 64 "iops": { 65 Type: schema.TypeInt, 66 Optional: true, 67 }, 68 "size": { 69 Type: schema.TypeInt, 70 Required: true, 71 }, 72 "type": { 73 Type: schema.TypeString, 74 Required: true, 75 ValidateFunc: validateAwsEmrEbsVolumeType, 76 }, 77 "volumes_per_instance": { 78 Type: schema.TypeInt, 79 Optional: true, 80 }, 81 }, 82 }, 83 }, 84 }, 85 } 86 } 87 88 // Populates an emr.EbsConfiguration struct 89 func readEmrEBSConfig(d *schema.ResourceData) *emr.EbsConfiguration { 90 result := &emr.EbsConfiguration{} 91 if v, ok := d.GetOk("ebs_optimized"); ok { 92 result.EbsOptimized = aws.Bool(v.(bool)) 93 } 94 95 ebsConfigs := make([]*emr.EbsBlockDeviceConfig, 0) 96 if rawConfig, ok := d.GetOk("ebs_config"); ok { 97 configList := rawConfig.(*schema.Set).List() 98 for _, config := range configList { 99 conf := config.(map[string]interface{}) 100 ebs := &emr.EbsBlockDeviceConfig{} 101 volumeSpec := &emr.VolumeSpecification{ 102 SizeInGB: aws.Int64(int64(conf["size"].(int))), 103 VolumeType: aws.String(conf["type"].(string)), 104 } 105 if v, ok := conf["iops"].(int); ok && v != 0 { 106 volumeSpec.Iops = aws.Int64(int64(v)) 107 } 108 if v, ok := conf["volumes_per_instance"].(int); ok && v != 0 { 109 ebs.VolumesPerInstance = aws.Int64(int64(v)) 110 } 111 ebs.VolumeSpecification = volumeSpec 112 ebsConfigs = append(ebsConfigs, ebs) 113 } 114 } 115 result.EbsBlockDeviceConfigs = ebsConfigs 116 return result 117 } 118 119 func resourceAwsEMRInstanceGroupCreate(d *schema.ResourceData, meta interface{}) error { 120 conn := meta.(*AWSClient).emrconn 121 122 clusterId := d.Get("cluster_id").(string) 123 instanceType := d.Get("instance_type").(string) 124 instanceCount := d.Get("instance_count").(int) 125 groupName := d.Get("name").(string) 126 127 ebsConfig := readEmrEBSConfig(d) 128 129 params := &emr.AddInstanceGroupsInput{ 130 InstanceGroups: []*emr.InstanceGroupConfig{ 131 { 132 InstanceRole: aws.String("TASK"), 133 InstanceCount: aws.Int64(int64(instanceCount)), 134 InstanceType: aws.String(instanceType), 135 Name: aws.String(groupName), 136 EbsConfiguration: ebsConfig, 137 }, 138 }, 139 JobFlowId: aws.String(clusterId), 140 } 141 142 log.Printf("[DEBUG] Creating EMR task group params: %s", params) 143 resp, err := conn.AddInstanceGroups(params) 144 if err != nil { 145 return err 146 } 147 148 log.Printf("[DEBUG] Created EMR task group finished: %#v", resp) 149 if resp == nil || len(resp.InstanceGroupIds) == 0 { 150 return fmt.Errorf("Error creating instance groups: no instance group returned") 151 } 152 d.SetId(*resp.InstanceGroupIds[0]) 153 154 return nil 155 } 156 157 func resourceAwsEMRInstanceGroupRead(d *schema.ResourceData, meta interface{}) error { 158 group, err := fetchEMRInstanceGroup(meta, d.Get("cluster_id").(string), d.Id()) 159 if err != nil { 160 switch err { 161 case emrInstanceGroupNotFound: 162 log.Printf("[DEBUG] EMR Instance Group (%s) not found, removing", d.Id()) 163 d.SetId("") 164 return nil 165 default: 166 return err 167 } 168 } 169 170 // Guard against the chance of fetchEMRInstanceGroup returning nil group but 171 // not a emrInstanceGroupNotFound error 172 if group == nil { 173 log.Printf("[DEBUG] EMR Instance Group (%s) not found, removing", d.Id()) 174 d.SetId("") 175 return nil 176 } 177 178 d.Set("name", group.Name) 179 d.Set("instance_count", group.RequestedInstanceCount) 180 d.Set("running_instance_count", group.RunningInstanceCount) 181 d.Set("instance_type", group.InstanceType) 182 if group.Status != nil && group.Status.State != nil { 183 d.Set("status", group.Status.State) 184 } 185 186 return nil 187 } 188 189 func fetchAllEMRInstanceGroups(meta interface{}, clusterId string) ([]*emr.InstanceGroup, error) { 190 conn := meta.(*AWSClient).emrconn 191 req := &emr.ListInstanceGroupsInput{ 192 ClusterId: aws.String(clusterId), 193 } 194 195 var groups []*emr.InstanceGroup 196 marker := aws.String("intitial") 197 for marker != nil { 198 log.Printf("[DEBUG] EMR Cluster Instance Marker: %s", *marker) 199 respGrps, errGrps := conn.ListInstanceGroups(req) 200 if errGrps != nil { 201 return nil, fmt.Errorf("[ERR] Error reading EMR cluster (%s): %s", clusterId, errGrps) 202 } 203 if respGrps == nil { 204 return nil, fmt.Errorf("[ERR] Error reading EMR Instance Group for cluster (%s)", clusterId) 205 } 206 207 if respGrps.InstanceGroups != nil { 208 for _, g := range respGrps.InstanceGroups { 209 groups = append(groups, g) 210 } 211 } else { 212 log.Printf("[DEBUG] EMR Instance Group list was empty") 213 } 214 marker = respGrps.Marker 215 } 216 217 if len(groups) == 0 { 218 return nil, fmt.Errorf("[WARN] No instance groups found for EMR Cluster (%s)", clusterId) 219 } 220 221 return groups, nil 222 } 223 224 func fetchEMRInstanceGroup(meta interface{}, clusterId, groupId string) (*emr.InstanceGroup, error) { 225 groups, err := fetchAllEMRInstanceGroups(meta, clusterId) 226 if err != nil { 227 return nil, err 228 } 229 230 var group *emr.InstanceGroup 231 for _, ig := range groups { 232 if groupId == *ig.Id { 233 group = ig 234 break 235 } 236 } 237 238 if group != nil { 239 return group, nil 240 } 241 242 return nil, emrInstanceGroupNotFound 243 } 244 245 func resourceAwsEMRInstanceGroupUpdate(d *schema.ResourceData, meta interface{}) error { 246 conn := meta.(*AWSClient).emrconn 247 248 log.Printf("[DEBUG] Modify EMR task group") 249 instanceCount := d.Get("instance_count").(int) 250 251 params := &emr.ModifyInstanceGroupsInput{ 252 InstanceGroups: []*emr.InstanceGroupModifyConfig{ 253 { 254 InstanceGroupId: aws.String(d.Id()), 255 InstanceCount: aws.Int64(int64(instanceCount)), 256 }, 257 }, 258 } 259 260 _, err := conn.ModifyInstanceGroups(params) 261 if err != nil { 262 return err 263 } 264 265 stateConf := &resource.StateChangeConf{ 266 Pending: []string{"PROVISIONING", "BOOTSTRAPPING", "RESIZING"}, 267 Target: []string{"RUNNING"}, 268 Refresh: instanceGroupStateRefresh(conn, d.Get("cluster_id").(string), d.Id()), 269 Timeout: 10 * time.Minute, 270 Delay: 10 * time.Second, 271 MinTimeout: 3 * time.Second, 272 } 273 274 _, err = stateConf.WaitForState() 275 if err != nil { 276 return fmt.Errorf( 277 "Error waiting for instance (%s) to terminate: %s", d.Id(), err) 278 } 279 280 return resourceAwsEMRInstanceGroupRead(d, meta) 281 } 282 283 func instanceGroupStateRefresh(meta interface{}, clusterID, igID string) resource.StateRefreshFunc { 284 return func() (interface{}, string, error) { 285 group, err := fetchEMRInstanceGroup(meta, clusterID, igID) 286 if err != nil { 287 return nil, "Not Found", err 288 } 289 290 if group.Status == nil || group.Status.State == nil { 291 log.Printf("[WARN] ERM Instance Group found, but without state") 292 return nil, "Undefined", fmt.Errorf("Undefined EMR Cluster Instance Group state") 293 } 294 295 return group, *group.Status.State, nil 296 } 297 } 298 299 func resourceAwsEMRInstanceGroupDelete(d *schema.ResourceData, meta interface{}) error { 300 log.Printf("[WARN] AWS EMR Instance Group does not support DELETE; resizing cluster to zero before removing from state") 301 conn := meta.(*AWSClient).emrconn 302 params := &emr.ModifyInstanceGroupsInput{ 303 InstanceGroups: []*emr.InstanceGroupModifyConfig{ 304 { 305 InstanceGroupId: aws.String(d.Id()), 306 InstanceCount: aws.Int64(0), 307 }, 308 }, 309 } 310 311 _, err := conn.ModifyInstanceGroups(params) 312 if err != nil { 313 return err 314 } 315 return nil 316 }