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