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