@@ -6,6 +6,7 @@ package restserver
66import (
77 "context"
88 "fmt"
9+ "math/rand"
910 "os"
1011 "reflect"
1112 "strconv"
@@ -21,6 +22,7 @@ import (
2122 "github.com/google/uuid"
2223 "github.com/pkg/errors"
2324 "github.com/stretchr/testify/assert"
25+ "golang.org/x/exp/maps"
2426)
2527
2628const (
@@ -336,7 +338,99 @@ func TestReconcileNCWithEmptyState(t *testing.T) {
336338 t .Errorf ("Unexpected failure on reconcile with no state %d" , returnCode )
337339 }
338340
339- validateNCStateAfterReconcile (t , nil , expectedNcCount , expectedAssignedPods )
341+ validateNCStateAfterReconcile (t , nil , expectedNcCount , expectedAssignedPods , nil )
342+ }
343+
344+ // TestReconcileNCWithEmptyStateAndPendingRelease tests the case where there is
345+ // no state (node reboot) and there are pending release IPs in the NNC that
346+ // may have been deallocated and should not be made available for assignment
347+ // to pods.
348+ func TestReconcileNCWithEmptyStateAndPendingRelease (t * testing.T ) {
349+ restartService ()
350+ setEnv (t )
351+ setOrchestratorTypeInternal (cns .KubernetesCRD )
352+
353+ expectedAssignedPods := make (map [string ]cns.PodInfo )
354+
355+ secondaryIPConfigs := make (map [string ]cns.SecondaryIPConfig )
356+ for i := 6 ; i < 22 ; i ++ {
357+ ipaddress := "10.0.0." + strconv .Itoa (i )
358+ secIPConfig := newSecondaryIPConfig (ipaddress , - 1 )
359+ ipID := uuid .New ()
360+ secondaryIPConfigs [ipID .String ()] = secIPConfig
361+ }
362+ pending := func () []string {
363+ numPending := rand .Intn (len (secondaryIPConfigs )) + 1 //nolint:gosec // weak rand is sufficient in test
364+ pendingIPs := []string {}
365+ for k := range secondaryIPConfigs {
366+ if numPending == 0 {
367+ break
368+ }
369+ pendingIPs = append (pendingIPs , k )
370+ numPending --
371+ }
372+ return pendingIPs
373+ }()
374+ req := generateNetworkContainerRequest (secondaryIPConfigs , "reconcileNc1" , "-1" )
375+ returnCode := svc .ReconcileNCState (req , expectedAssignedPods , & v1alpha.NodeNetworkConfig {
376+ Spec : v1alpha.NodeNetworkConfigSpec {
377+ IPsNotInUse : pending ,
378+ },
379+ })
380+ if returnCode != types .Success {
381+ t .Errorf ("Unexpected failure on reconcile with no state %d" , returnCode )
382+ }
383+ validateNetworkRequest (t , * req )
384+ // confirm that the correct number of IPs are now PendingRelease
385+ assert .EqualValues (t , len (pending ), len (svc .GetPendingReleaseIPConfigs ()))
386+ }
387+
388+ func TestReconcileNCWithExistingStateAndPendingRelease (t * testing.T ) {
389+ restartService ()
390+ setEnv (t )
391+ setOrchestratorTypeInternal (cns .KubernetesCRD )
392+
393+ secondaryIPConfigs := make (map [string ]cns.SecondaryIPConfig )
394+ for i := 6 ; i < 22 ; i ++ {
395+ ipaddress := "10.0.0." + strconv .Itoa (i )
396+ secIPConfig := newSecondaryIPConfig (ipaddress , - 1 )
397+ ipID := uuid .New ()
398+ secondaryIPConfigs [ipID .String ()] = secIPConfig
399+ }
400+ expectedAssignedPods := map [string ]cns.PodInfo {
401+ "10.0.0.6" : cns .NewPodInfo ("" , "" , "reconcilePod1" , "PodNS1" ),
402+ "10.0.0.7" : cns .NewPodInfo ("" , "" , "reconcilePod2" , "PodNS1" ),
403+ }
404+ pendingIPIDs := func () map [string ]cns.PodInfo {
405+ numPending := rand .Intn (len (secondaryIPConfigs )) + 1 //nolint:gosec // weak rand is sufficient in test
406+ pendingIPs := map [string ]cns.PodInfo {}
407+ for k := range secondaryIPConfigs {
408+ if numPending == 0 {
409+ break
410+ }
411+ if _ , ok := expectedAssignedPods [secondaryIPConfigs [k ].IPAddress ]; ok {
412+ continue
413+ }
414+ pendingIPs [k ] = nil
415+ numPending --
416+ }
417+ return pendingIPs
418+ }()
419+ req := generateNetworkContainerRequest (secondaryIPConfigs , "reconcileNc1" , "-1" )
420+
421+ expectedNcCount := len (svc .state .ContainerStatus )
422+ returnCode := svc .ReconcileNCState (req , expectedAssignedPods , & v1alpha.NodeNetworkConfig {
423+ Spec : v1alpha.NodeNetworkConfigSpec {
424+ IPsNotInUse : maps .Keys (pendingIPIDs ),
425+ },
426+ })
427+ if returnCode != types .Success {
428+ t .Errorf ("Unexpected failure on reconcile with no state %d" , returnCode )
429+ }
430+ validateNetworkRequest (t , * req )
431+ // confirm that the correct number of IPs are now PendingRelease
432+ assert .EqualValues (t , len (pendingIPIDs ), len (svc .GetPendingReleaseIPConfigs ()))
433+ validateNCStateAfterReconcile (t , req , expectedNcCount + 1 , expectedAssignedPods , pendingIPIDs )
340434}
341435
342436func TestReconcileNCWithExistingState (t * testing.T ) {
@@ -378,7 +472,7 @@ func TestReconcileNCWithExistingState(t *testing.T) {
378472 t .Errorf ("Unexpected failure on reconcile with no state %d" , returnCode )
379473 }
380474
381- validateNCStateAfterReconcile (t , req , expectedNcCount + 1 , expectedAssignedPods )
475+ validateNCStateAfterReconcile (t , req , expectedNcCount + 1 , expectedAssignedPods , nil )
382476}
383477
384478func TestReconcileNCWithExistingStateFromInterfaceID (t * testing.T ) {
@@ -422,7 +516,7 @@ func TestReconcileNCWithExistingStateFromInterfaceID(t *testing.T) {
422516 t .Errorf ("Unexpected failure on reconcile with no state %d" , returnCode )
423517 }
424518
425- validateNCStateAfterReconcile (t , req , expectedNcCount + 1 , expectedAssignedPods )
519+ validateNCStateAfterReconcile (t , req , expectedNcCount + 1 , expectedAssignedPods , nil )
426520}
427521
428522func TestReconcileNCWithSystemPods (t * testing.T ) {
@@ -466,7 +560,7 @@ func TestReconcileNCWithSystemPods(t *testing.T) {
466560 }
467561
468562 delete (expectedAssignedPods , "192.168.0.1" )
469- validateNCStateAfterReconcile (t , req , expectedNcCount , expectedAssignedPods )
563+ validateNCStateAfterReconcile (t , req , expectedNcCount , expectedAssignedPods , nil )
470564}
471565
472566func setOrchestratorTypeInternal (orchestratorType string ) {
@@ -653,21 +747,21 @@ func generateNetworkContainerRequest(secondaryIps map[string]cns.SecondaryIPConf
653747 return & req
654748}
655749
656- func validateNCStateAfterReconcile (t * testing.T , ncRequest * cns.CreateNetworkContainerRequest , expectedNcCount int , expectedAssignedPods map [string ]cns.PodInfo ) {
750+ func validateNCStateAfterReconcile (t * testing.T , ncRequest * cns.CreateNetworkContainerRequest , expectedNCCount int , expectedAssignedIPs , expectedPendingIPs map [string ]cns.PodInfo ) {
657751 if ncRequest == nil {
658752 // check svc ContainerStatus will be empty
659- if len (svc .state .ContainerStatus ) != expectedNcCount {
753+ if len (svc .state .ContainerStatus ) != expectedNCCount {
660754 t .Fatalf ("CNS has some stale ContainerStatus, count: %d, state: %+v" , len (svc .state .ContainerStatus ), svc .state .ContainerStatus )
661755 }
662756 } else {
663757 validateNetworkRequest (t , * ncRequest )
664758 }
665759
666- if len (expectedAssignedPods ) != len (svc .PodIPIDByPodInterfaceKey ) {
667- t .Fatalf ("Unexpected assigned pods, actual: %d, expected: %d" , len (svc .PodIPIDByPodInterfaceKey ), len (expectedAssignedPods ))
760+ if len (expectedAssignedIPs ) != len (svc .PodIPIDByPodInterfaceKey ) {
761+ t .Fatalf ("Unexpected assigned pods, actual: %d, expected: %d" , len (svc .PodIPIDByPodInterfaceKey ), len (expectedAssignedIPs ))
668762 }
669763
670- for ipaddress , podInfo := range expectedAssignedPods {
764+ for ipaddress , podInfo := range expectedAssignedIPs {
671765 for _ , ipID := range svc .PodIPIDByPodInterfaceKey [podInfo .Key ()] {
672766 ipConfigstate := svc .PodIPConfigState [ipID ]
673767 if ipConfigstate .GetState () != types .Assigned {
@@ -694,18 +788,26 @@ func validateNCStateAfterReconcile(t *testing.T, ncRequest *cns.CreateNetworkCon
694788
695789 // validate rest of Secondary IPs in Available state
696790 if ncRequest != nil {
697- for secIpId , secIpConfig := range ncRequest .SecondaryIPConfigs {
698- if _ , exists := expectedAssignedPods [secIpConfig .IPAddress ]; exists {
699- continue
700- }
701-
791+ for secIPID , secIPConfig := range ncRequest .SecondaryIPConfigs {
702792 // Validate IP state
703- if secIpConfigState , found := svc .PodIPConfigState [secIpId ]; found {
704- if secIpConfigState .GetState () != types .Available {
705- t .Fatalf ("IPId: %s State is not Available, ipStatus: %+v" , secIpId , secIpConfigState )
793+ if secIPConfigState , found := svc .PodIPConfigState [secIPID ]; found {
794+ if _ , exists := expectedAssignedIPs [secIPConfig .IPAddress ]; exists {
795+ if secIPConfigState .GetState () != types .Assigned {
796+ t .Fatalf ("IPId: %s State is not Assigned, ipStatus: %+v" , secIPID , secIPConfigState )
797+ }
798+ continue
799+ }
800+ if _ , exists := expectedPendingIPs [secIPID ]; exists {
801+ if secIPConfigState .GetState () != types .PendingRelease {
802+ t .Fatalf ("IPId: %s State is not PendingRelease, ipStatus: %+v" , secIPID , secIPConfigState )
803+ }
804+ continue
805+ }
806+ if secIPConfigState .GetState () != types .Available {
807+ t .Fatalf ("IPId: %s State is not Available, ipStatus: %+v" , secIPID , secIPConfigState )
706808 }
707809 } else {
708- t .Fatalf ("IPId: %s, IpAddress: %+v State doesnt exists in PodIp Map" , secIpId , secIpConfig )
810+ t .Fatalf ("IPId: %s, IpAddress: %+v State doesnt exists in PodIp Map" , secIPID , secIPConfig )
709811 }
710812 }
711813 }
0 commit comments