github.com/andresvia/terraform@v0.6.15-0.20160412045437-d51c75946785/builtin/providers/aws/resource_aws_codedeploy_deployment_group.go (about)

     1  package aws
     2  
     3  import (
     4  	"bytes"
     5  	"fmt"
     6  	"log"
     7  	"time"
     8  
     9  	"github.com/hashicorp/terraform/helper/hashcode"
    10  	"github.com/hashicorp/terraform/helper/resource"
    11  	"github.com/hashicorp/terraform/helper/schema"
    12  
    13  	"github.com/aws/aws-sdk-go/aws"
    14  	"github.com/aws/aws-sdk-go/aws/awserr"
    15  	"github.com/aws/aws-sdk-go/service/codedeploy"
    16  )
    17  
    18  func resourceAwsCodeDeployDeploymentGroup() *schema.Resource {
    19  	return &schema.Resource{
    20  		Create: resourceAwsCodeDeployDeploymentGroupCreate,
    21  		Read:   resourceAwsCodeDeployDeploymentGroupRead,
    22  		Update: resourceAwsCodeDeployDeploymentGroupUpdate,
    23  		Delete: resourceAwsCodeDeployDeploymentGroupDelete,
    24  
    25  		Schema: map[string]*schema.Schema{
    26  			"app_name": &schema.Schema{
    27  				Type:     schema.TypeString,
    28  				Required: true,
    29  				ValidateFunc: func(v interface{}, k string) (ws []string, errors []error) {
    30  					value := v.(string)
    31  					if len(value) > 100 {
    32  						errors = append(errors, fmt.Errorf(
    33  							"%q cannot exceed 100 characters", k))
    34  					}
    35  					return
    36  				},
    37  			},
    38  
    39  			"deployment_group_name": &schema.Schema{
    40  				Type:     schema.TypeString,
    41  				Required: true,
    42  				ForceNew: true,
    43  				ValidateFunc: func(v interface{}, k string) (ws []string, errors []error) {
    44  					value := v.(string)
    45  					if len(value) > 100 {
    46  						errors = append(errors, fmt.Errorf(
    47  							"%q cannot exceed 100 characters", k))
    48  					}
    49  					return
    50  				},
    51  			},
    52  
    53  			"service_role_arn": &schema.Schema{
    54  				Type:     schema.TypeString,
    55  				Required: true,
    56  			},
    57  
    58  			"autoscaling_groups": &schema.Schema{
    59  				Type:     schema.TypeSet,
    60  				Optional: true,
    61  				Elem:     &schema.Schema{Type: schema.TypeString},
    62  				Set:      schema.HashString,
    63  			},
    64  
    65  			"deployment_config_name": &schema.Schema{
    66  				Type:     schema.TypeString,
    67  				Optional: true,
    68  				Default:  "CodeDeployDefault.OneAtATime",
    69  				ValidateFunc: func(v interface{}, k string) (ws []string, errors []error) {
    70  					value := v.(string)
    71  					if len(value) > 100 {
    72  						errors = append(errors, fmt.Errorf(
    73  							"%q cannot exceed 100 characters", k))
    74  					}
    75  					return
    76  				},
    77  			},
    78  
    79  			"ec2_tag_filter": &schema.Schema{
    80  				Type:     schema.TypeSet,
    81  				Optional: true,
    82  				Elem: &schema.Resource{
    83  					Schema: map[string]*schema.Schema{
    84  						"key": &schema.Schema{
    85  							Type:     schema.TypeString,
    86  							Optional: true,
    87  						},
    88  
    89  						"type": &schema.Schema{
    90  							Type:         schema.TypeString,
    91  							Optional:     true,
    92  							ValidateFunc: validateTagFilters,
    93  						},
    94  
    95  						"value": &schema.Schema{
    96  							Type:     schema.TypeString,
    97  							Optional: true,
    98  						},
    99  					},
   100  				},
   101  				Set: resourceAwsCodeDeployTagFilterHash,
   102  			},
   103  
   104  			"on_premises_instance_tag_filter": &schema.Schema{
   105  				Type:     schema.TypeSet,
   106  				Optional: true,
   107  				Elem: &schema.Resource{
   108  					Schema: map[string]*schema.Schema{
   109  						"key": &schema.Schema{
   110  							Type:     schema.TypeString,
   111  							Optional: true,
   112  						},
   113  
   114  						"type": &schema.Schema{
   115  							Type:         schema.TypeString,
   116  							Optional:     true,
   117  							ValidateFunc: validateTagFilters,
   118  						},
   119  
   120  						"value": &schema.Schema{
   121  							Type:     schema.TypeString,
   122  							Optional: true,
   123  						},
   124  					},
   125  				},
   126  				Set: resourceAwsCodeDeployTagFilterHash,
   127  			},
   128  
   129  			"trigger_configuration": &schema.Schema{
   130  				Type:     schema.TypeSet,
   131  				Optional: true,
   132  				Elem: &schema.Resource{
   133  					Schema: map[string]*schema.Schema{
   134  						"trigger_events": &schema.Schema{
   135  							Type:     schema.TypeSet,
   136  							Required: true,
   137  							Set:      schema.HashString,
   138  							Elem: &schema.Schema{
   139  								Type:         schema.TypeString,
   140  								ValidateFunc: validateTriggerEvent,
   141  							},
   142  						},
   143  
   144  						"trigger_name": &schema.Schema{
   145  							Type:     schema.TypeString,
   146  							Required: true,
   147  						},
   148  
   149  						"trigger_target_arn": &schema.Schema{
   150  							Type:     schema.TypeString,
   151  							Required: true,
   152  						},
   153  					},
   154  				},
   155  				Set: resourceAwsCodeDeployTriggerConfigHash,
   156  			},
   157  		},
   158  	}
   159  }
   160  
   161  func resourceAwsCodeDeployDeploymentGroupCreate(d *schema.ResourceData, meta interface{}) error {
   162  	conn := meta.(*AWSClient).codedeployconn
   163  
   164  	application := d.Get("app_name").(string)
   165  	deploymentGroup := d.Get("deployment_group_name").(string)
   166  
   167  	input := codedeploy.CreateDeploymentGroupInput{
   168  		ApplicationName:     aws.String(application),
   169  		DeploymentGroupName: aws.String(deploymentGroup),
   170  		ServiceRoleArn:      aws.String(d.Get("service_role_arn").(string)),
   171  	}
   172  	if attr, ok := d.GetOk("deployment_config_name"); ok {
   173  		input.DeploymentConfigName = aws.String(attr.(string))
   174  	}
   175  	if attr, ok := d.GetOk("autoscaling_groups"); ok {
   176  		input.AutoScalingGroups = expandStringList(attr.(*schema.Set).List())
   177  	}
   178  	if attr, ok := d.GetOk("on_premises_instance_tag_filters"); ok {
   179  		onPremFilters := buildOnPremTagFilters(attr.(*schema.Set).List())
   180  		input.OnPremisesInstanceTagFilters = onPremFilters
   181  	}
   182  	if attr, ok := d.GetOk("ec2_tag_filter"); ok {
   183  		ec2TagFilters := buildEC2TagFilters(attr.(*schema.Set).List())
   184  		input.Ec2TagFilters = ec2TagFilters
   185  	}
   186  	if attr, ok := d.GetOk("trigger_configuration"); ok {
   187  		triggerConfigs := buildTriggerConfigs(attr.(*schema.Set).List())
   188  		input.TriggerConfigurations = triggerConfigs
   189  	}
   190  
   191  	// Retry to handle IAM role eventual consistency.
   192  	var resp *codedeploy.CreateDeploymentGroupOutput
   193  	var err error
   194  	err = resource.Retry(2*time.Minute, func() *resource.RetryError {
   195  		resp, err = conn.CreateDeploymentGroup(&input)
   196  		if err != nil {
   197  			codedeployErr, ok := err.(awserr.Error)
   198  			if !ok {
   199  				return resource.NonRetryableError(err)
   200  			}
   201  			if codedeployErr.Code() == "InvalidRoleException" {
   202  				log.Printf("[DEBUG] Trying to create deployment group again: %q",
   203  					codedeployErr.Message())
   204  				return resource.RetryableError(err)
   205  			}
   206  
   207  			return resource.NonRetryableError(err)
   208  		}
   209  		return nil
   210  	})
   211  	if err != nil {
   212  		return err
   213  	}
   214  
   215  	d.SetId(*resp.DeploymentGroupId)
   216  
   217  	return resourceAwsCodeDeployDeploymentGroupRead(d, meta)
   218  }
   219  
   220  func resourceAwsCodeDeployDeploymentGroupRead(d *schema.ResourceData, meta interface{}) error {
   221  	conn := meta.(*AWSClient).codedeployconn
   222  
   223  	log.Printf("[DEBUG] Reading CodeDeploy DeploymentGroup %s", d.Id())
   224  	resp, err := conn.GetDeploymentGroup(&codedeploy.GetDeploymentGroupInput{
   225  		ApplicationName:     aws.String(d.Get("app_name").(string)),
   226  		DeploymentGroupName: aws.String(d.Get("deployment_group_name").(string)),
   227  	})
   228  	if err != nil {
   229  		return err
   230  	}
   231  
   232  	d.Set("app_name", *resp.DeploymentGroupInfo.ApplicationName)
   233  	d.Set("autoscaling_groups", resp.DeploymentGroupInfo.AutoScalingGroups)
   234  	d.Set("deployment_config_name", *resp.DeploymentGroupInfo.DeploymentConfigName)
   235  	d.Set("deployment_group_name", *resp.DeploymentGroupInfo.DeploymentGroupName)
   236  	d.Set("service_role_arn", *resp.DeploymentGroupInfo.ServiceRoleArn)
   237  	if err := d.Set("ec2_tag_filter", ec2TagFiltersToMap(resp.DeploymentGroupInfo.Ec2TagFilters)); err != nil {
   238  		return err
   239  	}
   240  	if err := d.Set("on_premises_instance_tag_filter", onPremisesTagFiltersToMap(resp.DeploymentGroupInfo.OnPremisesInstanceTagFilters)); err != nil {
   241  		return err
   242  	}
   243  	if err := d.Set("trigger_configuration", triggerConfigsToMap(resp.DeploymentGroupInfo.TriggerConfigurations)); err != nil {
   244  		return err
   245  	}
   246  
   247  	return nil
   248  }
   249  
   250  func resourceAwsCodeDeployDeploymentGroupUpdate(d *schema.ResourceData, meta interface{}) error {
   251  	conn := meta.(*AWSClient).codedeployconn
   252  
   253  	input := codedeploy.UpdateDeploymentGroupInput{
   254  		ApplicationName:            aws.String(d.Get("app_name").(string)),
   255  		CurrentDeploymentGroupName: aws.String(d.Get("deployment_group_name").(string)),
   256  	}
   257  
   258  	if d.HasChange("autoscaling_groups") {
   259  		_, n := d.GetChange("autoscaling_groups")
   260  		input.AutoScalingGroups = expandStringList(n.(*schema.Set).List())
   261  	}
   262  	if d.HasChange("deployment_config_name") {
   263  		_, n := d.GetChange("deployment_config_name")
   264  		input.DeploymentConfigName = aws.String(n.(string))
   265  	}
   266  	if d.HasChange("deployment_group_name") {
   267  		_, n := d.GetChange("deployment_group_name")
   268  		input.NewDeploymentGroupName = aws.String(n.(string))
   269  	}
   270  
   271  	// TagFilters aren't like tags. They don't append. They simply replace.
   272  	if d.HasChange("on_premises_instance_tag_filter") {
   273  		_, n := d.GetChange("on_premises_instance_tag_filter")
   274  		onPremFilters := buildOnPremTagFilters(n.(*schema.Set).List())
   275  		input.OnPremisesInstanceTagFilters = onPremFilters
   276  	}
   277  	if d.HasChange("ec2_tag_filter") {
   278  		_, n := d.GetChange("ec2_tag_filter")
   279  		ec2Filters := buildEC2TagFilters(n.(*schema.Set).List())
   280  		input.Ec2TagFilters = ec2Filters
   281  	}
   282  	if d.HasChange("trigger_configuration") {
   283  		_, n := d.GetChange("trigger_configuration")
   284  		triggerConfigs := buildTriggerConfigs(n.(*schema.Set).List())
   285  		input.TriggerConfigurations = triggerConfigs
   286  	}
   287  
   288  	log.Printf("[DEBUG] Updating CodeDeploy DeploymentGroup %s", d.Id())
   289  	_, err := conn.UpdateDeploymentGroup(&input)
   290  	if err != nil {
   291  		return err
   292  	}
   293  
   294  	return resourceAwsCodeDeployDeploymentGroupRead(d, meta)
   295  }
   296  
   297  func resourceAwsCodeDeployDeploymentGroupDelete(d *schema.ResourceData, meta interface{}) error {
   298  	conn := meta.(*AWSClient).codedeployconn
   299  
   300  	log.Printf("[DEBUG] Deleting CodeDeploy DeploymentGroup %s", d.Id())
   301  	_, err := conn.DeleteDeploymentGroup(&codedeploy.DeleteDeploymentGroupInput{
   302  		ApplicationName:     aws.String(d.Get("app_name").(string)),
   303  		DeploymentGroupName: aws.String(d.Get("deployment_group_name").(string)),
   304  	})
   305  	if err != nil {
   306  		return err
   307  	}
   308  
   309  	d.SetId("")
   310  
   311  	return nil
   312  }
   313  
   314  // buildOnPremTagFilters converts raw schema lists into a list of
   315  // codedeploy.TagFilters.
   316  func buildOnPremTagFilters(configured []interface{}) []*codedeploy.TagFilter {
   317  	filters := make([]*codedeploy.TagFilter, 0)
   318  	for _, raw := range configured {
   319  		var filter codedeploy.TagFilter
   320  		m := raw.(map[string]interface{})
   321  
   322  		filter.Key = aws.String(m["key"].(string))
   323  		filter.Type = aws.String(m["type"].(string))
   324  		filter.Value = aws.String(m["value"].(string))
   325  
   326  		filters = append(filters, &filter)
   327  	}
   328  
   329  	return filters
   330  }
   331  
   332  // buildEC2TagFilters converts raw schema lists into a list of
   333  // codedeploy.EC2TagFilters.
   334  func buildEC2TagFilters(configured []interface{}) []*codedeploy.EC2TagFilter {
   335  	filters := make([]*codedeploy.EC2TagFilter, 0)
   336  	for _, raw := range configured {
   337  		var filter codedeploy.EC2TagFilter
   338  		m := raw.(map[string]interface{})
   339  
   340  		filter.Key = aws.String(m["key"].(string))
   341  		filter.Type = aws.String(m["type"].(string))
   342  		filter.Value = aws.String(m["value"].(string))
   343  
   344  		filters = append(filters, &filter)
   345  	}
   346  
   347  	return filters
   348  }
   349  
   350  // buildTriggerConfigs converts a raw schema list into a list of
   351  // codedeploy.TriggerConfig.
   352  func buildTriggerConfigs(configured []interface{}) []*codedeploy.TriggerConfig {
   353  	configs := make([]*codedeploy.TriggerConfig, 0, len(configured))
   354  	for _, raw := range configured {
   355  		var config codedeploy.TriggerConfig
   356  		m := raw.(map[string]interface{})
   357  
   358  		config.TriggerEvents = expandStringSet(m["trigger_events"].(*schema.Set))
   359  		config.TriggerName = aws.String(m["trigger_name"].(string))
   360  		config.TriggerTargetArn = aws.String(m["trigger_target_arn"].(string))
   361  
   362  		configs = append(configs, &config)
   363  	}
   364  	return configs
   365  }
   366  
   367  // ec2TagFiltersToMap converts lists of tag filters into a []map[string]string.
   368  func ec2TagFiltersToMap(list []*codedeploy.EC2TagFilter) []map[string]string {
   369  	result := make([]map[string]string, 0, len(list))
   370  	for _, tf := range list {
   371  		l := make(map[string]string)
   372  		if *tf.Key != "" {
   373  			l["key"] = *tf.Key
   374  		}
   375  		if *tf.Value != "" {
   376  			l["value"] = *tf.Value
   377  		}
   378  		if *tf.Type != "" {
   379  			l["type"] = *tf.Type
   380  		}
   381  		result = append(result, l)
   382  	}
   383  	return result
   384  }
   385  
   386  // onPremisesTagFiltersToMap converts lists of on-prem tag filters into a []map[string]string.
   387  func onPremisesTagFiltersToMap(list []*codedeploy.TagFilter) []map[string]string {
   388  	result := make([]map[string]string, 0, len(list))
   389  	for _, tf := range list {
   390  		l := make(map[string]string)
   391  		if *tf.Key != "" {
   392  			l["key"] = *tf.Key
   393  		}
   394  		if *tf.Value != "" {
   395  			l["value"] = *tf.Value
   396  		}
   397  		if *tf.Type != "" {
   398  			l["type"] = *tf.Type
   399  		}
   400  		result = append(result, l)
   401  	}
   402  	return result
   403  }
   404  
   405  // triggerConfigsToMap converts a list of []*codedeploy.TriggerConfig into a []map[string]interface{}
   406  func triggerConfigsToMap(list []*codedeploy.TriggerConfig) []map[string]interface{} {
   407  	result := make([]map[string]interface{}, 0, len(list))
   408  	for _, tc := range list {
   409  		item := make(map[string]interface{})
   410  		item["trigger_events"] = schema.NewSet(schema.HashString, flattenStringList(tc.TriggerEvents))
   411  		item["trigger_name"] = *tc.TriggerName
   412  		item["trigger_target_arn"] = *tc.TriggerTargetArn
   413  		result = append(result, item)
   414  	}
   415  	return result
   416  }
   417  
   418  func resourceAwsCodeDeployTagFilterHash(v interface{}) int {
   419  	var buf bytes.Buffer
   420  	m := v.(map[string]interface{})
   421  
   422  	// Nothing's actually required in tag filters, so we must check the
   423  	// presence of all values before attempting a hash.
   424  	if v, ok := m["key"]; ok {
   425  		buf.WriteString(fmt.Sprintf("%s-", v.(string)))
   426  	}
   427  	if v, ok := m["type"]; ok {
   428  		buf.WriteString(fmt.Sprintf("%s-", v.(string)))
   429  	}
   430  	if v, ok := m["value"]; ok {
   431  		buf.WriteString(fmt.Sprintf("%s-", v.(string)))
   432  	}
   433  
   434  	return hashcode.String(buf.String())
   435  }
   436  
   437  func resourceAwsCodeDeployTriggerConfigHash(v interface{}) int {
   438  	var buf bytes.Buffer
   439  	m := v.(map[string]interface{})
   440  	buf.WriteString(fmt.Sprintf("%s-", m["trigger_name"].(string)))
   441  	buf.WriteString(fmt.Sprintf("%s-", m["trigger_target_arn"].(string)))
   442  	return hashcode.String(buf.String())
   443  }
   444  
   445  func validateTriggerEvent(v interface{}, k string) (ws []string, errors []error) {
   446  	value := v.(string)
   447  	triggerEvents := map[string]bool{
   448  		"DeploymentStart":   true,
   449  		"DeploymentStop":    true,
   450  		"DeploymentSuccess": true,
   451  		"DeploymentFailure": true,
   452  		"InstanceStart":     true,
   453  		"InstanceSuccess":   true,
   454  		"InstanceFailure":   true,
   455  	}
   456  
   457  	if !triggerEvents[value] {
   458  		errors = append(errors, fmt.Errorf("%q must be a valid event type value: %q", k, value))
   459  	}
   460  	return
   461  }