@@ -182,57 +182,133 @@ func (g *Cloud) ensureInternalLoadBalancer(clusterName, clusterID string, svc *v
182
182
if err != nil {
183
183
return nil , err
184
184
}
185
- newFwdRule := & compute.ForwardingRule {
186
- Name : loadBalancerName ,
187
- Description : fwdRuleDescriptionString ,
188
- IPAddress : ipToUse ,
189
- BackendService : backendServiceLink ,
190
- Ports : ports ,
191
- IPProtocol : string (protocol ),
192
- LoadBalancingScheme : string (scheme ),
193
- // Given that CreateGCECloud will attempt to determine the subnet based off the network,
194
- // the subnetwork should rarely be unknown.
195
- Subnetwork : subnetworkURL ,
196
- Network : g .networkURL ,
197
- }
198
- if options .AllowGlobalAccess {
199
- newFwdRule .AllowGlobalAccess = options .AllowGlobalAccess
200
- }
201
- if len (ports ) > maxL4ILBPorts {
202
- newFwdRule .Ports = nil
203
- newFwdRule .AllPorts = true
204
- }
205
185
206
- fwdRuleDeleted := false
207
- if existingFwdRule != nil && ! forwardingRulesEqual (existingFwdRule , newFwdRule ) {
208
- // Delete existing forwarding rule before making changes to the backend service. For example - changing protocol
209
- // of backend service without first deleting forwarding rule will throw an error since the linked forwarding
210
- // rule would show the old protocol.
211
- if klogV := klog .V (2 ); klogV .Enabled () {
212
- frDiff := cmp .Diff (existingFwdRule , newFwdRule )
213
- klogV .Infof ("ensureInternalLoadBalancer(%v): forwarding rule changed - Existing - %+v\n , New - %+v\n , Diff(-existing, +new) - %s\n . Deleting existing forwarding rule." , loadBalancerName , existingFwdRule , newFwdRule , frDiff )
186
+ // Logic to handle multiple forwarding rules, one per protocol.
187
+ // Based on the logic for external load balancers.
188
+
189
+ // Get all existing forwarding rules for this service.
190
+ // A service can have a forwarding rule with the base name, or with a protocol suffix.
191
+ existingFwdRules := make (map [string ]* compute.ForwardingRule )
192
+ // The `existingFwdRule` is the one with the base name, passed into this function.
193
+ if existingFwdRule != nil {
194
+ existingFwdRules [existingFwdRule .Name ] = existingFwdRule
195
+ }
196
+ // Check for forwarding rules with protocol suffixes.
197
+ if len (groupedPorts ) > 1 {
198
+ for protocol := range groupedPorts {
199
+ frName := fmt .Sprintf ("%s-%s" , loadBalancerName , strings .ToLower (string (protocol )))
200
+ if _ , ok := existingFwdRules [frName ]; ok {
201
+ continue
202
+ }
203
+ fr , err := g .GetRegionForwardingRule (frName , g .region )
204
+ if err != nil && ! isNotFound (err ) {
205
+ return nil , err
206
+ }
207
+ if fr != nil {
208
+ existingFwdRules [fr .Name ] = fr
209
+ }
214
210
}
215
- if err = ignoreNotFound (g .DeleteRegionForwardingRule (loadBalancerName , g .region )); err != nil {
211
+ }
212
+
213
+ desiredFwdRuleNames := sets .NewString ()
214
+ var desiredFwdRuleProtocols []v1.Protocol
215
+ for protocol := range groupedPorts {
216
+ desiredFwdRuleProtocols = append (desiredFwdRuleProtocols , protocol )
217
+ }
218
+ // Sort protocols to have a stable order for naming and processing.
219
+ sort .Slice (desiredFwdRuleProtocols , func (i , j int ) bool {
220
+ return desiredFwdRuleProtocols [i ] < desiredFwdRuleProtocols [j ]
221
+ })
222
+
223
+ var createdFwdRules []* compute.ForwardingRule
224
+ var desiredBackendServices = make (map [string ]bool )
225
+
226
+ for _ , protocol := range desiredFwdRuleProtocols {
227
+ portStruct := groupedPorts [protocol ]
228
+ ports := portStruct .portRanges
229
+
230
+ // Each protocol gets its own backend service.
231
+ // The backend service name must be unique per protocol.
232
+ // Pass a single-protocol map to makeBackendServiceName.
233
+ singleProtocolGroupedPorts := map [v1.Protocol ]ProtocolPorts {protocol : portStruct }
234
+ backendServiceName := makeBackendServiceName (loadBalancerName , clusterID , sharedBackend , scheme , singleProtocolGroupedPorts , svc .Spec .SessionAffinity )
235
+ desiredBackendServices [backendServiceName ] = true
236
+ backendServiceLink := g .getBackendServiceLink (backendServiceName )
237
+
238
+ bsDescription := makeBackendServiceDescription (nm , sharedBackend )
239
+ err = g .ensureInternalBackendService (backendServiceName , bsDescription , svc .Spec .SessionAffinity , scheme , protocol , igLinks , hc .SelfLink )
240
+ if err != nil {
216
241
return nil , err
217
242
}
218
- fwdRuleDeleted = true
219
- }
220
243
221
- bsDescription := makeBackendServiceDescription (nm , sharedBackend )
222
- err = g .ensureInternalBackendService (backendServiceName , bsDescription , svc .Spec .SessionAffinity , scheme , protocol , igLinks , hc .SelfLink )
223
- if err != nil {
224
- return nil , err
244
+ // Each protocol gets its own forwarding rule.
245
+ // If there's only one protocol, the forwarding rule name is the load balancer name.
246
+ // Otherwise, it's load-balancer-name-<protocol>.
247
+ frName := loadBalancerName
248
+ if len (groupedPorts ) > 1 {
249
+ frName = fmt .Sprintf ("%s-%s" , loadBalancerName , strings .ToLower (string (protocol )))
250
+ }
251
+ desiredFwdRuleNames .Insert (frName )
252
+
253
+ newFwdRule := & compute.ForwardingRule {
254
+ Name : frName ,
255
+ Description : fwdRuleDescriptionString ,
256
+ IPAddress : ipToUse ,
257
+ BackendService : backendServiceLink ,
258
+ Ports : ports ,
259
+ IPProtocol : string (protocol ),
260
+ LoadBalancingScheme : string (scheme ),
261
+ Subnetwork : subnetworkURL ,
262
+ Network : g .networkURL ,
263
+ }
264
+ if options .AllowGlobalAccess {
265
+ newFwdRule .AllowGlobalAccess = options .AllowGlobalAccess
266
+ }
267
+ if len (ports ) > maxL4ILBPorts {
268
+ newFwdRule .Ports = nil
269
+ newFwdRule .AllPorts = true
270
+ }
271
+
272
+ // Check if a forwarding rule for this protocol already exists.
273
+ var existingFwdRuleForProtocol * compute.ForwardingRule
274
+ if fr , ok := existingFwdRules [frName ]; ok {
275
+ existingFwdRuleForProtocol = fr
276
+ }
277
+
278
+ if err := g .ensureInternalForwardingRule (existingFwdRuleForProtocol , newFwdRule ); err != nil {
279
+ return nil , err
280
+ }
281
+ createdFwdRules = append (createdFwdRules , newFwdRule )
225
282
}
226
283
227
- if fwdRuleDeleted || existingFwdRule == nil {
228
- // existing rule has been deleted, pass in nil
229
- if err := g .ensureInternalForwardingRule (nil , newFwdRule ); err != nil {
284
+ // Delete any forwarding rules that are no longer needed.
285
+ for frName , fr := range existingFwdRules {
286
+ if desiredFwdRuleNames .Has (frName ) {
287
+ continue
288
+ }
289
+ klog .V (2 ).Infof ("ensureInternalLoadBalancer(%v): deleting stale forwarding rule %s" , loadBalancerName , frName )
290
+ if err := ignoreNotFound (g .DeleteRegionForwardingRule (frName , g .region )); err != nil {
230
291
return nil , err
231
292
}
293
+ // Also delete the associated backend service if it's not used by other forwarding rules.
294
+ if fr .BackendService != "" {
295
+ bsName := getNameFromLink (fr .BackendService )
296
+ if ! desiredBackendServices [bsName ] {
297
+ klog .V (2 ).Infof ("ensureInternalLoadBalancer(%v): deleting stale backend service %s" , loadBalancerName , bsName )
298
+ if err := g .teardownInternalBackendService (bsName ); err != nil {
299
+ klog .Warningf ("ensureInternalLoadBalancer: could not delete old backend service %s: %v" , bsName , err )
300
+ }
301
+ }
302
+ }
303
+ }
304
+
305
+ if len (createdFwdRules ) == 0 {
306
+ klog .V (2 ).Infof ("ensureInternalLoadBalancer(%v): no forwarding rules needed, all deleted." , loadBalancerName )
307
+ return & v1.LoadBalancerStatus {}, nil
232
308
}
233
309
234
310
// Get the most recent forwarding rule for the address.
235
- updatedFwdRule , err := g .GetRegionForwardingRule (loadBalancerName , g .region )
311
+ updatedFwdRule , err := g .GetRegionForwardingRule (createdFwdRules [ 0 ]. Name , g .region )
236
312
if err != nil {
237
313
return nil , err
238
314
}
@@ -243,11 +319,6 @@ func (g *Cloud) ensureInternalLoadBalancer(clusterName, clusterID string, svc *v
243
319
return nil , err
244
320
}
245
321
246
- // Delete the previous internal load balancer resources if necessary
247
- if existingBackendService != nil {
248
- g .clearPreviousInternalResources (svc , loadBalancerName , existingBackendService , backendServiceName , hcName )
249
- }
250
-
251
322
serviceState .InSuccess = true
252
323
if options .AllowGlobalAccess {
253
324
serviceState .EnabledGlobalAccess = true
@@ -365,9 +436,17 @@ func (g *Cloud) updateInternalLoadBalancer(clusterName, clusterID string, svc *v
365
436
groupedPorts := getPortsAndProtocols (svc .Spec .Ports )
366
437
scheme := cloud .SchemeInternal
367
438
loadBalancerName := g .GetLoadBalancerName (context .TODO (), clusterName , svc )
368
- backendServiceName := makeBackendServiceName (loadBalancerName , clusterID , shareBackendService (svc ), scheme , groupedPorts , svc .Spec .SessionAffinity )
369
- // Ensure the backend service has the proper backend/instance-group links
370
- return g .ensureInternalBackendServiceGroups (backendServiceName , igLinks )
439
+ sharedBackend := shareBackendService (svc )
440
+
441
+ for protocol , portStruct := range groupedPorts {
442
+ singleProtocolGroupedPorts := map [v1.Protocol ]ProtocolPorts {protocol : portStruct }
443
+ backendServiceName := makeBackendServiceName (loadBalancerName , clusterID , sharedBackend , scheme , singleProtocolGroupedPorts , svc .Spec .SessionAffinity )
444
+ // Ensure the backend service has the proper backend/instance-group links
445
+ if err := g .ensureInternalBackendServiceGroups (backendServiceName , igLinks ); err != nil {
446
+ return err
447
+ }
448
+ }
449
+ return nil
371
450
}
372
451
373
452
func (g * Cloud ) ensureInternalLoadBalancerDeleted (clusterName , clusterID string , svc * v1.Service ) error {
@@ -397,15 +476,36 @@ func (g *Cloud) ensureInternalLoadBalancerDeleted(clusterName, clusterID string,
397
476
klog .V (2 ).Infof ("ensureInternalLoadBalancerDeleted(%v): attempting delete of region internal address" , loadBalancerName )
398
477
ensureAddressDeleted (g , loadBalancerName , g .region )
399
478
400
- klog .V (2 ).Infof ("ensureInternalLoadBalancerDeleted(%v): deleting region internal forwarding rule" , loadBalancerName )
401
- if err := ignoreNotFound (g .DeleteRegionForwardingRule (loadBalancerName , g .region )); err != nil {
402
- return err
479
+ frNames := sets .NewString (loadBalancerName )
480
+ if len (groupedPorts ) > 1 {
481
+ for protocol := range groupedPorts {
482
+ frNames .Insert (fmt .Sprintf ("%s-%s" , loadBalancerName , strings .ToLower (string (protocol ))))
483
+ }
484
+ }
485
+ // Sort for deterministic deletion order.
486
+ sortedFrNames := frNames .List ()
487
+ sort .Strings (sortedFrNames )
488
+ for _ , frName := range sortedFrNames {
489
+ klog .V (2 ).Infof ("ensureInternalLoadBalancerDeleted(%v): deleting region internal forwarding rule %s" , loadBalancerName , frName )
490
+ if err := ignoreNotFound (g .DeleteRegionForwardingRule (frName , g .region )); err != nil {
491
+ return err
492
+ }
403
493
}
404
494
405
- backendServiceName := makeBackendServiceName (loadBalancerName , clusterID , sharedBackend , scheme , groupedPorts , svc .Spec .SessionAffinity )
406
- klog .V (2 ).Infof ("ensureInternalLoadBalancerDeleted(%v): deleting region backend service %v" , loadBalancerName , backendServiceName )
407
- if err := g .teardownInternalBackendService (backendServiceName ); err != nil {
408
- return err
495
+ var protocols []v1.Protocol
496
+ for p := range groupedPorts {
497
+ protocols = append (protocols , p )
498
+ }
499
+ sort .Slice (protocols , func (i , j int ) bool { return protocols [i ] < protocols [j ] })
500
+
501
+ for _ , protocol := range protocols {
502
+ portStruct := groupedPorts [protocol ]
503
+ singleProtocolGroupedPorts := map [v1.Protocol ]ProtocolPorts {protocol : portStruct }
504
+ backendServiceName := makeBackendServiceName (loadBalancerName , clusterID , sharedBackend , scheme , singleProtocolGroupedPorts , svc .Spec .SessionAffinity )
505
+ klog .V (2 ).Infof ("ensureInternalLoadBalancerDeleted(%v): deleting region backend service %v" , loadBalancerName , backendServiceName )
506
+ if err := g .teardownInternalBackendService (backendServiceName ); err != nil {
507
+ return err
508
+ }
409
509
}
410
510
411
511
deleteFunc := func (fwName string ) error {
0 commit comments