github.com/inge4pres/terraform@v0.7.5-0.20160930053151-bd083f84f376/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 delete from the slice any items found in both `add` and 341 // `rm` above 342 if len(add) > 0 { 343 for i, r := range rm { 344 for _, a := range add { 345 // ResourceNames are optional. Some defaults come with it, some do 346 // not. We need to guard against nil/empty in state as well as 347 // nil/empty from the API 348 if a.ResourceName != nil { 349 if r.ResourceName == nil { 350 continue 351 } 352 if *r.ResourceName != *a.ResourceName { 353 continue 354 } 355 } 356 if *r.Namespace == *a.Namespace && *r.OptionName == *a.OptionName { 357 log.Printf("[DEBUG] Removing Beanstalk setting: (%s::%s)", *a.Namespace, *a.OptionName) 358 rm = append(rm[:i], rm[i+1:]...) 359 } 360 } 361 } 362 } 363 364 for _, elem := range rm { 365 updateOpts.OptionsToRemove = append(updateOpts.OptionsToRemove, &elasticbeanstalk.OptionSpecification{ 366 Namespace: elem.Namespace, 367 OptionName: elem.OptionName, 368 }) 369 } 370 371 updateOpts.OptionSettings = add 372 } 373 374 if d.HasChange("template_name") { 375 hasChange = true 376 if v, ok := d.GetOk("template_name"); ok { 377 updateOpts.TemplateName = aws.String(v.(string)) 378 } 379 } 380 381 if hasChange { 382 // Get the current time to filter describeBeanstalkEvents messages 383 t := time.Now() 384 log.Printf("[DEBUG] Elastic Beanstalk Environment update opts: %s", updateOpts) 385 _, err := conn.UpdateEnvironment(&updateOpts) 386 if err != nil { 387 return err 388 } 389 390 waitForReadyTimeOut, err := time.ParseDuration(d.Get("wait_for_ready_timeout").(string)) 391 if err != nil { 392 return err 393 } 394 pollInterval, err := time.ParseDuration(d.Get("poll_interval").(string)) 395 if err != nil { 396 pollInterval = 0 397 log.Printf("[WARN] Error parsing poll_interval, using default backoff") 398 } 399 400 stateConf := &resource.StateChangeConf{ 401 Pending: []string{"Launching", "Updating"}, 402 Target: []string{"Ready"}, 403 Refresh: environmentStateRefreshFunc(conn, d.Id()), 404 Timeout: waitForReadyTimeOut, 405 Delay: 10 * time.Second, 406 PollInterval: pollInterval, 407 MinTimeout: 3 * time.Second, 408 } 409 410 _, err = stateConf.WaitForState() 411 if err != nil { 412 return fmt.Errorf( 413 "Error waiting for Elastic Beanstalk Environment (%s) to become ready: %s", 414 d.Id(), err) 415 } 416 417 err = describeBeanstalkEvents(conn, d.Id(), t) 418 if err != nil { 419 return err 420 } 421 } 422 423 return resourceAwsElasticBeanstalkEnvironmentRead(d, meta) 424 } 425 426 func resourceAwsElasticBeanstalkEnvironmentRead(d *schema.ResourceData, meta interface{}) error { 427 conn := meta.(*AWSClient).elasticbeanstalkconn 428 429 envId := d.Id() 430 431 log.Printf("[DEBUG] Elastic Beanstalk environment read %s: id %s", d.Get("name").(string), d.Id()) 432 433 resp, err := conn.DescribeEnvironments(&elasticbeanstalk.DescribeEnvironmentsInput{ 434 EnvironmentIds: []*string{aws.String(envId)}, 435 }) 436 437 if err != nil { 438 return err 439 } 440 441 if len(resp.Environments) == 0 { 442 log.Printf("[DEBUG] Elastic Beanstalk environment properties: could not find environment %s", d.Id()) 443 444 d.SetId("") 445 return nil 446 } else if len(resp.Environments) != 1 { 447 return fmt.Errorf("Error reading application properties: found %d environments, expected 1", len(resp.Environments)) 448 } 449 450 env := resp.Environments[0] 451 452 if *env.Status == "Terminated" { 453 log.Printf("[DEBUG] Elastic Beanstalk environment %s was terminated", d.Id()) 454 455 d.SetId("") 456 return nil 457 } 458 459 resources, err := conn.DescribeEnvironmentResources(&elasticbeanstalk.DescribeEnvironmentResourcesInput{ 460 EnvironmentId: aws.String(envId), 461 }) 462 463 if err != nil { 464 return err 465 } 466 467 if err := d.Set("name", env.EnvironmentName); err != nil { 468 return err 469 } 470 471 if err := d.Set("application", env.ApplicationName); err != nil { 472 return err 473 } 474 475 if err := d.Set("description", env.Description); err != nil { 476 return err 477 } 478 479 if err := d.Set("cname", env.CNAME); err != nil { 480 return err 481 } 482 483 if err := d.Set("tier", *env.Tier.Name); err != nil { 484 return err 485 } 486 487 if env.CNAME != nil { 488 beanstalkCnamePrefixRegexp := regexp.MustCompile(`(^[^.]+)(.\w{2}-\w{4,9}-\d)?.elasticbeanstalk.com$`) 489 var cnamePrefix string 490 cnamePrefixMatch := beanstalkCnamePrefixRegexp.FindStringSubmatch(*env.CNAME) 491 492 if cnamePrefixMatch == nil { 493 cnamePrefix = "" 494 } else { 495 cnamePrefix = cnamePrefixMatch[1] 496 } 497 498 if err := d.Set("cname_prefix", cnamePrefix); err != nil { 499 return err 500 } 501 } else { 502 if err := d.Set("cname_prefix", ""); err != nil { 503 return err 504 } 505 } 506 507 if err := d.Set("solution_stack_name", env.SolutionStackName); err != nil { 508 return err 509 } 510 511 if err := d.Set("autoscaling_groups", flattenBeanstalkAsg(resources.EnvironmentResources.AutoScalingGroups)); err != nil { 512 return err 513 } 514 515 if err := d.Set("instances", flattenBeanstalkInstances(resources.EnvironmentResources.Instances)); err != nil { 516 return err 517 } 518 if err := d.Set("launch_configurations", flattenBeanstalkLc(resources.EnvironmentResources.LaunchConfigurations)); err != nil { 519 return err 520 } 521 if err := d.Set("load_balancers", flattenBeanstalkElb(resources.EnvironmentResources.LoadBalancers)); err != nil { 522 return err 523 } 524 if err := d.Set("queues", flattenBeanstalkSqs(resources.EnvironmentResources.Queues)); err != nil { 525 return err 526 } 527 if err := d.Set("triggers", flattenBeanstalkTrigger(resources.EnvironmentResources.Triggers)); err != nil { 528 return err 529 } 530 531 return resourceAwsElasticBeanstalkEnvironmentSettingsRead(d, meta) 532 } 533 534 func fetchAwsElasticBeanstalkEnvironmentSettings(d *schema.ResourceData, meta interface{}) (*schema.Set, error) { 535 conn := meta.(*AWSClient).elasticbeanstalkconn 536 537 app := d.Get("application").(string) 538 name := d.Get("name").(string) 539 540 resp, err := conn.DescribeConfigurationSettings(&elasticbeanstalk.DescribeConfigurationSettingsInput{ 541 ApplicationName: aws.String(app), 542 EnvironmentName: aws.String(name), 543 }) 544 545 if err != nil { 546 return nil, err 547 } 548 549 if len(resp.ConfigurationSettings) != 1 { 550 return nil, fmt.Errorf("Error reading environment settings: received %d settings groups, expected 1", len(resp.ConfigurationSettings)) 551 } 552 553 settings := &schema.Set{F: optionSettingValueHash} 554 for _, optionSetting := range resp.ConfigurationSettings[0].OptionSettings { 555 m := map[string]interface{}{} 556 557 if optionSetting.Namespace != nil { 558 m["namespace"] = *optionSetting.Namespace 559 } else { 560 return nil, fmt.Errorf("Error reading environment settings: option setting with no namespace: %v", optionSetting) 561 } 562 563 if optionSetting.OptionName != nil { 564 m["name"] = *optionSetting.OptionName 565 } else { 566 return nil, fmt.Errorf("Error reading environment settings: option setting with no name: %v", optionSetting) 567 } 568 569 if *optionSetting.Namespace == "aws:autoscaling:scheduledaction" && optionSetting.ResourceName != nil { 570 m["resource"] = *optionSetting.ResourceName 571 } 572 573 if optionSetting.Value != nil { 574 switch *optionSetting.OptionName { 575 case "SecurityGroups": 576 m["value"] = dropGeneratedSecurityGroup(*optionSetting.Value, meta) 577 case "Subnets", "ELBSubnets": 578 m["value"] = sortValues(*optionSetting.Value) 579 default: 580 m["value"] = *optionSetting.Value 581 } 582 } 583 584 settings.Add(m) 585 } 586 587 return settings, nil 588 } 589 590 func resourceAwsElasticBeanstalkEnvironmentSettingsRead(d *schema.ResourceData, meta interface{}) error { 591 log.Printf("[DEBUG] Elastic Beanstalk environment settings read %s: id %s", d.Get("name").(string), d.Id()) 592 593 allSettings, err := fetchAwsElasticBeanstalkEnvironmentSettings(d, meta) 594 if err != nil { 595 return err 596 } 597 598 settings := d.Get("setting").(*schema.Set) 599 600 log.Printf("[DEBUG] Elastic Beanstalk allSettings: %s", allSettings.GoString()) 601 log.Printf("[DEBUG] Elastic Beanstalk settings: %s", settings.GoString()) 602 603 // perform the set operation with only name/namespace as keys, excluding value 604 // this is so we override things in the settings resource data key with updated values 605 // from the api. we skip values we didn't know about before because there are so many 606 // defaults set by the eb api that we would delete many useful defaults. 607 // 608 // there is likely a better way to do this 609 allSettingsKeySet := schema.NewSet(optionSettingKeyHash, allSettings.List()) 610 settingsKeySet := schema.NewSet(optionSettingKeyHash, settings.List()) 611 updatedSettingsKeySet := allSettingsKeySet.Intersection(settingsKeySet) 612 613 log.Printf("[DEBUG] Elastic Beanstalk updatedSettingsKeySet: %s", updatedSettingsKeySet.GoString()) 614 615 updatedSettings := schema.NewSet(optionSettingValueHash, updatedSettingsKeySet.List()) 616 617 log.Printf("[DEBUG] Elastic Beanstalk updatedSettings: %s", updatedSettings.GoString()) 618 619 if err := d.Set("all_settings", allSettings.List()); err != nil { 620 return err 621 } 622 623 if err := d.Set("setting", updatedSettings.List()); err != nil { 624 return err 625 } 626 627 return nil 628 } 629 630 func resourceAwsElasticBeanstalkEnvironmentDelete(d *schema.ResourceData, meta interface{}) error { 631 conn := meta.(*AWSClient).elasticbeanstalkconn 632 633 opts := elasticbeanstalk.TerminateEnvironmentInput{ 634 EnvironmentId: aws.String(d.Id()), 635 TerminateResources: aws.Bool(true), 636 } 637 638 // Get the current time to filter describeBeanstalkEvents messages 639 t := time.Now() 640 log.Printf("[DEBUG] Elastic Beanstalk Environment terminate opts: %s", opts) 641 _, err := conn.TerminateEnvironment(&opts) 642 643 if err != nil { 644 return err 645 } 646 647 waitForReadyTimeOut, err := time.ParseDuration(d.Get("wait_for_ready_timeout").(string)) 648 if err != nil { 649 return err 650 } 651 pollInterval, err := time.ParseDuration(d.Get("poll_interval").(string)) 652 if err != nil { 653 pollInterval = 0 654 log.Printf("[WARN] Error parsing poll_interval, using default backoff") 655 } 656 657 stateConf := &resource.StateChangeConf{ 658 Pending: []string{"Terminating"}, 659 Target: []string{"Terminated"}, 660 Refresh: environmentStateRefreshFunc(conn, d.Id()), 661 Timeout: waitForReadyTimeOut, 662 Delay: 10 * time.Second, 663 PollInterval: pollInterval, 664 MinTimeout: 3 * time.Second, 665 } 666 667 _, err = stateConf.WaitForState() 668 if err != nil { 669 return fmt.Errorf( 670 "Error waiting for Elastic Beanstalk Environment (%s) to become terminated: %s", 671 d.Id(), err) 672 } 673 674 err = describeBeanstalkEvents(conn, d.Id(), t) 675 if err != nil { 676 return err 677 } 678 679 return nil 680 } 681 682 // environmentStateRefreshFunc returns a resource.StateRefreshFunc that is used to watch 683 // the creation of the Beanstalk Environment 684 func environmentStateRefreshFunc(conn *elasticbeanstalk.ElasticBeanstalk, environmentId string) resource.StateRefreshFunc { 685 return func() (interface{}, string, error) { 686 resp, err := conn.DescribeEnvironments(&elasticbeanstalk.DescribeEnvironmentsInput{ 687 EnvironmentIds: []*string{aws.String(environmentId)}, 688 }) 689 if err != nil { 690 log.Printf("[Err] Error waiting for Elastic Beanstalk Environment state: %s", err) 691 return -1, "failed", fmt.Errorf("[Err] Error waiting for Elastic Beanstalk Environment state: %s", err) 692 } 693 694 if resp == nil || len(resp.Environments) == 0 { 695 // Sometimes AWS just has consistency issues and doesn't see 696 // our instance yet. Return an empty state. 697 return nil, "", nil 698 } 699 700 var env *elasticbeanstalk.EnvironmentDescription 701 for _, e := range resp.Environments { 702 if environmentId == *e.EnvironmentId { 703 env = e 704 } 705 } 706 707 if env == nil { 708 return -1, "failed", fmt.Errorf("[Err] Error finding Elastic Beanstalk Environment, environment not found") 709 } 710 711 return env, *env.Status, nil 712 } 713 } 714 715 // we use the following two functions to allow us to split out defaults 716 // as they become overridden from within the template 717 func optionSettingValueHash(v interface{}) int { 718 rd := v.(map[string]interface{}) 719 namespace := rd["namespace"].(string) 720 optionName := rd["name"].(string) 721 var resourceName string 722 if v, ok := rd["resource"].(string); ok { 723 resourceName = v 724 } 725 value, _ := rd["value"].(string) 726 hk := fmt.Sprintf("%s:%s%s=%s", namespace, optionName, resourceName, sortValues(value)) 727 log.Printf("[DEBUG] Elastic Beanstalk optionSettingValueHash(%#v): %s: hk=%s,hc=%d", v, optionName, hk, hashcode.String(hk)) 728 return hashcode.String(hk) 729 } 730 731 func optionSettingKeyHash(v interface{}) int { 732 rd := v.(map[string]interface{}) 733 namespace := rd["namespace"].(string) 734 optionName := rd["name"].(string) 735 var resourceName string 736 if v, ok := rd["resource"].(string); ok { 737 resourceName = v 738 } 739 hk := fmt.Sprintf("%s:%s%s", namespace, optionName, resourceName) 740 log.Printf("[DEBUG] Elastic Beanstalk optionSettingKeyHash(%#v): %s: hk=%s,hc=%d", v, optionName, hk, hashcode.String(hk)) 741 return hashcode.String(hk) 742 } 743 744 func sortValues(v string) string { 745 values := strings.Split(v, ",") 746 sort.Strings(values) 747 return strings.Join(values, ",") 748 } 749 750 func extractOptionSettings(s *schema.Set) []*elasticbeanstalk.ConfigurationOptionSetting { 751 settings := []*elasticbeanstalk.ConfigurationOptionSetting{} 752 753 if s != nil { 754 for _, setting := range s.List() { 755 optionSetting := elasticbeanstalk.ConfigurationOptionSetting{ 756 Namespace: aws.String(setting.(map[string]interface{})["namespace"].(string)), 757 OptionName: aws.String(setting.(map[string]interface{})["name"].(string)), 758 Value: aws.String(setting.(map[string]interface{})["value"].(string)), 759 } 760 if *optionSetting.Namespace == "aws:autoscaling:scheduledaction" { 761 if v, ok := setting.(map[string]interface{})["resource"].(string); ok && v != "" { 762 optionSetting.ResourceName = aws.String(v) 763 } 764 } 765 settings = append(settings, &optionSetting) 766 } 767 } 768 769 return settings 770 } 771 772 func dropGeneratedSecurityGroup(settingValue string, meta interface{}) string { 773 conn := meta.(*AWSClient).ec2conn 774 775 groups := strings.Split(settingValue, ",") 776 777 // Check to see if groups are ec2-classic or vpc security groups 778 ec2Classic := true 779 beanstalkSGRegexp := "sg-[0-9a-fA-F]{8}" 780 for _, g := range groups { 781 if ok, _ := regexp.MatchString(beanstalkSGRegexp, g); ok { 782 ec2Classic = false 783 break 784 } 785 } 786 787 var resp *ec2.DescribeSecurityGroupsOutput 788 var err error 789 790 if ec2Classic { 791 resp, err = conn.DescribeSecurityGroups(&ec2.DescribeSecurityGroupsInput{ 792 GroupNames: aws.StringSlice(groups), 793 }) 794 } else { 795 resp, err = conn.DescribeSecurityGroups(&ec2.DescribeSecurityGroupsInput{ 796 GroupIds: aws.StringSlice(groups), 797 }) 798 } 799 800 if err != nil { 801 log.Printf("[DEBUG] Elastic Beanstalk error describing SecurityGroups: %v", err) 802 return settingValue 803 } 804 805 log.Printf("[DEBUG] Elastic Beanstalk using ec2-classic security-groups: %t", ec2Classic) 806 var legitGroups []string 807 for _, group := range resp.SecurityGroups { 808 log.Printf("[DEBUG] Elastic Beanstalk SecurityGroup: %v", *group.GroupName) 809 if !strings.HasPrefix(*group.GroupName, "awseb") { 810 if ec2Classic { 811 legitGroups = append(legitGroups, *group.GroupName) 812 } else { 813 legitGroups = append(legitGroups, *group.GroupId) 814 } 815 } 816 } 817 818 sort.Strings(legitGroups) 819 820 return strings.Join(legitGroups, ",") 821 } 822 823 func describeBeanstalkEvents(conn *elasticbeanstalk.ElasticBeanstalk, environmentId string, t time.Time) error { 824 beanstalkErrors, err := conn.DescribeEvents(&elasticbeanstalk.DescribeEventsInput{ 825 EnvironmentId: aws.String(environmentId), 826 Severity: aws.String("ERROR"), 827 StartTime: aws.Time(t), 828 }) 829 830 if err != nil { 831 log.Printf("[Err] Unable to get Elastic Beanstalk Evironment events: %s", err) 832 } 833 834 events := "" 835 for _, event := range beanstalkErrors.Events { 836 events = events + "\n" + event.EventDate.String() + ": " + *event.Message 837 } 838 839 if events != "" { 840 return fmt.Errorf("%s", events) 841 } 842 843 return nil 844 }