@@ -1968,3 +1968,160 @@ func TestFreeablePrefixesBothIPv4AndIPv6(t *testing.T) {
19681968 freeablePrefixes = ds .FreeablePrefixes ("eni-nonexistent" )
19691969 assert .Nil (t , freeablePrefixes , "Should return nil for non-existent ENI" )
19701970}
1971+
1972+ func TestDeallocateEmptyCIDR (t * testing.T ) {
1973+ ds := NewDataStore (Testlog , NullCheckpoint {}, false , defaultNetworkCard )
1974+
1975+ // Setup test ENI
1976+ eniID := "eni-test-dealloc"
1977+ err := ds .AddENI (eniID , 1 , false , false , false )
1978+ assert .NoError (t , err )
1979+
1980+ // Mark ENI as excluded for pod IPs
1981+ err = ds .SetENIExcludedForPodIPs (eniID , true )
1982+ assert .NoError (t , err )
1983+
1984+ t .Run ("IPv4 CIDR cleanup on excluded ENI" , func (t * testing.T ) {
1985+ // Add IPv4 CIDR to ENI
1986+ ipv4Cidr := net.IPNet {IP : net .ParseIP ("192.168.1.0" ), Mask : net .CIDRMask (24 , 32 )}
1987+ err = ds .AddIPv4CidrToStore (eniID , ipv4Cidr , false )
1988+ assert .NoError (t , err )
1989+
1990+ // Verify CIDR exists
1991+ eni := ds .eniPool [eniID ]
1992+ cidrStr := ipv4Cidr .String ()
1993+ cidrInfo , exists := eni .AvailableIPv4Cidrs [cidrStr ]
1994+ assert .True (t , exists , "IPv4 CIDR should exist before cleanup" )
1995+
1996+ // Call deallocateEmptyCIDR
1997+ ds .deallocateEmptyCIDR (eniID , cidrInfo )
1998+
1999+ // Verify CIDR is removed
2000+ _ , exists = eni .AvailableIPv4Cidrs [cidrStr ]
2001+ assert .False (t , exists , "IPv4 CIDR should be removed after cleanup" )
2002+ })
2003+
2004+ t .Run ("IPv6 CIDR cleanup on excluded ENI" , func (t * testing.T ) {
2005+ // Add IPv6 CIDR to ENI
2006+ ipv6Cidr := net.IPNet {IP : net .ParseIP ("2001:db8:1::" ), Mask : net .CIDRMask (64 , 128 )}
2007+ err = ds .AddIPv6CidrToStore (eniID , ipv6Cidr , false )
2008+ assert .NoError (t , err )
2009+
2010+ // Verify CIDR exists
2011+ eni := ds .eniPool [eniID ]
2012+ cidrStr := ipv6Cidr .String ()
2013+ cidrInfo , exists := eni .IPv6Cidrs [cidrStr ]
2014+ assert .True (t , exists , "IPv6 CIDR should exist before cleanup" )
2015+
2016+ // Call deallocateEmptyCIDR
2017+ ds .deallocateEmptyCIDR (eniID , cidrInfo )
2018+
2019+ // Verify CIDR is removed
2020+ _ , exists = eni .IPv6Cidrs [cidrStr ]
2021+ assert .False (t , exists , "IPv6 CIDR should be removed after cleanup" )
2022+ })
2023+
2024+ t .Run ("Skip cleanup when ENI is not excluded" , func (t * testing.T ) {
2025+ // Create new non-excluded ENI
2026+ nonExcludedENI := "eni-not-excluded"
2027+ err := ds .AddENI (nonExcludedENI , 1 , false , false , false )
2028+ assert .NoError (t , err )
2029+
2030+ // Don't mark as excluded (default is false)
2031+
2032+ // Add IPv4 CIDR
2033+ ipv4Cidr := net.IPNet {IP : net .ParseIP ("192.168.2.0" ), Mask : net .CIDRMask (24 , 32 )}
2034+ err = ds .AddIPv4CidrToStore (nonExcludedENI , ipv4Cidr , false )
2035+ assert .NoError (t , err )
2036+
2037+ // Get CIDR info
2038+ eni := ds .eniPool [nonExcludedENI ]
2039+ cidrStr := ipv4Cidr .String ()
2040+ cidrInfo , exists := eni .AvailableIPv4Cidrs [cidrStr ]
2041+ assert .True (t , exists , "IPv4 CIDR should exist" )
2042+
2043+ // Call deallocateEmptyCIDR - should not remove CIDR since ENI is not excluded
2044+ ds .deallocateEmptyCIDR (nonExcludedENI , cidrInfo )
2045+
2046+ // Verify CIDR still exists
2047+ _ , exists = eni .AvailableIPv4Cidrs [cidrStr ]
2048+ assert .True (t , exists , "IPv4 CIDR should remain since ENI is not excluded" )
2049+ })
2050+
2051+ t .Run ("Skip cleanup when CIDR has assigned IPs" , func (t * testing.T ) {
2052+ // Create fresh datastore for isolated test
2053+ dsIsolated := NewDataStore (Testlog , NullCheckpoint {}, false , defaultNetworkCard )
2054+
2055+ // Create new excluded ENI
2056+ excludedENI := "eni-excluded-with-ips"
2057+ err := dsIsolated .AddENI (excludedENI , 1 , false , false , false )
2058+ assert .NoError (t , err )
2059+
2060+ err = dsIsolated .SetENIExcludedForPodIPs (excludedENI , true )
2061+ assert .NoError (t , err )
2062+
2063+ // Add IPv4 CIDR
2064+ ipv4Cidr := net.IPNet {IP : net .ParseIP ("192.168.10.0" ), Mask : net .CIDRMask (24 , 32 )}
2065+ err = dsIsolated .AddIPv4CidrToStore (excludedENI , ipv4Cidr , false )
2066+ assert .NoError (t , err )
2067+
2068+ // Manually assign an IP to the CIDR to simulate non-empty state
2069+ eni := dsIsolated .eniPool [excludedENI ]
2070+ cidrStr := ipv4Cidr .String ()
2071+ cidrInfo , exists := eni .AvailableIPv4Cidrs [cidrStr ]
2072+ assert .True (t , exists , "IPv4 CIDR should exist" )
2073+
2074+ // Manually mark an IP as assigned in the CIDR
2075+ testIP := net .ParseIP ("192.168.10.1" )
2076+ cidrInfo .IPAddresses [testIP .String ()] = & AddressInfo {
2077+ Address : testIP .String (),
2078+ IPAMKey : IPAMKey {NetworkName : "test" , ContainerID : "test-container" , IfName : "eth0" },
2079+ IPAMMetadata : IPAMMetadata {K8SPodNamespace : "default" , K8SPodName : "test-pod" },
2080+ AssignedTime : time .Now (),
2081+ }
2082+
2083+ // Verify CIDR has assigned IPs
2084+ assert .Greater (t , cidrInfo .AssignedIPAddressesInCidr (), 0 , "CIDR should have assigned IPs" )
2085+
2086+ // Call deallocateEmptyCIDR - should not remove CIDR since it has assigned IPs
2087+ dsIsolated .deallocateEmptyCIDR (excludedENI , cidrInfo )
2088+
2089+ // Verify CIDR still exists
2090+ _ , exists = eni .AvailableIPv4Cidrs [cidrStr ]
2091+ assert .True (t , exists , "IPv4 CIDR should remain since it has assigned IPs" )
2092+ })
2093+
2094+ t .Run ("Handle non-existent ENI gracefully" , func (t * testing.T ) {
2095+ // Create dummy CIDR info
2096+ dummyCidr := & CidrInfo {
2097+ Cidr : net.IPNet {IP : net .ParseIP ("192.168.4.0" ), Mask : net .CIDRMask (24 , 32 )},
2098+ AddressFamily : "4" ,
2099+ }
2100+
2101+ // Should not panic when ENI doesn't exist
2102+ ds .deallocateEmptyCIDR ("eni-nonexistent" , dummyCidr )
2103+ })
2104+
2105+ t .Run ("Handle non-existent CIDR gracefully" , func (t * testing.T ) {
2106+ // Create ENI
2107+ testENI := "eni-cidr-test"
2108+ err := ds .AddENI (testENI , 1 , false , false , false )
2109+ assert .NoError (t , err )
2110+
2111+ err = ds .SetENIExcludedForPodIPs (testENI , true )
2112+ assert .NoError (t , err )
2113+
2114+ // Create CIDR info that doesn't exist in the ENI
2115+ nonExistentCidr := & CidrInfo {
2116+ Cidr : net.IPNet {IP : net .ParseIP ("192.168.5.0" ), Mask : net .CIDRMask (24 , 32 )},
2117+ AddressFamily : "4" ,
2118+ }
2119+
2120+ // Should handle gracefully when CIDR doesn't exist
2121+ ds .deallocateEmptyCIDR (testENI , nonExistentCidr )
2122+
2123+ // Verify ENI still exists and is unaffected
2124+ _ , exists := ds .eniPool [testENI ]
2125+ assert .True (t , exists , "ENI should still exist" )
2126+ })
2127+ }
0 commit comments