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 }