@@ -21,6 +21,8 @@ import (
2121
2222 "github.com/go-test/deep"
2323
24+ maps0 "maps"
25+
2426 "github.com/haproxytech/client-native/v6/models"
2527 "github.com/haproxytech/kubernetes-ingress/pkg/annotations"
2628 "github.com/haproxytech/kubernetes-ingress/pkg/fs"
@@ -150,26 +152,7 @@ func (c *HAProxyController) updateHAProxy() {
150152 logger .Error (err )
151153 }
152154
153- for _ , namespace := range c .store .Namespaces {
154- c .store .SecretsProcessed = map [string ]struct {}{}
155- for _ , ingResource := range namespace .Ingresses {
156- if ! namespace .Relevant && ! ingResource .Faked {
157- // As we watch only for white-listed namespaces, we should not worry about iterating over
158- // many ingresses in irrelevant namespaces.
159- // There should only be fake ingresses in irrelevant namespaces so loop should be whithin small amount of ingresses (Prometheus)
160- continue
161- }
162- i := ingress .New (ingResource , c .osArgs .IngressClass , c .osArgs .EmptyIngressClass , c .annotations )
163- if ! i .Supported (c .store , c .annotations ) {
164- logger .Debugf ("ingress '%s/%s' ignored: no matching" , ingResource .Namespace , ingResource .Name )
165- } else {
166- i .Update (c .store , c .haproxy , c .annotations )
167- }
168- if ingResource .Status == store .ADDED || ingResource .ClassUpdated {
169- c .updateStatusManager .AddIngress (i )
170- }
171- }
172- }
155+ c .processIngressesWithMerge ()
173156
174157 updated := deep .Equal (route .CurentCustomRoutes , route .CustomRoutes , deep .FLAG_IGNORE_SLICE_ORDER )
175158 if len (updated ) != 0 {
@@ -339,3 +322,104 @@ func (c *HAProxyController) clean(failedSync bool) {
339322func (c * HAProxyController ) SetGatewayAPIInstalled (gatewayAPIInstalled bool ) {
340323 c .gatewayManager .SetGatewayAPIInstalled (gatewayAPIInstalled )
341324}
325+
326+ func (c * HAProxyController ) manageIngress (ing * store.Ingress ) {
327+ i := ingress .New (ing , c .osArgs .IngressClass , c .osArgs .EmptyIngressClass , c .annotations )
328+ if ! i .Supported (c .store , c .annotations ) {
329+ logger .Debugf ("ingress '%s/%s' ignored: no matching" , ing .Namespace , ing .Name )
330+ } else {
331+ i .Update (c .store , c .haproxy , c .annotations )
332+ }
333+ if ing .Status == store .ADDED || ing .ClassUpdated {
334+ c .updateStatusManager .AddIngress (i )
335+ }
336+ }
337+
338+ func (c * HAProxyController ) processIngressesWithMerge () {
339+ for _ , namespace := range c .store .Namespaces {
340+ c .store .SecretsProcessed = map [string ]struct {}{}
341+ // Iterate over services
342+ for _ , service := range namespace .Services {
343+ ingressesOrderedList := c .store .IngressesByService [service .Namespace + "/" + service .Name ]
344+ if ingressesOrderedList == nil {
345+ continue
346+ }
347+ ingresses := ingressesOrderedList .Items ()
348+ if len (ingresses ) == 0 {
349+ continue
350+ }
351+ // Put standalone ingresses aside.
352+ var standaloneIngresses []* store.Ingress
353+ // Get the name of ingresses referring to the service
354+ var ingressesToMerge []* store.Ingress
355+ for _ , ing := range ingresses {
356+ i := ingress .New (ing , c .osArgs .IngressClass , c .osArgs .EmptyIngressClass , c .annotations )
357+ if ! i .Supported (c .store , c .annotations ) {
358+ continue
359+ }
360+ // if the ingress has standalone-backend annotation, put it aside and continue.
361+ if ing .Annotations ["standalone-backend" ] == "true" {
362+ standaloneIngresses = append (standaloneIngresses , ing )
363+ continue
364+ }
365+ ingressesToMerge = append (ingressesToMerge , ing )
366+ }
367+
368+ // Get copy of annotationsFromAllIngresses from all ingresses
369+ annotationsFromAllIngresses := map [string ]string {}
370+
371+ for _ , ingressToMerge := range ingressesToMerge {
372+ // Gather all annotations from all ingresses referring to the service in a consistent order based on ingress name.
373+ for ann , value := range ingressToMerge .Annotations {
374+ if _ , specific := annotations .SpecificAnnotations [ann ]; specific {
375+ continue
376+ }
377+ annotationsFromAllIngresses [ann ] = value
378+ }
379+ }
380+
381+ // Now we've gathered the annotations set we can process all ingresses.
382+ for _ , ingressToMerge := range ingressesToMerge {
383+ // We copy the ingress
384+ consolidatedIngress := * ingressToMerge
385+ // We assign the general set of annotations
386+ consolidatedIngressAnns := map [string ]string {}
387+ maps0 .Copy (consolidatedIngressAnns , annotationsFromAllIngresses )
388+
389+ consolidatedIngress .Annotations = consolidatedIngressAnns
390+ for ann , value := range ingressToMerge .Annotations {
391+ if _ , specific := annotations .SpecificAnnotations [ann ]; ! specific {
392+ continue
393+ }
394+ consolidatedIngress .Annotations [ann ] = value
395+ }
396+ // We will reprocess the rules because we need to skip the ones referring to an other service.
397+ rules := map [string ]* store.IngressRule {}
398+ consolidatedIngress .Rules = rules
399+ for _ , rule := range ingressToMerge .Rules {
400+ newRule := store.IngressRule {
401+ Host : rule .Host ,
402+ Paths : map [string ]* store.IngressPath {},
403+ }
404+ for _ , path := range rule .Paths {
405+ // if the rule refers to the service then keep it ...
406+ if path .SvcNamespace == service .Namespace && path .SvcName == service .Name {
407+ newRule .Paths [path .Path ] = path
408+ }
409+ }
410+ // .. if it's not empty
411+ if len (newRule .Paths ) > 0 {
412+ rules [newRule .Host ] = & newRule
413+ }
414+ }
415+ // Back to the usual processing of the ingress
416+
417+ c .manageIngress (& consolidatedIngress )
418+ }
419+ // Now process the standalone ingresses as usual.
420+ for _ , standaloneIngress := range standaloneIngresses {
421+ c .manageIngress (standaloneIngress )
422+ }
423+ }
424+ }
425+ }
0 commit comments