Skip to content

Commit d9473a3

Browse files
authored
Fix the problem that if available ip is 0 but there is a value in excludeIPs, the fixed ip is used as the ip in excludeIPs but the error noAddressAvaliable is still reported. (#5561)
Signed-off-by: clyi <clyi@alauda.io>
1 parent edfe8af commit d9473a3

File tree

2 files changed

+98
-3
lines changed

2 files changed

+98
-3
lines changed

pkg/controller/pod.go

Lines changed: 33 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1632,18 +1632,18 @@ func (c *Controller) getPodDefaultSubnet(pod *v1.Pod) (*kubeovnv1.Subnet, error)
16321632

16331633
switch subnet.Spec.Protocol {
16341634
case kubeovnv1.ProtocolDual:
1635-
if subnet.Status.V6AvailableIPs == 0 {
1635+
if subnet.Status.V6AvailableIPs == 0 && !c.podCanUseExcludeIPs(pod, subnet) {
16361636
klog.Infof("there's no available ipv6 address in subnet %s, try next one", subnet.Name)
16371637
continue
16381638
}
16391639
fallthrough
16401640
case kubeovnv1.ProtocolIPv4:
1641-
if subnet.Status.V4AvailableIPs == 0 {
1641+
if subnet.Status.V4AvailableIPs == 0 && !c.podCanUseExcludeIPs(pod, subnet) {
16421642
klog.Infof("there's no available ipv4 address in subnet %s, try next one", subnet.Name)
16431643
continue
16441644
}
16451645
case kubeovnv1.ProtocolIPv6:
1646-
if subnet.Status.V6AvailableIPs == 0 {
1646+
if subnet.Status.V6AvailableIPs == 0 && !c.podCanUseExcludeIPs(pod, subnet) {
16471647
klog.Infof("there's no available ipv6 address in subnet %s, try next one", subnet.Name)
16481648
continue
16491649
}
@@ -1653,6 +1653,36 @@ func (c *Controller) getPodDefaultSubnet(pod *v1.Pod) (*kubeovnv1.Subnet, error)
16531653
return nil, ipam.ErrNoAvailable
16541654
}
16551655

1656+
func (c *Controller) podCanUseExcludeIPs(pod *v1.Pod, subnet *kubeovnv1.Subnet) bool {
1657+
if ipAddr := pod.Annotations[util.IPAddressAnnotation]; ipAddr != "" {
1658+
return c.checkIPsInExcludeList(ipAddr, subnet.Spec.ExcludeIps, subnet.Spec.CIDRBlock)
1659+
}
1660+
if ipPool := pod.Annotations[util.IPPoolAnnotation]; ipPool != "" {
1661+
return c.checkIPsInExcludeList(ipPool, subnet.Spec.ExcludeIps, subnet.Spec.CIDRBlock)
1662+
}
1663+
1664+
return false
1665+
}
1666+
1667+
func (c *Controller) checkIPsInExcludeList(ips string, excludeIPs []string, cidr string) bool {
1668+
expandedExcludeIPs := util.ExpandExcludeIPs(excludeIPs, cidr)
1669+
1670+
for ipAddr := range strings.SplitSeq(strings.TrimSpace(ips), ",") {
1671+
ipAddr = strings.TrimSpace(ipAddr)
1672+
if ipAddr == "" {
1673+
continue
1674+
}
1675+
1676+
for _, excludeIP := range expandedExcludeIPs {
1677+
if util.ContainsIPs(excludeIP, ipAddr) {
1678+
klog.V(3).Infof("IP %s is found in exclude IP %s, allowing allocation", ipAddr, excludeIP)
1679+
return true
1680+
}
1681+
}
1682+
}
1683+
return false
1684+
}
1685+
16561686
type providerType int
16571687

16581688
const (

test/e2e/kube-ovn/subnet/subnet.go

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -289,6 +289,71 @@ var _ = framework.Describe("[group:subnet]", func() {
289289
}
290290
})
291291

292+
framework.ConformanceIt("should allow pod with fixed IP or IP pool in excludeIPs when available IPs is 0", func() {
293+
ginkgo.By("Creating a small subnet with very limited IP range")
294+
var smallCIDR string
295+
var excludeIPs []string
296+
var usableIPs []string
297+
298+
switch f.ClusterIPFamily {
299+
case "ipv4":
300+
smallCIDR = "192.168.200.0/30"
301+
excludeIPs = []string{"192.168.200.1", "192.168.200.2"}
302+
usableIPs = []string{"192.168.200.2"}
303+
case "ipv6":
304+
smallCIDR = "fd00:192:168:200::/126"
305+
excludeIPs = []string{"fd00:192:168:200::1", "fd00:192:168:200::2"}
306+
usableIPs = []string{"fd00:192:168:200::2"}
307+
case "dual":
308+
smallCIDR = "192.168.200.0/30,fd00:192:168:200::/126"
309+
excludeIPs = []string{"192.168.200.1", "192.168.200.2", "fd00:192:168:200::1", "fd00:192:168:200::2"}
310+
usableIPs = []string{"192.168.200.2", "fd00:192:168:200::2"}
311+
}
312+
313+
subnetName = "small-subnet-" + framework.RandomSuffix()
314+
ginkgo.By(fmt.Sprintf("Creating small subnet %s with exclude IPs %v", subnetName, excludeIPs))
315+
smallSubnet := framework.MakeSubnet(subnetName, "", smallCIDR, "", "", "", excludeIPs, nil, []string{namespaceName})
316+
smallSubnet = subnetClient.CreateSync(smallSubnet)
317+
318+
ginkgo.By("Verifying available IPs is 0 after excluding the only usable IPs")
319+
framework.ExpectZero(smallSubnet.Status.V4AvailableIPs + smallSubnet.Status.V6AvailableIPs)
320+
321+
// Test cases: both fixed IP and IP pool annotations
322+
testCases := []struct {
323+
name string
324+
annotationKey string
325+
annotationValue string
326+
}{
327+
{
328+
name: "fix ip",
329+
annotationKey: util.IPAddressAnnotation,
330+
annotationValue: strings.Join(usableIPs, ","),
331+
},
332+
{
333+
name: "fix ip pool",
334+
annotationKey: util.IPPoolAnnotation,
335+
annotationValue: strings.Join(usableIPs, ","),
336+
},
337+
}
338+
339+
for _, tc := range testCases {
340+
ginkgo.By(fmt.Sprintf("Creating pod with %s annotation that matches excludeIPs", tc.name))
341+
podName = fmt.Sprintf("pod-%s-%s", strings.ReplaceAll(tc.name, " ", "-"), framework.RandomSuffix())
342+
annotations := map[string]string{
343+
tc.annotationKey: tc.annotationValue,
344+
}
345+
cmd := []string{"sleep", "infinity"}
346+
pod := framework.MakePrivilegedPod(namespaceName, podName, nil, annotations, f.KubeOVNImage, cmd, nil)
347+
pod = podClient.CreateSync(pod)
348+
349+
ginkgo.By(fmt.Sprintf("Verifying pod gets the %s IPs despite availableIPs being 0", tc.name))
350+
framework.ExpectHaveKeyWithValue(pod.Annotations, tc.annotationKey, tc.annotationValue)
351+
352+
ginkgo.By(fmt.Sprintf("Cleaning up test pod for %s", tc.name))
353+
podClient.DeleteSync(podName)
354+
}
355+
})
356+
292357
framework.ConformanceIt("should create subnet with centralized gateway", func() {
293358
ginkgo.By("Getting nodes")
294359
nodes, err := e2enode.GetReadySchedulableNodes(context.Background(), cs)

0 commit comments

Comments
 (0)