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