Skip to content

Commit 120475c

Browse files
Deregister elb targets before registering targets
1 parent a0402ce commit 120475c

2 files changed

Lines changed: 167 additions & 12 deletions

File tree

pkg/providers/v1/aws_loadbalancer.go

Lines changed: 13 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -690,18 +690,7 @@ func (c *Cloud) ensureTargetGroup(ctx context.Context, targetGroup *elbv2types.T
690690

691691
func (c *Cloud) ensureTargetGroupTargets(ctx context.Context, tgARN string, expectedTargets []*elbv2types.TargetDescription, actualTargets []*elbv2types.TargetDescription) error {
692692
targetsToRegister, targetsToDeregister := c.diffTargetGroupTargets(expectedTargets, actualTargets)
693-
if len(targetsToRegister) > 0 {
694-
targetsToRegisterChunks := c.chunkTargetDescriptions(targetsToRegister, defaultRegisterTargetsChunkSize)
695-
for _, targetsChunk := range targetsToRegisterChunks {
696-
req := &elbv2.RegisterTargetsInput{
697-
TargetGroupArn: aws.String(tgARN),
698-
Targets: targetsChunk,
699-
}
700-
if _, err := c.elbv2.RegisterTargets(ctx, req); err != nil {
701-
return fmt.Errorf("error trying to register targets in target group: %q", err)
702-
}
703-
}
704-
}
693+
// deregister targets prior to registering to allow instance replacements when the LB is at max instance capacity
705694
if len(targetsToDeregister) > 0 {
706695
targetsToDeregisterChunks := c.chunkTargetDescriptions(targetsToDeregister, defaultDeregisterTargetsChunkSize)
707696
for _, targetsChunk := range targetsToDeregisterChunks {
@@ -872,6 +861,18 @@ func (c *Cloud) updateInstanceSecurityGroupsForNLB(ctx context.Context, lbName s
872861
}
873862
}
874863
}
864+
if len(targetsToRegister) > 0 {
865+
targetsToRegisterChunks := c.chunkTargetDescriptions(targetsToRegister, defaultRegisterTargetsChunkSize)
866+
for _, targetsChunk := range targetsToRegisterChunks {
867+
req := &elbv2.RegisterTargetsInput{
868+
TargetGroupArn: aws.String(tgARN),
869+
Targets: targetsChunk,
870+
}
871+
if _, err := c.elbv2.RegisterTargets(ctx, req); err != nil {
872+
return fmt.Errorf("error trying to register targets in target group: %q", err)
873+
}
874+
}
875+
}
875876
return nil
876877
}
877878

pkg/providers/v1/aws_loadbalancer_test.go

Lines changed: 154 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1120,3 +1120,157 @@ func TestEnsureSSLNegotiationPolicyErrorHandling(t *testing.T) {
11201120
})
11211121
}
11221122
}
1123+
1124+
// Test-specific mock for ELB v2 client that embeds MockedFakeELBV2
1125+
type mockELBV2ClientForEnsureTargetGroupTargets struct {
1126+
*MockedFakeELBV2
1127+
1128+
MaxTargets int
1129+
NumTargets int
1130+
}
1131+
1132+
func (m *mockELBV2ClientForEnsureTargetGroupTargets) RegisterTargets(ctx context.Context, input *elbv2.RegisterTargetsInput, optFns ...func(*elbv2.Options)) (*elbv2.RegisterTargetsOutput, error) {
1133+
m.NumTargets += len(input.Targets)
1134+
1135+
if m.NumTargets > m.MaxTargets {
1136+
return nil, fmt.Errorf("TooManyTargets")
1137+
}
1138+
1139+
return m.MockedFakeELBV2.RegisterTargets(ctx, input, optFns...)
1140+
}
1141+
1142+
func (m *mockELBV2ClientForEnsureTargetGroupTargets) DeregisterTargets(ctx context.Context, input *elbv2.DeregisterTargetsInput, optFns ...func(*elbv2.Options)) (*elbv2.DeregisterTargetsOutput, error) {
1143+
m.NumTargets -= len(input.Targets)
1144+
1145+
return m.MockedFakeELBV2.DeregisterTargets(ctx, input, optFns...)
1146+
}
1147+
1148+
func TestCloud_ensureTargetGroupTargets(t *testing.T) {
1149+
testTargetGroupArn := "arn:aws:elasticloadbalancing:us-east-1:123456789012:targetgroup/test-tg/1234567890123456"
1150+
1151+
tests := []struct {
1152+
name string
1153+
maxTargets int
1154+
expectedTargets []*elbv2types.TargetDescription
1155+
actualTargets []*elbv2types.TargetDescription
1156+
expectedError string
1157+
description string
1158+
}{
1159+
{
1160+
name: "target replacement at max target limit should not fail",
1161+
maxTargets: 4,
1162+
expectedTargets: []*elbv2types.TargetDescription{
1163+
{
1164+
Id: aws.String("i-abcdefg1"),
1165+
Port: aws.Int32(8080),
1166+
},
1167+
{
1168+
Id: aws.String("i-abcdefg2"),
1169+
Port: aws.Int32(8080),
1170+
},
1171+
{
1172+
Id: aws.String("i-abcdefg3"),
1173+
Port: aws.Int32(8080),
1174+
},
1175+
{
1176+
Id: aws.String("i-abcdefg4"),
1177+
Port: aws.Int32(8080),
1178+
},
1179+
},
1180+
actualTargets: []*elbv2types.TargetDescription{
1181+
{
1182+
Id: aws.String("i-replacement"),
1183+
Port: aws.Int32(8080),
1184+
},
1185+
{
1186+
Id: aws.String("i-abcdefg2"),
1187+
Port: aws.Int32(8080),
1188+
},
1189+
{
1190+
Id: aws.String("i-abcdefg3"),
1191+
Port: aws.Int32(8080),
1192+
},
1193+
{
1194+
Id: aws.String("i-abcdefg4"),
1195+
Port: aws.Int32(8080),
1196+
},
1197+
},
1198+
description: "Function should succeed when replacing an instance for a LB at max capacity",
1199+
},
1200+
{
1201+
name: "exceeding max target limit should fail",
1202+
maxTargets: 4,
1203+
expectedTargets: []*elbv2types.TargetDescription{
1204+
{
1205+
Id: aws.String("i-abcdefg1"),
1206+
Port: aws.Int32(8080),
1207+
},
1208+
{
1209+
Id: aws.String("i-abcdefg2"),
1210+
Port: aws.Int32(8080),
1211+
},
1212+
{
1213+
Id: aws.String("i-abcdefg3"),
1214+
Port: aws.Int32(8080),
1215+
},
1216+
{
1217+
Id: aws.String("i-abcdefg4"),
1218+
Port: aws.Int32(8080),
1219+
},
1220+
{
1221+
Id: aws.String("i-abcdefg5"),
1222+
Port: aws.Int32(8080),
1223+
},
1224+
},
1225+
actualTargets: []*elbv2types.TargetDescription{
1226+
{
1227+
Id: aws.String("i-replacement"),
1228+
Port: aws.Int32(8080),
1229+
},
1230+
{
1231+
Id: aws.String("i-abcdefg2"),
1232+
Port: aws.Int32(8080),
1233+
},
1234+
{
1235+
Id: aws.String("i-abcdefg3"),
1236+
Port: aws.Int32(8080),
1237+
},
1238+
{
1239+
Id: aws.String("i-abcdefg4"),
1240+
Port: aws.Int32(8080),
1241+
},
1242+
},
1243+
expectedError: "TooManyTargets",
1244+
description: "Function should fail when adding an instance to a LB at max capacity",
1245+
},
1246+
}
1247+
1248+
for _, tt := range tests {
1249+
t.Run(tt.name, func(t *testing.T) {
1250+
mockClient := &mockELBV2ClientForEnsureTargetGroupTargets{
1251+
MockedFakeELBV2: &MockedFakeELBV2{
1252+
LoadBalancers: []*elbv2types.LoadBalancer{},
1253+
TargetGroups: []*elbv2types.TargetGroup{},
1254+
Listeners: []*elbv2types.Listener{},
1255+
LoadBalancerAttributes: make(map[string]map[string]string),
1256+
Tags: make(map[string][]elbv2types.Tag),
1257+
RegisteredInstances: make(map[string][]string),
1258+
},
1259+
MaxTargets: tt.maxTargets,
1260+
NumTargets: len(tt.actualTargets),
1261+
}
1262+
c := &Cloud{
1263+
elbv2: mockClient,
1264+
}
1265+
1266+
err := c.ensureTargetGroupTargets(context.TODO(), testTargetGroupArn, tt.expectedTargets, tt.actualTargets)
1267+
1268+
if len(tt.expectedError) > 0 {
1269+
assert.Error(t, err, "Expected error for test case: %s", tt.description)
1270+
assert.Contains(t, err.Error(), tt.expectedError, "Error message should contain expected text for test case: %s", tt.description)
1271+
} else {
1272+
assert.NoError(t, err, "Expected no error for test case: %s", tt.description)
1273+
}
1274+
})
1275+
}
1276+
}

0 commit comments

Comments
 (0)