github.com/danp/terraform@v0.9.5-0.20170426144147-39d740081351/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": { 28 Type: schema.TypeString, 29 ForceNew: true, 30 Required: true, 31 }, 32 "release_label": { 33 Type: schema.TypeString, 34 ForceNew: true, 35 Required: true, 36 }, 37 "master_instance_type": { 38 Type: schema.TypeString, 39 Required: true, 40 ForceNew: true, 41 }, 42 "core_instance_type": { 43 Type: schema.TypeString, 44 Optional: true, 45 ForceNew: true, 46 Computed: true, 47 }, 48 "core_instance_count": { 49 Type: schema.TypeInt, 50 Optional: true, 51 Default: 1, 52 }, 53 "cluster_state": { 54 Type: schema.TypeString, 55 Computed: true, 56 }, 57 "log_uri": { 58 Type: schema.TypeString, 59 ForceNew: true, 60 Optional: true, 61 }, 62 "master_public_dns": { 63 Type: schema.TypeString, 64 Computed: true, 65 }, 66 "applications": { 67 Type: schema.TypeSet, 68 Optional: true, 69 ForceNew: true, 70 Elem: &schema.Schema{Type: schema.TypeString}, 71 Set: schema.HashString, 72 }, 73 "termination_protection": { 74 Type: schema.TypeBool, 75 Optional: true, 76 Computed: true, 77 }, 78 "keep_job_flow_alive_when_no_steps": { 79 Type: schema.TypeBool, 80 ForceNew: true, 81 Optional: true, 82 Computed: true, 83 }, 84 "ec2_attributes": { 85 Type: schema.TypeList, 86 MaxItems: 1, 87 Optional: true, 88 ForceNew: true, 89 Elem: &schema.Resource{ 90 Schema: map[string]*schema.Schema{ 91 "key_name": { 92 Type: schema.TypeString, 93 Optional: true, 94 }, 95 "subnet_id": { 96 Type: schema.TypeString, 97 Optional: true, 98 }, 99 "additional_master_security_groups": { 100 Type: schema.TypeString, 101 Optional: true, 102 }, 103 "additional_slave_security_groups": { 104 Type: schema.TypeString, 105 Optional: true, 106 }, 107 "emr_managed_master_security_group": { 108 Type: schema.TypeString, 109 Optional: true, 110 }, 111 "emr_managed_slave_security_group": { 112 Type: schema.TypeString, 113 Optional: true, 114 }, 115 "instance_profile": { 116 Type: schema.TypeString, 117 Required: true, 118 }, 119 "service_access_security_group": { 120 Type: schema.TypeString, 121 Optional: true, 122 }, 123 }, 124 }, 125 }, 126 "bootstrap_action": { 127 Type: schema.TypeSet, 128 Optional: true, 129 ForceNew: true, 130 Elem: &schema.Resource{ 131 Schema: map[string]*schema.Schema{ 132 "name": { 133 Type: schema.TypeString, 134 Required: true, 135 }, 136 "path": { 137 Type: schema.TypeString, 138 Required: true, 139 }, 140 "args": { 141 Type: schema.TypeList, 142 Optional: true, 143 ForceNew: true, 144 Elem: &schema.Schema{Type: schema.TypeString}, 145 }, 146 }, 147 }, 148 }, 149 "tags": tagsSchema(), 150 "configurations": { 151 Type: schema.TypeString, 152 ForceNew: true, 153 Optional: true, 154 }, 155 "service_role": { 156 Type: schema.TypeString, 157 ForceNew: true, 158 Required: true, 159 }, 160 "autoscaling_role": &schema.Schema{ 161 Type: schema.TypeString, 162 ForceNew: true, 163 Optional: true, 164 }, 165 "visible_to_all_users": { 166 Type: schema.TypeBool, 167 Optional: true, 168 Default: true, 169 }, 170 }, 171 } 172 } 173 174 func resourceAwsEMRClusterCreate(d *schema.ResourceData, meta interface{}) error { 175 conn := meta.(*AWSClient).emrconn 176 177 log.Printf("[DEBUG] Creating EMR cluster") 178 masterInstanceType := d.Get("master_instance_type").(string) 179 coreInstanceType := masterInstanceType 180 if v, ok := d.GetOk("core_instance_type"); ok { 181 coreInstanceType = v.(string) 182 } 183 coreInstanceCount := d.Get("core_instance_count").(int) 184 185 applications := d.Get("applications").(*schema.Set).List() 186 187 keepJobFlowAliveWhenNoSteps := true 188 if v, ok := d.GetOk("keep_job_flow_alive_when_no_steps"); ok { 189 keepJobFlowAliveWhenNoSteps = v.(bool) 190 } 191 192 terminationProtection := false 193 if v, ok := d.GetOk("termination_protection"); ok { 194 terminationProtection = v.(bool) 195 } 196 instanceConfig := &emr.JobFlowInstancesConfig{ 197 MasterInstanceType: aws.String(masterInstanceType), 198 SlaveInstanceType: aws.String(coreInstanceType), 199 InstanceCount: aws.Int64(int64(coreInstanceCount)), 200 201 KeepJobFlowAliveWhenNoSteps: aws.Bool(keepJobFlowAliveWhenNoSteps), 202 TerminationProtected: aws.Bool(terminationProtection), 203 } 204 205 var instanceProfile string 206 if a, ok := d.GetOk("ec2_attributes"); ok { 207 ec2Attributes := a.([]interface{}) 208 attributes := ec2Attributes[0].(map[string]interface{}) 209 210 if v, ok := attributes["key_name"]; ok { 211 instanceConfig.Ec2KeyName = aws.String(v.(string)) 212 } 213 if v, ok := attributes["subnet_id"]; ok { 214 instanceConfig.Ec2SubnetId = aws.String(v.(string)) 215 } 216 if v, ok := attributes["subnet_id"]; ok { 217 instanceConfig.Ec2SubnetId = aws.String(v.(string)) 218 } 219 220 if v, ok := attributes["additional_master_security_groups"]; ok { 221 strSlice := strings.Split(v.(string), ",") 222 for i, s := range strSlice { 223 strSlice[i] = strings.TrimSpace(s) 224 } 225 instanceConfig.AdditionalMasterSecurityGroups = aws.StringSlice(strSlice) 226 } 227 228 if v, ok := attributes["additional_slave_security_groups"]; ok { 229 strSlice := strings.Split(v.(string), ",") 230 for i, s := range strSlice { 231 strSlice[i] = strings.TrimSpace(s) 232 } 233 instanceConfig.AdditionalSlaveSecurityGroups = aws.StringSlice(strSlice) 234 } 235 236 if v, ok := attributes["emr_managed_master_security_group"]; ok { 237 instanceConfig.EmrManagedMasterSecurityGroup = aws.String(v.(string)) 238 } 239 if v, ok := attributes["emr_managed_slave_security_group"]; ok { 240 instanceConfig.EmrManagedSlaveSecurityGroup = aws.String(v.(string)) 241 } 242 243 if len(strings.TrimSpace(attributes["instance_profile"].(string))) != 0 { 244 instanceProfile = strings.TrimSpace(attributes["instance_profile"].(string)) 245 } 246 247 if v, ok := attributes["service_access_security_group"]; ok { 248 instanceConfig.ServiceAccessSecurityGroup = aws.String(v.(string)) 249 } 250 } 251 252 emrApps := expandApplications(applications) 253 254 params := &emr.RunJobFlowInput{ 255 Instances: instanceConfig, 256 Name: aws.String(d.Get("name").(string)), 257 Applications: emrApps, 258 259 ReleaseLabel: aws.String(d.Get("release_label").(string)), 260 ServiceRole: aws.String(d.Get("service_role").(string)), 261 VisibleToAllUsers: aws.Bool(d.Get("visible_to_all_users").(bool)), 262 } 263 264 if v, ok := d.GetOk("log_uri"); ok { 265 params.LogUri = aws.String(v.(string)) 266 } 267 if v, ok := d.GetOk("autoscaling_role"); ok { 268 params.AutoScalingRole = aws.String(v.(string)) 269 } 270 271 if instanceProfile != "" { 272 params.JobFlowRole = aws.String(instanceProfile) 273 } 274 275 if v, ok := d.GetOk("bootstrap_action"); ok { 276 bootstrapActions := v.(*schema.Set).List() 277 params.BootstrapActions = expandBootstrapActions(bootstrapActions) 278 } 279 if v, ok := d.GetOk("tags"); ok { 280 tagsIn := v.(map[string]interface{}) 281 params.Tags = expandTags(tagsIn) 282 } 283 if v, ok := d.GetOk("configurations"); ok { 284 confUrl := v.(string) 285 params.Configurations = expandConfigures(confUrl) 286 } 287 288 log.Printf("[DEBUG] EMR Cluster create options: %s", params) 289 resp, err := conn.RunJobFlow(params) 290 291 if err != nil { 292 log.Printf("[ERROR] %s", err) 293 return err 294 } 295 296 d.SetId(*resp.JobFlowId) 297 298 log.Println( 299 "[INFO] Waiting for EMR Cluster to be available") 300 301 stateConf := &resource.StateChangeConf{ 302 Pending: []string{"STARTING", "BOOTSTRAPPING"}, 303 Target: []string{"WAITING", "RUNNING"}, 304 Refresh: resourceAwsEMRClusterStateRefreshFunc(d, meta), 305 Timeout: 75 * time.Minute, 306 MinTimeout: 10 * time.Second, 307 Delay: 30 * time.Second, // Wait 30 secs before starting 308 } 309 310 _, err = stateConf.WaitForState() 311 if err != nil { 312 return fmt.Errorf("[WARN] Error waiting for EMR Cluster state to be \"WAITING\" or \"RUNNING\": %s", err) 313 } 314 315 return resourceAwsEMRClusterRead(d, meta) 316 } 317 318 func resourceAwsEMRClusterRead(d *schema.ResourceData, meta interface{}) error { 319 emrconn := meta.(*AWSClient).emrconn 320 321 req := &emr.DescribeClusterInput{ 322 ClusterId: aws.String(d.Id()), 323 } 324 325 resp, err := emrconn.DescribeCluster(req) 326 if err != nil { 327 return fmt.Errorf("Error reading EMR cluster: %s", err) 328 } 329 330 if resp.Cluster == nil { 331 log.Printf("[DEBUG] EMR Cluster (%s) not found", d.Id()) 332 d.SetId("") 333 return nil 334 } 335 336 cluster := resp.Cluster 337 338 if cluster.Status != nil { 339 if *cluster.Status.State == "TERMINATED" { 340 log.Printf("[DEBUG] EMR Cluster (%s) was TERMINATED already", d.Id()) 341 d.SetId("") 342 return nil 343 } 344 345 if *cluster.Status.State == "TERMINATED_WITH_ERRORS" { 346 log.Printf("[DEBUG] EMR Cluster (%s) was TERMINATED_WITH_ERRORS already", d.Id()) 347 d.SetId("") 348 return nil 349 } 350 351 d.Set("cluster_state", cluster.Status.State) 352 } 353 354 instanceGroups, err := fetchAllEMRInstanceGroups(meta, d.Id()) 355 if err == nil { 356 coreGroup := findGroup(instanceGroups, "CORE") 357 if coreGroup != nil { 358 d.Set("core_instance_type", coreGroup.InstanceType) 359 } 360 } 361 362 d.Set("name", cluster.Name) 363 d.Set("service_role", cluster.ServiceRole) 364 d.Set("autoscaling_role", cluster.AutoScalingRole) 365 d.Set("release_label", cluster.ReleaseLabel) 366 d.Set("log_uri", cluster.LogUri) 367 d.Set("master_public_dns", cluster.MasterPublicDnsName) 368 d.Set("visible_to_all_users", cluster.VisibleToAllUsers) 369 d.Set("tags", tagsToMapEMR(cluster.Tags)) 370 371 if err := d.Set("applications", flattenApplications(cluster.Applications)); err != nil { 372 log.Printf("[ERR] Error setting EMR Applications for cluster (%s): %s", d.Id(), err) 373 } 374 375 // Configurations is a JSON document. It's built with an expand method but a 376 // simple string should be returned as JSON 377 if err := d.Set("configurations", cluster.Configurations); err != nil { 378 log.Printf("[ERR] Error setting EMR configurations for cluster (%s): %s", d.Id(), err) 379 } 380 381 if err := d.Set("ec2_attributes", flattenEc2Attributes(cluster.Ec2InstanceAttributes)); err != nil { 382 log.Printf("[ERR] Error setting EMR Ec2 Attributes: %s", err) 383 } 384 385 respBootstraps, err := emrconn.ListBootstrapActions(&emr.ListBootstrapActionsInput{ 386 ClusterId: cluster.Id, 387 }) 388 if err != nil { 389 log.Printf("[WARN] Error listing bootstrap actions: %s", err) 390 } 391 392 if err := d.Set("bootstrap_action", flattenBootstrapArguments(respBootstraps.BootstrapActions)); err != nil { 393 log.Printf("[WARN] Error setting Bootstrap Actions: %s", err) 394 } 395 396 return nil 397 } 398 399 func resourceAwsEMRClusterUpdate(d *schema.ResourceData, meta interface{}) error { 400 conn := meta.(*AWSClient).emrconn 401 402 d.Partial(true) 403 404 if d.HasChange("core_instance_count") { 405 d.SetPartial("core_instance_count") 406 log.Printf("[DEBUG] Modify EMR cluster") 407 groups, err := fetchAllEMRInstanceGroups(meta, d.Id()) 408 if err != nil { 409 log.Printf("[DEBUG] Error finding all instance groups: %s", err) 410 return err 411 } 412 413 coreInstanceCount := d.Get("core_instance_count").(int) 414 coreGroup := findGroup(groups, "CORE") 415 if coreGroup == nil { 416 return fmt.Errorf("[ERR] Error finding core group") 417 } 418 419 params := &emr.ModifyInstanceGroupsInput{ 420 InstanceGroups: []*emr.InstanceGroupModifyConfig{ 421 { 422 InstanceGroupId: coreGroup.Id, 423 InstanceCount: aws.Int64(int64(coreInstanceCount) - 1), 424 }, 425 }, 426 } 427 _, errModify := conn.ModifyInstanceGroups(params) 428 if errModify != nil { 429 log.Printf("[ERROR] %s", errModify) 430 return errModify 431 } 432 433 log.Printf("[DEBUG] Modify EMR Cluster done...") 434 435 log.Println("[INFO] Waiting for EMR Cluster to be available") 436 437 stateConf := &resource.StateChangeConf{ 438 Pending: []string{"STARTING", "BOOTSTRAPPING"}, 439 Target: []string{"WAITING", "RUNNING"}, 440 Refresh: resourceAwsEMRClusterStateRefreshFunc(d, meta), 441 Timeout: 40 * time.Minute, 442 MinTimeout: 10 * time.Second, 443 Delay: 5 * time.Second, 444 } 445 446 _, err = stateConf.WaitForState() 447 if err != nil { 448 return fmt.Errorf("[WARN] Error waiting for EMR Cluster state to be \"WAITING\" or \"RUNNING\" after modification: %s", err) 449 } 450 } 451 452 if d.HasChange("visible_to_all_users") { 453 d.SetPartial("visible_to_all_users") 454 _, errModify := conn.SetVisibleToAllUsers(&emr.SetVisibleToAllUsersInput{ 455 JobFlowIds: []*string{aws.String(d.Id())}, 456 VisibleToAllUsers: aws.Bool(d.Get("visible_to_all_users").(bool)), 457 }) 458 if errModify != nil { 459 log.Printf("[ERROR] %s", errModify) 460 return errModify 461 } 462 } 463 464 if d.HasChange("termination_protection") { 465 d.SetPartial("termination_protection") 466 _, errModify := conn.SetTerminationProtection(&emr.SetTerminationProtectionInput{ 467 JobFlowIds: []*string{aws.String(d.Id())}, 468 TerminationProtected: aws.Bool(d.Get("termination_protection").(bool)), 469 }) 470 if errModify != nil { 471 log.Printf("[ERROR] %s", errModify) 472 return errModify 473 } 474 } 475 476 if err := setTagsEMR(conn, d); err != nil { 477 return err 478 } else { 479 d.SetPartial("tags") 480 } 481 482 d.Partial(false) 483 484 return resourceAwsEMRClusterRead(d, meta) 485 } 486 487 func resourceAwsEMRClusterDelete(d *schema.ResourceData, meta interface{}) error { 488 conn := meta.(*AWSClient).emrconn 489 490 req := &emr.TerminateJobFlowsInput{ 491 JobFlowIds: []*string{ 492 aws.String(d.Id()), 493 }, 494 } 495 496 _, err := conn.TerminateJobFlows(req) 497 if err != nil { 498 log.Printf("[ERROR], %s", err) 499 return err 500 } 501 502 err = resource.Retry(10*time.Minute, func() *resource.RetryError { 503 resp, err := conn.ListInstances(&emr.ListInstancesInput{ 504 ClusterId: aws.String(d.Id()), 505 }) 506 507 if err != nil { 508 return resource.NonRetryableError(err) 509 } 510 511 instanceCount := len(resp.Instances) 512 513 if resp == nil || instanceCount == 0 { 514 log.Printf("[DEBUG] No instances found for EMR Cluster (%s)", d.Id()) 515 return nil 516 } 517 518 // Collect instance status states, wait for all instances to be terminated 519 // before moving on 520 var terminated []string 521 for j, i := range resp.Instances { 522 if i.Status != nil { 523 if *i.Status.State == "TERMINATED" { 524 terminated = append(terminated, *i.Ec2InstanceId) 525 } 526 } else { 527 log.Printf("[DEBUG] Cluster instance (%d : %s) has no status", j, *i.Ec2InstanceId) 528 } 529 } 530 if len(terminated) == instanceCount { 531 log.Printf("[DEBUG] All (%d) EMR Cluster (%s) Instances terminated", instanceCount, d.Id()) 532 return nil 533 } 534 return resource.RetryableError(fmt.Errorf("[DEBUG] EMR Cluster (%s) has (%d) Instances remaining, retrying", d.Id(), len(resp.Instances))) 535 }) 536 537 if err != nil { 538 log.Printf("[ERR] Error waiting for EMR Cluster (%s) Instances to drain", d.Id()) 539 } 540 541 d.SetId("") 542 return nil 543 } 544 545 func expandApplications(apps []interface{}) []*emr.Application { 546 appOut := make([]*emr.Application, 0, len(apps)) 547 548 for _, appName := range expandStringList(apps) { 549 app := &emr.Application{ 550 Name: appName, 551 } 552 appOut = append(appOut, app) 553 } 554 return appOut 555 } 556 557 func flattenApplications(apps []*emr.Application) []interface{} { 558 appOut := make([]interface{}, 0, len(apps)) 559 560 for _, app := range apps { 561 appOut = append(appOut, *app.Name) 562 } 563 return appOut 564 } 565 566 func flattenEc2Attributes(ia *emr.Ec2InstanceAttributes) []map[string]interface{} { 567 attrs := map[string]interface{}{} 568 result := make([]map[string]interface{}, 0) 569 570 if ia.Ec2KeyName != nil { 571 attrs["key_name"] = *ia.Ec2KeyName 572 } 573 if ia.Ec2SubnetId != nil { 574 attrs["subnet_id"] = *ia.Ec2SubnetId 575 } 576 if ia.IamInstanceProfile != nil { 577 attrs["instance_profile"] = *ia.IamInstanceProfile 578 } 579 if ia.EmrManagedMasterSecurityGroup != nil { 580 attrs["emr_managed_master_security_group"] = *ia.EmrManagedMasterSecurityGroup 581 } 582 if ia.EmrManagedSlaveSecurityGroup != nil { 583 attrs["emr_managed_slave_security_group"] = *ia.EmrManagedSlaveSecurityGroup 584 } 585 586 if len(ia.AdditionalMasterSecurityGroups) > 0 { 587 strs := aws.StringValueSlice(ia.AdditionalMasterSecurityGroups) 588 attrs["additional_master_security_groups"] = strings.Join(strs, ",") 589 } 590 if len(ia.AdditionalSlaveSecurityGroups) > 0 { 591 strs := aws.StringValueSlice(ia.AdditionalSlaveSecurityGroups) 592 attrs["additional_slave_security_groups"] = strings.Join(strs, ",") 593 } 594 595 if ia.ServiceAccessSecurityGroup != nil { 596 attrs["service_access_security_group"] = *ia.ServiceAccessSecurityGroup 597 } 598 599 result = append(result, attrs) 600 601 return result 602 } 603 604 func flattenBootstrapArguments(actions []*emr.Command) []map[string]interface{} { 605 result := make([]map[string]interface{}, 0) 606 607 for _, b := range actions { 608 attrs := make(map[string]interface{}) 609 attrs["name"] = *b.Name 610 attrs["path"] = *b.ScriptPath 611 attrs["args"] = flattenStringList(b.Args) 612 result = append(result, attrs) 613 } 614 615 return result 616 } 617 618 func loadGroups(d *schema.ResourceData, meta interface{}) ([]*emr.InstanceGroup, error) { 619 emrconn := meta.(*AWSClient).emrconn 620 reqGrps := &emr.ListInstanceGroupsInput{ 621 ClusterId: aws.String(d.Id()), 622 } 623 624 respGrps, errGrps := emrconn.ListInstanceGroups(reqGrps) 625 if errGrps != nil { 626 return nil, fmt.Errorf("Error reading EMR cluster: %s", errGrps) 627 } 628 return respGrps.InstanceGroups, nil 629 } 630 631 func findGroup(grps []*emr.InstanceGroup, typ string) *emr.InstanceGroup { 632 for _, grp := range grps { 633 if grp.InstanceGroupType != nil { 634 if *grp.InstanceGroupType == typ { 635 return grp 636 } 637 } 638 } 639 return nil 640 } 641 642 func expandTags(m map[string]interface{}) []*emr.Tag { 643 var result []*emr.Tag 644 for k, v := range m { 645 result = append(result, &emr.Tag{ 646 Key: aws.String(k), 647 Value: aws.String(v.(string)), 648 }) 649 } 650 651 return result 652 } 653 654 func tagsToMapEMR(ts []*emr.Tag) map[string]string { 655 result := make(map[string]string) 656 for _, t := range ts { 657 result[*t.Key] = *t.Value 658 } 659 660 return result 661 } 662 663 func diffTagsEMR(oldTags, newTags []*emr.Tag) ([]*emr.Tag, []*emr.Tag) { 664 // First, we're creating everything we have 665 create := make(map[string]interface{}) 666 for _, t := range newTags { 667 create[*t.Key] = *t.Value 668 } 669 670 // Build the list of what to remove 671 var remove []*emr.Tag 672 for _, t := range oldTags { 673 old, ok := create[*t.Key] 674 if !ok || old != *t.Value { 675 // Delete it! 676 remove = append(remove, t) 677 } 678 } 679 680 return expandTags(create), remove 681 } 682 683 func setTagsEMR(conn *emr.EMR, d *schema.ResourceData) error { 684 if d.HasChange("tags") { 685 oraw, nraw := d.GetChange("tags") 686 o := oraw.(map[string]interface{}) 687 n := nraw.(map[string]interface{}) 688 create, remove := diffTagsEMR(expandTags(o), expandTags(n)) 689 690 // Set tags 691 if len(remove) > 0 { 692 log.Printf("[DEBUG] Removing tags: %s", remove) 693 k := make([]*string, len(remove), len(remove)) 694 for i, t := range remove { 695 k[i] = t.Key 696 } 697 698 _, err := conn.RemoveTags(&emr.RemoveTagsInput{ 699 ResourceId: aws.String(d.Id()), 700 TagKeys: k, 701 }) 702 if err != nil { 703 return err 704 } 705 } 706 if len(create) > 0 { 707 log.Printf("[DEBUG] Creating tags: %s", create) 708 _, err := conn.AddTags(&emr.AddTagsInput{ 709 ResourceId: aws.String(d.Id()), 710 Tags: create, 711 }) 712 if err != nil { 713 return err 714 } 715 } 716 } 717 718 return nil 719 } 720 721 func expandBootstrapActions(bootstrapActions []interface{}) []*emr.BootstrapActionConfig { 722 actionsOut := []*emr.BootstrapActionConfig{} 723 724 for _, raw := range bootstrapActions { 725 actionAttributes := raw.(map[string]interface{}) 726 actionName := actionAttributes["name"].(string) 727 actionPath := actionAttributes["path"].(string) 728 actionArgs := actionAttributes["args"].([]interface{}) 729 730 action := &emr.BootstrapActionConfig{ 731 Name: aws.String(actionName), 732 ScriptBootstrapAction: &emr.ScriptBootstrapActionConfig{ 733 Path: aws.String(actionPath), 734 Args: expandStringList(actionArgs), 735 }, 736 } 737 actionsOut = append(actionsOut, action) 738 } 739 740 return actionsOut 741 } 742 743 func expandConfigures(input string) []*emr.Configuration { 744 configsOut := []*emr.Configuration{} 745 if strings.HasPrefix(input, "http") { 746 if err := readHttpJson(input, &configsOut); err != nil { 747 log.Printf("[ERR] Error reading HTTP JSON: %s", err) 748 } 749 } else if strings.HasSuffix(input, ".json") { 750 if err := readLocalJson(input, &configsOut); err != nil { 751 log.Printf("[ERR] Error reading local JSON: %s", err) 752 } 753 } else { 754 if err := readBodyJson(input, &configsOut); err != nil { 755 log.Printf("[ERR] Error reading body JSON: %s", err) 756 } 757 } 758 log.Printf("[DEBUG] Expanded EMR Configurations %s", configsOut) 759 760 return configsOut 761 } 762 763 func readHttpJson(url string, target interface{}) error { 764 r, err := http.Get(url) 765 if err != nil { 766 return err 767 } 768 defer r.Body.Close() 769 770 return json.NewDecoder(r.Body).Decode(target) 771 } 772 773 func readLocalJson(localFile string, target interface{}) error { 774 file, e := ioutil.ReadFile(localFile) 775 if e != nil { 776 log.Printf("[ERROR] %s", e) 777 return e 778 } 779 780 return json.Unmarshal(file, target) 781 } 782 783 func readBodyJson(body string, target interface{}) error { 784 log.Printf("[DEBUG] Raw Body %s\n", body) 785 err := json.Unmarshal([]byte(body), target) 786 if err != nil { 787 log.Printf("[ERROR] parsing JSON %s", err) 788 return err 789 } 790 return nil 791 } 792 793 func resourceAwsEMRClusterStateRefreshFunc(d *schema.ResourceData, meta interface{}) resource.StateRefreshFunc { 794 return func() (interface{}, string, error) { 795 conn := meta.(*AWSClient).emrconn 796 797 log.Printf("[INFO] Reading EMR Cluster Information: %s", d.Id()) 798 params := &emr.DescribeClusterInput{ 799 ClusterId: aws.String(d.Id()), 800 } 801 802 resp, err := conn.DescribeCluster(params) 803 804 if err != nil { 805 if awsErr, ok := err.(awserr.Error); ok { 806 if "ClusterNotFound" == awsErr.Code() { 807 return 42, "destroyed", nil 808 } 809 } 810 log.Printf("[WARN] Error on retrieving EMR Cluster (%s) when waiting: %s", d.Id(), err) 811 return nil, "", err 812 } 813 814 emrc := resp.Cluster 815 816 if emrc == nil { 817 return 42, "destroyed", nil 818 } 819 820 if resp.Cluster.Status != nil { 821 log.Printf("[DEBUG] EMR Cluster status (%s): %s", d.Id(), *resp.Cluster.Status) 822 } 823 824 return emrc, *emrc.Status.State, nil 825 } 826 }