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