github.com/turtlemonvh/terraform@v0.6.9-0.20151204001754-8e40b6b855e8/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  	}
   130  }
   131  
   132  func resourceAwsCodeDeployDeploymentGroupCreate(d *schema.ResourceData, meta interface{}) error {
   133  	conn := meta.(*AWSClient).codedeployconn
   134  
   135  	application := d.Get("app_name").(string)
   136  	deploymentGroup := d.Get("deployment_group_name").(string)
   137  
   138  	input := codedeploy.CreateDeploymentGroupInput{
   139  		ApplicationName:     aws.String(application),
   140  		DeploymentGroupName: aws.String(deploymentGroup),
   141  		ServiceRoleArn:      aws.String(d.Get("service_role_arn").(string)),
   142  	}
   143  	if attr, ok := d.GetOk("deployment_config_name"); ok {
   144  		input.DeploymentConfigName = aws.String(attr.(string))
   145  	}
   146  	if attr, ok := d.GetOk("autoscaling_groups"); ok {
   147  		input.AutoScalingGroups = expandStringList(attr.(*schema.Set).List())
   148  	}
   149  	if attr, ok := d.GetOk("on_premises_instance_tag_filters"); ok {
   150  		onPremFilters := buildOnPremTagFilters(attr.(*schema.Set).List())
   151  		input.OnPremisesInstanceTagFilters = onPremFilters
   152  	}
   153  	if attr, ok := d.GetOk("ec2_tag_filter"); ok {
   154  		ec2TagFilters := buildEC2TagFilters(attr.(*schema.Set).List())
   155  		input.Ec2TagFilters = ec2TagFilters
   156  	}
   157  
   158  	// Retry to handle IAM role eventual consistency.
   159  	var resp *codedeploy.CreateDeploymentGroupOutput
   160  	var err error
   161  	err = resource.Retry(2*time.Minute, func() error {
   162  		resp, err = conn.CreateDeploymentGroup(&input)
   163  		if err != nil {
   164  			codedeployErr, ok := err.(awserr.Error)
   165  			if !ok {
   166  				return &resource.RetryError{Err: err}
   167  			}
   168  			if codedeployErr.Code() == "InvalidRoleException" {
   169  				log.Printf("[DEBUG] Trying to create deployment group again: %q",
   170  					codedeployErr.Message())
   171  				return err
   172  			}
   173  
   174  			return &resource.RetryError{Err: err}
   175  		}
   176  		return nil
   177  	})
   178  	if err != nil {
   179  		return err
   180  	}
   181  
   182  	d.SetId(*resp.DeploymentGroupId)
   183  
   184  	return resourceAwsCodeDeployDeploymentGroupRead(d, meta)
   185  }
   186  
   187  func resourceAwsCodeDeployDeploymentGroupRead(d *schema.ResourceData, meta interface{}) error {
   188  	conn := meta.(*AWSClient).codedeployconn
   189  
   190  	log.Printf("[DEBUG] Reading CodeDeploy DeploymentGroup %s", d.Id())
   191  	resp, err := conn.GetDeploymentGroup(&codedeploy.GetDeploymentGroupInput{
   192  		ApplicationName:     aws.String(d.Get("app_name").(string)),
   193  		DeploymentGroupName: aws.String(d.Get("deployment_group_name").(string)),
   194  	})
   195  	if err != nil {
   196  		return err
   197  	}
   198  
   199  	d.Set("app_name", *resp.DeploymentGroupInfo.ApplicationName)
   200  	d.Set("autoscaling_groups", resp.DeploymentGroupInfo.AutoScalingGroups)
   201  	d.Set("deployment_config_name", *resp.DeploymentGroupInfo.DeploymentConfigName)
   202  	d.Set("deployment_group_name", *resp.DeploymentGroupInfo.DeploymentGroupName)
   203  	d.Set("service_role_arn", *resp.DeploymentGroupInfo.ServiceRoleArn)
   204  	if err := d.Set("ec2_tag_filter", ec2TagFiltersToMap(resp.DeploymentGroupInfo.Ec2TagFilters)); err != nil {
   205  		return err
   206  	}
   207  	if err := d.Set("on_premises_instance_tag_filter", onPremisesTagFiltersToMap(resp.DeploymentGroupInfo.OnPremisesInstanceTagFilters)); err != nil {
   208  		return err
   209  	}
   210  
   211  	return nil
   212  }
   213  
   214  func resourceAwsCodeDeployDeploymentGroupUpdate(d *schema.ResourceData, meta interface{}) error {
   215  	conn := meta.(*AWSClient).codedeployconn
   216  
   217  	input := codedeploy.UpdateDeploymentGroupInput{
   218  		ApplicationName:            aws.String(d.Get("app_name").(string)),
   219  		CurrentDeploymentGroupName: aws.String(d.Get("deployment_group_name").(string)),
   220  	}
   221  
   222  	if d.HasChange("autoscaling_groups") {
   223  		_, n := d.GetChange("autoscaling_groups")
   224  		input.AutoScalingGroups = expandStringList(n.(*schema.Set).List())
   225  	}
   226  	if d.HasChange("deployment_config_name") {
   227  		_, n := d.GetChange("deployment_config_name")
   228  		input.DeploymentConfigName = aws.String(n.(string))
   229  	}
   230  	if d.HasChange("deployment_group_name") {
   231  		_, n := d.GetChange("deployment_group_name")
   232  		input.NewDeploymentGroupName = aws.String(n.(string))
   233  	}
   234  
   235  	// TagFilters aren't like tags. They don't append. They simply replace.
   236  	if d.HasChange("on_premises_instance_tag_filter") {
   237  		_, n := d.GetChange("on_premises_instance_tag_filter")
   238  		onPremFilters := buildOnPremTagFilters(n.(*schema.Set).List())
   239  		input.OnPremisesInstanceTagFilters = onPremFilters
   240  	}
   241  	if d.HasChange("ec2_tag_filter") {
   242  		_, n := d.GetChange("ec2_tag_filter")
   243  		ec2Filters := buildEC2TagFilters(n.(*schema.Set).List())
   244  		input.Ec2TagFilters = ec2Filters
   245  	}
   246  
   247  	log.Printf("[DEBUG] Updating CodeDeploy DeploymentGroup %s", d.Id())
   248  	_, err := conn.UpdateDeploymentGroup(&input)
   249  	if err != nil {
   250  		return err
   251  	}
   252  
   253  	return resourceAwsCodeDeployDeploymentGroupRead(d, meta)
   254  }
   255  
   256  func resourceAwsCodeDeployDeploymentGroupDelete(d *schema.ResourceData, meta interface{}) error {
   257  	conn := meta.(*AWSClient).codedeployconn
   258  
   259  	log.Printf("[DEBUG] Deleting CodeDeploy DeploymentGroup %s", d.Id())
   260  	_, err := conn.DeleteDeploymentGroup(&codedeploy.DeleteDeploymentGroupInput{
   261  		ApplicationName:     aws.String(d.Get("app_name").(string)),
   262  		DeploymentGroupName: aws.String(d.Get("deployment_group_name").(string)),
   263  	})
   264  	if err != nil {
   265  		return err
   266  	}
   267  
   268  	d.SetId("")
   269  
   270  	return nil
   271  }
   272  
   273  // buildOnPremTagFilters converts raw schema lists into a list of
   274  // codedeploy.TagFilters.
   275  func buildOnPremTagFilters(configured []interface{}) []*codedeploy.TagFilter {
   276  	filters := make([]*codedeploy.TagFilter, 0)
   277  	for _, raw := range configured {
   278  		var filter codedeploy.TagFilter
   279  		m := raw.(map[string]interface{})
   280  
   281  		filter.Key = aws.String(m["key"].(string))
   282  		filter.Type = aws.String(m["type"].(string))
   283  		filter.Value = aws.String(m["value"].(string))
   284  
   285  		filters = append(filters, &filter)
   286  	}
   287  
   288  	return filters
   289  }
   290  
   291  // buildEC2TagFilters converts raw schema lists into a list of
   292  // codedeploy.EC2TagFilters.
   293  func buildEC2TagFilters(configured []interface{}) []*codedeploy.EC2TagFilter {
   294  	filters := make([]*codedeploy.EC2TagFilter, 0)
   295  	for _, raw := range configured {
   296  		var filter codedeploy.EC2TagFilter
   297  		m := raw.(map[string]interface{})
   298  
   299  		filter.Key = aws.String(m["key"].(string))
   300  		filter.Type = aws.String(m["type"].(string))
   301  		filter.Value = aws.String(m["value"].(string))
   302  
   303  		filters = append(filters, &filter)
   304  	}
   305  
   306  	return filters
   307  }
   308  
   309  // ec2TagFiltersToMap converts lists of tag filters into a []map[string]string.
   310  func ec2TagFiltersToMap(list []*codedeploy.EC2TagFilter) []map[string]string {
   311  	result := make([]map[string]string, 0, len(list))
   312  	for _, tf := range list {
   313  		l := make(map[string]string)
   314  		if *tf.Key != "" {
   315  			l["key"] = *tf.Key
   316  		}
   317  		if *tf.Value != "" {
   318  			l["value"] = *tf.Value
   319  		}
   320  		if *tf.Type != "" {
   321  			l["type"] = *tf.Type
   322  		}
   323  		result = append(result, l)
   324  	}
   325  	return result
   326  }
   327  
   328  // onPremisesTagFiltersToMap converts lists of on-prem tag filters into a []map[string]string.
   329  func onPremisesTagFiltersToMap(list []*codedeploy.TagFilter) []map[string]string {
   330  	result := make([]map[string]string, 0, len(list))
   331  	for _, tf := range list {
   332  		l := make(map[string]string)
   333  		if *tf.Key != "" {
   334  			l["key"] = *tf.Key
   335  		}
   336  		if *tf.Value != "" {
   337  			l["value"] = *tf.Value
   338  		}
   339  		if *tf.Type != "" {
   340  			l["type"] = *tf.Type
   341  		}
   342  		result = append(result, l)
   343  	}
   344  	return result
   345  }
   346  
   347  // validateTagFilters confirms the "value" component of a tag filter is one of
   348  // AWS's three allowed types.
   349  func validateTagFilters(v interface{}, k string) (ws []string, errors []error) {
   350  	value := v.(string)
   351  	if value != "KEY_ONLY" && value != "VALUE_ONLY" && value != "KEY_AND_VALUE" {
   352  		errors = append(errors, fmt.Errorf(
   353  			"%q must be one of \"KEY_ONLY\", \"VALUE_ONLY\", or \"KEY_AND_VALUE\"", k))
   354  	}
   355  	return
   356  }
   357  
   358  func resourceAwsCodeDeployTagFilterHash(v interface{}) int {
   359  	var buf bytes.Buffer
   360  	m := v.(map[string]interface{})
   361  
   362  	// Nothing's actually required in tag filters, so we must check the
   363  	// presence of all values before attempting a hash.
   364  	if v, ok := m["key"]; ok {
   365  		buf.WriteString(fmt.Sprintf("%s-", v.(string)))
   366  	}
   367  	if v, ok := m["type"]; ok {
   368  		buf.WriteString(fmt.Sprintf("%s-", v.(string)))
   369  	}
   370  	if v, ok := m["value"]; ok {
   371  		buf.WriteString(fmt.Sprintf("%s-", v.(string)))
   372  	}
   373  
   374  	return hashcode.String(buf.String())
   375  }