Skip to content

Commit 1253ce8

Browse files
csnitkerkperry-godaddykeperry
authored andcommitted
Support for IPv6 and NLB (kubernetes-sigs#1677)
* Added dualstack support for NLB * Removed unneeded import * add tests * minor formatting changes * remove blank line * Update docs for ip address type * Corrected address type documentation * Corrected address type documentation * Corrected address type documentation Co-authored-by: Keith B. Perry <[email protected]> Co-authored-by: Keith Perry <[email protected]>
1 parent cf5cc3e commit 1253ce8

File tree

6 files changed

+259
-5
lines changed

6 files changed

+259
-5
lines changed

docs/guide/service/annotations.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
| service.beta.kubernetes.io/aws-load-balancer-type | string | | |
1515
| service.beta.kubernetes.io/aws-load-balancer-internal | boolean | false | |
1616
| [service.beta.kubernetes.io/aws-load-balancer-proxy-protocol](#proxy-protocol-v2) | string | | Set to `"*"` to enable |
17+
| service.beta.kubernetes.io/aws-load-balancer-ip-address-type | string | ipv4 | ipv4 \| dualstack |
1718
| service.beta.kubernetes.io/aws-load-balancer-access-log-enabled | boolean | false | |
1819
| service.beta.kubernetes.io/aws-load-balancer-access-log-s3-bucket-name | string | | |
1920
| service.beta.kubernetes.io/aws-load-balancer-access-log-s3-bucket-prefix | string | | |

pkg/annotations/constants.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ const (
4646
SvcLBSuffixSourceRanges = "load-balancer-source-ranges"
4747
SvcLBSuffixLoadBalancerType = "aws-load-balancer-type"
4848
SvcLBSuffixInternal = "aws-load-balancer-internal"
49+
SvcLBSuffixIPAddressType = "aws-load-balancer-ip-address-type"
4950
SvcLBSuffixProxyProtocol = "aws-load-balancer-proxy-protocol"
5051
SvcLBSuffixAccessLogEnabled = "aws-load-balancer-access-log-enabled"
5152
SvcLBSuffixAccessLogS3BucketName = "aws-load-balancer-access-log-s3-bucket-name"

pkg/service/model_build_load_balancer.go

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,10 @@ func (t *defaultModelBuildTask) buildLoadBalancer(ctx context.Context, scheme el
3434
}
3535

3636
func (t *defaultModelBuildTask) buildLoadBalancerSpec(ctx context.Context, scheme elbv2model.LoadBalancerScheme) (elbv2model.LoadBalancerSpec, error) {
37-
ipAddressType := elbv2model.IPAddressTypeIPV4
37+
ipAddressType, err := t.buildLoadBalancerIPAddressType(ctx)
38+
if err != nil {
39+
return elbv2model.LoadBalancerSpec{}, err
40+
}
3841
lbAttributes, err := t.buildLoadBalancerAttributes(ctx)
3942
if err != nil {
4043
return elbv2model.LoadBalancerSpec{}, err
@@ -60,6 +63,23 @@ func (t *defaultModelBuildTask) buildLoadBalancerSpec(ctx context.Context, schem
6063
return spec, nil
6164
}
6265

66+
func (t *defaultModelBuildTask) buildLoadBalancerIPAddressType(_ context.Context) (elbv2model.IPAddressType, error) {
67+
rawIPAddressType := ""
68+
if exists := t.annotationParser.ParseStringAnnotation(annotations.SvcLBSuffixIPAddressType, &rawIPAddressType, t.service.Annotations); !exists{
69+
return t.defaultIPAddressType, nil
70+
}
71+
72+
switch rawIPAddressType {
73+
case string(elbv2model.IPAddressTypeIPV4):
74+
return elbv2model.IPAddressTypeIPV4, nil
75+
case string(elbv2model.IPAddressTypeDualStack):
76+
return elbv2model.IPAddressTypeDualStack, nil
77+
default:
78+
return "", errors.Errorf("unknown IPAddressType: %v", rawIPAddressType)
79+
}
80+
}
81+
82+
6383
func (t *defaultModelBuildTask) buildLoadBalancerScheme(_ context.Context) (elbv2model.LoadBalancerScheme, error) {
6484
internal := false
6585
if _, err := t.annotationParser.ParseBoolAnnotation(annotations.SvcLBSuffixInternal, &internal, t.service.Annotations); err != nil {

pkg/service/model_build_load_balancer_test.go

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -306,3 +306,72 @@ func Test_defaultModelBuilderTask_resolveLoadBalancerSubnets(t *testing.T) {
306306
})
307307
}
308308
}
309+
310+
func Test_defaultModelBuildTask_buildLoadBalancerIPAddressType(t *testing.T) {
311+
tests := []struct {
312+
name string
313+
service *corev1.Service
314+
want elbv2.IPAddressType
315+
wantErr bool
316+
}{
317+
{
318+
name: "ipv4_specified_expect_ipv4",
319+
service: &corev1.Service{
320+
ObjectMeta: metav1.ObjectMeta{
321+
Annotations: map[string]string{"service.beta.kubernetes.io/aws-load-balancer-ip-address-type": "ipv4"},
322+
},
323+
},
324+
want: elbv2.IPAddressTypeIPV4,
325+
wantErr: false,
326+
},
327+
{
328+
name: "dualstack_specified_expect_dualstack",
329+
service: &corev1.Service{
330+
ObjectMeta: metav1.ObjectMeta{
331+
Annotations: map[string]string{"service.beta.kubernetes.io/aws-load-balancer-ip-address-type": "dualstack"},
332+
},
333+
},
334+
want: elbv2.IPAddressTypeDualStack,
335+
wantErr: false,
336+
},
337+
{
338+
name: "default_value_no_ip_address_type_specified_expect_ipv4",
339+
service: &corev1.Service{
340+
ObjectMeta: metav1.ObjectMeta{
341+
Annotations: map[string]string{"service.beta.kubernetes.io/aws-load-balancer-other-annotation": "somevalue"},
342+
},
343+
},
344+
want: elbv2.IPAddressTypeIPV4,
345+
wantErr: false,
346+
},
347+
{
348+
name: "invalid_value_expect_error",
349+
service: &corev1.Service{
350+
ObjectMeta: metav1.ObjectMeta{
351+
Annotations: map[string]string{"service.beta.kubernetes.io/aws-load-balancer-ip-address-type": "DualStack"},
352+
},
353+
},
354+
want: "",
355+
wantErr: true,
356+
},
357+
}
358+
for _, tt := range tests {
359+
t.Run(tt.name, func(t *testing.T) {
360+
parser := annotations.NewSuffixAnnotationParser("service.beta.kubernetes.io")
361+
builder := &defaultModelBuildTask{
362+
annotationParser: parser,
363+
service: tt.service,
364+
defaultIPAddressType: elbv2.IPAddressTypeIPV4,
365+
}
366+
367+
got, err := builder.buildLoadBalancerIPAddressType(context.Background())
368+
if (err != nil) != tt.wantErr {
369+
t.Errorf("buildLoadBalancerIPAddressType() error = %v, wantErr %v", err, tt.wantErr)
370+
return
371+
}
372+
if got != tt.want {
373+
t.Errorf("buildLoadBalancerIPAddressType() got = %v, want %v", got, tt.want)
374+
}
375+
})
376+
}
377+
}

pkg/service/model_builder.go

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package service
22

33
import (
44
"context"
5+
56
"github.com/aws/aws-sdk-go/service/ec2"
67
corev1 "k8s.io/api/core/v1"
78
"sigs.k8s.io/aws-load-balancer-controller/pkg/annotations"
@@ -48,6 +49,7 @@ func (b *defaultModelBuilder) Build(ctx context.Context, service *corev1.Service
4849
defaultAccessLogS3Enabled: false,
4950
defaultAccessLogsS3Bucket: "",
5051
defaultAccessLogsS3Prefix: "",
52+
defaultIPAddressType: elbv2model.IPAddressTypeIPV4,
5153
defaultLoadBalancingCrossZoneEnabled: false,
5254
defaultProxyProtocolV2Enabled: false,
5355
defaultHealthCheckProtocol: elbv2model.ProtocolTCP,
@@ -79,7 +81,8 @@ type defaultModelBuildTask struct {
7981
defaultAccessLogS3Enabled bool
8082
defaultAccessLogsS3Bucket string
8183
defaultAccessLogsS3Prefix string
82-
defaultLoadBalancingCrossZoneEnabled bool
84+
defaultIPAddressType elbv2model.IPAddressType
85+
defaultLoadBalancingCrossZoneEnabled bool
8386
defaultProxyProtocolV2Enabled bool
8487
defaultHealthCheckProtocol elbv2model.Protocol
8588
defaultHealthCheckPort string

pkg/service/model_builder_test.go

Lines changed: 163 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,9 @@ package service
22

33
import (
44
"context"
5+
"testing"
6+
"time"
7+
58
"github.com/aws/aws-sdk-go/aws"
69
"github.com/aws/aws-sdk-go/service/ec2"
710
"github.com/golang/mock/gomock"
@@ -12,8 +15,6 @@ import (
1215
mock_networking "sigs.k8s.io/aws-load-balancer-controller/mocks/networking"
1316
"sigs.k8s.io/aws-load-balancer-controller/pkg/annotations"
1417
"sigs.k8s.io/aws-load-balancer-controller/pkg/deploy"
15-
"testing"
16-
"time"
1718
)
1819

1920
func Test_defaultModelBuilderTask_Build(t *testing.T) {
@@ -223,7 +224,166 @@ func Test_defaultModelBuilderTask_Build(t *testing.T) {
223224
}
224225
`,
225226
wantNumResources: 4,
226-
},
227+
},
228+
{
229+
testName: "Dualstack service",
230+
svc: &corev1.Service{
231+
ObjectMeta: metav1.ObjectMeta{
232+
Name: "nlb-ip-svc-tls",
233+
Namespace: "default",
234+
UID: "bdca2bd0-bfc6-449a-88a3-03451f05f18c",
235+
Annotations: map[string]string{
236+
"service.beta.kubernetes.io/aws-load-balancer-type": "nlb-ip",
237+
"service.beta.kubernetes.io/aws-load-balancer-ip-address-type": "dualstack",
238+
},
239+
},
240+
Spec: corev1.ServiceSpec{
241+
Type: corev1.ServiceTypeLoadBalancer,
242+
Selector: map[string]string{"app": "hello"},
243+
Ports: []corev1.ServicePort{
244+
{
245+
Port: 80,
246+
TargetPort: intstr.FromInt(80),
247+
Protocol: corev1.ProtocolTCP,
248+
},
249+
},
250+
},
251+
},
252+
resolveViaDiscoveryCalls: []resolveViaDiscoveryCall{resolveViaDiscoveryCallForOneSubnet},
253+
wantError: false,
254+
wantValue: `
255+
{
256+
"id":"default/nlb-ip-svc-tls",
257+
"resources":{
258+
"AWS::ElasticLoadBalancingV2::Listener":{
259+
"80":{
260+
"spec":{
261+
"loadBalancerARN":{
262+
"$ref":"#/resources/AWS::ElasticLoadBalancingV2::LoadBalancer/LoadBalancer/status/loadBalancerARN"
263+
},
264+
"port":80,
265+
"protocol":"TCP",
266+
"defaultActions":[
267+
{
268+
"type":"forward",
269+
"forwardConfig":{
270+
"targetGroups":[
271+
{
272+
"targetGroupARN":{
273+
"$ref":"#/resources/AWS::ElasticLoadBalancingV2::TargetGroup/default/nlb-ip-svc-tls:80/status/targetGroupARN"
274+
}
275+
}
276+
]
277+
}
278+
}
279+
]
280+
}
281+
}
282+
},
283+
"AWS::ElasticLoadBalancingV2::LoadBalancer":{
284+
"LoadBalancer":{
285+
"spec":{
286+
"name":"k8s-default-nlbipsvc-4d831c6ca6",
287+
"type":"network",
288+
"scheme":"internet-facing",
289+
"ipAddressType":"dualstack",
290+
"subnetMapping":[
291+
{
292+
"subnetID":"subnet-1"
293+
}
294+
],
295+
"loadBalancerAttributes":[
296+
{
297+
"key":"access_logs.s3.enabled",
298+
"value":"false"
299+
},
300+
{
301+
"key":"access_logs.s3.bucket",
302+
"value":""
303+
},
304+
{
305+
"key":"access_logs.s3.prefix",
306+
"value":""
307+
},
308+
{
309+
"key":"load_balancing.cross_zone.enabled",
310+
"value":"false"
311+
}
312+
]
313+
}
314+
}
315+
},
316+
"AWS::ElasticLoadBalancingV2::TargetGroup":{
317+
"default/nlb-ip-svc-tls:80":{
318+
"spec":{
319+
"name":"k8s-default-nlbipsvc-d4818dcd51",
320+
"targetType":"ip",
321+
"port":80,
322+
"protocol":"TCP",
323+
"healthCheckConfig":{
324+
"port":"traffic-port",
325+
"protocol":"TCP",
326+
"intervalSeconds":10,
327+
"timeoutSeconds":10,
328+
"healthyThresholdCount":3,
329+
"unhealthyThresholdCount":3
330+
},
331+
"targetGroupAttributes":[
332+
{
333+
"key":"proxy_protocol_v2.enabled",
334+
"value":"false"
335+
}
336+
]
337+
}
338+
}
339+
},
340+
"K8S::ElasticLoadBalancingV2::TargetGroupBinding":{
341+
"default/nlb-ip-svc-tls:80":{
342+
"spec":{
343+
"template":{
344+
"metadata":{
345+
"name":"k8s-default-nlbipsvc-d4818dcd51",
346+
"namespace":"default",
347+
"creationTimestamp":null
348+
},
349+
"spec":{
350+
"targetGroupARN":{
351+
"$ref":"#/resources/AWS::ElasticLoadBalancingV2::TargetGroup/default/nlb-ip-svc-tls:80/status/targetGroupARN"
352+
},
353+
"targetType":"ip",
354+
"serviceRef":{
355+
"name":"nlb-ip-svc-tls",
356+
"port":80
357+
},
358+
"networking":{
359+
"ingress":[
360+
{
361+
"from":[
362+
{
363+
"ipBlock":{
364+
"cidr":"192.168.0.0/19"
365+
}
366+
}
367+
],
368+
"ports":[
369+
{
370+
"protocol":"TCP",
371+
"port":80
372+
}
373+
]
374+
}
375+
]
376+
}
377+
}
378+
}
379+
}
380+
}
381+
}
382+
}
383+
}
384+
`,
385+
wantNumResources: 4,
386+
},
227387
{
228388
testName: "Multiple listeners, multiple target groups",
229389
svc: &corev1.Service{

0 commit comments

Comments
 (0)