provider/aws: Support Attachment of ALB Target Groups to Autoscaling Groups (#12855)

Fixes: #12563

```
% make testacc TEST=./builtin/providers/aws TESTARGS='-run=TestAccAwsAutoscalingAttachment_'
==> Checking that code complies with gofmt requirements...
go generate $(go list ./... | grep -v /terraform/vendor/)
2017/03/18 21:04:31 Generated command/internal_plugin_list.go
TF_ACC=1 go test ./builtin/providers/aws -v -run=TestAccAwsAutoscalingAttachment_ -timeout 120m
=== RUN   TestAccAwsAutoscalingAttachment_elb
--- PASS: TestAccAwsAutoscalingAttachment_elb (168.21s)
=== RUN   TestAccAwsAutoscalingAttachment_albTargetGroup
--- PASS: TestAccAwsAutoscalingAttachment_albTargetGroup (363.10s)
PASS
ok  	github.com/hashicorp/terraform/builtin/providers/aws	531.334s
```
This commit is contained in:
Paul Stack 2017-03-23 11:23:23 +02:00 committed by GitHub
parent 0bf2345917
commit e87b2d30c4
4 changed files with 311 additions and 76 deletions

View File

@ -18,16 +18,22 @@ func resourceAwsAutoscalingAttachment() *schema.Resource {
Delete: resourceAwsAutoscalingAttachmentDelete,
Schema: map[string]*schema.Schema{
"autoscaling_group_name": &schema.Schema{
"autoscaling_group_name": {
Type: schema.TypeString,
ForceNew: true,
Required: true,
},
"elb": &schema.Schema{
"elb": {
Type: schema.TypeString,
ForceNew: true,
Required: true,
Optional: true,
},
"alb_target_group_arn": {
Type: schema.TypeString,
ForceNew: true,
Optional: true,
},
},
}
@ -36,17 +42,31 @@ func resourceAwsAutoscalingAttachment() *schema.Resource {
func resourceAwsAutoscalingAttachmentCreate(d *schema.ResourceData, meta interface{}) error {
asgconn := meta.(*AWSClient).autoscalingconn
asgName := d.Get("autoscaling_group_name").(string)
elbName := d.Get("elb").(string)
attachElbInput := &autoscaling.AttachLoadBalancersInput{
if v, ok := d.GetOk("elb"); ok {
attachOpts := &autoscaling.AttachLoadBalancersInput{
AutoScalingGroupName: aws.String(asgName),
LoadBalancerNames: []*string{aws.String(elbName)},
LoadBalancerNames: []*string{aws.String(v.(string))},
}
log.Printf("[INFO] registering asg %s with ELBs %s", asgName, elbName)
log.Printf("[INFO] registering asg %s with ELBs %s", asgName, v.(string))
if _, err := asgconn.AttachLoadBalancers(attachElbInput); err != nil {
return errwrap.Wrapf(fmt.Sprintf("Failure attaching AutoScaling Group %s with Elastic Load Balancer: %s: {{err}}", asgName, elbName), err)
if _, err := asgconn.AttachLoadBalancers(attachOpts); err != nil {
return errwrap.Wrapf(fmt.Sprintf("Failure attaching AutoScaling Group %s with Elastic Load Balancer: %s: {{err}}", asgName, v.(string)), err)
}
}
if v, ok := d.GetOk("alb_target_group_arn"); ok {
attachOpts := &autoscaling.AttachLoadBalancerTargetGroupsInput{
AutoScalingGroupName: aws.String(asgName),
TargetGroupARNs: []*string{aws.String(v.(string))},
}
log.Printf("[INFO] registering asg %s with ALB Target Group %s", asgName, v.(string))
if _, err := asgconn.AttachLoadBalancerTargetGroups(attachOpts); err != nil {
return errwrap.Wrapf(fmt.Sprintf("Failure attaching AutoScaling Group %s with ALB Target Group: %s: {{err}}", asgName, v.(string)), err)
}
}
d.SetId(resource.PrefixedUniqueId(fmt.Sprintf("%s-", asgName)))
@ -57,7 +77,6 @@ func resourceAwsAutoscalingAttachmentCreate(d *schema.ResourceData, meta interfa
func resourceAwsAutoscalingAttachmentRead(d *schema.ResourceData, meta interface{}) error {
asgconn := meta.(*AWSClient).autoscalingconn
asgName := d.Get("autoscaling_group_name").(string)
elbName := d.Get("elb").(string)
// Retrieve the ASG properites to get list of associated ELBs
asg, err := getAwsAutoscalingGroup(asgName, asgconn)
@ -71,19 +90,37 @@ func resourceAwsAutoscalingAttachmentRead(d *schema.ResourceData, meta interface
return nil
}
if v, ok := d.GetOk("elb"); ok {
found := false
for _, i := range asg.LoadBalancerNames {
if elbName == *i {
d.Set("elb", elbName)
if v.(string) == *i {
d.Set("elb", v.(string))
found = true
break
}
}
if !found {
log.Printf("[WARN] Association for %s was not found in ASG assocation", elbName)
log.Printf("[WARN] Association for %s was not found in ASG assocation", v.(string))
d.SetId("")
}
}
if v, ok := d.GetOk("alb_target_group_arn"); ok {
found := false
for _, i := range asg.TargetGroupARNs {
if v.(string) == *i {
d.Set("alb_target_group_arn", v.(string))
found = true
break
}
}
if !found {
log.Printf("[WARN] Association for %s was not found in ASG assocation", v.(string))
d.SetId("")
}
}
return nil
}
@ -91,17 +128,29 @@ func resourceAwsAutoscalingAttachmentRead(d *schema.ResourceData, meta interface
func resourceAwsAutoscalingAttachmentDelete(d *schema.ResourceData, meta interface{}) error {
asgconn := meta.(*AWSClient).autoscalingconn
asgName := d.Get("autoscaling_group_name").(string)
elbName := d.Get("elb").(string)
log.Printf("[INFO] Deleting ELB %s association from: %s", elbName, asgName)
if v, ok := d.GetOk("elb"); ok {
detachOpts := &autoscaling.DetachLoadBalancersInput{
AutoScalingGroupName: aws.String(asgName),
LoadBalancerNames: []*string{aws.String(elbName)},
LoadBalancerNames: []*string{aws.String(v.(string))},
}
log.Printf("[INFO] Deleting ELB %s association from: %s", v.(string), asgName)
if _, err := asgconn.DetachLoadBalancers(detachOpts); err != nil {
return errwrap.Wrapf(fmt.Sprintf("Failure detaching AutoScaling Group %s with Elastic Load Balancer: %s: {{err}}", asgName, elbName), err)
return errwrap.Wrapf(fmt.Sprintf("Failure detaching AutoScaling Group %s with Elastic Load Balancer: %s: {{err}}", asgName, v.(string)), err)
}
}
if v, ok := d.GetOk("alb_target_group_arn"); ok {
detachOpts := &autoscaling.DetachLoadBalancerTargetGroupsInput{
AutoScalingGroupName: aws.String(asgName),
TargetGroupARNs: []*string{aws.String(v.(string))},
}
log.Printf("[INFO] Deleting ALB Target Group %s association from: %s", v.(string), asgName)
if _, err := asgconn.DetachLoadBalancerTargetGroups(detachOpts); err != nil {
return errwrap.Wrapf(fmt.Sprintf("Failure detaching AutoScaling Group %s with ALB Target Group: %s: {{err}}", asgName, v.(string)), err)
}
}
return nil

View File

@ -11,7 +11,7 @@ import (
"github.com/hashicorp/terraform/terraform"
)
func TestAccAwsAutoscalingAttachment_basic(t *testing.T) {
func TestAccAwsAutoscalingAttachment_elb(t *testing.T) {
rInt := acctest.RandInt()
@ -19,45 +19,83 @@ func TestAccAwsAutoscalingAttachment_basic(t *testing.T) {
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
Steps: []resource.TestStep{
resource.TestStep{
Config: testAccAWSAutoscalingAttachment_basic(rInt),
{
Config: testAccAWSAutoscalingAttachment_elb(rInt),
Check: resource.ComposeTestCheckFunc(
testAccCheckAWSAutocalingAttachmentExists("aws_autoscaling_group.asg", 0),
testAccCheckAWSAutocalingElbAttachmentExists("aws_autoscaling_group.asg", 0),
),
},
// Add in one association
resource.TestStep{
Config: testAccAWSAutoscalingAttachment_associated(rInt),
{
Config: testAccAWSAutoscalingAttachment_elb_associated(rInt),
Check: resource.ComposeTestCheckFunc(
testAccCheckAWSAutocalingAttachmentExists("aws_autoscaling_group.asg", 1),
testAccCheckAWSAutocalingElbAttachmentExists("aws_autoscaling_group.asg", 1),
),
},
// Test adding a 2nd
resource.TestStep{
Config: testAccAWSAutoscalingAttachment_double_associated(rInt),
{
Config: testAccAWSAutoscalingAttachment_elb_double_associated(rInt),
Check: resource.ComposeTestCheckFunc(
testAccCheckAWSAutocalingAttachmentExists("aws_autoscaling_group.asg", 2),
testAccCheckAWSAutocalingElbAttachmentExists("aws_autoscaling_group.asg", 2),
),
},
// Now remove that newest one
resource.TestStep{
Config: testAccAWSAutoscalingAttachment_associated(rInt),
{
Config: testAccAWSAutoscalingAttachment_elb_associated(rInt),
Check: resource.ComposeTestCheckFunc(
testAccCheckAWSAutocalingAttachmentExists("aws_autoscaling_group.asg", 1),
testAccCheckAWSAutocalingElbAttachmentExists("aws_autoscaling_group.asg", 1),
),
},
// Now remove them both
resource.TestStep{
Config: testAccAWSAutoscalingAttachment_basic(rInt),
{
Config: testAccAWSAutoscalingAttachment_elb(rInt),
Check: resource.ComposeTestCheckFunc(
testAccCheckAWSAutocalingAttachmentExists("aws_autoscaling_group.asg", 0),
testAccCheckAWSAutocalingElbAttachmentExists("aws_autoscaling_group.asg", 0),
),
},
},
})
}
func testAccCheckAWSAutocalingAttachmentExists(asgname string, loadBalancerCount int) resource.TestCheckFunc {
func TestAccAwsAutoscalingAttachment_albTargetGroup(t *testing.T) {
rInt := acctest.RandInt()
resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
Steps: []resource.TestStep{
{
Config: testAccAWSAutoscalingAttachment_alb(rInt),
Check: resource.ComposeTestCheckFunc(
testAccCheckAWSAutocalingAlbAttachmentExists("aws_autoscaling_group.asg", 0),
),
},
{
Config: testAccAWSAutoscalingAttachment_alb_associated(rInt),
Check: resource.ComposeTestCheckFunc(
testAccCheckAWSAutocalingAlbAttachmentExists("aws_autoscaling_group.asg", 1),
),
},
{
Config: testAccAWSAutoscalingAttachment_alb_double_associated(rInt),
Check: resource.ComposeTestCheckFunc(
testAccCheckAWSAutocalingAlbAttachmentExists("aws_autoscaling_group.asg", 2),
),
},
{
Config: testAccAWSAutoscalingAttachment_alb_associated(rInt),
Check: resource.ComposeTestCheckFunc(
testAccCheckAWSAutocalingAlbAttachmentExists("aws_autoscaling_group.asg", 1),
),
},
{
Config: testAccAWSAutoscalingAttachment_alb(rInt),
Check: resource.ComposeTestCheckFunc(
testAccCheckAWSAutocalingAlbAttachmentExists("aws_autoscaling_group.asg", 0),
),
},
},
})
}
func testAccCheckAWSAutocalingElbAttachmentExists(asgname string, loadBalancerCount int) resource.TestCheckFunc {
return func(s *terraform.State) error {
rs, ok := s.RootModule().Resources[asgname]
if !ok {
@ -83,7 +121,126 @@ func testAccCheckAWSAutocalingAttachmentExists(asgname string, loadBalancerCount
}
}
func testAccAWSAutoscalingAttachment_basic(rInt int) string {
func testAccCheckAWSAutocalingAlbAttachmentExists(asgname string, targetGroupCount int) resource.TestCheckFunc {
return func(s *terraform.State) error {
rs, ok := s.RootModule().Resources[asgname]
if !ok {
return fmt.Errorf("Not found: %s", asgname)
}
conn := testAccProvider.Meta().(*AWSClient).autoscalingconn
asg := rs.Primary.ID
actual, err := conn.DescribeAutoScalingGroups(&autoscaling.DescribeAutoScalingGroupsInput{
AutoScalingGroupNames: []*string{aws.String(asg)},
})
if err != nil {
return fmt.Errorf("Recieved an error when attempting to load %s: %s", asg, err)
}
if targetGroupCount != len(actual.AutoScalingGroups[0].TargetGroupARNs) {
return fmt.Errorf("Error: ASG has the wrong number of Target Groups associated. Expected [%d] but got [%d]", targetGroupCount, len(actual.AutoScalingGroups[0].TargetGroupARNs))
}
return nil
}
}
func testAccAWSAutoscalingAttachment_alb(rInt int) string {
return fmt.Sprintf(`
resource "aws_alb_target_group" "test" {
name = "test-alb-%d"
port = 443
protocol = "HTTPS"
vpc_id = "${aws_vpc.test.id}"
deregistration_delay = 200
stickiness {
type = "lb_cookie"
cookie_duration = 10000
}
health_check {
path = "/health"
interval = 60
port = 8081
protocol = "HTTP"
timeout = 3
healthy_threshold = 3
unhealthy_threshold = 3
matcher = "200-299"
}
tags {
TestName = "TestAccAWSALBTargetGroup_basic"
}
}
resource "aws_alb_target_group" "another_test" {
name = "atest-alb-%d"
port = 443
protocol = "HTTPS"
vpc_id = "${aws_vpc.test.id}"
deregistration_delay = 200
stickiness {
type = "lb_cookie"
cookie_duration = 10000
}
health_check {
path = "/health"
interval = 60
port = 8081
protocol = "HTTP"
timeout = 3
healthy_threshold = 3
unhealthy_threshold = 3
matcher = "200-299"
}
tags {
TestName = "TestAccAWSALBTargetGroup_basic"
}
}
resource "aws_autoscaling_group" "asg" {
availability_zones = ["us-west-2a", "us-west-2b", "us-west-2c"]
name = "asg-lb-assoc-terraform-test_%d"
max_size = 1
min_size = 0
desired_capacity = 0
health_check_grace_period = 300
force_delete = true
launch_configuration = "${aws_launch_configuration.as_conf.name}"
tag {
key = "Name"
value = "terraform-asg-lg-assoc-test"
propagate_at_launch = true
}
}
resource "aws_launch_configuration" "as_conf" {
name = "test_config_%d"
image_id = "ami-f34032c3"
instance_type = "t1.micro"
}
resource "aws_vpc" "test" {
cidr_block = "10.0.0.0/16"
tags {
TestName = "TestAccAWSALBTargetGroup_basic"
}
}
`, rInt, rInt, rInt, rInt)
}
func testAccAWSAutoscalingAttachment_elb(rInt int) string {
return fmt.Sprintf(`
resource "aws_elb" "foo" {
availability_zones = ["us-west-2a", "us-west-2b", "us-west-2c"]
@ -131,18 +288,34 @@ resource "aws_autoscaling_group" "asg" {
}`, rInt, rInt)
}
func testAccAWSAutoscalingAttachment_associated(rInt int) string {
return testAccAWSAutoscalingAttachment_basic(rInt) + `
func testAccAWSAutoscalingAttachment_elb_associated(rInt int) string {
return testAccAWSAutoscalingAttachment_elb(rInt) + `
resource "aws_autoscaling_attachment" "asg_attachment_foo" {
autoscaling_group_name = "${aws_autoscaling_group.asg.id}"
elb = "${aws_elb.foo.id}"
}`
}
func testAccAWSAutoscalingAttachment_double_associated(rInt int) string {
return testAccAWSAutoscalingAttachment_associated(rInt) + `
func testAccAWSAutoscalingAttachment_alb_associated(rInt int) string {
return testAccAWSAutoscalingAttachment_alb(rInt) + `
resource "aws_autoscaling_attachment" "asg_attachment_foo" {
autoscaling_group_name = "${aws_autoscaling_group.asg.id}"
alb_target_group_arn = "${aws_alb_target_group.test.arn}"
}`
}
func testAccAWSAutoscalingAttachment_elb_double_associated(rInt int) string {
return testAccAWSAutoscalingAttachment_elb_associated(rInt) + `
resource "aws_autoscaling_attachment" "asg_attachment_bar" {
autoscaling_group_name = "${aws_autoscaling_group.asg.id}"
elb = "${aws_elb.bar.id}"
}`
}
func testAccAWSAutoscalingAttachment_alb_double_associated(rInt int) string {
return testAccAWSAutoscalingAttachment_alb_associated(rInt) + `
resource "aws_autoscaling_attachment" "asg_attachment_bar" {
autoscaling_group_name = "${aws_autoscaling_group.asg.id}"
alb_target_group_arn = "${aws_alb_target_group.another_test.arn}"
}`
}

View File

@ -58,57 +58,57 @@ func resourceAwsAutoscalingGroup() *schema.Resource {
},
},
"launch_configuration": &schema.Schema{
"launch_configuration": {
Type: schema.TypeString,
Required: true,
},
"desired_capacity": &schema.Schema{
"desired_capacity": {
Type: schema.TypeInt,
Optional: true,
Computed: true,
},
"min_elb_capacity": &schema.Schema{
"min_elb_capacity": {
Type: schema.TypeInt,
Optional: true,
},
"min_size": &schema.Schema{
"min_size": {
Type: schema.TypeInt,
Required: true,
},
"max_size": &schema.Schema{
"max_size": {
Type: schema.TypeInt,
Required: true,
},
"default_cooldown": &schema.Schema{
"default_cooldown": {
Type: schema.TypeInt,
Optional: true,
Computed: true,
},
"force_delete": &schema.Schema{
"force_delete": {
Type: schema.TypeBool,
Optional: true,
Default: false,
},
"health_check_grace_period": &schema.Schema{
"health_check_grace_period": {
Type: schema.TypeInt,
Optional: true,
Default: 300,
},
"health_check_type": &schema.Schema{
"health_check_type": {
Type: schema.TypeString,
Optional: true,
Computed: true,
},
"availability_zones": &schema.Schema{
"availability_zones": {
Type: schema.TypeSet,
Optional: true,
Computed: true,
@ -116,12 +116,12 @@ func resourceAwsAutoscalingGroup() *schema.Resource {
Set: schema.HashString,
},
"placement_group": &schema.Schema{
"placement_group": {
Type: schema.TypeString,
Optional: true,
},
"load_balancers": &schema.Schema{
"load_balancers": {
Type: schema.TypeSet,
Optional: true,
Computed: true,
@ -129,7 +129,7 @@ func resourceAwsAutoscalingGroup() *schema.Resource {
Set: schema.HashString,
},
"vpc_zone_identifier": &schema.Schema{
"vpc_zone_identifier": {
Type: schema.TypeSet,
Optional: true,
Computed: true,
@ -137,13 +137,13 @@ func resourceAwsAutoscalingGroup() *schema.Resource {
Set: schema.HashString,
},
"termination_policies": &schema.Schema{
"termination_policies": {
Type: schema.TypeList,
Optional: true,
Elem: &schema.Schema{Type: schema.TypeString},
},
"wait_for_capacity_timeout": &schema.Schema{
"wait_for_capacity_timeout": {
Type: schema.TypeString,
Optional: true,
Default: "10m",
@ -162,12 +162,12 @@ func resourceAwsAutoscalingGroup() *schema.Resource {
},
},
"wait_for_elb_capacity": &schema.Schema{
"wait_for_elb_capacity": {
Type: schema.TypeInt,
Optional: true,
},
"enabled_metrics": &schema.Schema{
"enabled_metrics": {
Type: schema.TypeSet,
Optional: true,
Elem: &schema.Schema{Type: schema.TypeString},
@ -181,31 +181,32 @@ func resourceAwsAutoscalingGroup() *schema.Resource {
Set: schema.HashString,
},
"metrics_granularity": &schema.Schema{
"metrics_granularity": {
Type: schema.TypeString,
Optional: true,
Default: "1Minute",
},
"protect_from_scale_in": &schema.Schema{
"protect_from_scale_in": {
Type: schema.TypeBool,
Optional: true,
Default: false,
},
"target_group_arns": &schema.Schema{
"target_group_arns": {
Type: schema.TypeSet,
Optional: true,
Computed: true,
Elem: &schema.Schema{Type: schema.TypeString},
Set: schema.HashString,
},
"arn": &schema.Schema{
"arn": {
Type: schema.TypeString,
Computed: true,
},
"initial_lifecycle_hook": &schema.Schema{
"initial_lifecycle_hook": {
Type: schema.TypeSet,
Optional: true,
Elem: &schema.Resource{
@ -445,6 +446,8 @@ func resourceAwsAutoscalingGroupRead(d *schema.ResourceData, meta interface{}) e
d.Set("health_check_type", g.HealthCheckType)
d.Set("launch_configuration", g.LaunchConfigurationName)
d.Set("load_balancers", flattenStringList(g.LoadBalancerNames))
d.Set("target_group_arns", flattenStringList(g.TargetGroupARNs))
if err := d.Set("suspended_processes", flattenAsgSuspendedProcesses(g.SuspendedProcesses)); err != nil {
log.Printf("[WARN] Error setting suspended_processes for %q: %s", d.Id(), err)
}

View File

@ -16,6 +16,7 @@ an ELB), and an [AutoScaling Group resource](autoscaling_group.html) with
`load_balancers` defined in-line. At this time you cannot use an ASG with in-line
load balancers in conjunction with an ASG Attachment resource. Doing so will cause a
conflict and will overwrite attachments.
## Example Usage
```
@ -26,10 +27,19 @@ resource "aws_autoscaling_attachment" "asg_attachment_bar" {
}
```
```
# Create a new ALB Target Group attachment
resource "aws_autoscaling_attachment" "asg_attachment_bar" {
autoscaling_group_name = "${aws_autoscaling_group.asg.id}"
alb_target_group_arn = "${aws_alb_target_group.test.arn}"
}
```
## Argument Reference
The following arguments are supported:
* `autoscaling_group_name` - (Required) Name of ASG to associate with the ELB.
* `elb` - (Required) The name of the ELB.
* `elb` - (Optional) The name of the ELB.
* `alb_target_group_arn` - (Optional) The ARN of an ALB Target Group.