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 }