Repository: kubernetes-sigs/aws-alb-ingress-controller
Pull request #1087 (2019-12-01 14:17:43)

Load balancing algorithm for target groups support
ALB's now support an additional algorithm, set at the target group level. This PR adds support for this.

Fixes https://github.com/kubernetes-sigs/aws-alb-ingress-controller/issues/1082

https://aws.amazon.com/about-aws/whats-new/2019/11/application-load-balancer-now-supports-least-outstanding-requests-algorithm-for-load-balancing-requests/


Patch:
diff --git a/docs/guide/ingress/annotation.md b/docs/guide/ingress/annotation.md
index 9cba7aacb..1e56e9881 100644
--- a/docs/guide/ingress/annotation.md
+++ b/docs/guide/ingress/annotation.md
@@ -455,6 +455,10 @@ Custom attributes to LoadBalancers and TargetGroups can be controlled with follo
             ```
             alb.ingress.kubernetes.io/target-group-attributes: stickiness.enabled=true,stickiness.lb_cookie.duration_seconds=60
             ```
+        - set load balancing algorithm to least outstanding requests
+                    ```
+                    alb.ingress.kubernetes.io/target-group-attributes: load_balancing.algorithm.type=least_outstanding_requests
+                    ```
 
 ## Resource Tags
 ALB Ingress controller will automatically apply following tags to AWS resources(ALB/TargetGroups/SecurityGroups) created.
diff --git a/internal/alb/tg/attributes.go b/internal/alb/tg/attributes.go
index c9f31a2ec..3ab96a323 100644
--- a/internal/alb/tg/attributes.go
+++ b/internal/alb/tg/attributes.go
@@ -18,12 +18,14 @@ const (
 	StickinessEnabledKey                 = "stickiness.enabled"
 	StickinessTypeKey                    = "stickiness.type"
 	StickinessLbCookieDurationSecondsKey = "stickiness.lb_cookie.duration_seconds"
+	LoadBalancingAlgorithmTypeKey        = "load_balancing.algorithm.type"
 
 	DeregistrationDelayTimeoutSeconds = 300
 	SlowStartDurationSeconds          = 0
 	StickinessEnabled                 = false
 	StickinessType                    = "lb_cookie"
 	StickinessLbCookieDurationSeconds = 86400
+	LoadBalancingAlgorithmType        = "round_robin"
 )
 
 // Attributes represents the desired state of attributes for a target group.
@@ -55,6 +57,11 @@ type Attributes struct {
 	// considered stale. The range is 1 second to 1 week (604800 seconds). The
 	// default value is 1 day (86400 seconds).
 	StickinessLbCookieDurationSeconds int64
+
+	// LoadBalancingAlgorithmType: load_balancing.algorithm.type - The load balancing algorithm determines
+	// how the load balancer selects targets when routing requests. The value is round_robin or
+	// least_outstanding_requests. The default is round_robin.
+	LoadBalancingAlgorithmType string
 }
 
 func NewAttributes(attrs []*elbv2.TargetGroupAttribute) (a *Attributes, err error) {
@@ -64,6 +71,7 @@ func NewAttributes(attrs []*elbv2.TargetGroupAttribute) (a *Attributes, err erro
 		StickinessEnabled:                 StickinessEnabled,
 		StickinessType:                    StickinessType,
 		StickinessLbCookieDurationSeconds: StickinessLbCookieDurationSeconds,
+		LoadBalancingAlgorithmType:        LoadBalancingAlgorithmType,
 	}
 	var e error
 	for _, attr := range attrs {
@@ -103,6 +111,11 @@ func NewAttributes(attrs []*elbv2.TargetGroupAttribute) (a *Attributes, err erro
 			if a.StickinessLbCookieDurationSeconds < 1 || a.StickinessLbCookieDurationSeconds > 604800 {
 				return a, fmt.Errorf("%s must be within 1-604800 seconds, not %v", attrKey, attrValue)
 			}
+		case LoadBalancingAlgorithmTypeKey:
+			a.LoadBalancingAlgorithmType = attrValue
+			if attrValue != "round_robin" && attrValue != "least_outstanding_requests" {
+				return a, fmt.Errorf("invalid target group attribute value %s=%s", attrKey, attrValue)
+			}
 		default:
 			e = NewInvalidAttribute(attrKey)
 		}
@@ -180,6 +193,10 @@ func attributesChangeSet(a, b *Attributes) (changeSet []*elbv2.TargetGroupAttrib
 		changeSet = append(changeSet, tgAttribute(StickinessLbCookieDurationSecondsKey, fmt.Sprintf("%v", b.StickinessLbCookieDurationSeconds)))
 	}
 
+	if a.LoadBalancingAlgorithmType != b.LoadBalancingAlgorithmType {
+		changeSet = append(changeSet, tgAttribute(LoadBalancingAlgorithmTypeKey, b.LoadBalancingAlgorithmType))
+	}
+
 	return
 }
 


Test patch:
diff --git a/internal/alb/tg/attributes_test.go b/internal/alb/tg/attributes_test.go
index 74d7129d9..98ff4beb8 100644
--- a/internal/alb/tg/attributes_test.go
+++ b/internal/alb/tg/attributes_test.go
@@ -138,6 +138,18 @@ func Test_NewAttributes(t *testing.T) {
 			attributes: []*elbv2.TargetGroupAttribute{tgAttribute(StickinessLbCookieDurationSecondsKey, "error")},
 		},
 
+		{
+			name:       "LoadBalancingAlgorithmTypeKey is default",
+			ok:         true,
+			attributes: []*elbv2.TargetGroupAttribute{tgAttribute(LoadBalancingAlgorithmTypeKey, "round_robin")},
+			output:     MustNewAttributes([]*elbv2.TargetGroupAttribute{tgAttribute(LoadBalancingAlgorithmTypeKey, "round_robin")}),
+		},
+		{
+			name:       "LoadBalancingAlgorithmTypeKey is not round_robin or least_outstanding_requests",
+			ok:         false,
+			attributes: []*elbv2.TargetGroupAttribute{tgAttribute(LoadBalancingAlgorithmTypeKey, "error")},
+		},
+
 		{
 			name:       "Invalid attribute",
 			ok:         false,
@@ -290,6 +302,18 @@ func Test_attributesChangeSet(t *testing.T) {
 			b:         MustNewAttributes([]*elbv2.TargetGroupAttribute{tgAttribute(StickinessLbCookieDurationSecondsKey, "501")}),
 			changeSet: []*elbv2.TargetGroupAttribute{tgAttribute(StickinessLbCookieDurationSecondsKey, "501")},
 		},
+
+		{
+			name: "LoadBalancingAlgorithmType: a=default b=default",
+			a:    MustNewAttributes([]*elbv2.TargetGroupAttribute{tgAttribute(LoadBalancingAlgorithmTypeKey, "round_robin")}),
+			b:    MustNewAttributes([]*elbv2.TargetGroupAttribute{tgAttribute(LoadBalancingAlgorithmTypeKey, "round_robin")}),
+		},
+		{
+			name:      "LoadBalancingAlgorithmType: a=default b=nondefault a!=b",
+			a:         MustNewAttributes([]*elbv2.TargetGroupAttribute{tgAttribute(LoadBalancingAlgorithmTypeKey, "round_robin")}),
+			b:         MustNewAttributes([]*elbv2.TargetGroupAttribute{tgAttribute(LoadBalancingAlgorithmTypeKey, "least_outstanding_requests")}),
+			changeSet: []*elbv2.TargetGroupAttribute{tgAttribute(LoadBalancingAlgorithmTypeKey, "least_outstanding_requests")},
+		},
 	} {
 		t.Run(tc.name, func(t *testing.T) {
 			changeSet := attributesChangeSet(tc.a, tc.b)