Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add stickiness support for NLB target_groups #15295

Merged
merged 10 commits into from
Oct 9, 2020
42 changes: 10 additions & 32 deletions aws/resource_aws_lb_target_group.go
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,13 @@ func resourceAwsLbTargetGroup() *schema.Resource {
Optional: true,
Default: 86400,
ValidateFunc: validation.IntBetween(0, 604800),
DiffSuppressFunc: func(k, old, new string, d *schema.ResourceData) bool {
madpipeline marked this conversation as resolved.
Show resolved Hide resolved
switch d.Get("protocol").(string) {
case elbv2.ProtocolEnumTcp, elbv2.ProtocolEnumUdp, elbv2.ProtocolEnumTcpUdp, elbv2.ProtocolEnumTls:
return true
}
return false
},
},
},
},
Expand Down Expand Up @@ -282,39 +289,9 @@ func resourceAwsLbTargetGroupCreate(d *schema.ResourceData, meta interface{}) er
if _, ok := d.GetOk("vpc_id"); !ok {
return fmt.Errorf("vpc_id should be set when target type is %s", d.Get("target_type").(string))
}

params.Port = aws.Int64(int64(d.Get("port").(int)))
params.Protocol = aws.String(d.Get("protocol").(string))
params.VpcId = aws.String(d.Get("vpc_id").(string))

stickinessBlocks := d.Get("stickiness").([]interface{})
if len(stickinessBlocks) > 0 {
stickiness := stickinessBlocks[0].(map[string]interface{})

if d.Get("protocol").(string) == elbv2.ProtocolEnumHttp ||
d.Get("protocol").(string) == elbv2.ProtocolEnumHttps {
if stickiness["type"].(string) != "lb_cookie" {
return fmt.Errorf("stickiness type can only be \"lb_cookie\" when protocol is %s", d.Get("protocol").(string))
}
} else if d.Get("protocol").(string) == elbv2.ProtocolEnumTcp ||
d.Get("protocol").(string) == elbv2.ProtocolEnumUdp ||
d.Get("protocol").(string) == elbv2.ProtocolEnumTcpUdp {
if stickiness["type"].(string) != "source_ip" {
return fmt.Errorf("stickiness type can only be \"source_ip\" when protocol is %s", d.Get("protocol").(string))
}
}
} else {
if d.Get("protocol").(string) == elbv2.ProtocolEnumTcp ||
madpipeline marked this conversation as resolved.
Show resolved Hide resolved
d.Get("protocol").(string) == elbv2.ProtocolEnumUdp ||
d.Get("protocol").(string) == elbv2.ProtocolEnumTcpUdp {
stickiness := make(map[string]interface{})
stickiness["type"] = "source_ip"

if err := d.Set("stickiness", []interface{}{stickiness}); err != nil {
return fmt.Errorf("error setting default NLB stickiness: %s", err)
}
}
}
}

if healthChecks := d.Get("health_check").([]interface{}); len(healthChecks) == 1 {
Expand Down Expand Up @@ -479,8 +456,9 @@ func resourceAwsLbTargetGroupUpdate(d *schema.ResourceData, meta interface{}) er
Key: aws.String("stickiness.type"),
Value: aws.String(stickiness["type"].(string)),
})
if d.Get("protocol").(string) == elbv2.ProtocolEnumHttp ||
d.Get("protocol").(string) == elbv2.ProtocolEnumHttps {

switch d.Get("protocol").(string) {
case elbv2.ProtocolEnumHttp, elbv2.ProtocolEnumHttps:
attrs = append(attrs,
&elbv2.TargetGroupAttribute{
Key: aws.String("stickiness.lb_cookie.duration_seconds"),
Expand Down
161 changes: 95 additions & 66 deletions aws/resource_aws_lb_target_group_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -964,7 +964,7 @@ protocol = "TCP"
})
}

func TestAccAWSLBTargetGroup_defaultStickinessForNLB(t *testing.T) {
func TestAccAWSLBTargetGroup_stickinessDefaultNLB(t *testing.T) {
var conf elbv2.TargetGroup
resourceName := "aws_lb_target_group.test"

Expand All @@ -975,7 +975,7 @@ func TestAccAWSLBTargetGroup_defaultStickinessForNLB(t *testing.T) {
CheckDestroy: testAccCheckAWSLBTargetGroupDestroy,
Steps: []resource.TestStep{
{
Config: testAccAWSLBTargetGroupConfig_NLBdefaultStickiness("TCP"),
Config: testAccAWSLBTargetGroupConfig_stickinessDefault("TCP"),
Check: resource.ComposeAggregateTestCheckFunc(
testAccCheckAWSLBTargetGroupExists(resourceName, &conf),
resource.TestCheckResourceAttr(resourceName, "stickiness.#", "1"),
Expand All @@ -984,7 +984,7 @@ func TestAccAWSLBTargetGroup_defaultStickinessForNLB(t *testing.T) {
),
},
{
Config: testAccAWSLBTargetGroupConfig_NLBdefaultStickiness("UDP"),
Config: testAccAWSLBTargetGroupConfig_stickinessDefault("UDP"),
Check: resource.ComposeAggregateTestCheckFunc(
testAccCheckAWSLBTargetGroupExists(resourceName, &conf),
resource.TestCheckResourceAttr(resourceName, "stickiness.#", "1"),
Expand All @@ -993,7 +993,7 @@ func TestAccAWSLBTargetGroup_defaultStickinessForNLB(t *testing.T) {
),
},
{
Config: testAccAWSLBTargetGroupConfig_NLBdefaultStickiness("TCP_UDP"),
Config: testAccAWSLBTargetGroupConfig_stickinessDefault("TCP_UDP"),
Check: resource.ComposeAggregateTestCheckFunc(
testAccCheckAWSLBTargetGroupExists(resourceName, &conf),
resource.TestCheckResourceAttr(resourceName, "stickiness.#", "1"),
Expand All @@ -1005,7 +1005,7 @@ func TestAccAWSLBTargetGroup_defaultStickinessForNLB(t *testing.T) {
})
}

func TestAccAWSLBTargetGroup_validStickinessForNLB(t *testing.T) {
func TestAccAWSLBTargetGroup_stickinessDefaultALB(t *testing.T) {
var conf elbv2.TargetGroup
resourceName := "aws_lb_target_group.test"

Expand All @@ -1016,7 +1016,30 @@ func TestAccAWSLBTargetGroup_validStickinessForNLB(t *testing.T) {
CheckDestroy: testAccCheckAWSLBTargetGroupDestroy,
Steps: []resource.TestStep{
{
Config: testAccAWSLBTargetGroupConfig_NLBvalidStickiness("TCP", false),
Config: testAccAWSLBTargetGroupConfig_stickinessDefault("HTTP"),
Check: resource.ComposeAggregateTestCheckFunc(
testAccCheckAWSLBTargetGroupExists(resourceName, &conf),
resource.TestCheckResourceAttr(resourceName, "stickiness.#", "1"),
resource.TestCheckResourceAttr(resourceName, "stickiness.0.enabled", "false"),
resource.TestCheckResourceAttr(resourceName, "stickiness.0.type", "lb_cookie"),
),
},
},
})
}

func TestAccAWSLBTargetGroup_stickinessValidNLB(t *testing.T) {
var conf elbv2.TargetGroup
resourceName := "aws_lb_target_group.test"

resource.ParallelTest(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
IDRefreshName: resourceName,
Providers: testAccProviders,
CheckDestroy: testAccCheckAWSLBTargetGroupDestroy,
Steps: []resource.TestStep{
{
Config: testAccAWSLBTargetGroupConfig_stickinessValidity("TCP", "source_ip", false),
Check: resource.ComposeAggregateTestCheckFunc(
testAccCheckAWSLBTargetGroupExists(resourceName, &conf),
resource.TestCheckResourceAttr(resourceName, "stickiness.#", "1"),
Expand All @@ -1025,7 +1048,7 @@ func TestAccAWSLBTargetGroup_validStickinessForNLB(t *testing.T) {
),
},
{
Config: testAccAWSLBTargetGroupConfig_NLBvalidStickiness("TCP", true),
Config: testAccAWSLBTargetGroupConfig_stickinessValidity("TCP", "source_ip", true),
Check: resource.ComposeAggregateTestCheckFunc(
testAccCheckAWSLBTargetGroupExists(resourceName, &conf),
resource.TestCheckResourceAttr(resourceName, "stickiness.#", "1"),
Expand All @@ -1034,80 +1057,110 @@ func TestAccAWSLBTargetGroup_validStickinessForNLB(t *testing.T) {
),
},
{
Config: testAccAWSLBTargetGroupConfig_NLBvalidStickiness("UDP", false),
Config: testAccAWSLBTargetGroupConfig_stickinessValidity("UDP", "source_ip", true),
Check: resource.ComposeAggregateTestCheckFunc(
testAccCheckAWSLBTargetGroupExists(resourceName, &conf),
resource.TestCheckResourceAttr(resourceName, "stickiness.#", "1"),
resource.TestCheckResourceAttr(resourceName, "stickiness.0.enabled", "false"),
resource.TestCheckResourceAttr(resourceName, "stickiness.0.enabled", "true"),
resource.TestCheckResourceAttr(resourceName, "stickiness.0.type", "source_ip"),
),
},
{
Config: testAccAWSLBTargetGroupConfig_NLBvalidStickiness("UDP", true),
Config: testAccAWSLBTargetGroupConfig_stickinessValidity("TCP_UDP", "source_ip", true),
Check: resource.ComposeAggregateTestCheckFunc(
testAccCheckAWSLBTargetGroupExists(resourceName, &conf),
resource.TestCheckResourceAttr(resourceName, "stickiness.#", "1"),
resource.TestCheckResourceAttr(resourceName, "stickiness.0.enabled", "true"),
resource.TestCheckResourceAttr(resourceName, "stickiness.0.type", "source_ip"),
),
},
},
})
}

func TestAccAWSLBTargetGroup_stickinessValidALB(t *testing.T) {
var conf elbv2.TargetGroup
resourceName := "aws_lb_target_group.test"

resource.ParallelTest(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
IDRefreshName: resourceName,
Providers: testAccProviders,
CheckDestroy: testAccCheckAWSLBTargetGroupDestroy,
Steps: []resource.TestStep{
{
Config: testAccAWSLBTargetGroupConfig_NLBvalidStickiness("TCP_UDP", false),
Config: testAccAWSLBTargetGroupConfig_stickinessValidity("HTTP", "lb_cookie", true),
Check: resource.ComposeAggregateTestCheckFunc(
testAccCheckAWSLBTargetGroupExists(resourceName, &conf),
resource.TestCheckResourceAttr(resourceName, "stickiness.#", "1"),
resource.TestCheckResourceAttr(resourceName, "stickiness.0.enabled", "false"),
resource.TestCheckResourceAttr(resourceName, "stickiness.0.type", "source_ip"),
resource.TestCheckResourceAttr(resourceName, "stickiness.0.enabled", "true"),
resource.TestCheckResourceAttr(resourceName, "stickiness.0.type", "lb_cookie"),
resource.TestCheckResourceAttr(resourceName, "stickiness.0.cookie_duration", "86400"),
),
},
{
Config: testAccAWSLBTargetGroupConfig_NLBvalidStickiness("TCP_UDP", true),
Config: testAccAWSLBTargetGroupConfig_stickinessValidity("HTTPS", "lb_cookie", true),
Check: resource.ComposeAggregateTestCheckFunc(
testAccCheckAWSLBTargetGroupExists(resourceName, &conf),
resource.TestCheckResourceAttr(resourceName, "stickiness.#", "1"),
resource.TestCheckResourceAttr(resourceName, "stickiness.0.enabled", "true"),
resource.TestCheckResourceAttr(resourceName, "stickiness.0.type", "source_ip"),
resource.TestCheckResourceAttr(resourceName, "stickiness.0.type", "lb_cookie"),
resource.TestCheckResourceAttr(resourceName, "stickiness.0.cookie_duration", "86400"),
),
},
},
})
}

func TestAccAWSLBTargetGroup_invalidStickinessForNLBShouldError(t *testing.T) {
func TestAccAWSLBTargetGroup_stickinessInvalidNLB(t *testing.T) {
resource.ParallelTest(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
CheckDestroy: testAccCheckAWSLBTargetGroupDestroy,
Steps: []resource.TestStep{
{
Config: testAccAWSLBTargetGroupConfig_NLBinvalidStickiness("TCP", true),
PlanOnly: true,
ExpectError: regexp.MustCompile("Network Load Balancers can only use \"source_ip\" stickiness."),
Config: testAccAWSLBTargetGroupConfig_stickinessValidity("TCP", "lb_cookie", true),
ExpectError: regexp.MustCompile("Stickiness type 'lb_cookie' is not supported for target groups with"),
},
{
Config: testAccAWSLBTargetGroupConfig_NLBinvalidStickiness("TCP", false),
PlanOnly: true,
ExpectError: regexp.MustCompile("Network Load Balancers can only use \"source_ip\" stickiness."),
Config: testAccAWSLBTargetGroupConfig_stickinessValidity("UDP", "lb_cookie", true),
ExpectError: regexp.MustCompile("Stickiness type 'lb_cookie' is not supported for target groups with"),
},
{
Config: testAccAWSLBTargetGroupConfig_NLBinvalidStickiness("UDP", true),
PlanOnly: true,
ExpectError: regexp.MustCompile("Network Load Balancers can only use \"source_ip\" stickiness."),
Config: testAccAWSLBTargetGroupConfig_stickinessValidity("TCP_UDP", "lb_cookie", true),
ExpectError: regexp.MustCompile("Stickiness type 'lb_cookie' is not supported for target groups with"),
},
{
Config: testAccAWSLBTargetGroupConfig_NLBinvalidStickiness("UDP", false),
PlanOnly: true,
ExpectError: regexp.MustCompile("Network Load Balancers can only use \"source_ip\" stickiness."),
Config: testAccAWSLBTargetGroupConfig_stickinessValidity("TCP_UDP", "lb_cookie", false),
PlanOnly: true,
ExpectNonEmptyPlan: true,
},
},
})
}

func TestAccAWSLBTargetGroup_stickinessInvalidALB(t *testing.T) {
resource.ParallelTest(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
CheckDestroy: testAccCheckAWSLBTargetGroupDestroy,
Steps: []resource.TestStep{
{
Config: testAccAWSLBTargetGroupConfig_NLBinvalidStickiness("TCP_UDP", true),
PlanOnly: true,
ExpectError: regexp.MustCompile("Network Load Balancers can only use \"source_ip\" stickiness."),
Config: testAccAWSLBTargetGroupConfig_stickinessValidity("HTTP", "source_ip", true),
ExpectError: regexp.MustCompile("Stickiness type 'source_ip' is not supported for target groups with"),
},
{
Config: testAccAWSLBTargetGroupConfig_NLBinvalidStickiness("TCP_UDP", false),
PlanOnly: true,
ExpectError: regexp.MustCompile("Network Load Balancers can only use \"source_ip\" stickiness."),
Config: testAccAWSLBTargetGroupConfig_stickinessValidity("HTTPS", "source_ip", true),
ExpectError: regexp.MustCompile("Stickiness type 'source_ip' is not supported for target groups with"),
},
{
Config: testAccAWSLBTargetGroupConfig_stickinessValidity("TLS", "lb_cookie", true),
ExpectError: regexp.MustCompile("Stickiness type 'lb_cookie' is not supported for target groups with"),
},
{
Config: testAccAWSLBTargetGroupConfig_stickinessValidity("TCP_UDP", "lb_cookie", false),
PlanOnly: true,
ExpectNonEmptyPlan: true,
},
},
})
Expand Down Expand Up @@ -2033,59 +2086,35 @@ resource "aws_vpc" "test" {
}
`

func testAccAWSLBTargetGroupConfig_NLBdefaultStickiness(protocol string) string {
func testAccAWSLBTargetGroupConfig_stickinessDefault(protocol string) string {
return fmt.Sprintf(`
resource "aws_lb_target_group" "test" {
name_prefix = "tf-"
port = 25
protocol = "%s"
protocol = %q
vpc_id = aws_vpc.test.id
}

resource "aws_vpc" "test" {
cidr_block = "10.0.0.0/16"

tags = {
Name = "testAccAWSLBTargetGroupConfig_namePrefix"
Name = "testAccAWSLBTargetGroupConfig_stickinessDefault"
}
}
`, protocol)
}

func testAccAWSLBTargetGroupConfig_NLBvalidStickiness(protocol string, enabled bool) string {
return fmt.Sprintf(`
resource "aws_lb_target_group" "test" {
name_prefix = "tf-"
port = 25
protocol = "%s"
vpc_id = aws_vpc.test.id

stickiness {
type = "source_ip"
enabled = %t
}
}

resource "aws_vpc" "test" {
cidr_block = "10.0.0.0/16"

tags = {
Name = "testAccAWSLBTargetGroupConfig_namePrefix"
}
}
`, protocol, enabled)
}

func testAccAWSLBTargetGroupConfig_NLBinvalidStickiness(protocol string, enabled bool) string {
func testAccAWSLBTargetGroupConfig_stickinessValidity(protocol, stickyType string, enabled bool) string {
return fmt.Sprintf(`
resource "aws_lb_target_group" "test" {
name_prefix = "tf-"
port = 25
protocol = "%s"
protocol = %q
vpc_id = aws_vpc.test.id

stickiness {
type = "lb_cookie"
type = %q
enabled = %t
}
}
Expand All @@ -2094,8 +2123,8 @@ resource "aws_vpc" "test" {
cidr_block = "10.0.0.0/16"

tags = {
Name = "testAccAWSLBTargetGroupConfig_namePrefix"
Name = "testAccAWSLBTargetGroupConfig_stickinessValidity"
}
}
`, protocol, enabled)
`, protocol, stickyType, enabled)
}
6 changes: 3 additions & 3 deletions website/docs/r/lb_target_group.html.markdown
Original file line number Diff line number Diff line change
Expand Up @@ -69,8 +69,8 @@ The following arguments are supported:
* `load_balancing_algorithm_type` - (Optional) Determines how the load balancer selects targets when routing requests. Only applicable for Application Load Balancer Target Groups. The value is `round_robin` or `least_outstanding_requests`. The default is `round_robin`.
* `lambda_multi_value_headers_enabled` - (Optional) Boolean whether the request and response headers exchanged between the load balancer and the Lambda function include arrays of values or strings. Only applies when `target_type` is `lambda`.
* `proxy_protocol_v2` - (Optional) Boolean to enable / disable support for proxy protocol v2 on Network Load Balancers. See [doc](https://docs.aws.amazon.com/elasticloadbalancing/latest/network/load-balancer-target-groups.html#proxy-protocol) for more information.
* `stickiness` - (Optional) A Stickiness block. Stickiness blocks are documented below.
* `health_check` - (Optional) A Health Check block. Health Check blocks are documented below.
* `stickiness` - (Optional, Maximum of 1) A Stickiness block. Stickiness blocks are documented below.
* `health_check` - (Optional, Maximum of 1) A Health Check block. Health Check blocks are documented below.
* `target_type` - (Optional, Forces new resource) The type of target that you must specify when registering targets with this target group.
The possible values are `instance` (targets are specified by instance ID) or `ip` (targets are specified by IP address) or `lambda` (targets are specified by lambda arn).
The default is `instance`. Note that you can't specify targets for a target group using both instance IDs and IP addresses.
Expand All @@ -82,7 +82,7 @@ You can't specify publicly routable IP addresses.
Stickiness Blocks (`stickiness`) support the following:

* `type` - (Required) The type of sticky sessions. The only current possible values are `lb_cookie` for ALBs and `source_ip` for NLBs.
* `cookie_duration` - (Optional) The time period, in seconds, during which requests from a client should be routed to the same target. After this time period expires, the load balancer-generated cookie is considered stale. The range is 1 second to 1 week (604800 seconds). The default value is 1 day (86400 seconds).
* `cookie_duration` - (Optional) Only used when the type is `lb_cookie`. The time period, in seconds, during which requests from a client should be routed to the same target. After this time period expires, the load balancer-generated cookie is considered stale. The range is 1 second to 1 week (604800 seconds). The default value is 1 day (86400 seconds).
* `enabled` - (Optional) Boolean to enable / disable `stickiness`. Default is `true`

~> **NOTE:** To help facilitate the authoring of modules that support target groups of any protocol, you can define `stickiness` regardless of the protocol chosen. However, for `TCP` target groups, `enabled` must be `false`.
Expand Down