Skip to content

Commit 06f20a0

Browse files
Deregister elb targets before registering targets
1 parent ea961d6 commit 06f20a0

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
@@ -859,18 +859,7 @@ func (c *Cloud) ensureTargetGroup(ctx context.Context, targetGroup *elbv2types.T
859859

860860
func (c *Cloud) ensureTargetGroupTargets(ctx context.Context, tgARN string, expectedTargets []*elbv2types.TargetDescription, actualTargets []*elbv2types.TargetDescription) error {
861861
targetsToRegister, targetsToDeregister := c.diffTargetGroupTargets(expectedTargets, actualTargets)
862-
if len(targetsToRegister) > 0 {
863-
targetsToRegisterChunks := c.chunkTargetDescriptions(targetsToRegister, defaultRegisterTargetsChunkSize)
864-
for _, targetsChunk := range targetsToRegisterChunks {
865-
req := &elbv2.RegisterTargetsInput{
866-
TargetGroupArn: aws.String(tgARN),
867-
Targets: targetsChunk,
868-
}
869-
if _, err := c.elbv2.RegisterTargets(ctx, req); err != nil {
870-
return fmt.Errorf("error trying to register targets in target group: %q", err)
871-
}
872-
}
873-
}
862+
// deregister targets prior to registering to allow instance replacements when the LB is at max instance capacity
874863
if len(targetsToDeregister) > 0 {
875864
targetsToDeregisterChunks := c.chunkTargetDescriptions(targetsToDeregister, defaultDeregisterTargetsChunkSize)
876865
for _, targetsChunk := range targetsToDeregisterChunks {
@@ -883,6 +872,18 @@ func (c *Cloud) ensureTargetGroupTargets(ctx context.Context, tgARN string, expe
883872
}
884873
}
885874
}
875+
if len(targetsToRegister) > 0 {
876+
targetsToRegisterChunks := c.chunkTargetDescriptions(targetsToRegister, defaultRegisterTargetsChunkSize)
877+
for _, targetsChunk := range targetsToRegisterChunks {
878+
req := &elbv2.RegisterTargetsInput{
879+
TargetGroupArn: aws.String(tgARN),
880+
Targets: targetsChunk,
881+
}
882+
if _, err := c.elbv2.RegisterTargets(ctx, req); err != nil {
883+
return fmt.Errorf("error trying to register targets in target group: %q", err)
884+
}
885+
}
886+
}
886887
return nil
887888
}
888889

pkg/providers/v1/aws_loadbalancer_test.go

Lines changed: 154 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1921,3 +1921,157 @@ func TestCloud_reconcileTargetGroupsAttributes(t *testing.T) {
19211921
})
19221922
}
19231923
}
1924+
1925+
// Test-specific mock for ELB v2 client that embeds MockedFakeELBV2
1926+
type mockELBV2ClientForEnsureTargetGroupTargets struct {
1927+
*MockedFakeELBV2
1928+
1929+
MaxTargets int
1930+
NumTargets int
1931+
}
1932+
1933+
func (m *mockELBV2ClientForEnsureTargetGroupTargets) RegisterTargets(ctx context.Context, input *elbv2.RegisterTargetsInput, optFns ...func(*elbv2.Options)) (*elbv2.RegisterTargetsOutput, error) {
1934+
m.NumTargets += len(input.Targets)
1935+
1936+
if m.NumTargets > m.MaxTargets {
1937+
return nil, fmt.Errorf("TooManyTargets")
1938+
}
1939+
1940+
return m.MockedFakeELBV2.RegisterTargets(ctx, input, optFns...)
1941+
}
1942+
1943+
func (m *mockELBV2ClientForEnsureTargetGroupTargets) DeregisterTargets(ctx context.Context, input *elbv2.DeregisterTargetsInput, optFns ...func(*elbv2.Options)) (*elbv2.DeregisterTargetsOutput, error) {
1944+
m.NumTargets -= len(input.Targets)
1945+
1946+
return m.MockedFakeELBV2.DeregisterTargets(ctx, input, optFns...)
1947+
}
1948+
1949+
func TestCloud_ensureTargetGroupTargets(t *testing.T) {
1950+
testTargetGroupArn := "arn:aws:elasticloadbalancing:us-east-1:123456789012:targetgroup/test-tg/1234567890123456"
1951+
1952+
tests := []struct {
1953+
name string
1954+
maxTargets int
1955+
expectedTargets []*elbv2types.TargetDescription
1956+
actualTargets []*elbv2types.TargetDescription
1957+
expectedError string
1958+
description string
1959+
}{
1960+
{
1961+
name: "target replacement at max target limit should not fail",
1962+
maxTargets: 4,
1963+
expectedTargets: []*elbv2types.TargetDescription{
1964+
{
1965+
Id: aws.String("i-abcdefg1"),
1966+
Port: aws.Int32(8080),
1967+
},
1968+
{
1969+
Id: aws.String("i-abcdefg2"),
1970+
Port: aws.Int32(8080),
1971+
},
1972+
{
1973+
Id: aws.String("i-abcdefg3"),
1974+
Port: aws.Int32(8080),
1975+
},
1976+
{
1977+
Id: aws.String("i-abcdefg4"),
1978+
Port: aws.Int32(8080),
1979+
},
1980+
},
1981+
actualTargets: []*elbv2types.TargetDescription{
1982+
{
1983+
Id: aws.String("i-replacement"),
1984+
Port: aws.Int32(8080),
1985+
},
1986+
{
1987+
Id: aws.String("i-abcdefg2"),
1988+
Port: aws.Int32(8080),
1989+
},
1990+
{
1991+
Id: aws.String("i-abcdefg3"),
1992+
Port: aws.Int32(8080),
1993+
},
1994+
{
1995+
Id: aws.String("i-abcdefg4"),
1996+
Port: aws.Int32(8080),
1997+
},
1998+
},
1999+
description: "Function should succeed when replacing an instance for a LB at max capacity",
2000+
},
2001+
{
2002+
name: "exceeding max target limit should fail",
2003+
maxTargets: 4,
2004+
expectedTargets: []*elbv2types.TargetDescription{
2005+
{
2006+
Id: aws.String("i-abcdefg1"),
2007+
Port: aws.Int32(8080),
2008+
},
2009+
{
2010+
Id: aws.String("i-abcdefg2"),
2011+
Port: aws.Int32(8080),
2012+
},
2013+
{
2014+
Id: aws.String("i-abcdefg3"),
2015+
Port: aws.Int32(8080),
2016+
},
2017+
{
2018+
Id: aws.String("i-abcdefg4"),
2019+
Port: aws.Int32(8080),
2020+
},
2021+
{
2022+
Id: aws.String("i-abcdefg5"),
2023+
Port: aws.Int32(8080),
2024+
},
2025+
},
2026+
actualTargets: []*elbv2types.TargetDescription{
2027+
{
2028+
Id: aws.String("i-replacement"),
2029+
Port: aws.Int32(8080),
2030+
},
2031+
{
2032+
Id: aws.String("i-abcdefg2"),
2033+
Port: aws.Int32(8080),
2034+
},
2035+
{
2036+
Id: aws.String("i-abcdefg3"),
2037+
Port: aws.Int32(8080),
2038+
},
2039+
{
2040+
Id: aws.String("i-abcdefg4"),
2041+
Port: aws.Int32(8080),
2042+
},
2043+
},
2044+
expectedError: "TooManyTargets",
2045+
description: "Function should fail when adding an instance to a LB at max capacity",
2046+
},
2047+
}
2048+
2049+
for _, tt := range tests {
2050+
t.Run(tt.name, func(t *testing.T) {
2051+
mockClient := &mockELBV2ClientForEnsureTargetGroupTargets{
2052+
MockedFakeELBV2: &MockedFakeELBV2{
2053+
LoadBalancers: []*elbv2types.LoadBalancer{},
2054+
TargetGroups: []*elbv2types.TargetGroup{},
2055+
Listeners: []*elbv2types.Listener{},
2056+
LoadBalancerAttributes: make(map[string]map[string]string),
2057+
Tags: make(map[string][]elbv2types.Tag),
2058+
RegisteredInstances: make(map[string][]string),
2059+
},
2060+
MaxTargets: tt.maxTargets,
2061+
NumTargets: len(tt.actualTargets),
2062+
}
2063+
c := &Cloud{
2064+
elbv2: mockClient,
2065+
}
2066+
2067+
err := c.ensureTargetGroupTargets(context.TODO(), testTargetGroupArn, tt.expectedTargets, tt.actualTargets)
2068+
2069+
if len(tt.expectedError) > 0 {
2070+
assert.Error(t, err, "Expected error for test case: %s", tt.description)
2071+
assert.Contains(t, err.Error(), tt.expectedError, "Error message should contain expected text for test case: %s", tt.description)
2072+
} else {
2073+
assert.NoError(t, err, "Expected no error for test case: %s", tt.description)
2074+
}
2075+
})
2076+
}
2077+
}

0 commit comments

Comments
 (0)