@@ -269,95 +269,150 @@ func (g *Cloud) ensureExternalLoadBalancer(clusterName string, clusterID string,
269269 }
270270
271271 // Forwarding rule logic
272- // First, handle the old forwarding rule name for backward compatibility .
273- fwd , err := g .GetRegionForwardingRule (loadBalancerName , g . region )
274- if err != nil && ! isNotFound ( err ) {
275- return nil , err
272+ // We build the desired forwarding rules and then sync them with what exists .
273+ existingFRs , err := g .getExistingForwardingRules (loadBalancerName )
274+ if err != nil {
275+ return nil , fmt . Errorf ( "error getting existing forwarding rules for %s: %v" , loadBalancerName , err )
276276 }
277- if fwd != nil {
278- // If the old forwarding rule exists, we need to check if it matches one of the protocols.
279- // If so, we keep it. If not, we delete it.
280- protocol , err := getProtocol (fwd .IPProtocol )
281- if err != nil {
282- return nil , err
283- }
284- if _ , ok := groupedPorts [protocol ]; ! ok {
285- // This forwarding rule's protocol is not in the service spec, so delete it.
286- if err := g .DeleteRegionForwardingRule (loadBalancerName , g .region ); err != nil && ! isNotFound (err ) {
277+
278+ // Get the old forwarding rule for backward compatibility.
279+ var oldFwdRule * compute.ForwardingRule
280+ if fwd , ok := existingFRs [loadBalancerName ]; ok {
281+ oldFwdRule = fwd
282+ }
283+
284+ desiredFRs , err := g .buildDesiredForwardingRules (loadBalancerName , serviceName .String (), ipAddressToUse , g .targetPoolURL (loadBalancerName ), apiService , netTier , oldFwdRule )
285+ if err != nil {
286+ return nil , fmt .Errorf ("error building desired forwarding rules for %s: %v" , loadBalancerName , err )
287+ }
288+
289+ // Delete unwanted forwarding rules.
290+ for _ , fr := range existingFRs {
291+ if _ , ok := desiredFRs [fr .Name ]; ! ok {
292+ klog .Infof ("ensureExternalLoadBalancer(%s): Deleting orphaned forwarding rule %s." , lbRefStr , fr .Name )
293+ if err := g .DeleteRegionForwardingRule (fr .Name , g .region ); err != nil && ! isNotFound (err ) {
287294 return nil , err
288295 }
289296 }
290297 }
291298
292- // Then, iterate over the protocols and create/update forwarding rules.
293- for protocol , protocolPorts := range groupedPorts {
294- var frName string
295- if g .AlphaFeatureGate .Enabled (AlphaFeatureMultiProtocolLB ) {
296- frName = g .getProtocolForwardingRuleName (loadBalancerName , protocol )
297- // If the old forwarding rule matches this protocol, use its name.
298- if fwd != nil && fwd .IPProtocol == string (protocol ) {
299- frName = loadBalancerName
300- }
301- } else {
302- frName = loadBalancerName
303- }
299+ // Create or update forwarding rules.
300+ for _ , desiredFR := range desiredFRs {
301+ existingFR , exists := existingFRs [desiredFR .Name ]
304302
305- exists , needsUpdate , _ , err := g .forwardingRuleNeedsUpdate (frName , g .region , ipAddressToUse , protocolPorts )
306- if err != nil {
307- return nil , err
303+ needsUpdate := false
304+ if exists {
305+ if ! g .forwardingRulesEqual (existingFR , desiredFR ) {
306+ needsUpdate = true
307+ }
308308 }
309309
310- if needsUpdate || tpNeedsRecreation {
310+ if ! exists || needsUpdate || tpNeedsRecreation {
311311 if exists {
312- if err := g .DeleteRegionForwardingRule (frName , g .region ); err != nil && ! isNotFound (err ) {
312+ klog .Infof ("ensureExternalLoadBalancer(%s): Deleting forwarding rule %s to update." , lbRefStr , desiredFR .Name )
313+ if err := g .DeleteRegionForwardingRule (desiredFR .Name , g .region ); err != nil && ! isNotFound (err ) {
313314 return nil , err
314315 }
315316 }
316- klog .Infof ("ensureExternalLoadBalancer(%s): Creating forwarding rule %s for protocol %s, IP %s (tier: %s)." , lbRefStr , frName , protocol , ipAddressToUse , netTier )
317- if err := createForwardingRule (g , frName , serviceName .String (), g .region , ipAddressToUse , g .targetPoolURL (loadBalancerName ), protocolPorts , netTier , g .enableDiscretePortForwarding ); err != nil {
317+ klog .Infof ("ensureExternalLoadBalancer(%s): Creating forwarding rule %s for protocol %s, IP %s (tier: %s)." , lbRefStr , desiredFR .Name , desiredFR .IPProtocol , ipAddressToUse , netTier )
318+ // The desiredFR is a complete spec, so we can just pass it to CreateRegionForwardingRule.
319+ if err := g .CreateRegionForwardingRule (desiredFR , g .region ); err != nil && ! isHTTPErrorCode (err , http .StatusConflict ) {
318320 return nil , fmt .Errorf ("failed to create forwarding rule for load balancer (%s): %v" , lbRefStr , err )
319321 }
320- klog .Infof ("ensureExternalLoadBalancer(%s): Created forwarding rule %s." , lbRefStr , frName )
322+ klog .Infof ("ensureExternalLoadBalancer(%s): Created forwarding rule %s." , lbRefStr , desiredFR . Name )
321323 }
322324 }
323325
324- // Garbage collect old forwarding rules.
325- if g .AlphaFeatureGate .Enabled (AlphaFeatureMultiProtocolLB ) {
326- activeFRNames := sets .NewString ()
327- for protocol := range groupedPorts {
328- activeFRNames .Insert (g .getProtocolForwardingRuleName (loadBalancerName , protocol ))
326+ isSafeToReleaseIP = true
327+ status := & v1.LoadBalancerStatus {}
328+ status .Ingress = []v1.LoadBalancerIngress {{IP : ipAddressToUse }}
329+
330+ return status , nil
331+ }
332+
333+ // getExistingForwardingRules returns a map of forwarding rules for the given load balancer.
334+ func (g * Cloud ) getExistingForwardingRules (loadBalancerName string ) (map [string ]* compute.ForwardingRule , error ) {
335+ frs , err := g .ListRegionForwardingRules (g .region )
336+ if err != nil {
337+ return nil , err
338+ }
339+
340+ existingFRs := make (map [string ]* compute.ForwardingRule )
341+ for _ , fr := range frs {
342+ if strings .HasPrefix (fr .Name , loadBalancerName ) {
343+ existingFRs [fr .Name ] = fr
329344 }
330- // Check for the old forwarding rule name.
331- if fwd != nil {
332- protocol , err := getProtocol (fwd .IPProtocol )
333- if err != nil {
334- return nil , err
335- }
336- if _ , ok := groupedPorts [protocol ]; ok {
337- activeFRNames .Insert (loadBalancerName )
345+ }
346+ return existingFRs , nil
347+ }
348+
349+ // buildDesiredForwardingRules builds the desired forwarding rules for the given load balancer.
350+ func (g * Cloud ) buildDesiredForwardingRules (loadBalancerName , serviceName , ipAddress , targetPoolURL string , apiService * v1.Service , netTier cloud.NetworkTier , oldFwdRule * compute.ForwardingRule ) (map [string ]* compute.ForwardingRule , error ) {
351+ desiredFRs := make (map [string ]* compute.ForwardingRule )
352+ groupedPorts := groupPortsByProtocol (apiService .Spec .Ports )
353+ desc := makeServiceDescription (serviceName )
354+
355+ for protocol , protocolPorts := range groupedPorts {
356+ var frName string
357+ if g .AlphaFeatureGate .Enabled (AlphaFeatureMultiProtocolLB ) {
358+ frName = g .getProtocolForwardingRuleName (loadBalancerName , protocol )
359+ // If the old forwarding rule matches this protocol, use its name for backward compatibility.
360+ if oldFwdRule != nil && oldFwdRule .IPProtocol == string (protocol ) {
361+ frName = loadBalancerName
338362 }
363+ } else {
364+ frName = loadBalancerName
339365 }
340366
341- // List all forwarding rules for this service and delete the ones that are not active.
342- frs , err := g . ListRegionForwardingRules ( g . region )
367+ frPorts := getPorts ( protocolPorts )
368+ portRange , err := loadBalancerPortRange ( protocolPorts )
343369 if err != nil {
344370 return nil , err
345371 }
346- for _ , fr := range frs {
347- if strings .HasPrefix (fr .Name , loadBalancerName ) && ! activeFRNames .Has (fr .Name ) {
348- klog .Infof ("ensureExternalLoadBalancer(%s): Deleting orphaned forwarding rule %s." , lbRefStr , fr .Name )
349- if err := g .DeleteRegionForwardingRule (fr .Name , g .region ); err != nil && ! isNotFound (err ) {
350- return nil , err
351- }
352- }
372+
373+ rule := & compute.ForwardingRule {
374+ Name : frName ,
375+ Description : desc ,
376+ IPAddress : ipAddress ,
377+ IPProtocol : string (protocol ),
378+ PortRange : portRange ,
379+ Target : targetPoolURL ,
380+ NetworkTier : netTier .ToGCEValue (),
353381 }
382+
383+ if len (frPorts ) <= maxForwardedPorts && g .enableDiscretePortForwarding {
384+ rule .Ports = frPorts
385+ rule .PortRange = ""
386+ }
387+
388+ desiredFRs [frName ] = rule
354389 }
390+ return desiredFRs , nil
391+ }
355392
356- isSafeToReleaseIP = true
357- status := & v1.LoadBalancerStatus {}
358- status .Ingress = []v1.LoadBalancerIngress {{IP : ipAddressToUse }}
393+ // forwardingRulesEqual checks if two forwarding rules are equal.
394+ func (g * Cloud ) forwardingRulesEqual (existing , desired * compute.ForwardingRule ) bool {
395+ if existing .IPAddress != desired .IPAddress {
396+ klog .V (3 ).Infof ("Forwarding rule %s IP changed from %s to %s" , existing .Name , existing .IPAddress , desired .IPAddress )
397+ return false
398+ }
399+ if existing .IPProtocol != desired .IPProtocol {
400+ klog .V (3 ).Infof ("Forwarding rule %s protocol changed from %s to %s" , existing .Name , existing .IPProtocol , desired .IPProtocol )
401+ return false
402+ }
359403
360- return status , nil
404+ frEqualPorts := equalPorts (existing .Ports , desired .Ports , existing .PortRange , desired .PortRange , g .enableDiscretePortForwarding )
405+ if ! frEqualPorts {
406+ klog .V (3 ).Infof ("Forwarding rule %s ports changed from (range: %v, ports: %v) to (range: %v, ports: %v)" , existing .Name , existing .PortRange , existing .Ports , desired .PortRange , desired .Ports )
407+ return false
408+ }
409+
410+ if existing .NetworkTier != desired .NetworkTier {
411+ klog .V (3 ).Infof ("Forwarding rule %s network tier changed from %s to %s" , existing .Name , existing .NetworkTier , desired .NetworkTier )
412+ return false
413+ }
414+
415+ return true
361416}
362417
363418
@@ -853,58 +908,7 @@ func (g *Cloud) ensureHTTPHealthCheck(name, path string, port int32) (hc *comput
853908 return hc , nil
854909}
855910
856- // Passing nil for requested IP is perfectly fine - it just means that no specific
857- // IP is being requested.
858- // Returns whether the forwarding rule exists, whether it needs to be updated,
859- // what its IP address is (if it exists), and any error we encountered.
860- func (g * Cloud ) forwardingRuleNeedsUpdate (name , region string , loadBalancerIP string , ports []v1.ServicePort ) (exists bool , needsUpdate bool , ipAddress string , err error ) {
861- fwd , err := g .GetRegionForwardingRule (name , region )
862- if err != nil {
863- if isHTTPErrorCode (err , http .StatusNotFound ) {
864- return false , true , "" , nil
865- }
866- // Err on the side of caution in case of errors. Caller should notice the error and retry.
867- // We never want to end up recreating resources because g api flaked.
868- return true , false , "" , fmt .Errorf ("error getting load balancer's forwarding rule: %v" , err )
869- }
870- // If the user asks for a specific static ip through the Service spec,
871- // check that we're actually using it.
872- // TODO: we report loadbalancer IP through status, so we want to verify if
873- // that matches the forwarding rule as well.
874- if loadBalancerIP != "" && loadBalancerIP != fwd .IPAddress {
875- klog .Infof ("LoadBalancer ip for forwarding rule %v was expected to be %v, but was actually %v" , fwd .Name , fwd .IPAddress , loadBalancerIP )
876- return true , true , fwd .IPAddress , nil
877- }
878911
879- protocol , err := getProtocol (ports )
880- if err != nil {
881- return true , false , "" , err
882- }
883-
884- newPortRange , err := loadBalancerPortRange (ports )
885- if err != nil {
886- // Err on the side of caution in case of errors. Caller should notice the error and retry.
887- // We never want to end up recreating resources because g api flaked.
888- return true , false , "" , err
889- }
890- newPorts := []string {}
891- if frPorts := getPorts (ports ); len (frPorts ) <= maxForwardedPorts && g .enableDiscretePortForwarding {
892- newPorts = frPorts
893- newPortRange = ""
894- }
895- frEqualPorts := equalPorts (fwd .Ports , newPorts , fwd .PortRange , newPortRange , g .enableDiscretePortForwarding )
896- if ! frEqualPorts {
897- klog .Infof ("Forwarding rule port range / ports are not equal, old (port range: %v, ports: %v), new (port range: %v, ports: %v)" , fwd .PortRange , fwd .Ports , newPortRange , newPorts )
898- return true , true , fwd .IPAddress , nil
899- }
900-
901- if string (protocol ) != fwd .IPProtocol {
902- klog .Infof ("LoadBalancer protocol for forwarding rule %v was expected to be %v, but was actually %v" , fwd .Name , fwd .IPProtocol , string (protocol ))
903- return true , true , fwd .IPAddress , nil
904- }
905-
906- return true , false , fwd .IPAddress , nil
907- }
908912
909913// Doesn't check whether the hosts have changed, since host updating is handled
910914// separately.
@@ -1141,41 +1145,7 @@ func (g *Cloud) ensureHTTPHealthCheckFirewall(svc *v1.Service, serviceName, ipAd
11411145 return nil
11421146}
11431147
1144- func createForwardingRule (s CloudForwardingRuleService , name , serviceName , region , ipAddress , target string , ports []v1.ServicePort , netTier cloud.NetworkTier , enableDiscretePortForwarding bool ) error {
1145- frPorts := getPorts (ports )
1146- protocol , err := getProtocol (ports )
1147- if err != nil {
1148- return err
1149- }
1150- portRange , err := loadBalancerPortRange (ports )
1151- if err != nil {
1152- return err
1153- }
1154- desc := makeServiceDescription (serviceName )
11551148
1156- rule := & compute.ForwardingRule {
1157- Name : name ,
1158- Description : desc ,
1159- IPAddress : ipAddress ,
1160- IPProtocol : string (protocol ),
1161- PortRange : portRange ,
1162- Target : target ,
1163- NetworkTier : netTier .ToGCEValue (),
1164- }
1165-
1166- if len (frPorts ) <= maxForwardedPorts && enableDiscretePortForwarding {
1167- rule .Ports = frPorts
1168- rule .PortRange = ""
1169- }
1170-
1171- err = s .CreateRegionForwardingRule (rule , region )
1172-
1173- if err != nil && ! isHTTPErrorCode (err , http .StatusConflict ) {
1174- return err
1175- }
1176-
1177- return nil
1178- }
11791149
11801150func (g * Cloud ) createFirewall (svc * v1.Service , name , desc , destinationIP string , sourceRanges utilnet.IPNetSet , ports []v1.ServicePort , hosts []* gceInstance ) error {
11811151 firewall , err := g .firewallObject (name , desc , destinationIP , sourceRanges , ports , hosts )
0 commit comments