github.com/vtorhonen/terraform@v0.9.0-beta2.0.20170307220345-5d894e4ffda7/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  }