@@ -182,57 +182,133 @@ func (g *Cloud) ensureInternalLoadBalancer(clusterName, clusterID string, svc *v
182182 if err != nil {
183183 return nil , err
184184 }
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- }
205185
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+ }
214210 }
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 {
216241 return nil , err
217242 }
218- fwdRuleDeleted = true
219- }
220243
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 )
225282 }
226283
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 {
230291 return nil , err
231292 }
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
232308 }
233309
234310 // 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 )
236312 if err != nil {
237313 return nil , err
238314 }
@@ -243,11 +319,6 @@ func (g *Cloud) ensureInternalLoadBalancer(clusterName, clusterID string, svc *v
243319 return nil , err
244320 }
245321
246- // Delete the previous internal load balancer resources if necessary
247- if existingBackendService != nil {
248- g .clearPreviousInternalResources (svc , loadBalancerName , existingBackendService , backendServiceName , hcName )
249- }
250-
251322 serviceState .InSuccess = true
252323 if options .AllowGlobalAccess {
253324 serviceState .EnabledGlobalAccess = true
@@ -365,9 +436,17 @@ func (g *Cloud) updateInternalLoadBalancer(clusterName, clusterID string, svc *v
365436 groupedPorts := getPortsAndProtocols (svc .Spec .Ports )
366437 scheme := cloud .SchemeInternal
367438 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
371450}
372451
373452func (g * Cloud ) ensureInternalLoadBalancerDeleted (clusterName , clusterID string , svc * v1.Service ) error {
@@ -397,15 +476,36 @@ func (g *Cloud) ensureInternalLoadBalancerDeleted(clusterName, clusterID string,
397476 klog .V (2 ).Infof ("ensureInternalLoadBalancerDeleted(%v): attempting delete of region internal address" , loadBalancerName )
398477 ensureAddressDeleted (g , loadBalancerName , g .region )
399478
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+ }
403493 }
404494
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+ }
409509 }
410510
411511 deleteFunc := func (fwName string ) error {
0 commit comments