Skip to content

Commit 23828d5

Browse files
committed
Don't reconcile security groups on older NLBs
Network load balancers created under older versions of CAPI did not have security groups added at creation, and therefore could not have any security groups set at all. Allow NLBs created in this configuration to continue reconciling. Signed-off-by: Nolan Brubaker <[email protected]>
1 parent 50a7f5b commit 23828d5

File tree

2 files changed

+112
-6
lines changed

2 files changed

+112
-6
lines changed

pkg/cloud/services/elb/loadbalancer.go

Lines changed: 20 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -137,7 +137,7 @@ func (s *Service) reconcileV2LB(lbSpec *infrav1.AWSLoadBalancerSpec) error {
137137
}
138138

139139
// Reconcile the security groups from the spec and the ones currently attached to the load balancer
140-
if !sets.NewString(lb.SecurityGroupIDs...).Equal(sets.NewString(spec.SecurityGroupIDs...)) {
140+
if shouldReconcileSGs(s.scope, lb, spec.SecurityGroupIDs) {
141141
_, err := s.ELBV2Client.SetSecurityGroups(&elbv2.SetSecurityGroupsInput{
142142
LoadBalancerArn: &lb.ARN,
143143
SecurityGroups: aws.StringSlice(spec.SecurityGroupIDs),
@@ -1536,11 +1536,11 @@ func fromSDKTypeToLB(v *elbv2.LoadBalancer, attrs []*elbv2.LoadBalancerAttribute
15361536
availabilityZones[i] = az.ZoneName
15371537
}
15381538
res := &infrav1.LoadBalancer{
1539-
ARN: aws.StringValue(v.LoadBalancerArn),
1540-
Name: aws.StringValue(v.LoadBalancerName),
1541-
Scheme: infrav1.ELBScheme(aws.StringValue(v.Scheme)),
1542-
SubnetIDs: aws.StringValueSlice(subnetIds),
1543-
// SecurityGroupIDs: aws.StringValueSlice(v.SecurityGroups),
1539+
ARN: aws.StringValue(v.LoadBalancerArn),
1540+
Name: aws.StringValue(v.LoadBalancerName),
1541+
Scheme: infrav1.ELBScheme(aws.StringValue(v.Scheme)),
1542+
SubnetIDs: aws.StringValueSlice(subnetIds),
1543+
SecurityGroupIDs: aws.StringValueSlice(v.SecurityGroups),
15441544
AvailabilityZones: aws.StringValueSlice(availabilityZones),
15451545
DNSName: aws.StringValue(v.DNSName),
15461546
Tags: converters.V2TagsToMap(tags),
@@ -1567,3 +1567,17 @@ func chunkELBs(names []string) [][]string {
15671567
}
15681568
return chunked
15691569
}
1570+
1571+
func shouldReconcileSGs(scope scope.ELBScope, lb *infrav1.LoadBalancer, specSGs []string) bool {
1572+
// Backwards compat: NetworkLoadBalancers were not always capable of having security groups attached.
1573+
// Once created without a security group, the NLB can never have any added.
1574+
// (https://docs.aws.amazon.com/elasticloadbalancing/latest/network/load-balancer-security-groups.html)
1575+
if lb.LoadBalancerType == infrav1.LoadBalancerTypeNLB && len(lb.SecurityGroupIDs) == 0 {
1576+
scope.Info("Pre-existing NLB %s without security groups, cannot reconcile security groups.", lb.Name)
1577+
return false
1578+
}
1579+
if !sets.NewString(lb.SecurityGroupIDs...).Equal(sets.NewString(specSGs...)) {
1580+
return true
1581+
}
1582+
return true
1583+
}

pkg/cloud/services/elb/loadbalancer_test.go

Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1847,6 +1847,96 @@ func TestReconcileV2LB(t *testing.T) {
18471847
}
18481848
},
18491849
},
1850+
{
1851+
name: "ensure NLB without SGs doesn't attempt to add new SGs",
1852+
spec: func(spec infrav1.LoadBalancer) infrav1.LoadBalancer {
1853+
return spec
1854+
},
1855+
awsCluster: func(acl infrav1.AWSCluster) infrav1.AWSCluster {
1856+
acl.Spec.ControlPlaneLoadBalancer.Name = aws.String(elbName)
1857+
acl.Spec.ControlPlaneLoadBalancer.LoadBalancerType = infrav1.LoadBalancerTypeNLB
1858+
acl.Spec.ControlPlaneLoadBalancer.AdditionalSecurityGroups = []string{"sg-001"}
1859+
return acl
1860+
},
1861+
elbV2APIMocks: func(m *mocks.MockELBV2APIMockRecorder) {
1862+
m.DescribeLoadBalancers(gomock.Eq(&elbv2.DescribeLoadBalancersInput{
1863+
Names: aws.StringSlice([]string{elbName}),
1864+
})).
1865+
Return(&elbv2.DescribeLoadBalancersOutput{
1866+
LoadBalancers: []*elbv2.LoadBalancer{
1867+
{
1868+
LoadBalancerArn: aws.String(elbArn),
1869+
LoadBalancerName: aws.String(elbName),
1870+
Scheme: aws.String(string(infrav1.ELBSchemeInternetFacing)),
1871+
AvailabilityZones: []*elbv2.AvailabilityZone{
1872+
{
1873+
SubnetId: aws.String(clusterSubnetID),
1874+
ZoneName: aws.String(az),
1875+
},
1876+
},
1877+
VpcId: aws.String(vpcID),
1878+
},
1879+
},
1880+
}, nil)
1881+
m.ModifyLoadBalancerAttributes(&elbv2.ModifyLoadBalancerAttributesInput{
1882+
LoadBalancerArn: aws.String(elbArn),
1883+
Attributes: []*elbv2.LoadBalancerAttribute{
1884+
{
1885+
Key: aws.String("load_balancing.cross_zone.enabled"),
1886+
Value: aws.String("false"),
1887+
},
1888+
}}).
1889+
Return(&elbv2.ModifyLoadBalancerAttributesOutput{}, nil)
1890+
m.DescribeLoadBalancerAttributes(&elbv2.DescribeLoadBalancerAttributesInput{LoadBalancerArn: aws.String(elbArn)}).Return(
1891+
&elbv2.DescribeLoadBalancerAttributesOutput{
1892+
Attributes: []*elbv2.LoadBalancerAttribute{
1893+
{
1894+
Key: aws.String("load_balancing.cross_zone.enabled"),
1895+
Value: aws.String("false"),
1896+
},
1897+
{
1898+
Key: aws.String(infrav1.ClusterTagKey(clusterName)),
1899+
Value: aws.String(string(infrav1.ResourceLifecycleOwned)),
1900+
},
1901+
},
1902+
},
1903+
nil,
1904+
)
1905+
m.DescribeTags(&elbv2.DescribeTagsInput{ResourceArns: []*string{aws.String(elbArn)}}).Return(
1906+
&elbv2.DescribeTagsOutput{
1907+
TagDescriptions: []*elbv2.TagDescription{
1908+
{
1909+
ResourceArn: aws.String(elbArn),
1910+
Tags: []*elbv2.Tag{
1911+
{
1912+
Key: aws.String(infrav1.ClusterTagKey(clusterName)),
1913+
Value: aws.String(string(infrav1.ResourceLifecycleOwned)),
1914+
},
1915+
},
1916+
},
1917+
},
1918+
},
1919+
nil,
1920+
)
1921+
1922+
// Avoid the need to sort the AddTagsInput.Tags slice
1923+
m.AddTags(gomock.AssignableToTypeOf(&elbv2.AddTagsInput{})).Return(&elbv2.AddTagsOutput{}, nil)
1924+
1925+
m.SetSubnets(&elbv2.SetSubnetsInput{
1926+
LoadBalancerArn: aws.String(elbArn),
1927+
Subnets: []*string{},
1928+
}).Return(&elbv2.SetSubnetsOutput{}, nil)
1929+
},
1930+
check: func(t *testing.T, lb *infrav1.LoadBalancer, err error) {
1931+
t.Helper()
1932+
if err != nil {
1933+
t.Fatalf("did not expect error: %v", err)
1934+
}
1935+
if len(lb.SecurityGroupIDs) != 0 {
1936+
t.Errorf("Expected LB to contain 0 security groups, got %v", len(lb.SecurityGroupIDs))
1937+
}
1938+
},
1939+
},
18501940
}
18511941

18521942
for _, tc := range tests {
@@ -1897,6 +1987,7 @@ func TestReconcileV2LB(t *testing.T) {
18971987
}
18981988
err = s.reconcileV2LB(clusterScope.ControlPlaneLoadBalancer())
18991989
lb := s.scope.Network().APIServerELB
1990+
19001991
tc.check(t, &lb, err)
19011992
})
19021993
}
@@ -2886,6 +2977,7 @@ func TestGetHealthCheckProtocol(t *testing.T) {
28862977
})
28872978
}
28882979
}
2980+
28892981
func setupScheme() (*runtime.Scheme, error) {
28902982
scheme := runtime.NewScheme()
28912983
if err := clusterv1.AddToScheme(scheme); err != nil {

0 commit comments

Comments
 (0)