@@ -269,95 +269,150 @@ func (g *Cloud) ensureExternalLoadBalancer(clusterName string, clusterID string,
269
269
}
270
270
271
271
// 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 )
276
276
}
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 ) {
287
294
return nil , err
288
295
}
289
296
}
290
297
}
291
298
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 ]
304
302
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
+ }
308
308
}
309
309
310
- if needsUpdate || tpNeedsRecreation {
310
+ if ! exists || needsUpdate || tpNeedsRecreation {
311
311
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 ) {
313
314
return nil , err
314
315
}
315
316
}
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 ) {
318
320
return nil , fmt .Errorf ("failed to create forwarding rule for load balancer (%s): %v" , lbRefStr , err )
319
321
}
320
- klog .Infof ("ensureExternalLoadBalancer(%s): Created forwarding rule %s." , lbRefStr , frName )
322
+ klog .Infof ("ensureExternalLoadBalancer(%s): Created forwarding rule %s." , lbRefStr , desiredFR . Name )
321
323
}
322
324
}
323
325
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
329
344
}
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
338
362
}
363
+ } else {
364
+ frName = loadBalancerName
339
365
}
340
366
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 )
343
369
if err != nil {
344
370
return nil , err
345
371
}
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 (),
353
381
}
382
+
383
+ if len (frPorts ) <= maxForwardedPorts && g .enableDiscretePortForwarding {
384
+ rule .Ports = frPorts
385
+ rule .PortRange = ""
386
+ }
387
+
388
+ desiredFRs [frName ] = rule
354
389
}
390
+ return desiredFRs , nil
391
+ }
355
392
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
+ }
359
403
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
361
416
}
362
417
363
418
@@ -853,58 +908,7 @@ func (g *Cloud) ensureHTTPHealthCheck(name, path string, port int32) (hc *comput
853
908
return hc , nil
854
909
}
855
910
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
- }
878
911
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
- }
908
912
909
913
// Doesn't check whether the hosts have changed, since host updating is handled
910
914
// separately.
@@ -1141,41 +1145,7 @@ func (g *Cloud) ensureHTTPHealthCheckFirewall(svc *v1.Service, serviceName, ipAd
1141
1145
return nil
1142
1146
}
1143
1147
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 )
1155
1148
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
- }
1179
1149
1180
1150
func (g * Cloud ) createFirewall (svc * v1.Service , name , desc , destinationIP string , sourceRanges utilnet.IPNetSet , ports []v1.ServicePort , hosts []* gceInstance ) error {
1181
1151
firewall , err := g .firewallObject (name , desc , destinationIP , sourceRanges , ports , hosts )
0 commit comments