Skip to content

Commit ed44ef5

Browse files
authored
[fix]: Fix getNodeBalancerByStatus bug when IPv6 ingress is enabled (#386)
* Refactor getNodeBalancerByIPv4 to support IPv6 * Add tests for getNodeBalancerByStatus
1 parent cd5a75d commit ed44ef5

File tree

2 files changed

+85
-5
lines changed

2 files changed

+85
-5
lines changed

cloud/linode/loadbalancers.go

Lines changed: 17 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import (
77
"fmt"
88
"net"
99
"net/http"
10+
"net/netip"
1011
"os"
1112
"reflect"
1213
"strconv"
@@ -99,12 +100,17 @@ func (l *loadbalancers) getLatestServiceLoadBalancerStatus(ctx context.Context,
99100
return service.Status.LoadBalancer, nil
100101
}
101102

102-
// getNodeBalancerByStatus attempts to get the NodeBalancer from the IPv4 specified in the
103+
// getNodeBalancerByStatus attempts to get the NodeBalancer from the IP or hostname specified in the
103104
// most recent LoadBalancer status.
104105
func (l *loadbalancers) getNodeBalancerByStatus(ctx context.Context, service *v1.Service) (nb *linodego.NodeBalancer, err error) {
105106
for _, ingress := range service.Status.LoadBalancer.Ingress {
106107
if ingress.IP != "" {
107-
return l.getNodeBalancerByIPv4(ctx, service, ingress.IP)
108+
address, err := netip.ParseAddr(ingress.IP)
109+
if err != nil {
110+
klog.Warningf("failed to parse IP address %s from service %s/%s status, error: %s", ingress.IP, service.Namespace, service.Name, err)
111+
} else {
112+
return l.getNodeBalancerByIP(ctx, service, address)
113+
}
108114
}
109115
if ingress.Hostname != "" {
110116
return l.getNodeBalancerByHostname(ctx, service, ingress.Hostname)
@@ -603,16 +609,22 @@ func (l *loadbalancers) getNodeBalancerByHostname(ctx context.Context, service *
603609
return nil, lbNotFoundError{serviceNn: getServiceNn(service)}
604610
}
605611

606-
func (l *loadbalancers) getNodeBalancerByIPv4(ctx context.Context, service *v1.Service, ipv4 string) (*linodego.NodeBalancer, error) {
607-
filter := fmt.Sprintf(`{"ipv4": "%v"}`, ipv4)
612+
func (l *loadbalancers) getNodeBalancerByIP(ctx context.Context, service *v1.Service, ip netip.Addr) (*linodego.NodeBalancer, error) {
613+
var filter string
614+
if ip.Is6() {
615+
filter = fmt.Sprintf(`{"ipv6": "%v"}`, ip.String())
616+
} else {
617+
filter = fmt.Sprintf(`{"ipv4": "%v"}`, ip.String())
618+
}
619+
608620
lbs, err := l.client.ListNodeBalancers(ctx, &linodego.ListOptions{Filter: filter})
609621
if err != nil {
610622
return nil, err
611623
}
612624
if len(lbs) == 0 {
613625
return nil, lbNotFoundError{serviceNn: getServiceNn(service)}
614626
}
615-
klog.V(2).Infof("found NodeBalancer (%d) for service (%s) via IPv4 (%s)", lbs[0].ID, getServiceNn(service), ipv4)
627+
klog.V(2).Infof("found NodeBalancer (%d) for service (%s) via IP (%s)", lbs[0].ID, getServiceNn(service), ip.String())
616628
return &lbs[0], nil
617629
}
618630

cloud/linode/loadbalancers_test.go

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -282,6 +282,10 @@ func TestCCMLoadBalancers(t *testing.T) {
282282
name: "Create Load Balancer - Very long Service name",
283283
f: testVeryLongServiceName,
284284
},
285+
{
286+
name: "getNodeBalancerByStatus with IPv4 and IPv6 addresses",
287+
f: testGetNodeBalancerByStatus,
288+
},
285289
}
286290

287291
for _, tc := range testCases {
@@ -3660,6 +3664,70 @@ func testUpdateLoadBalancerNoNodes(t *testing.T, client *linodego.Client, _ *fak
36603664
}
36613665
}
36623666

3667+
func testGetNodeBalancerByStatus(t *testing.T, client *linodego.Client, _ *fakeAPI) {
3668+
t.Helper()
3669+
3670+
lb, assertion := newLoadbalancers(client, "us-west").(*loadbalancers)
3671+
if !assertion {
3672+
t.Error("type assertion failed")
3673+
}
3674+
fakeClientset := fake.NewSimpleClientset()
3675+
lb.kubeClient = fakeClientset
3676+
3677+
for _, test := range []struct {
3678+
name string
3679+
service *v1.Service
3680+
}{
3681+
{
3682+
name: "hostname only",
3683+
service: &v1.Service{
3684+
ObjectMeta: metav1.ObjectMeta{
3685+
Name: "hostname-ingress-" + randString(),
3686+
Annotations: map[string]string{annotations.AnnLinodeHostnameOnlyIngress: "true"},
3687+
},
3688+
},
3689+
},
3690+
{
3691+
name: "ipv4",
3692+
service: &v1.Service{
3693+
ObjectMeta: metav1.ObjectMeta{
3694+
Name: "ipv4-ingress-" + randString(),
3695+
},
3696+
},
3697+
},
3698+
{
3699+
name: "ipv4 and ipv6",
3700+
service: &v1.Service{
3701+
ObjectMeta: metav1.ObjectMeta{
3702+
Name: "ipv6-ingress-" + randString(),
3703+
Annotations: map[string]string{annotations.AnnLinodeEnableIPv6Ingress: "true"},
3704+
},
3705+
},
3706+
},
3707+
} {
3708+
t.Run(test.name, func(t *testing.T) {
3709+
expectedNB, err := lb.createNodeBalancer(t.Context(), "linodelb", test.service, []*linodego.NodeBalancerConfigCreateOptions{})
3710+
if err != nil {
3711+
t.Fatal(err)
3712+
}
3713+
test.service.Status.LoadBalancer = *makeLoadBalancerStatus(test.service, expectedNB)
3714+
3715+
actualNB, err := lb.getNodeBalancerByStatus(t.Context(), test.service)
3716+
if err != nil {
3717+
t.Fatal(err)
3718+
}
3719+
3720+
if expectedNB.ID != actualNB.ID {
3721+
t.Error("unexpected nodebalancer ID")
3722+
t.Logf("expected: %v", expectedNB.ID)
3723+
t.Logf("actual: %v", actualNB.ID)
3724+
}
3725+
3726+
_ = lb.EnsureLoadBalancerDeleted(t.Context(), "linodelb", test.service)
3727+
})
3728+
}
3729+
}
3730+
36633731
func testGetNodeBalancerForServiceIDDoesNotExist(t *testing.T, client *linodego.Client, _ *fakeAPI) {
36643732
t.Helper()
36653733

0 commit comments

Comments
 (0)