Skip to content

Commit 1fceea2

Browse files
fix(slr): address family and familypolicy isn't correct (#5349)
Signed-off-by: SkalaNetworks <contact@skala.network>
1 parent 49bbed8 commit 1fceea2

File tree

2 files changed

+107
-0
lines changed

2 files changed

+107
-0
lines changed

pkg/controller/switch_lb_rule.go

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import (
1313
"k8s.io/apimachinery/pkg/util/intstr"
1414
"k8s.io/client-go/tools/cache"
1515
"k8s.io/klog/v2"
16+
"k8s.io/utils/set"
1617

1718
kubeovnv1 "github.com/kubeovn/kube-ovn/pkg/apis/kubeovn/v1"
1819
"github.com/kubeovn/kube-ovn/pkg/ovsdb/ovnnb"
@@ -323,6 +324,12 @@ func generateHeadlessService(slr *kubeovnv1.SwitchLBRule, oldSvc *corev1.Service
323324
}
324325

325326
name = generateSvcName(slr.Name)
327+
328+
// We need to set the correct IPFamilies and IPFamilyPolicy on the service
329+
// If the VIP is an IPv4, the Service needs to be configured in IPv4, and the opposite for an IPv6
330+
// If the VIP has a mix of IPv4s and IPv6s, the Service must be DualStack, with both families set
331+
families, policy := getIPFamilies(slr.Spec.Vip)
332+
326333
if oldSvc != nil {
327334
newSvc = oldSvc.DeepCopy()
328335
newSvc.Name = name
@@ -331,6 +338,8 @@ func generateHeadlessService(slr *kubeovnv1.SwitchLBRule, oldSvc *corev1.Service
331338
newSvc.Spec.Ports = ports
332339
newSvc.Spec.Selector = selectors
333340
newSvc.Spec.SessionAffinity = corev1.ServiceAffinity(slr.Spec.SessionAffinity)
341+
newSvc.Spec.IPFamilies = families
342+
newSvc.Spec.IPFamilyPolicy = &policy
334343
} else {
335344
newSvc = &corev1.Service{
336345
ObjectMeta: metav1.ObjectMeta{
@@ -344,6 +353,8 @@ func generateHeadlessService(slr *kubeovnv1.SwitchLBRule, oldSvc *corev1.Service
344353
ClusterIP: corev1.ClusterIPNone,
345354
Type: corev1.ServiceTypeClusterIP,
346355
SessionAffinity: corev1.ServiceAffinity(slr.Spec.SessionAffinity),
356+
IPFamilies: families,
357+
IPFamilyPolicy: &policy,
347358
},
348359
}
349360
}
@@ -406,3 +417,25 @@ func generateEndpoints(slr *kubeovnv1.SwitchLBRule, oldEps *corev1.Endpoints) *c
406417
}
407418
return newEps
408419
}
420+
421+
// getIPFamilies returns the IP families (IPv6/IPv4) for a set of IPs within a VIP
422+
// This function is used to correctly construct the Service of a SwitchLBRule
423+
// It also returns the corresponding IPFamilyPolicy to set in the Service
424+
func getIPFamilies(vip string) (families []corev1.IPFamily, policy corev1.IPFamilyPolicy) {
425+
// Check every IP in the VIP, assess if it is an IPv6 or an IPv4
426+
ipFamilies := set.New[corev1.IPFamily]()
427+
for ip := range strings.SplitSeq(vip, ",") {
428+
switch util.CheckProtocol(ip) {
429+
case kubeovnv1.ProtocolIPv6:
430+
ipFamilies.Insert(corev1.IPv6Protocol)
431+
case kubeovnv1.ProtocolIPv4:
432+
ipFamilies.Insert(corev1.IPv4Protocol)
433+
}
434+
}
435+
436+
policy = corev1.IPFamilyPolicySingleStack
437+
if ipFamilies.Len() > 1 {
438+
policy = corev1.IPFamilyPolicyPreferDualStack
439+
}
440+
return ipFamilies.SortedList(), policy
441+
}
Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
package controller
2+
3+
import (
4+
"reflect"
5+
"testing"
6+
7+
corev1 "k8s.io/api/core/v1"
8+
)
9+
10+
func Test_getIPFamilies(t *testing.T) {
11+
tests := []struct {
12+
name string
13+
vip string
14+
expectedFamilies []corev1.IPFamily
15+
expectedFamilyPolicy corev1.IPFamilyPolicy
16+
}{
17+
{
18+
name: "IPv4 VIP",
19+
vip: "10.0.0.0",
20+
expectedFamilies: []corev1.IPFamily{corev1.IPv4Protocol},
21+
expectedFamilyPolicy: corev1.IPFamilyPolicySingleStack,
22+
},
23+
{
24+
name: "IPv6 VIP",
25+
vip: "fd00::1",
26+
expectedFamilies: []corev1.IPFamily{corev1.IPv6Protocol},
27+
expectedFamilyPolicy: corev1.IPFamilyPolicySingleStack,
28+
},
29+
{
30+
name: "IPv6/v4 VIP",
31+
vip: "fd00::1,10.0.0.0",
32+
expectedFamilies: []corev1.IPFamily{corev1.IPv4Protocol, corev1.IPv6Protocol},
33+
expectedFamilyPolicy: corev1.IPFamilyPolicyPreferDualStack,
34+
},
35+
{
36+
name: "IPv4/v6 VIP",
37+
vip: "10.0.0.0,fd00::1",
38+
expectedFamilies: []corev1.IPFamily{corev1.IPv4Protocol, corev1.IPv6Protocol},
39+
expectedFamilyPolicy: corev1.IPFamilyPolicyPreferDualStack,
40+
},
41+
{
42+
name: "Many v4 VIP",
43+
vip: "10.0.0.0,10.0.0.1,10.0.0.2",
44+
expectedFamilies: []corev1.IPFamily{corev1.IPv4Protocol},
45+
expectedFamilyPolicy: corev1.IPFamilyPolicySingleStack,
46+
},
47+
{
48+
name: "Many v6 VIP",
49+
vip: "fd00::1,fd00::2,fd00::3",
50+
expectedFamilies: []corev1.IPFamily{corev1.IPv6Protocol},
51+
expectedFamilyPolicy: corev1.IPFamilyPolicySingleStack,
52+
},
53+
{
54+
name: "Many v6/v4 VIP",
55+
vip: "fd00::1,fd00::2,fd00::3,10.0.0.0,10.0.0.1,10.0.0.2",
56+
expectedFamilies: []corev1.IPFamily{corev1.IPv4Protocol, corev1.IPv6Protocol},
57+
expectedFamilyPolicy: corev1.IPFamilyPolicyPreferDualStack,
58+
},
59+
}
60+
61+
for _, tt := range tests {
62+
t.Run(tt.name, func(t *testing.T) {
63+
families, policy := getIPFamilies(tt.vip)
64+
65+
if !reflect.DeepEqual(families, tt.expectedFamilies) {
66+
t.Errorf("Expected families %v, but got %v", tt.expectedFamilies, families)
67+
}
68+
69+
if policy != tt.expectedFamilyPolicy {
70+
t.Errorf("Expected familiyPolicy %s, but got %s", tt.expectedFamilyPolicy, policy)
71+
}
72+
})
73+
}
74+
}

0 commit comments

Comments
 (0)