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