github.com/minamijoyo/terraform@v0.7.8-0.20161029001309-18b3736ba44b/builtin/providers/aws/resource_aws_alb_target_group.go (about) 1 package aws 2 3 import ( 4 "errors" 5 "fmt" 6 "log" 7 "strconv" 8 "strings" 9 10 "github.com/aws/aws-sdk-go/aws" 11 "github.com/aws/aws-sdk-go/aws/awserr" 12 "github.com/aws/aws-sdk-go/service/elbv2" 13 "github.com/hashicorp/errwrap" 14 "github.com/hashicorp/terraform/helper/schema" 15 ) 16 17 func resourceAwsAlbTargetGroup() *schema.Resource { 18 return &schema.Resource{ 19 Create: resourceAwsAlbTargetGroupCreate, 20 Read: resourceAwsAlbTargetGroupRead, 21 Update: resourceAwsAlbTargetGroupUpdate, 22 Delete: resourceAwsAlbTargetGroupDelete, 23 Importer: &schema.ResourceImporter{ 24 State: schema.ImportStatePassthrough, 25 }, 26 27 Schema: map[string]*schema.Schema{ 28 "arn": { 29 Type: schema.TypeString, 30 Computed: true, 31 }, 32 33 "name": { 34 Type: schema.TypeString, 35 Required: true, 36 ForceNew: true, 37 }, 38 39 "port": { 40 Type: schema.TypeInt, 41 Required: true, 42 ForceNew: true, 43 ValidateFunc: validateAwsAlbTargetGroupPort, 44 }, 45 46 "protocol": { 47 Type: schema.TypeString, 48 Required: true, 49 ForceNew: true, 50 ValidateFunc: validateAwsAlbTargetGroupProtocol, 51 }, 52 53 "vpc_id": { 54 Type: schema.TypeString, 55 Required: true, 56 ForceNew: true, 57 }, 58 59 "deregistration_delay": { 60 Type: schema.TypeInt, 61 Optional: true, 62 Default: 300, 63 ValidateFunc: validateAwsAlbTargetGroupDeregistrationDelay, 64 }, 65 66 "stickiness": { 67 Type: schema.TypeList, 68 Optional: true, 69 MaxItems: 1, 70 Elem: &schema.Resource{ 71 Schema: map[string]*schema.Schema{ 72 "type": { 73 Type: schema.TypeString, 74 Required: true, 75 ValidateFunc: validateAwsAlbTargetGroupStickinessType, 76 }, 77 "cookie_duration": { 78 Type: schema.TypeInt, 79 Optional: true, 80 Default: 86400, 81 ValidateFunc: validateAwsAlbTargetGroupStickinessCookieDuration, 82 }, 83 }, 84 }, 85 }, 86 87 "health_check": { 88 Type: schema.TypeList, 89 Optional: true, 90 Computed: true, 91 MaxItems: 1, 92 Elem: &schema.Resource{ 93 Schema: map[string]*schema.Schema{ 94 "interval": { 95 Type: schema.TypeInt, 96 Optional: true, 97 Default: 30, 98 }, 99 100 "path": { 101 Type: schema.TypeString, 102 Optional: true, 103 Default: "/", 104 ValidateFunc: validateAwsAlbTargetGroupHealthCheckPath, 105 }, 106 107 "port": { 108 Type: schema.TypeString, 109 Optional: true, 110 Default: "traffic-port", 111 ValidateFunc: validateAwsAlbTargetGroupHealthCheckPort, 112 }, 113 114 "protocol": { 115 Type: schema.TypeString, 116 Optional: true, 117 Default: "HTTP", 118 StateFunc: func(v interface{}) string { 119 return strings.ToUpper(v.(string)) 120 }, 121 ValidateFunc: validateAwsAlbTargetGroupHealthCheckProtocol, 122 }, 123 124 "timeout": { 125 Type: schema.TypeInt, 126 Optional: true, 127 Default: 5, 128 ValidateFunc: validateAwsAlbTargetGroupHealthCheckTimeout, 129 }, 130 131 "healthy_threshold": { 132 Type: schema.TypeInt, 133 Optional: true, 134 Default: 5, 135 ValidateFunc: validateAwsAlbTargetGroupHealthCheckHealthyThreshold, 136 }, 137 138 "matcher": { 139 Type: schema.TypeString, 140 Optional: true, 141 Default: "200", 142 }, 143 144 "unhealthy_threshold": { 145 Type: schema.TypeInt, 146 Optional: true, 147 Default: 2, 148 ValidateFunc: validateAwsAlbTargetGroupHealthCheckHealthyThreshold, 149 }, 150 }, 151 }, 152 }, 153 154 "tags": tagsSchema(), 155 }, 156 } 157 } 158 159 func resourceAwsAlbTargetGroupCreate(d *schema.ResourceData, meta interface{}) error { 160 elbconn := meta.(*AWSClient).elbv2conn 161 162 params := &elbv2.CreateTargetGroupInput{ 163 Name: aws.String(d.Get("name").(string)), 164 Port: aws.Int64(int64(d.Get("port").(int))), 165 Protocol: aws.String(d.Get("protocol").(string)), 166 VpcId: aws.String(d.Get("vpc_id").(string)), 167 } 168 169 if healthChecks := d.Get("health_check").([]interface{}); len(healthChecks) == 1 { 170 healthCheck := healthChecks[0].(map[string]interface{}) 171 172 params.HealthCheckIntervalSeconds = aws.Int64(int64(healthCheck["interval"].(int))) 173 params.HealthCheckPath = aws.String(healthCheck["path"].(string)) 174 params.HealthCheckPort = aws.String(healthCheck["port"].(string)) 175 params.HealthCheckProtocol = aws.String(healthCheck["protocol"].(string)) 176 params.HealthCheckTimeoutSeconds = aws.Int64(int64(healthCheck["timeout"].(int))) 177 params.HealthyThresholdCount = aws.Int64(int64(healthCheck["healthy_threshold"].(int))) 178 params.UnhealthyThresholdCount = aws.Int64(int64(healthCheck["unhealthy_threshold"].(int))) 179 params.Matcher = &elbv2.Matcher{ 180 HttpCode: aws.String(healthCheck["matcher"].(string)), 181 } 182 } 183 184 resp, err := elbconn.CreateTargetGroup(params) 185 if err != nil { 186 return errwrap.Wrapf("Error creating ALB Target Group: {{err}}", err) 187 } 188 189 if len(resp.TargetGroups) == 0 { 190 return errors.New("Error creating ALB Target Group: no groups returned in response") 191 } 192 193 targetGroupArn := resp.TargetGroups[0].TargetGroupArn 194 d.SetId(*targetGroupArn) 195 196 return resourceAwsAlbTargetGroupUpdate(d, meta) 197 } 198 199 func resourceAwsAlbTargetGroupRead(d *schema.ResourceData, meta interface{}) error { 200 elbconn := meta.(*AWSClient).elbv2conn 201 202 resp, err := elbconn.DescribeTargetGroups(&elbv2.DescribeTargetGroupsInput{ 203 TargetGroupArns: []*string{aws.String(d.Id())}, 204 }) 205 if err != nil { 206 if isTargetGroupNotFound(err) { 207 log.Printf("[DEBUG] DescribeTargetGroups - removing %s from state", d.Id()) 208 d.SetId("") 209 return nil 210 } 211 return errwrap.Wrapf("Error retrieving Target Group: {{err}}", err) 212 } 213 214 if len(resp.TargetGroups) != 1 { 215 return fmt.Errorf("Error retrieving Target Group %q", d.Id()) 216 } 217 218 targetGroup := resp.TargetGroups[0] 219 220 d.Set("arn", targetGroup.TargetGroupArn) 221 d.Set("name", targetGroup.TargetGroupName) 222 d.Set("port", targetGroup.Port) 223 d.Set("protocol", targetGroup.Protocol) 224 d.Set("vpc_id", targetGroup.VpcId) 225 226 healthCheck := make(map[string]interface{}) 227 healthCheck["interval"] = *targetGroup.HealthCheckIntervalSeconds 228 healthCheck["path"] = *targetGroup.HealthCheckPath 229 healthCheck["port"] = *targetGroup.HealthCheckPort 230 healthCheck["protocol"] = *targetGroup.HealthCheckProtocol 231 healthCheck["timeout"] = *targetGroup.HealthCheckTimeoutSeconds 232 healthCheck["healthy_threshold"] = *targetGroup.HealthyThresholdCount 233 healthCheck["unhealthy_threshold"] = *targetGroup.UnhealthyThresholdCount 234 healthCheck["matcher"] = *targetGroup.Matcher.HttpCode 235 d.Set("health_check", []interface{}{healthCheck}) 236 237 attrResp, err := elbconn.DescribeTargetGroupAttributes(&elbv2.DescribeTargetGroupAttributesInput{ 238 TargetGroupArn: aws.String(d.Id()), 239 }) 240 if err != nil { 241 return errwrap.Wrapf("Error retrieving Target Group Attributes: {{err}}", err) 242 } 243 244 stickinessMap := map[string]interface{}{} 245 for _, attr := range attrResp.Attributes { 246 switch *attr.Key { 247 case "stickiness.type": 248 stickinessMap["type"] = *attr.Value 249 case "stickiness.lb_cookie.duration_seconds": 250 stickinessMap["cookie_duration"] = *attr.Value 251 case "deregistration_delay.timeout_seconds": 252 timeout, err := strconv.Atoi(*attr.Value) 253 if err != nil { 254 return fmt.Errorf("Error converting deregistration_delay.timeout_seconds to int: %s", *attr.Value) 255 } 256 d.Set("deregistration_delay", timeout) 257 } 258 } 259 d.Set("stickiness", []interface{}{stickinessMap}) 260 261 return nil 262 } 263 264 func resourceAwsAlbTargetGroupUpdate(d *schema.ResourceData, meta interface{}) error { 265 elbconn := meta.(*AWSClient).elbv2conn 266 267 if err := setElbV2Tags(elbconn, d); err != nil { 268 return errwrap.Wrapf("Error Modifying Tags on ALB Target Group: {{err}}", err) 269 } 270 271 if d.HasChange("health_check") { 272 healthChecks := d.Get("health_check").([]interface{}) 273 274 var params *elbv2.ModifyTargetGroupInput 275 if len(healthChecks) == 1 { 276 healthCheck := healthChecks[0].(map[string]interface{}) 277 278 params = &elbv2.ModifyTargetGroupInput{ 279 TargetGroupArn: aws.String(d.Id()), 280 HealthCheckIntervalSeconds: aws.Int64(int64(healthCheck["interval"].(int))), 281 HealthCheckPath: aws.String(healthCheck["path"].(string)), 282 HealthCheckPort: aws.String(healthCheck["port"].(string)), 283 HealthCheckProtocol: aws.String(healthCheck["protocol"].(string)), 284 HealthCheckTimeoutSeconds: aws.Int64(int64(healthCheck["timeout"].(int))), 285 HealthyThresholdCount: aws.Int64(int64(healthCheck["healthy_threshold"].(int))), 286 UnhealthyThresholdCount: aws.Int64(int64(healthCheck["unhealthy_threshold"].(int))), 287 Matcher: &elbv2.Matcher{ 288 HttpCode: aws.String(healthCheck["matcher"].(string)), 289 }, 290 } 291 } else { 292 params = &elbv2.ModifyTargetGroupInput{ 293 TargetGroupArn: aws.String(d.Id()), 294 } 295 } 296 297 _, err := elbconn.ModifyTargetGroup(params) 298 if err != nil { 299 return errwrap.Wrapf("Error modifying Target Group: {{err}}", err) 300 } 301 } 302 303 var attrs []*elbv2.TargetGroupAttribute 304 305 if d.HasChange("deregistration_delay") { 306 attrs = append(attrs, &elbv2.TargetGroupAttribute{ 307 Key: aws.String("deregistration_delay.timeout_seconds"), 308 Value: aws.String(fmt.Sprintf("%d", d.Get("deregistration_delay").(int))), 309 }) 310 } 311 312 if d.HasChange("stickiness") { 313 stickinessBlocks := d.Get("stickiness").([]interface{}) 314 if len(stickinessBlocks) == 1 { 315 stickiness := stickinessBlocks[0].(map[string]interface{}) 316 317 attrs = append(attrs, 318 &elbv2.TargetGroupAttribute{ 319 Key: aws.String("stickiness.enabled"), 320 Value: aws.String("true"), 321 }, 322 &elbv2.TargetGroupAttribute{ 323 Key: aws.String("stickiness.type"), 324 Value: aws.String(stickiness["type"].(string)), 325 }, 326 &elbv2.TargetGroupAttribute{ 327 Key: aws.String("stickiness.lb_cookie.duration_seconds"), 328 Value: aws.String(fmt.Sprintf("%d", stickiness["cookie_duration"].(int))), 329 }) 330 } else if len(stickinessBlocks) == 0 { 331 attrs = append(attrs, &elbv2.TargetGroupAttribute{ 332 Key: aws.String("stickiness.enabled"), 333 Value: aws.String("false"), 334 }) 335 } 336 } 337 338 if len(attrs) > 0 { 339 params := &elbv2.ModifyTargetGroupAttributesInput{ 340 TargetGroupArn: aws.String(d.Id()), 341 Attributes: attrs, 342 } 343 344 _, err := elbconn.ModifyTargetGroupAttributes(params) 345 if err != nil { 346 return errwrap.Wrapf("Error modifying Target Group Attributes: {{err}}", err) 347 } 348 } 349 350 return resourceAwsAlbTargetGroupRead(d, meta) 351 } 352 353 func resourceAwsAlbTargetGroupDelete(d *schema.ResourceData, meta interface{}) error { 354 elbconn := meta.(*AWSClient).elbv2conn 355 356 _, err := elbconn.DeleteTargetGroup(&elbv2.DeleteTargetGroupInput{ 357 TargetGroupArn: aws.String(d.Id()), 358 }) 359 if err != nil { 360 return errwrap.Wrapf("Error deleting Target Group: {{err}}", err) 361 } 362 363 return nil 364 } 365 366 func isTargetGroupNotFound(err error) bool { 367 elberr, ok := err.(awserr.Error) 368 return ok && elberr.Code() == "TargetGroupNotFound" 369 } 370 371 func validateAwsAlbTargetGroupHealthCheckPath(v interface{}, k string) (ws []string, errors []error) { 372 value := v.(string) 373 if len(value) > 1024 { 374 errors = append(errors, fmt.Errorf( 375 "%q cannot be longer than 1024 characters: %q", k, value)) 376 } 377 return 378 } 379 380 func validateAwsAlbTargetGroupHealthCheckPort(v interface{}, k string) (ws []string, errors []error) { 381 value := v.(string) 382 383 if value == "traffic-port" { 384 return 385 } 386 387 port, err := strconv.Atoi(value) 388 if err != nil { 389 errors = append(errors, fmt.Errorf("%q must be a valid port number (1-65536) or %q", k, "traffic-port")) 390 } 391 392 if port < 1 || port > 65536 { 393 errors = append(errors, fmt.Errorf("%q must be a valid port number (1-65536) or %q", k, "traffic-port")) 394 } 395 396 return 397 } 398 399 func validateAwsAlbTargetGroupHealthCheckHealthyThreshold(v interface{}, k string) (ws []string, errors []error) { 400 value := v.(int) 401 if value < 2 || value > 10 { 402 errors = append(errors, fmt.Errorf("%q must be an integer between 2 and 10", k)) 403 } 404 return 405 } 406 407 func validateAwsAlbTargetGroupHealthCheckTimeout(v interface{}, k string) (ws []string, errors []error) { 408 value := v.(int) 409 if value < 2 || value > 60 { 410 errors = append(errors, fmt.Errorf("%q must be an integer between 2 and 60", k)) 411 } 412 return 413 } 414 415 func validateAwsAlbTargetGroupHealthCheckProtocol(v interface{}, k string) (ws []string, errors []error) { 416 value := strings.ToLower(v.(string)) 417 if value == "http" || value == "https" { 418 return 419 } 420 421 errors = append(errors, fmt.Errorf("%q must be either %q or %q", k, "HTTP", "HTTPS")) 422 return 423 } 424 425 func validateAwsAlbTargetGroupPort(v interface{}, k string) (ws []string, errors []error) { 426 port := v.(int) 427 if port < 1 || port > 65536 { 428 errors = append(errors, fmt.Errorf("%q must be a valid port number (1-65536)", k)) 429 } 430 return 431 } 432 433 func validateAwsAlbTargetGroupProtocol(v interface{}, k string) (ws []string, errors []error) { 434 protocol := strings.ToLower(v.(string)) 435 if protocol == "http" || protocol == "https" { 436 return 437 } 438 439 errors = append(errors, fmt.Errorf("%q must be either %q or %q", k, "HTTP", "HTTPS")) 440 return 441 } 442 443 func validateAwsAlbTargetGroupDeregistrationDelay(v interface{}, k string) (ws []string, errors []error) { 444 delay := v.(int) 445 if delay < 0 || delay > 3600 { 446 errors = append(errors, fmt.Errorf("%q must be in the range 0-3600 seconds", k)) 447 } 448 return 449 } 450 451 func validateAwsAlbTargetGroupStickinessType(v interface{}, k string) (ws []string, errors []error) { 452 stickinessType := v.(string) 453 if stickinessType != "lb_cookie" { 454 errors = append(errors, fmt.Errorf("%q must have the value %q", k, "lb_cookie")) 455 } 456 return 457 } 458 459 func validateAwsAlbTargetGroupStickinessCookieDuration(v interface{}, k string) (ws []string, errors []error) { 460 duration := v.(int) 461 if duration < 1 || duration > 604800 { 462 errors = append(errors, fmt.Errorf("%q must be a between 1 second and 1 week (1-604800 seconds))", k)) 463 } 464 return 465 }