github.com/mohanarpit/terraform@v0.6.16-0.20160909104007-291f29853544/builtin/providers/aws/resource_aws_elastic_beanstalk_environment.go (about) 1 package aws 2 3 import ( 4 "fmt" 5 "log" 6 "regexp" 7 "sort" 8 "strings" 9 "time" 10 11 "github.com/hashicorp/terraform/helper/hashcode" 12 "github.com/hashicorp/terraform/helper/resource" 13 "github.com/hashicorp/terraform/helper/schema" 14 15 "github.com/aws/aws-sdk-go/aws" 16 "github.com/aws/aws-sdk-go/service/ec2" 17 "github.com/aws/aws-sdk-go/service/elasticbeanstalk" 18 ) 19 20 func resourceAwsElasticBeanstalkOptionSetting() *schema.Resource { 21 return &schema.Resource{ 22 Schema: map[string]*schema.Schema{ 23 "namespace": &schema.Schema{ 24 Type: schema.TypeString, 25 Required: true, 26 }, 27 "name": &schema.Schema{ 28 Type: schema.TypeString, 29 Required: true, 30 }, 31 "value": &schema.Schema{ 32 Type: schema.TypeString, 33 Required: true, 34 }, 35 "resource": &schema.Schema{ 36 Type: schema.TypeString, 37 Optional: true, 38 }, 39 }, 40 } 41 } 42 43 func resourceAwsElasticBeanstalkEnvironment() *schema.Resource { 44 return &schema.Resource{ 45 Create: resourceAwsElasticBeanstalkEnvironmentCreate, 46 Read: resourceAwsElasticBeanstalkEnvironmentRead, 47 Update: resourceAwsElasticBeanstalkEnvironmentUpdate, 48 Delete: resourceAwsElasticBeanstalkEnvironmentDelete, 49 Importer: &schema.ResourceImporter{ 50 State: schema.ImportStatePassthrough, 51 }, 52 53 SchemaVersion: 1, 54 MigrateState: resourceAwsElasticBeanstalkEnvironmentMigrateState, 55 56 Schema: map[string]*schema.Schema{ 57 "name": &schema.Schema{ 58 Type: schema.TypeString, 59 Required: true, 60 ForceNew: true, 61 }, 62 "application": &schema.Schema{ 63 Type: schema.TypeString, 64 Required: true, 65 }, 66 "description": &schema.Schema{ 67 Type: schema.TypeString, 68 Optional: true, 69 }, 70 "cname": &schema.Schema{ 71 Type: schema.TypeString, 72 Computed: true, 73 }, 74 "cname_prefix": &schema.Schema{ 75 Type: schema.TypeString, 76 Computed: true, 77 Optional: true, 78 ForceNew: true, 79 }, 80 "tier": &schema.Schema{ 81 Type: schema.TypeString, 82 Optional: true, 83 Default: "WebServer", 84 ValidateFunc: func(v interface{}, k string) (ws []string, errors []error) { 85 value := v.(string) 86 switch value { 87 case 88 "Worker", 89 "WebServer": 90 return 91 } 92 errors = append(errors, fmt.Errorf("%s is not a valid tier. Valid options are WebServer or Worker", value)) 93 return 94 }, 95 ForceNew: true, 96 }, 97 "setting": &schema.Schema{ 98 Type: schema.TypeSet, 99 Optional: true, 100 Elem: resourceAwsElasticBeanstalkOptionSetting(), 101 Set: optionSettingValueHash, 102 }, 103 "all_settings": &schema.Schema{ 104 Type: schema.TypeSet, 105 Computed: true, 106 Elem: resourceAwsElasticBeanstalkOptionSetting(), 107 Set: optionSettingValueHash, 108 }, 109 "solution_stack_name": &schema.Schema{ 110 Type: schema.TypeString, 111 Optional: true, 112 Computed: true, 113 ConflictsWith: []string{"template_name"}, 114 }, 115 "template_name": &schema.Schema{ 116 Type: schema.TypeString, 117 Optional: true, 118 }, 119 "wait_for_ready_timeout": &schema.Schema{ 120 Type: schema.TypeString, 121 Optional: true, 122 Default: "10m", 123 ValidateFunc: func(v interface{}, k string) (ws []string, errors []error) { 124 value := v.(string) 125 duration, err := time.ParseDuration(value) 126 if err != nil { 127 errors = append(errors, fmt.Errorf( 128 "%q cannot be parsed as a duration: %s", k, err)) 129 } 130 if duration < 0 { 131 errors = append(errors, fmt.Errorf( 132 "%q must be greater than zero", k)) 133 } 134 return 135 }, 136 }, 137 "poll_interval": &schema.Schema{ 138 Type: schema.TypeString, 139 Optional: true, 140 ValidateFunc: func(v interface{}, k string) (ws []string, errors []error) { 141 value := v.(string) 142 duration, err := time.ParseDuration(value) 143 if err != nil { 144 errors = append(errors, fmt.Errorf( 145 "%q cannot be parsed as a duration: %s", k, err)) 146 } 147 if duration < 10*time.Second || duration > 60*time.Second { 148 errors = append(errors, fmt.Errorf( 149 "%q must be between 10s and 180s", k)) 150 } 151 return 152 }, 153 }, 154 "autoscaling_groups": &schema.Schema{ 155 Type: schema.TypeList, 156 Computed: true, 157 Elem: &schema.Schema{Type: schema.TypeString}, 158 }, 159 "instances": &schema.Schema{ 160 Type: schema.TypeList, 161 Computed: true, 162 Elem: &schema.Schema{Type: schema.TypeString}, 163 }, 164 "launch_configurations": &schema.Schema{ 165 Type: schema.TypeList, 166 Computed: true, 167 Elem: &schema.Schema{Type: schema.TypeString}, 168 }, 169 "load_balancers": &schema.Schema{ 170 Type: schema.TypeList, 171 Computed: true, 172 Elem: &schema.Schema{Type: schema.TypeString}, 173 }, 174 "queues": &schema.Schema{ 175 Type: schema.TypeList, 176 Computed: true, 177 Elem: &schema.Schema{Type: schema.TypeString}, 178 }, 179 "triggers": &schema.Schema{ 180 Type: schema.TypeList, 181 Computed: true, 182 Elem: &schema.Schema{Type: schema.TypeString}, 183 }, 184 185 "tags": tagsSchema(), 186 }, 187 } 188 } 189 190 func resourceAwsElasticBeanstalkEnvironmentCreate(d *schema.ResourceData, meta interface{}) error { 191 conn := meta.(*AWSClient).elasticbeanstalkconn 192 193 // Get values from config 194 name := d.Get("name").(string) 195 cnamePrefix := d.Get("cname_prefix").(string) 196 tier := d.Get("tier").(string) 197 app := d.Get("application").(string) 198 desc := d.Get("description").(string) 199 settings := d.Get("setting").(*schema.Set) 200 solutionStack := d.Get("solution_stack_name").(string) 201 templateName := d.Get("template_name").(string) 202 203 // TODO set tags 204 // Note: at time of writing, you cannot view or edit Tags after creation 205 // d.Set("tags", tagsToMap(instance.Tags)) 206 createOpts := elasticbeanstalk.CreateEnvironmentInput{ 207 EnvironmentName: aws.String(name), 208 ApplicationName: aws.String(app), 209 OptionSettings: extractOptionSettings(settings), 210 Tags: tagsFromMapBeanstalk(d.Get("tags").(map[string]interface{})), 211 } 212 213 if desc != "" { 214 createOpts.Description = aws.String(desc) 215 } 216 217 if cnamePrefix != "" { 218 if tier != "WebServer" { 219 return fmt.Errorf("Cannont set cname_prefix for tier: %s.", tier) 220 } 221 createOpts.CNAMEPrefix = aws.String(cnamePrefix) 222 } 223 224 if tier != "" { 225 var tierType string 226 227 switch tier { 228 case "WebServer": 229 tierType = "Standard" 230 case "Worker": 231 tierType = "SQS/HTTP" 232 } 233 environmentTier := elasticbeanstalk.EnvironmentTier{ 234 Name: aws.String(tier), 235 Type: aws.String(tierType), 236 } 237 createOpts.Tier = &environmentTier 238 } 239 240 if solutionStack != "" { 241 createOpts.SolutionStackName = aws.String(solutionStack) 242 } 243 244 if templateName != "" { 245 createOpts.TemplateName = aws.String(templateName) 246 } 247 248 // Get the current time to filter describeBeanstalkEvents messages 249 t := time.Now() 250 log.Printf("[DEBUG] Elastic Beanstalk Environment create opts: %s", createOpts) 251 resp, err := conn.CreateEnvironment(&createOpts) 252 if err != nil { 253 return err 254 } 255 256 // Assign the application name as the resource ID 257 d.SetId(*resp.EnvironmentId) 258 259 waitForReadyTimeOut, err := time.ParseDuration(d.Get("wait_for_ready_timeout").(string)) 260 if err != nil { 261 return err 262 } 263 264 pollInterval, err := time.ParseDuration(d.Get("poll_interval").(string)) 265 if err != nil { 266 pollInterval = 0 267 log.Printf("[WARN] Error parsing poll_interval, using default backoff") 268 } 269 270 stateConf := &resource.StateChangeConf{ 271 Pending: []string{"Launching", "Updating"}, 272 Target: []string{"Ready"}, 273 Refresh: environmentStateRefreshFunc(conn, d.Id()), 274 Timeout: waitForReadyTimeOut, 275 Delay: 10 * time.Second, 276 PollInterval: pollInterval, 277 MinTimeout: 3 * time.Second, 278 } 279 280 _, err = stateConf.WaitForState() 281 if err != nil { 282 return fmt.Errorf( 283 "Error waiting for Elastic Beanstalk Environment (%s) to become ready: %s", 284 d.Id(), err) 285 } 286 287 err = describeBeanstalkEvents(conn, d.Id(), t) 288 if err != nil { 289 return err 290 } 291 292 return resourceAwsElasticBeanstalkEnvironmentRead(d, meta) 293 } 294 295 func resourceAwsElasticBeanstalkEnvironmentUpdate(d *schema.ResourceData, meta interface{}) error { 296 conn := meta.(*AWSClient).elasticbeanstalkconn 297 298 envId := d.Id() 299 300 var hasChange bool 301 302 updateOpts := elasticbeanstalk.UpdateEnvironmentInput{ 303 EnvironmentId: aws.String(envId), 304 } 305 306 if d.HasChange("description") { 307 hasChange = true 308 updateOpts.Description = aws.String(d.Get("description").(string)) 309 } 310 311 if d.HasChange("solution_stack_name") { 312 hasChange = true 313 if v, ok := d.GetOk("solution_stack_name"); ok { 314 updateOpts.SolutionStackName = aws.String(v.(string)) 315 } 316 } 317 318 if d.HasChange("setting") { 319 hasChange = true 320 o, n := d.GetChange("setting") 321 if o == nil { 322 o = &schema.Set{F: optionSettingValueHash} 323 } 324 if n == nil { 325 n = &schema.Set{F: optionSettingValueHash} 326 } 327 328 os := o.(*schema.Set) 329 ns := n.(*schema.Set) 330 331 rm := extractOptionSettings(os.Difference(ns)) 332 add := extractOptionSettings(ns.Difference(os)) 333 334 // Additions and removals of options are done in a single API call, so we 335 // can't do our normal "remove these" and then later "add these", re-adding 336 // any updated settings. 337 // Because of this, we need to remove any settings in the "removable" 338 // settings that are also found in the "add" settings, otherwise they 339 // conflict. Here we loop through all the initial removables from the set 340 // difference, and we build up a slice of settings not found in the "add" 341 // set 342 var remove []*elasticbeanstalk.ConfigurationOptionSetting 343 if len(add) > 0 { 344 for _, r := range rm { 345 for _, a := range add { 346 // ResourceNames are optional. Some defaults come with it, some do 347 // not. We need to guard against nil/empty in state as well as 348 // nil/empty from the API 349 if a.ResourceName != nil { 350 if r.ResourceName == nil { 351 continue 352 } 353 if *r.ResourceName != *a.ResourceName { 354 continue 355 } 356 } 357 if *r.Namespace == *a.Namespace && *r.OptionName == *a.OptionName { 358 continue 359 } 360 remove = append(remove, r) 361 } 362 } 363 } else { 364 remove = rm 365 } 366 367 for _, elem := range remove { 368 updateOpts.OptionsToRemove = append(updateOpts.OptionsToRemove, &elasticbeanstalk.OptionSpecification{ 369 Namespace: elem.Namespace, 370 OptionName: elem.OptionName, 371 }) 372 } 373 374 updateOpts.OptionSettings = add 375 } 376 377 if d.HasChange("template_name") { 378 hasChange = true 379 if v, ok := d.GetOk("template_name"); ok { 380 updateOpts.TemplateName = aws.String(v.(string)) 381 } 382 } 383 384 if hasChange { 385 // Get the current time to filter describeBeanstalkEvents messages 386 t := time.Now() 387 log.Printf("[DEBUG] Elastic Beanstalk Environment update opts: %s", updateOpts) 388 _, err := conn.UpdateEnvironment(&updateOpts) 389 if err != nil { 390 return err 391 } 392 393 waitForReadyTimeOut, err := time.ParseDuration(d.Get("wait_for_ready_timeout").(string)) 394 if err != nil { 395 return err 396 } 397 pollInterval, err := time.ParseDuration(d.Get("poll_interval").(string)) 398 if err != nil { 399 pollInterval = 0 400 log.Printf("[WARN] Error parsing poll_interval, using default backoff") 401 } 402 403 stateConf := &resource.StateChangeConf{ 404 Pending: []string{"Launching", "Updating"}, 405 Target: []string{"Ready"}, 406 Refresh: environmentStateRefreshFunc(conn, d.Id()), 407 Timeout: waitForReadyTimeOut, 408 Delay: 10 * time.Second, 409 PollInterval: pollInterval, 410 MinTimeout: 3 * time.Second, 411 } 412 413 _, err = stateConf.WaitForState() 414 if err != nil { 415 return fmt.Errorf( 416 "Error waiting for Elastic Beanstalk Environment (%s) to become ready: %s", 417 d.Id(), err) 418 } 419 420 err = describeBeanstalkEvents(conn, d.Id(), t) 421 if err != nil { 422 return err 423 } 424 } 425 426 return resourceAwsElasticBeanstalkEnvironmentRead(d, meta) 427 } 428 429 func resourceAwsElasticBeanstalkEnvironmentRead(d *schema.ResourceData, meta interface{}) error { 430 conn := meta.(*AWSClient).elasticbeanstalkconn 431 432 envId := d.Id() 433 434 log.Printf("[DEBUG] Elastic Beanstalk environment read %s: id %s", d.Get("name").(string), d.Id()) 435 436 resp, err := conn.DescribeEnvironments(&elasticbeanstalk.DescribeEnvironmentsInput{ 437 EnvironmentIds: []*string{aws.String(envId)}, 438 }) 439 440 if err != nil { 441 return err 442 } 443 444 if len(resp.Environments) == 0 { 445 log.Printf("[DEBUG] Elastic Beanstalk environment properties: could not find environment %s", d.Id()) 446 447 d.SetId("") 448 return nil 449 } else if len(resp.Environments) != 1 { 450 return fmt.Errorf("Error reading application properties: found %d environments, expected 1", len(resp.Environments)) 451 } 452 453 env := resp.Environments[0] 454 455 if *env.Status == "Terminated" { 456 log.Printf("[DEBUG] Elastic Beanstalk environment %s was terminated", d.Id()) 457 458 d.SetId("") 459 return nil 460 } 461 462 resources, err := conn.DescribeEnvironmentResources(&elasticbeanstalk.DescribeEnvironmentResourcesInput{ 463 EnvironmentId: aws.String(envId), 464 }) 465 466 if err != nil { 467 return err 468 } 469 470 if err := d.Set("name", env.EnvironmentName); err != nil { 471 return err 472 } 473 474 if err := d.Set("application", env.ApplicationName); err != nil { 475 return err 476 } 477 478 if err := d.Set("description", env.Description); err != nil { 479 return err 480 } 481 482 if err := d.Set("cname", env.CNAME); err != nil { 483 return err 484 } 485 486 if err := d.Set("tier", *env.Tier.Name); err != nil { 487 return err 488 } 489 490 if env.CNAME != nil { 491 beanstalkCnamePrefixRegexp := regexp.MustCompile(`(^[^.]+)(.\w{2}-\w{4,9}-\d)?.elasticbeanstalk.com$`) 492 var cnamePrefix string 493 cnamePrefixMatch := beanstalkCnamePrefixRegexp.FindStringSubmatch(*env.CNAME) 494 495 if cnamePrefixMatch == nil { 496 cnamePrefix = "" 497 } else { 498 cnamePrefix = cnamePrefixMatch[1] 499 } 500 501 if err := d.Set("cname_prefix", cnamePrefix); err != nil { 502 return err 503 } 504 } else { 505 if err := d.Set("cname_prefix", ""); err != nil { 506 return err 507 } 508 } 509 510 if err := d.Set("solution_stack_name", env.SolutionStackName); err != nil { 511 return err 512 } 513 514 if err := d.Set("autoscaling_groups", flattenBeanstalkAsg(resources.EnvironmentResources.AutoScalingGroups)); err != nil { 515 return err 516 } 517 518 if err := d.Set("instances", flattenBeanstalkInstances(resources.EnvironmentResources.Instances)); err != nil { 519 return err 520 } 521 if err := d.Set("launch_configurations", flattenBeanstalkLc(resources.EnvironmentResources.LaunchConfigurations)); err != nil { 522 return err 523 } 524 if err := d.Set("load_balancers", flattenBeanstalkElb(resources.EnvironmentResources.LoadBalancers)); err != nil { 525 return err 526 } 527 if err := d.Set("queues", flattenBeanstalkSqs(resources.EnvironmentResources.Queues)); err != nil { 528 return err 529 } 530 if err := d.Set("triggers", flattenBeanstalkTrigger(resources.EnvironmentResources.Triggers)); err != nil { 531 return err 532 } 533 534 return resourceAwsElasticBeanstalkEnvironmentSettingsRead(d, meta) 535 } 536 537 func fetchAwsElasticBeanstalkEnvironmentSettings(d *schema.ResourceData, meta interface{}) (*schema.Set, error) { 538 conn := meta.(*AWSClient).elasticbeanstalkconn 539 540 app := d.Get("application").(string) 541 name := d.Get("name").(string) 542 543 resp, err := conn.DescribeConfigurationSettings(&elasticbeanstalk.DescribeConfigurationSettingsInput{ 544 ApplicationName: aws.String(app), 545 EnvironmentName: aws.String(name), 546 }) 547 548 if err != nil { 549 return nil, err 550 } 551 552 if len(resp.ConfigurationSettings) != 1 { 553 return nil, fmt.Errorf("Error reading environment settings: received %d settings groups, expected 1", len(resp.ConfigurationSettings)) 554 } 555 556 settings := &schema.Set{F: optionSettingValueHash} 557 for _, optionSetting := range resp.ConfigurationSettings[0].OptionSettings { 558 m := map[string]interface{}{} 559 560 if optionSetting.Namespace != nil { 561 m["namespace"] = *optionSetting.Namespace 562 } else { 563 return nil, fmt.Errorf("Error reading environment settings: option setting with no namespace: %v", optionSetting) 564 } 565 566 if optionSetting.OptionName != nil { 567 m["name"] = *optionSetting.OptionName 568 } else { 569 return nil, fmt.Errorf("Error reading environment settings: option setting with no name: %v", optionSetting) 570 } 571 572 if *optionSetting.Namespace == "aws:autoscaling:scheduledaction" && optionSetting.ResourceName != nil { 573 m["resource"] = *optionSetting.ResourceName 574 } 575 576 if optionSetting.Value != nil { 577 switch *optionSetting.OptionName { 578 case "SecurityGroups": 579 m["value"] = dropGeneratedSecurityGroup(*optionSetting.Value, meta) 580 case "Subnets", "ELBSubnets": 581 m["value"] = sortValues(*optionSetting.Value) 582 default: 583 m["value"] = *optionSetting.Value 584 } 585 } 586 587 settings.Add(m) 588 } 589 590 return settings, nil 591 } 592 593 func resourceAwsElasticBeanstalkEnvironmentSettingsRead(d *schema.ResourceData, meta interface{}) error { 594 log.Printf("[DEBUG] Elastic Beanstalk environment settings read %s: id %s", d.Get("name").(string), d.Id()) 595 596 allSettings, err := fetchAwsElasticBeanstalkEnvironmentSettings(d, meta) 597 if err != nil { 598 return err 599 } 600 601 settings := d.Get("setting").(*schema.Set) 602 603 log.Printf("[DEBUG] Elastic Beanstalk allSettings: %s", allSettings.GoString()) 604 log.Printf("[DEBUG] Elastic Beanstalk settings: %s", settings.GoString()) 605 606 // perform the set operation with only name/namespace as keys, excluding value 607 // this is so we override things in the settings resource data key with updated values 608 // from the api. we skip values we didn't know about before because there are so many 609 // defaults set by the eb api that we would delete many useful defaults. 610 // 611 // there is likely a better way to do this 612 allSettingsKeySet := schema.NewSet(optionSettingKeyHash, allSettings.List()) 613 settingsKeySet := schema.NewSet(optionSettingKeyHash, settings.List()) 614 updatedSettingsKeySet := allSettingsKeySet.Intersection(settingsKeySet) 615 616 log.Printf("[DEBUG] Elastic Beanstalk updatedSettingsKeySet: %s", updatedSettingsKeySet.GoString()) 617 618 updatedSettings := schema.NewSet(optionSettingValueHash, updatedSettingsKeySet.List()) 619 620 log.Printf("[DEBUG] Elastic Beanstalk updatedSettings: %s", updatedSettings.GoString()) 621 622 if err := d.Set("all_settings", allSettings.List()); err != nil { 623 return err 624 } 625 626 if err := d.Set("setting", updatedSettings.List()); err != nil { 627 return err 628 } 629 630 return nil 631 } 632 633 func resourceAwsElasticBeanstalkEnvironmentDelete(d *schema.ResourceData, meta interface{}) error { 634 conn := meta.(*AWSClient).elasticbeanstalkconn 635 636 opts := elasticbeanstalk.TerminateEnvironmentInput{ 637 EnvironmentId: aws.String(d.Id()), 638 TerminateResources: aws.Bool(true), 639 } 640 641 // Get the current time to filter describeBeanstalkEvents messages 642 t := time.Now() 643 log.Printf("[DEBUG] Elastic Beanstalk Environment terminate opts: %s", opts) 644 _, err := conn.TerminateEnvironment(&opts) 645 646 if err != nil { 647 return err 648 } 649 650 waitForReadyTimeOut, err := time.ParseDuration(d.Get("wait_for_ready_timeout").(string)) 651 if err != nil { 652 return err 653 } 654 pollInterval, err := time.ParseDuration(d.Get("poll_interval").(string)) 655 if err != nil { 656 pollInterval = 0 657 log.Printf("[WARN] Error parsing poll_interval, using default backoff") 658 } 659 660 stateConf := &resource.StateChangeConf{ 661 Pending: []string{"Terminating"}, 662 Target: []string{"Terminated"}, 663 Refresh: environmentStateRefreshFunc(conn, d.Id()), 664 Timeout: waitForReadyTimeOut, 665 Delay: 10 * time.Second, 666 PollInterval: pollInterval, 667 MinTimeout: 3 * time.Second, 668 } 669 670 _, err = stateConf.WaitForState() 671 if err != nil { 672 return fmt.Errorf( 673 "Error waiting for Elastic Beanstalk Environment (%s) to become terminated: %s", 674 d.Id(), err) 675 } 676 677 err = describeBeanstalkEvents(conn, d.Id(), t) 678 if err != nil { 679 return err 680 } 681 682 return nil 683 } 684 685 // environmentStateRefreshFunc returns a resource.StateRefreshFunc that is used to watch 686 // the creation of the Beanstalk Environment 687 func environmentStateRefreshFunc(conn *elasticbeanstalk.ElasticBeanstalk, environmentId string) resource.StateRefreshFunc { 688 return func() (interface{}, string, error) { 689 resp, err := conn.DescribeEnvironments(&elasticbeanstalk.DescribeEnvironmentsInput{ 690 EnvironmentIds: []*string{aws.String(environmentId)}, 691 }) 692 if err != nil { 693 log.Printf("[Err] Error waiting for Elastic Beanstalk Environment state: %s", err) 694 return -1, "failed", fmt.Errorf("[Err] Error waiting for Elastic Beanstalk Environment state: %s", err) 695 } 696 697 if resp == nil || len(resp.Environments) == 0 { 698 // Sometimes AWS just has consistency issues and doesn't see 699 // our instance yet. Return an empty state. 700 return nil, "", nil 701 } 702 703 var env *elasticbeanstalk.EnvironmentDescription 704 for _, e := range resp.Environments { 705 if environmentId == *e.EnvironmentId { 706 env = e 707 } 708 } 709 710 if env == nil { 711 return -1, "failed", fmt.Errorf("[Err] Error finding Elastic Beanstalk Environment, environment not found") 712 } 713 714 return env, *env.Status, nil 715 } 716 } 717 718 // we use the following two functions to allow us to split out defaults 719 // as they become overridden from within the template 720 func optionSettingValueHash(v interface{}) int { 721 rd := v.(map[string]interface{}) 722 namespace := rd["namespace"].(string) 723 optionName := rd["name"].(string) 724 var resourceName string 725 if v, ok := rd["resource"].(string); ok { 726 resourceName = v 727 } 728 value, _ := rd["value"].(string) 729 hk := fmt.Sprintf("%s:%s%s=%s", namespace, optionName, resourceName, sortValues(value)) 730 log.Printf("[DEBUG] Elastic Beanstalk optionSettingValueHash(%#v): %s: hk=%s,hc=%d", v, optionName, hk, hashcode.String(hk)) 731 return hashcode.String(hk) 732 } 733 734 func optionSettingKeyHash(v interface{}) int { 735 rd := v.(map[string]interface{}) 736 namespace := rd["namespace"].(string) 737 optionName := rd["name"].(string) 738 var resourceName string 739 if v, ok := rd["resource"].(string); ok { 740 resourceName = v 741 } 742 hk := fmt.Sprintf("%s:%s%s", namespace, optionName, resourceName) 743 log.Printf("[DEBUG] Elastic Beanstalk optionSettingKeyHash(%#v): %s: hk=%s,hc=%d", v, optionName, hk, hashcode.String(hk)) 744 return hashcode.String(hk) 745 } 746 747 func sortValues(v string) string { 748 values := strings.Split(v, ",") 749 sort.Strings(values) 750 return strings.Join(values, ",") 751 } 752 753 func extractOptionSettings(s *schema.Set) []*elasticbeanstalk.ConfigurationOptionSetting { 754 settings := []*elasticbeanstalk.ConfigurationOptionSetting{} 755 756 if s != nil { 757 for _, setting := range s.List() { 758 optionSetting := elasticbeanstalk.ConfigurationOptionSetting{ 759 Namespace: aws.String(setting.(map[string]interface{})["namespace"].(string)), 760 OptionName: aws.String(setting.(map[string]interface{})["name"].(string)), 761 Value: aws.String(setting.(map[string]interface{})["value"].(string)), 762 } 763 if *optionSetting.Namespace == "aws:autoscaling:scheduledaction" { 764 if v, ok := setting.(map[string]interface{})["resource"].(string); ok && v != "" { 765 optionSetting.ResourceName = aws.String(v) 766 } 767 } 768 settings = append(settings, &optionSetting) 769 } 770 } 771 772 return settings 773 } 774 775 func dropGeneratedSecurityGroup(settingValue string, meta interface{}) string { 776 conn := meta.(*AWSClient).ec2conn 777 778 groups := strings.Split(settingValue, ",") 779 780 // Check to see if groups are ec2-classic or vpc security groups 781 ec2Classic := true 782 beanstalkSGRegexp := "sg-[0-9a-fA-F]{8}" 783 for _, g := range groups { 784 if ok, _ := regexp.MatchString(beanstalkSGRegexp, g); ok { 785 ec2Classic = false 786 break 787 } 788 } 789 790 var resp *ec2.DescribeSecurityGroupsOutput 791 var err error 792 793 if ec2Classic { 794 resp, err = conn.DescribeSecurityGroups(&ec2.DescribeSecurityGroupsInput{ 795 GroupNames: aws.StringSlice(groups), 796 }) 797 } else { 798 resp, err = conn.DescribeSecurityGroups(&ec2.DescribeSecurityGroupsInput{ 799 GroupIds: aws.StringSlice(groups), 800 }) 801 } 802 803 if err != nil { 804 log.Printf("[DEBUG] Elastic Beanstalk error describing SecurityGroups: %v", err) 805 return settingValue 806 } 807 808 log.Printf("[DEBUG] Elastic Beanstalk using ec2-classic security-groups: %t", ec2Classic) 809 var legitGroups []string 810 for _, group := range resp.SecurityGroups { 811 log.Printf("[DEBUG] Elastic Beanstalk SecurityGroup: %v", *group.GroupName) 812 if !strings.HasPrefix(*group.GroupName, "awseb") { 813 if ec2Classic { 814 legitGroups = append(legitGroups, *group.GroupName) 815 } else { 816 legitGroups = append(legitGroups, *group.GroupId) 817 } 818 } 819 } 820 821 sort.Strings(legitGroups) 822 823 return strings.Join(legitGroups, ",") 824 } 825 826 func describeBeanstalkEvents(conn *elasticbeanstalk.ElasticBeanstalk, environmentId string, t time.Time) error { 827 beanstalkErrors, err := conn.DescribeEvents(&elasticbeanstalk.DescribeEventsInput{ 828 EnvironmentId: aws.String(environmentId), 829 Severity: aws.String("ERROR"), 830 StartTime: aws.Time(t), 831 }) 832 833 if err != nil { 834 log.Printf("[Err] Unable to get Elastic Beanstalk Evironment events: %s", err) 835 } 836 837 events := "" 838 for _, event := range beanstalkErrors.Events { 839 events = events + "\n" + event.EventDate.String() + ": " + *event.Message 840 } 841 842 if events != "" { 843 return fmt.Errorf("%s", events) 844 } 845 846 return nil 847 }