@@ -276,13 +276,21 @@ func (cs *CSCloud) UpdateLoadBalancer(ctx context.Context, clusterName string, s
276276 for _ , lbRule := range lb .rules {
277277 p := lb .LoadBalancer .NewListLoadBalancerRuleInstancesParams (lbRule .Id )
278278
279- // Retrieve all VMs currently associated to this load balancer rule.
280- l , err := lb .LoadBalancer .ListLoadBalancerRuleInstances (p )
279+ // Retrieve all VMs currently associated to this load balancer rule. There
280+ // is one per load balanced node, so this grows with the cluster.
281+ instances , err := listAll (p , func () (int , []* cloudstack.VirtualMachine , error ) {
282+ l , err := lb .LoadBalancer .ListLoadBalancerRuleInstances (p )
283+ if err != nil {
284+ return 0 , nil , err
285+ }
286+
287+ return l .Count , l .LoadBalancerRuleInstances , nil
288+ })
281289 if err != nil {
282290 return fmt .Errorf ("error retrieving associated instances: %v" , err )
283291 }
284292
285- assign , remove := symmetricDifference (lb .hostIDs , l . LoadBalancerRuleInstances )
293+ assign , remove := symmetricDifference (lb .hostIDs , instances )
286294
287295 if len (assign ) > 0 {
288296 klog .V (4 ).Infof ("Assigning new hosts (%v) to load balancer rule: %v" , assign , lbRule .Name )
@@ -448,12 +456,21 @@ func (cs *CSCloud) getLoadBalancer(service *corev1.Service) (*loadBalancer, erro
448456 p .SetProjectid (cs .projectID )
449457 }
450458
451- l , err := cs .client .LoadBalancer .ListLoadBalancerRules (p )
459+ // The keyword is matched as a substring server side, so this can return more
460+ // rules than just this service's and has to be paged through.
461+ lbRules , err := listAll (p , func () (int , []* cloudstack.LoadBalancerRule , error ) {
462+ l , err := cs .client .LoadBalancer .ListLoadBalancerRules (p )
463+ if err != nil {
464+ return 0 , nil , err
465+ }
466+
467+ return l .Count , l .LoadBalancerRules , nil
468+ })
452469 if err != nil {
453470 return nil , fmt .Errorf ("error retrieving load balancer rules: %v" , err )
454471 }
455472
456- for _ , lbRule := range l . LoadBalancerRules {
473+ for _ , lbRule := range lbRules {
457474 lb .rules [lbRule .Name ] = lbRule
458475
459476 if lb .ipAddr != "" && lb .ipAddr != lbRule .Publicip {
@@ -507,24 +524,37 @@ func (cs *CSCloud) verifyHosts(nodes []*corev1.Node) ([]string, string, error) {
507524 p .SetProjectid (cs .projectID )
508525 }
509526
510- l , err := cs .client .VirtualMachine .ListVirtualMachines (p )
527+ vms , err := listAll (p , func () (int , []* cloudstack.VirtualMachine , error ) {
528+ l , err := cs .client .VirtualMachine .ListVirtualMachines (p )
529+ if err != nil {
530+ return 0 , nil , err
531+ }
532+
533+ return l .Count , l .VirtualMachines , nil
534+ })
511535 if err != nil {
512536 return nil , "" , fmt .Errorf ("error retrieving list of hosts: %v" , err )
513537 }
514538
515539 var hostIDs []string
516540 var networkID string
541+ seen := map [string ]bool {}
517542
518543 // Check if the virtual machine is in the hosts slice, then add the corresponding ID.
519- for _ , vm := range l .VirtualMachines {
520- if hostNames [strings .ToLower (vm .Name )] {
521- if networkID != "" && networkID != vm .Nic [0 ].Networkid {
522- return nil , "" , fmt .Errorf ("found hosts that belong to different networks" )
523- }
544+ for _ , vm := range vms {
545+ // Paging over a set of VMs that is changing underneath us can return
546+ // the same VM on more than one page.
547+ if ! hostNames [strings .ToLower (vm .Name )] || seen [vm .Id ] {
548+ continue
549+ }
550+ seen [vm .Id ] = true
524551
525- networkID = vm .Nic [0 ].Networkid
526- hostIDs = append ( hostIDs , vm . Id )
552+ if networkID != "" && networkID != vm .Nic [0 ].Networkid {
553+ return nil , "" , fmt . Errorf ( "found hosts that belong to different networks" )
527554 }
555+
556+ networkID = vm .Nic [0 ].Networkid
557+ hostIDs = append (hostIDs , vm .Id )
528558 }
529559
530560 if len (hostIDs ) == 0 || len (networkID ) == 0 {
@@ -807,8 +837,19 @@ func symmetricDifference(hostIDs []string, lbInstances []*cloudstack.VirtualMach
807837 new [hostID ] = true
808838 }
809839
840+ // Paging over the instances of a rule can return the same instance twice. A
841+ // duplicate would otherwise be dropped from new on its first occurrence and
842+ // then added to remove on its second, so the same host would be both kept
843+ // and removed.
844+ seen := make (map [string ]bool )
845+
810846 var remove []string
811847 for _ , instance := range lbInstances {
848+ if seen [instance .Id ] {
849+ continue
850+ }
851+ seen [instance .Id ] = true
852+
812853 if new [instance .Id ] {
813854 delete (new , instance .Id )
814855 continue
@@ -900,6 +941,31 @@ func rulesMapToString(rules map[*cloudstack.FirewallRule]bool) string {
900941 return ls .String ()
901942}
902943
944+ // listFirewallRules retrieves all firewall rules associated with a public IP.
945+ func (lb * loadBalancer ) listFirewallRules (publicIpId string ) ([]* cloudstack.FirewallRule , error ) {
946+ p := lb .Firewall .NewListFirewallRulesParams ()
947+ p .SetIpaddressid (publicIpId )
948+ p .SetListall (true )
949+ if lb .projectID != "" {
950+ p .SetProjectid (lb .projectID )
951+ }
952+
953+ klog .V (4 ).Infof ("Listing firewall rules for %v" , p )
954+ rules , err := listAll (p , func () (int , []* cloudstack.FirewallRule , error ) {
955+ r , err := lb .Firewall .ListFirewallRules (p )
956+ if err != nil {
957+ return 0 , nil , err
958+ }
959+
960+ return r .Count , r .FirewallRules , nil
961+ })
962+ if err != nil {
963+ return nil , fmt .Errorf ("error fetching firewall rules for public IP %v: %v" , publicIpId , err )
964+ }
965+
966+ return rules , nil
967+ }
968+
903969// updateFirewallRule creates a firewall rule for a load balancer rule
904970//
905971// If the rule list is empty, all internet (IPv4: 0.0.0.0/0) is opened for the
@@ -911,23 +977,16 @@ func (lb *loadBalancer) updateFirewallRule(publicIpId string, publicPort int, pr
911977 allowedIPs = []string {defaultAllowedCIDR }
912978 }
913979
914- p := lb .Firewall .NewListFirewallRulesParams ()
915- p .SetIpaddressid (publicIpId )
916- p .SetListall (true )
917- if lb .projectID != "" {
918- p .SetProjectid (lb .projectID )
919- }
920- klog .V (4 ).Infof ("Listing firewall rules for %v" , p )
921- r , err := lb .Firewall .ListFirewallRules (p )
980+ firewallRules , err := lb .listFirewallRules (publicIpId )
922981 if err != nil {
923- return false , fmt . Errorf ( "error fetching firewall rules for public IP %v: %v" , publicIpId , err )
982+ return false , err
924983 }
925- klog .V (4 ).Infof ("All firewall rules for %v: %v" , lb .ipAddr , rulesToString (r . FirewallRules ))
984+ klog .V (4 ).Infof ("All firewall rules for %v: %v" , lb .ipAddr , rulesToString (firewallRules ))
926985
927986 // find all rules that have a matching proto+port
928987 // a map may or may not be faster, but is a bit easier to understand
929988 filtered := make (map [* cloudstack.FirewallRule ]bool )
930- for _ , rule := range r . FirewallRules {
989+ for _ , rule := range firewallRules {
931990 if rule .Protocol == protocol .IPProtocol () && rule .Startport == publicPort && rule .Endport == publicPort {
932991 filtered [rule ] = true
933992 }
@@ -1003,17 +1062,27 @@ func (lb *loadBalancer) updateNetworkACL(publicPort int, protocol LoadBalancerPr
10031062 networkAclParams := lb .NetworkACL .NewListNetworkACLsParams ()
10041063 networkAclParams .SetAclid (network .Aclid )
10051064 networkAclParams .SetNetworkid (networkId )
1065+ networkAclParams .SetListall (true )
1066+ if lb .projectID != "" {
1067+ networkAclParams .SetProjectid (lb .projectID )
1068+ }
10061069
1007- networkAclResponse , err := lb .NetworkACL .ListNetworkACLs (networkAclParams )
1070+ networkAcls , err := listAll (networkAclParams , func () (int , []* cloudstack.NetworkACL , error ) {
1071+ networkAclResponse , err := lb .NetworkACL .ListNetworkACLs (networkAclParams )
1072+ if err != nil {
1073+ return 0 , nil , err
1074+ }
10081075
1076+ return networkAclResponse .Count , networkAclResponse .NetworkACLs , nil
1077+ })
10091078 if err != nil {
10101079 return false , fmt .Errorf ("error fetching Network ACL with ID: %v for network with id: %v, due to: %s" , network .Aclid , networkId , err )
10111080 }
10121081
10131082 // find all network ACL rules that have a matching proto+port
10141083 // a map may or may not be faster, but is a bit easier to understand
10151084 filtered := make (map [* cloudstack.NetworkACL ]bool )
1016- for _ , netAclRule := range networkAclResponse . NetworkACLs {
1085+ for _ , netAclRule := range networkAcls {
10171086 if netAclRule .Protocol == protocol .IPProtocol () && netAclRule .Startport == strconv .Itoa (publicPort ) && netAclRule .Endport == strconv .Itoa (publicPort ) {
10181087 filtered [netAclRule ] = true
10191088 }
@@ -1045,20 +1114,14 @@ func (lb *loadBalancer) updateNetworkACL(publicPort int, protocol LoadBalancerPr
10451114//
10461115// returns true when corresponding rules were deleted
10471116func (lb * loadBalancer ) deleteFirewallRule (publicIpId string , publicPort int , protocol LoadBalancerProtocol ) (bool , error ) {
1048- p := lb .Firewall .NewListFirewallRulesParams ()
1049- p .SetIpaddressid (publicIpId )
1050- p .SetListall (true )
1051- if lb .projectID != "" {
1052- p .SetProjectid (lb .projectID )
1053- }
1054- r , err := lb .Firewall .ListFirewallRules (p )
1117+ firewallRules , err := lb .listFirewallRules (publicIpId )
10551118 if err != nil {
1056- return false , fmt . Errorf ( "error fetching firewall rules for public IP %v: %v" , publicIpId , err )
1119+ return false , err
10571120 }
10581121
10591122 // filter by proto:port
10601123 filtered := make ([]* cloudstack.FirewallRule , 0 , 1 )
1061- for _ , rule := range r . FirewallRules {
1124+ for _ , rule := range firewallRules {
10621125 if rule .Protocol == protocol .IPProtocol () && rule .Startport == publicPort && rule .Endport == publicPort {
10631126 filtered = append (filtered , rule )
10641127 }
@@ -1088,14 +1151,21 @@ func (lb *loadBalancer) deleteNetworkACLRule(publicPort int, protocol LoadBalanc
10881151 p .SetProjectid (lb .projectID )
10891152 }
10901153
1091- r , err := lb .NetworkACL .ListNetworkACLs (p )
1154+ networkAcls , err := listAll (p , func () (int , []* cloudstack.NetworkACL , error ) {
1155+ r , err := lb .NetworkACL .ListNetworkACLs (p )
1156+ if err != nil {
1157+ return 0 , nil , err
1158+ }
1159+
1160+ return r .Count , r .NetworkACLs , nil
1161+ })
10921162 if err != nil {
10931163 return false , fmt .Errorf ("error fetching Network ACL rules Network ID %v: %v" , networkID , err )
10941164 }
10951165
10961166 // filter by proto:port
10971167 filtered := make ([]* cloudstack.NetworkACL , 0 , 1 )
1098- for _ , rule := range r . NetworkACLs {
1168+ for _ , rule := range networkAcls {
10991169 if rule .Protocol == protocol .IPProtocol () && rule .Startport == strconv .Itoa (publicPort ) && rule .Endport == strconv .Itoa (publicPort ) {
11001170 filtered = append (filtered , rule )
11011171 }
0 commit comments