Skip to content

Commit 6ee80c3

Browse files
committed
Fix: order must match specified subnets
1 parent e96c227 commit 6ee80c3

File tree

2 files changed

+101
-6
lines changed

2 files changed

+101
-6
lines changed

pkg/service/model_build_load_balancer.go

Lines changed: 23 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -300,11 +300,17 @@ func (t *defaultModelBuildTask) buildLoadBalancerTags(ctx context.Context) (map[
300300
func (t *defaultModelBuildTask) buildLoadBalancerSubnetMappings(_ context.Context, ipAddressType elbv2model.IPAddressType, scheme elbv2model.LoadBalancerScheme, ec2Subnets []ec2types.Subnet) ([]elbv2model.SubnetMapping, error) {
301301
var eipAllocation []string
302302
eipConfigured := t.annotationParser.ParseStringSliceAnnotation(annotations.SvcLBSuffixEIPAllocations, &eipAllocation, t.service.Annotations)
303+
var subnetsId []string
304+
subnetIDConfigured := t.annotationParser.ParseStringSliceAnnotation(annotations.SvcLBSuffixSubnets, &subnetsId, t.service.Annotations)
305+
reorderedSubnets := append(ec2Subnets[:0:0], ec2Subnets...)
306+
if subnetIDConfigured {
307+
reorderedSubnets = sortSubnetsBySubnetIdConfigured(subnetsId, ec2Subnets)
308+
}
303309
if eipConfigured {
304310
if scheme != elbv2model.LoadBalancerSchemeInternetFacing {
305311
return nil, errors.Errorf("EIP allocations can only be set for internet facing load balancers")
306312
}
307-
if len(eipAllocation) != len(ec2Subnets) {
313+
if len(eipAllocation) != len(reorderedSubnets) {
308314
return nil, errors.Errorf("count of EIP allocations (%d) and subnets (%d) must match", len(eipAllocation), len(ec2Subnets))
309315
}
310316
}
@@ -317,7 +323,7 @@ func (t *defaultModelBuildTask) buildLoadBalancerSubnetMappings(_ context.Contex
317323
return nil, errors.Errorf("private IPv4 addresses can only be set for internal load balancers")
318324
}
319325
// TODO: consider relax this requirement as ELBv2 API don't require every subnet to have IPv4 address specified.
320-
if len(rawIPv4Addresses) != len(ec2Subnets) {
326+
if len(rawIPv4Addresses) != len(reorderedSubnets) {
321327
return nil, errors.Errorf("count of private IPv4 addresses (%d) and subnets (%d) must match", len(rawIPv4Addresses), len(ec2Subnets))
322328
}
323329
for _, rawIPv4Address := range rawIPv4Addresses {
@@ -340,7 +346,7 @@ func (t *defaultModelBuildTask) buildLoadBalancerSubnetMappings(_ context.Contex
340346
return nil, errors.Errorf("IPv6 addresses can only be set for dualstack load balancers")
341347
}
342348
// TODO: consider relax this requirement as ELBv2 API don't require every subnet to have IPv6 address specified.
343-
if len(rawIPv6Addresses) != len(ec2Subnets) {
349+
if len(rawIPv6Addresses) != len(reorderedSubnets) {
344350
return nil, errors.Errorf("count of IPv6 addresses (%d) and subnets (%d) must match", len(rawIPv6Addresses), len(ec2Subnets))
345351
}
346352
for _, rawIPv6Address := range rawIPv6Addresses {
@@ -355,8 +361,8 @@ func (t *defaultModelBuildTask) buildLoadBalancerSubnetMappings(_ context.Contex
355361
}
356362
}
357363

358-
subnetMappings := make([]elbv2model.SubnetMapping, 0, len(ec2Subnets))
359-
for idx, subnet := range ec2Subnets {
364+
subnetMappings := make([]elbv2model.SubnetMapping, 0, len(reorderedSubnets))
365+
for idx, subnet := range reorderedSubnets {
360366
mapping := elbv2model.SubnetMapping{
361367
SubnetID: awssdk.ToString(subnet.SubnetId),
362368
}
@@ -534,3 +540,15 @@ func (t *defaultModelBuildTask) buildLoadBalancerName(_ context.Context, scheme
534540
sanitizedName := invalidLoadBalancerNamePattern.ReplaceAllString(t.service.Name, "")
535541
return fmt.Sprintf("k8s-%.8s-%.8s-%.10s", sanitizedNamespace, sanitizedName, uuid), nil
536542
}
543+
544+
func sortSubnetsBySubnetIdConfigured(subnetId []string, ec2Subnets []ec2types.Subnet) []ec2types.Subnet {
545+
subnetIndex := make(map[string]int)
546+
for index, id := range subnetId {
547+
subnetIndex[id] = index
548+
}
549+
sortedSubnets := append(ec2Subnets[:0:0], ec2Subnets...)
550+
sort.Slice(sortedSubnets, func(i, j int) bool {
551+
return subnetIndex[*sortedSubnets[i].SubnetId] < subnetIndex[*sortedSubnets[j].SubnetId]
552+
})
553+
return sortedSubnets
554+
}

pkg/service/model_build_load_balancer_test.go

Lines changed: 78 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,11 @@ package service
33
import (
44
"context"
55
"errors"
6+
"testing"
7+
8+
awssdk "github.com/aws/aws-sdk-go-v2/aws"
69
ec2types "github.com/aws/aws-sdk-go-v2/service/ec2/types"
710
elbv2types "github.com/aws/aws-sdk-go-v2/service/elasticloadbalancingv2/types"
8-
"testing"
911

1012
"k8s.io/apimachinery/pkg/util/sets"
1113
"sigs.k8s.io/aws-load-balancer-controller/pkg/model/core"
@@ -1445,3 +1447,78 @@ func Test_defaultModelBuildTask_buildLoadBalancerName(t *testing.T) {
14451447
})
14461448
}
14471449
}
1450+
1451+
func Test_sortSubnetsBySubnetIdConfigured(t *testing.T) {
1452+
type args struct {
1453+
subnetId []string
1454+
ec2Subnets []ec2types.Subnet
1455+
}
1456+
tests := []struct {
1457+
name string
1458+
args args
1459+
wantSubnets []ec2types.Subnet
1460+
}{
1461+
{
1462+
name: "Sorting",
1463+
args: args{
1464+
subnetId: []string{"subnet-a", "subnet-b", "subnet-c"},
1465+
ec2Subnets: []ec2types.Subnet{
1466+
{
1467+
SubnetId: awssdk.String("subnet-b"),
1468+
},
1469+
{
1470+
SubnetId: awssdk.String("subnet-a"),
1471+
},
1472+
{
1473+
SubnetId: awssdk.String("subnet-c"),
1474+
},
1475+
},
1476+
},
1477+
wantSubnets: []ec2types.Subnet{
1478+
{
1479+
SubnetId: awssdk.String("subnet-a"),
1480+
},
1481+
{
1482+
SubnetId: awssdk.String("subnet-b"),
1483+
},
1484+
{
1485+
SubnetId: awssdk.String("subnet-c"),
1486+
},
1487+
},
1488+
},
1489+
{
1490+
name: "sorted subnets",
1491+
args: args{
1492+
subnetId: []string{"subnet-a", "subnet-b", "subnet-c"},
1493+
ec2Subnets: []ec2types.Subnet{
1494+
{
1495+
SubnetId: awssdk.String("subnet-a"),
1496+
},
1497+
{
1498+
SubnetId: awssdk.String("subnet-b"),
1499+
},
1500+
{
1501+
SubnetId: awssdk.String("subnet-c"),
1502+
},
1503+
},
1504+
},
1505+
wantSubnets: []ec2types.Subnet{
1506+
{
1507+
SubnetId: awssdk.String("subnet-a"),
1508+
},
1509+
{
1510+
SubnetId: awssdk.String("subnet-b"),
1511+
},
1512+
{
1513+
SubnetId: awssdk.String("subnet-c"),
1514+
},
1515+
},
1516+
},
1517+
}
1518+
for _, tt := range tests {
1519+
t.Run(tt.name, func(t *testing.T) {
1520+
result := sortSubnetsBySubnetIdConfigured(tt.args.subnetId, tt.args.ec2Subnets)
1521+
assert.Equal(t, tt.wantSubnets, result)
1522+
})
1523+
}
1524+
}

0 commit comments

Comments
 (0)