github.com/minamijoyo/terraform@v0.7.8-0.20161029001309-18b3736ba44b/builtin/providers/aws/resource_aws_emr_cluster.go (about)

     1  package aws
     2  
     3  import (
     4  	"log"
     5  
     6  	"encoding/json"
     7  	"fmt"
     8  	"io/ioutil"
     9  	"net/http"
    10  	"strings"
    11  	"time"
    12  
    13  	"github.com/aws/aws-sdk-go/aws"
    14  	"github.com/aws/aws-sdk-go/aws/awserr"
    15  	"github.com/aws/aws-sdk-go/service/emr"
    16  	"github.com/hashicorp/terraform/helper/resource"
    17  	"github.com/hashicorp/terraform/helper/schema"
    18  )
    19  
    20  func resourceAwsEMRCluster() *schema.Resource {
    21  	return &schema.Resource{
    22  		Create: resourceAwsEMRClusterCreate,
    23  		Read:   resourceAwsEMRClusterRead,
    24  		Update: resourceAwsEMRClusterUpdate,
    25  		Delete: resourceAwsEMRClusterDelete,
    26  		Schema: map[string]*schema.Schema{
    27  			"name": &schema.Schema{
    28  				Type:     schema.TypeString,
    29  				ForceNew: true,
    30  				Required: true,
    31  			},
    32  			"release_label": &schema.Schema{
    33  				Type:     schema.TypeString,
    34  				ForceNew: true,
    35  				Required: true,
    36  			},
    37  			"master_instance_type": &schema.Schema{
    38  				Type:     schema.TypeString,
    39  				Required: true,
    40  				ForceNew: true,
    41  			},
    42  			"core_instance_type": &schema.Schema{
    43  				Type:     schema.TypeString,
    44  				Optional: true,
    45  				ForceNew: true,
    46  				Computed: true,
    47  			},
    48  			"core_instance_count": &schema.Schema{
    49  				Type:     schema.TypeInt,
    50  				Optional: true,
    51  				Default:  0,
    52  			},
    53  			"cluster_state": &schema.Schema{
    54  				Type:     schema.TypeString,
    55  				Computed: true,
    56  			},
    57  			"log_uri": &schema.Schema{
    58  				Type:     schema.TypeString,
    59  				ForceNew: true,
    60  				Optional: true,
    61  			},
    62  			"master_public_dns": &schema.Schema{
    63  				Type:     schema.TypeString,
    64  				Computed: true,
    65  			},
    66  			"applications": &schema.Schema{
    67  				Type:     schema.TypeSet,
    68  				Optional: true,
    69  				ForceNew: true,
    70  				Elem:     &schema.Schema{Type: schema.TypeString},
    71  				Set:      schema.HashString,
    72  			},
    73  			"ec2_attributes": &schema.Schema{
    74  				Type:     schema.TypeList,
    75  				MaxItems: 1,
    76  				Optional: true,
    77  				ForceNew: true,
    78  				Elem: &schema.Resource{
    79  					Schema: map[string]*schema.Schema{
    80  						"key_name": &schema.Schema{
    81  							Type:     schema.TypeString,
    82  							Optional: true,
    83  						},
    84  						"subnet_id": &schema.Schema{
    85  							Type:     schema.TypeString,
    86  							Optional: true,
    87  						},
    88  						"additional_master_security_groups": &schema.Schema{
    89  							Type:     schema.TypeString,
    90  							Optional: true,
    91  						},
    92  						"additional_slave_security_groups": &schema.Schema{
    93  							Type:     schema.TypeString,
    94  							Optional: true,
    95  						},
    96  						"emr_managed_master_security_group": &schema.Schema{
    97  							Type:     schema.TypeString,
    98  							Optional: true,
    99  						},
   100  						"emr_managed_slave_security_group": &schema.Schema{
   101  							Type:     schema.TypeString,
   102  							Optional: true,
   103  						},
   104  						"instance_profile": &schema.Schema{
   105  							Type:     schema.TypeString,
   106  							Required: true,
   107  						},
   108  					},
   109  				},
   110  			},
   111  			"bootstrap_action": &schema.Schema{
   112  				Type:     schema.TypeSet,
   113  				Optional: true,
   114  				ForceNew: true,
   115  				Elem: &schema.Resource{
   116  					Schema: map[string]*schema.Schema{
   117  						"name": &schema.Schema{
   118  							Type:     schema.TypeString,
   119  							Required: true,
   120  						},
   121  						"path": &schema.Schema{
   122  							Type:     schema.TypeString,
   123  							Required: true,
   124  						},
   125  						"args": &schema.Schema{
   126  							Type:     schema.TypeSet,
   127  							Optional: true,
   128  							Elem:     &schema.Schema{Type: schema.TypeString},
   129  							Set:      schema.HashString,
   130  						},
   131  					},
   132  				},
   133  			},
   134  			"tags": tagsSchema(),
   135  			"configurations": &schema.Schema{
   136  				Type:     schema.TypeString,
   137  				ForceNew: true,
   138  				Optional: true,
   139  			},
   140  			"service_role": &schema.Schema{
   141  				Type:     schema.TypeString,
   142  				ForceNew: true,
   143  				Required: true,
   144  			},
   145  			"visible_to_all_users": &schema.Schema{
   146  				Type:     schema.TypeBool,
   147  				Optional: true,
   148  				ForceNew: true,
   149  				Default:  true,
   150  			},
   151  		},
   152  	}
   153  }
   154  
   155  func resourceAwsEMRClusterCreate(d *schema.ResourceData, meta interface{}) error {
   156  	conn := meta.(*AWSClient).emrconn
   157  
   158  	log.Printf("[DEBUG] Creating EMR cluster")
   159  	masterInstanceType := d.Get("master_instance_type").(string)
   160  	coreInstanceType := masterInstanceType
   161  	if v, ok := d.GetOk("core_instance_type"); ok {
   162  		coreInstanceType = v.(string)
   163  	}
   164  	coreInstanceCount := d.Get("core_instance_count").(int)
   165  
   166  	applications := d.Get("applications").(*schema.Set).List()
   167  
   168  	instanceConfig := &emr.JobFlowInstancesConfig{
   169  		MasterInstanceType: aws.String(masterInstanceType),
   170  		SlaveInstanceType:  aws.String(coreInstanceType),
   171  		InstanceCount:      aws.Int64(int64(coreInstanceCount)),
   172  		// Default values that we can open up in the future
   173  		KeepJobFlowAliveWhenNoSteps: aws.Bool(true),
   174  		TerminationProtected:        aws.Bool(false),
   175  	}
   176  
   177  	var instanceProfile string
   178  	if a, ok := d.GetOk("ec2_attributes"); ok {
   179  		ec2Attributes := a.([]interface{})
   180  		attributes := ec2Attributes[0].(map[string]interface{})
   181  
   182  		if v, ok := attributes["key_name"]; ok {
   183  			instanceConfig.Ec2KeyName = aws.String(v.(string))
   184  		}
   185  		if v, ok := attributes["subnet_id"]; ok {
   186  			instanceConfig.Ec2SubnetId = aws.String(v.(string))
   187  		}
   188  		if v, ok := attributes["subnet_id"]; ok {
   189  			instanceConfig.Ec2SubnetId = aws.String(v.(string))
   190  		}
   191  
   192  		if v, ok := attributes["additional_master_security_groups"]; ok {
   193  			strSlice := strings.Split(v.(string), ",")
   194  			for i, s := range strSlice {
   195  				strSlice[i] = strings.TrimSpace(s)
   196  			}
   197  			instanceConfig.AdditionalMasterSecurityGroups = aws.StringSlice(strSlice)
   198  		}
   199  
   200  		if v, ok := attributes["additional_slave_security_groups"]; ok {
   201  			strSlice := strings.Split(v.(string), ",")
   202  			for i, s := range strSlice {
   203  				strSlice[i] = strings.TrimSpace(s)
   204  			}
   205  			instanceConfig.AdditionalSlaveSecurityGroups = aws.StringSlice(strSlice)
   206  		}
   207  
   208  		if v, ok := attributes["emr_managed_master_security_group"]; ok {
   209  			instanceConfig.EmrManagedMasterSecurityGroup = aws.String(v.(string))
   210  		}
   211  		if v, ok := attributes["emr_managed_slave_security_group"]; ok {
   212  			instanceConfig.EmrManagedSlaveSecurityGroup = aws.String(v.(string))
   213  		}
   214  
   215  		if len(strings.TrimSpace(attributes["instance_profile"].(string))) != 0 {
   216  			instanceProfile = strings.TrimSpace(attributes["instance_profile"].(string))
   217  		}
   218  	}
   219  
   220  	emrApps := expandApplications(applications)
   221  
   222  	params := &emr.RunJobFlowInput{
   223  		Instances:    instanceConfig,
   224  		Name:         aws.String(d.Get("name").(string)),
   225  		Applications: emrApps,
   226  
   227  		ReleaseLabel:      aws.String(d.Get("release_label").(string)),
   228  		ServiceRole:       aws.String(d.Get("service_role").(string)),
   229  		VisibleToAllUsers: aws.Bool(d.Get("visible_to_all_users").(bool)),
   230  	}
   231  
   232  	if v, ok := d.GetOk("log_uri"); ok {
   233  		params.LogUri = aws.String(v.(string))
   234  	}
   235  
   236  	if instanceProfile != "" {
   237  		params.JobFlowRole = aws.String(instanceProfile)
   238  	}
   239  
   240  	if v, ok := d.GetOk("bootstrap_action"); ok {
   241  		bootstrapActions := v.(*schema.Set).List()
   242  		params.BootstrapActions = expandBootstrapActions(bootstrapActions)
   243  	}
   244  	if v, ok := d.GetOk("tags"); ok {
   245  		tagsIn := v.(map[string]interface{})
   246  		params.Tags = expandTags(tagsIn)
   247  	}
   248  	if v, ok := d.GetOk("configurations"); ok {
   249  		confUrl := v.(string)
   250  		params.Configurations = expandConfigures(confUrl)
   251  	}
   252  
   253  	log.Printf("[DEBUG] EMR Cluster create options: %s", params)
   254  	resp, err := conn.RunJobFlow(params)
   255  
   256  	if err != nil {
   257  		log.Printf("[ERROR] %s", err)
   258  		return err
   259  	}
   260  
   261  	d.SetId(*resp.JobFlowId)
   262  
   263  	log.Println(
   264  		"[INFO] Waiting for EMR Cluster to be available")
   265  
   266  	stateConf := &resource.StateChangeConf{
   267  		Pending:    []string{"STARTING", "BOOTSTRAPPING"},
   268  		Target:     []string{"WAITING", "RUNNING"},
   269  		Refresh:    resourceAwsEMRClusterStateRefreshFunc(d, meta),
   270  		Timeout:    40 * time.Minute,
   271  		MinTimeout: 10 * time.Second,
   272  		Delay:      30 * time.Second, // Wait 30 secs before starting
   273  	}
   274  
   275  	_, err = stateConf.WaitForState()
   276  	if err != nil {
   277  		return fmt.Errorf("[WARN] Error waiting for EMR Cluster state to be \"WAITING\" or \"RUNNING\": %s", err)
   278  	}
   279  
   280  	return resourceAwsEMRClusterRead(d, meta)
   281  }
   282  
   283  func resourceAwsEMRClusterRead(d *schema.ResourceData, meta interface{}) error {
   284  	emrconn := meta.(*AWSClient).emrconn
   285  
   286  	req := &emr.DescribeClusterInput{
   287  		ClusterId: aws.String(d.Id()),
   288  	}
   289  
   290  	resp, err := emrconn.DescribeCluster(req)
   291  	if err != nil {
   292  		return fmt.Errorf("Error reading EMR cluster: %s", err)
   293  	}
   294  
   295  	if resp.Cluster == nil {
   296  		log.Printf("[DEBUG] EMR Cluster (%s) not found", d.Id())
   297  		d.SetId("")
   298  		return nil
   299  	}
   300  
   301  	cluster := resp.Cluster
   302  
   303  	if cluster.Status != nil {
   304  		if *cluster.Status.State == "TERMINATED" {
   305  			log.Printf("[DEBUG] EMR Cluster (%s) was TERMINATED already", d.Id())
   306  			d.SetId("")
   307  			return nil
   308  		}
   309  
   310  		if *cluster.Status.State == "TERMINATED_WITH_ERRORS" {
   311  			log.Printf("[DEBUG] EMR Cluster (%s) was TERMINATED_WITH_ERRORS already", d.Id())
   312  			d.SetId("")
   313  			return nil
   314  		}
   315  
   316  		d.Set("cluster_state", cluster.Status.State)
   317  	}
   318  
   319  	instanceGroups, err := fetchAllEMRInstanceGroups(meta, d.Id())
   320  	if err == nil {
   321  		coreGroup := findGroup(instanceGroups, "CORE")
   322  		if coreGroup != nil {
   323  			d.Set("core_instance_type", coreGroup.InstanceType)
   324  		}
   325  	}
   326  
   327  	d.Set("name", cluster.Name)
   328  	d.Set("service_role", cluster.ServiceRole)
   329  	d.Set("release_label", cluster.ReleaseLabel)
   330  	d.Set("log_uri", cluster.LogUri)
   331  	d.Set("master_public_dns", cluster.MasterPublicDnsName)
   332  	d.Set("visible_to_all_users", cluster.VisibleToAllUsers)
   333  	d.Set("tags", tagsToMapEMR(cluster.Tags))
   334  
   335  	if err := d.Set("applications", flattenApplications(cluster.Applications)); err != nil {
   336  		log.Printf("[ERR] Error setting EMR Applications for cluster (%s): %s", d.Id(), err)
   337  	}
   338  
   339  	// Configurations is a JSON document. It's built with an expand method but a
   340  	// simple string should be returned as JSON
   341  	if err := d.Set("configurations", cluster.Configurations); err != nil {
   342  		log.Printf("[ERR] Error setting EMR configurations for cluster (%s): %s", d.Id(), err)
   343  	}
   344  
   345  	if err := d.Set("ec2_attributes", flattenEc2Attributes(cluster.Ec2InstanceAttributes)); err != nil {
   346  		log.Printf("[ERR] Error setting EMR Ec2 Attributes: %s", err)
   347  	}
   348  	return nil
   349  }
   350  
   351  func resourceAwsEMRClusterUpdate(d *schema.ResourceData, meta interface{}) error {
   352  	conn := meta.(*AWSClient).emrconn
   353  
   354  	if d.HasChange("core_instance_count") {
   355  		log.Printf("[DEBUG] Modify EMR cluster")
   356  		groups, err := fetchAllEMRInstanceGroups(meta, d.Id())
   357  		if err != nil {
   358  			log.Printf("[DEBUG] Error finding all instance groups: %s", err)
   359  			return err
   360  		}
   361  
   362  		coreInstanceCount := d.Get("core_instance_count").(int)
   363  		coreGroup := findGroup(groups, "CORE")
   364  		if coreGroup == nil {
   365  			return fmt.Errorf("[ERR] Error finding core group")
   366  		}
   367  
   368  		params := &emr.ModifyInstanceGroupsInput{
   369  			InstanceGroups: []*emr.InstanceGroupModifyConfig{
   370  				{
   371  					InstanceGroupId: coreGroup.Id,
   372  					InstanceCount:   aws.Int64(int64(coreInstanceCount)),
   373  				},
   374  			},
   375  		}
   376  		_, errModify := conn.ModifyInstanceGroups(params)
   377  		if errModify != nil {
   378  			log.Printf("[ERROR] %s", errModify)
   379  			return errModify
   380  		}
   381  
   382  		log.Printf("[DEBUG] Modify EMR Cluster done...")
   383  	}
   384  
   385  	log.Println(
   386  		"[INFO] Waiting for EMR Cluster to be available")
   387  
   388  	stateConf := &resource.StateChangeConf{
   389  		Pending:    []string{"STARTING", "BOOTSTRAPPING"},
   390  		Target:     []string{"WAITING", "RUNNING"},
   391  		Refresh:    resourceAwsEMRClusterStateRefreshFunc(d, meta),
   392  		Timeout:    40 * time.Minute,
   393  		MinTimeout: 10 * time.Second,
   394  		Delay:      5 * time.Second,
   395  	}
   396  
   397  	_, err := stateConf.WaitForState()
   398  	if err != nil {
   399  		return fmt.Errorf("[WARN] Error waiting for EMR Cluster state to be \"WAITING\" or \"RUNNING\" after modification: %s", err)
   400  	}
   401  
   402  	return resourceAwsEMRClusterRead(d, meta)
   403  }
   404  
   405  func resourceAwsEMRClusterDelete(d *schema.ResourceData, meta interface{}) error {
   406  	conn := meta.(*AWSClient).emrconn
   407  
   408  	req := &emr.TerminateJobFlowsInput{
   409  		JobFlowIds: []*string{
   410  			aws.String(d.Id()),
   411  		},
   412  	}
   413  
   414  	_, err := conn.TerminateJobFlows(req)
   415  	if err != nil {
   416  		log.Printf("[ERROR], %s", err)
   417  		return err
   418  	}
   419  
   420  	err = resource.Retry(10*time.Minute, func() *resource.RetryError {
   421  		resp, err := conn.ListInstances(&emr.ListInstancesInput{
   422  			ClusterId: aws.String(d.Id()),
   423  		})
   424  
   425  		if err != nil {
   426  			return resource.NonRetryableError(err)
   427  		}
   428  
   429  		instanceCount := len(resp.Instances)
   430  
   431  		if resp == nil || instanceCount == 0 {
   432  			log.Printf("[DEBUG] No instances found for EMR Cluster (%s)", d.Id())
   433  			return nil
   434  		}
   435  
   436  		// Collect instance status states, wait for all instances to be terminated
   437  		// before moving on
   438  		var terminated []string
   439  		for j, i := range resp.Instances {
   440  			if i.Status != nil {
   441  				if *i.Status.State == "TERMINATED" {
   442  					terminated = append(terminated, *i.Ec2InstanceId)
   443  				}
   444  			} else {
   445  				log.Printf("[DEBUG] Cluster instance (%d : %s) has no status", j, *i.Ec2InstanceId)
   446  			}
   447  		}
   448  		if len(terminated) == instanceCount {
   449  			log.Printf("[DEBUG] All (%d) EMR Cluster (%s) Instances terminated", instanceCount, d.Id())
   450  			return nil
   451  		}
   452  		return resource.RetryableError(fmt.Errorf("[DEBUG] EMR Cluster (%s) has (%d) Instances remaining, retrying", d.Id(), len(resp.Instances)))
   453  	})
   454  
   455  	if err != nil {
   456  		log.Printf("[ERR] Error waiting for EMR Cluster (%s) Instances to drain", d.Id())
   457  	}
   458  
   459  	d.SetId("")
   460  	return nil
   461  }
   462  
   463  func expandApplications(apps []interface{}) []*emr.Application {
   464  	appOut := make([]*emr.Application, 0, len(apps))
   465  
   466  	for _, appName := range expandStringList(apps) {
   467  		app := &emr.Application{
   468  			Name: appName,
   469  		}
   470  		appOut = append(appOut, app)
   471  	}
   472  	return appOut
   473  }
   474  
   475  func flattenApplications(apps []*emr.Application) []interface{} {
   476  	appOut := make([]interface{}, 0, len(apps))
   477  
   478  	for _, app := range apps {
   479  		appOut = append(appOut, *app.Name)
   480  	}
   481  	return appOut
   482  }
   483  
   484  func flattenEc2Attributes(ia *emr.Ec2InstanceAttributes) []map[string]interface{} {
   485  	attrs := map[string]interface{}{}
   486  	result := make([]map[string]interface{}, 0)
   487  
   488  	if ia.Ec2KeyName != nil {
   489  		attrs["key_name"] = *ia.Ec2KeyName
   490  	}
   491  	if ia.Ec2SubnetId != nil {
   492  		attrs["subnet_id"] = *ia.Ec2SubnetId
   493  	}
   494  	if ia.IamInstanceProfile != nil {
   495  		attrs["instance_profile"] = *ia.IamInstanceProfile
   496  	}
   497  	if ia.EmrManagedMasterSecurityGroup != nil {
   498  		attrs["emr_managed_master_security_group"] = *ia.EmrManagedMasterSecurityGroup
   499  	}
   500  	if ia.EmrManagedSlaveSecurityGroup != nil {
   501  		attrs["emr_managed_slave_security_group"] = *ia.EmrManagedSlaveSecurityGroup
   502  	}
   503  
   504  	if len(ia.AdditionalMasterSecurityGroups) > 0 {
   505  		strs := aws.StringValueSlice(ia.AdditionalMasterSecurityGroups)
   506  		attrs["additional_master_security_groups"] = strings.Join(strs, ",")
   507  	}
   508  	if len(ia.AdditionalSlaveSecurityGroups) > 0 {
   509  		strs := aws.StringValueSlice(ia.AdditionalSlaveSecurityGroups)
   510  		attrs["additional_slave_security_groups"] = strings.Join(strs, ",")
   511  	}
   512  
   513  	result = append(result, attrs)
   514  
   515  	return result
   516  }
   517  
   518  func loadGroups(d *schema.ResourceData, meta interface{}) ([]*emr.InstanceGroup, error) {
   519  	emrconn := meta.(*AWSClient).emrconn
   520  	reqGrps := &emr.ListInstanceGroupsInput{
   521  		ClusterId: aws.String(d.Id()),
   522  	}
   523  
   524  	respGrps, errGrps := emrconn.ListInstanceGroups(reqGrps)
   525  	if errGrps != nil {
   526  		return nil, fmt.Errorf("Error reading EMR cluster: %s", errGrps)
   527  	}
   528  	return respGrps.InstanceGroups, nil
   529  }
   530  
   531  func findGroup(grps []*emr.InstanceGroup, typ string) *emr.InstanceGroup {
   532  	for _, grp := range grps {
   533  		if grp.InstanceGroupType != nil {
   534  			if *grp.InstanceGroupType == typ {
   535  				return grp
   536  			}
   537  		}
   538  	}
   539  	return nil
   540  }
   541  
   542  func expandTags(m map[string]interface{}) []*emr.Tag {
   543  	var result []*emr.Tag
   544  	for k, v := range m {
   545  		result = append(result, &emr.Tag{
   546  			Key:   aws.String(k),
   547  			Value: aws.String(v.(string)),
   548  		})
   549  	}
   550  
   551  	return result
   552  }
   553  
   554  func tagsToMapEMR(ts []*emr.Tag) map[string]string {
   555  	result := make(map[string]string)
   556  	for _, t := range ts {
   557  		result[*t.Key] = *t.Value
   558  	}
   559  
   560  	return result
   561  }
   562  
   563  func expandBootstrapActions(bootstrapActions []interface{}) []*emr.BootstrapActionConfig {
   564  	actionsOut := []*emr.BootstrapActionConfig{}
   565  
   566  	for _, raw := range bootstrapActions {
   567  		actionAttributes := raw.(map[string]interface{})
   568  		actionName := actionAttributes["name"].(string)
   569  		actionPath := actionAttributes["path"].(string)
   570  		actionArgs := actionAttributes["args"].(*schema.Set).List()
   571  
   572  		action := &emr.BootstrapActionConfig{
   573  			Name: aws.String(actionName),
   574  			ScriptBootstrapAction: &emr.ScriptBootstrapActionConfig{
   575  				Path: aws.String(actionPath),
   576  				Args: expandStringList(actionArgs),
   577  			},
   578  		}
   579  		actionsOut = append(actionsOut, action)
   580  	}
   581  
   582  	return actionsOut
   583  }
   584  
   585  func expandConfigures(input string) []*emr.Configuration {
   586  	configsOut := []*emr.Configuration{}
   587  	if strings.HasPrefix(input, "http") {
   588  		if err := readHttpJson(input, &configsOut); err != nil {
   589  			log.Printf("[ERR] Error reading HTTP JSON: %s", err)
   590  		}
   591  	} else if strings.HasSuffix(input, ".json") {
   592  		if err := readLocalJson(input, &configsOut); err != nil {
   593  			log.Printf("[ERR] Error reading local JSON: %s", err)
   594  		}
   595  	} else {
   596  		if err := readBodyJson(input, &configsOut); err != nil {
   597  			log.Printf("[ERR] Error reading body JSON: %s", err)
   598  		}
   599  	}
   600  	log.Printf("[DEBUG] Expanded EMR Configurations %s", configsOut)
   601  
   602  	return configsOut
   603  }
   604  
   605  func readHttpJson(url string, target interface{}) error {
   606  	r, err := http.Get(url)
   607  	if err != nil {
   608  		return err
   609  	}
   610  	defer r.Body.Close()
   611  
   612  	return json.NewDecoder(r.Body).Decode(target)
   613  }
   614  
   615  func readLocalJson(localFile string, target interface{}) error {
   616  	file, e := ioutil.ReadFile(localFile)
   617  	if e != nil {
   618  		log.Printf("[ERROR] %s", e)
   619  		return e
   620  	}
   621  
   622  	return json.Unmarshal(file, target)
   623  }
   624  
   625  func readBodyJson(body string, target interface{}) error {
   626  	log.Printf("[DEBUG] Raw Body %s\n", body)
   627  	err := json.Unmarshal([]byte(body), target)
   628  	if err != nil {
   629  		log.Printf("[ERROR] parsing JSON %s", err)
   630  		return err
   631  	}
   632  	return nil
   633  }
   634  
   635  func resourceAwsEMRClusterStateRefreshFunc(d *schema.ResourceData, meta interface{}) resource.StateRefreshFunc {
   636  	return func() (interface{}, string, error) {
   637  		conn := meta.(*AWSClient).emrconn
   638  
   639  		log.Printf("[INFO] Reading EMR Cluster Information: %s", d.Id())
   640  		params := &emr.DescribeClusterInput{
   641  			ClusterId: aws.String(d.Id()),
   642  		}
   643  
   644  		resp, err := conn.DescribeCluster(params)
   645  
   646  		if err != nil {
   647  			if awsErr, ok := err.(awserr.Error); ok {
   648  				if "ClusterNotFound" == awsErr.Code() {
   649  					return 42, "destroyed", nil
   650  				}
   651  			}
   652  			log.Printf("[WARN] Error on retrieving EMR Cluster (%s) when waiting: %s", d.Id(), err)
   653  			return nil, "", err
   654  		}
   655  
   656  		emrc := resp.Cluster
   657  
   658  		if emrc == nil {
   659  			return 42, "destroyed", nil
   660  		}
   661  
   662  		if resp.Cluster.Status != nil {
   663  			log.Printf("[DEBUG] EMR Cluster status (%s): %s", d.Id(), *resp.Cluster.Status)
   664  		}
   665  
   666  		return emrc, *emrc.Status.State, nil
   667  	}
   668  }