99 "bytes"
1010 "fmt"
1111 "net/url"
12+ "sort"
1213 "strconv"
1314 "strings"
1415
@@ -24,6 +25,7 @@ import (
2425 "google.golang.org/protobuf/types/known/durationpb"
2526 "google.golang.org/protobuf/types/known/wrapperspb"
2627 goyaml "gopkg.in/yaml.v3" // nolint: depguard
28+ "k8s.io/apimachinery/pkg/util/sets"
2729
2830 egv1a1 "github.com/envoyproxy/gateway/api/v1alpha1"
2931 "github.com/envoyproxy/gateway/internal/ir"
@@ -72,50 +74,33 @@ func (t *Translator) buildRateLimitFilter(irListener *ir.HTTPListener) []*hcmv3.
7274 if irListener == nil || irListener .Routes == nil {
7375 return nil
7476 }
75-
76- filters := []* hcmv3.HttpFilter {}
77- created := make (map [string ]bool )
78-
77+ domains := sets .New [string ]()
7978 for _ , route := range irListener .Routes {
8079 if ! isValidGlobalRateLimit (route ) {
8180 continue
8281 }
83-
84- hasShared , hasNonShared := false , false
85- var sharedRuleName string
86-
8782 for _ , rule := range route .Traffic .RateLimit .Global .Rules {
8883 if isRuleShared (rule ) {
89- hasShared = true
90- sharedRuleName = stripRuleIndexSuffix (rule .Name )
91- } else {
92- hasNonShared = true
84+ domains .Insert (stripRuleIndexSuffix (rule .Name ))
9385 }
9486 }
87+ }
88+ domains .Insert (irListener .Name )
9589
96- if hasShared {
97- sharedDomain := sharedRuleName
98- if ! created [sharedDomain ] {
99- filterName := fmt .Sprintf ("%s/%s" , egv1a1 .EnvoyFilterRateLimit .String (), sharedRuleName )
100- if filter := createRateLimitFilter (t , irListener , sharedDomain , filterName ); filter != nil {
101- filters = append (filters , filter )
102- }
103- created [sharedDomain ] = true
104- }
105- }
90+ // Deterministic order otherwise tests break
91+ domainList := sets .List (domains )
92+ sort .Strings (domainList )
10693
107- if hasNonShared {
108- nonSharedDomain := irListener .Name
109- if ! created [nonSharedDomain ] {
110- filterName := egv1a1 .EnvoyFilterRateLimit .String ()
111- if filter := createRateLimitFilter (t , irListener , nonSharedDomain , filterName ); filter != nil {
112- filters = append (filters , filter )
113- }
114- created [nonSharedDomain ] = true
115- }
94+ var filters []* hcmv3.HttpFilter
95+ for _ , domain := range domainList {
96+ filterName := egv1a1 .EnvoyFilterRateLimit .String ()
97+ if domain != irListener .Name {
98+ filterName += "/" + domain
99+ }
100+ if filter := createRateLimitFilter (t , irListener , domain , filterName ); filter != nil {
101+ filters = append (filters , filter )
116102 }
117103 }
118-
119104 return filters
120105}
121106
@@ -438,13 +423,15 @@ func BuildRateLimitServiceConfig(irListeners []*ir.HTTPListener) []*rlsconfv3.Ra
438423 continue
439424 }
440425
441- // Process shared rules - add to traffic policy domain
442- sharedDomain := getDomainSharedName (route )
443- addRateLimitDescriptor (route , descriptors , sharedDomain , domainDesc , true )
444-
445- // Process non-shared rules - add to listener-specific domain
446- listenerDomain := irListener .Name
447- addRateLimitDescriptor (route , descriptors , listenerDomain , domainDesc , false )
426+ // For each rule, add to the correct domain only
427+ for rIdx , rule := range route .Traffic .RateLimit .Global .Rules {
428+ descriptor := descriptors [rIdx ]
429+ domain := irListener .Name
430+ if isRuleShared (rule ) {
431+ domain = stripRuleIndexSuffix (rule .Name )
432+ }
433+ addRateLimitDescriptor (route , rule , descriptor , domain , domainDesc )
434+ }
448435 }
449436 }
450437
@@ -488,7 +475,7 @@ func descriptorsEqual(a, b *rlsconfv3.RateLimitDescriptor) bool {
488475 return true
489476}
490477
491- // addRateLimitDescriptor adds rate limit descriptors to the domain descriptor map.
478+ // addRateLimitDescriptor adds rate limit descriptors from a single rule to the domain descriptor map.
492479// Handles both shared and route-specific rate limits.
493480//
494481// An example of route descriptor looks like this:
@@ -501,54 +488,48 @@ func descriptorsEqual(a, b *rlsconfv3.RateLimitDescriptor) bool {
501488// - ...
502489func addRateLimitDescriptor (
503490 route * ir.HTTPRoute ,
504- serviceDescriptors []* rlsconfv3.RateLimitDescriptor ,
491+ rule * ir.RateLimitRule ,
492+ descriptor * rlsconfv3.RateLimitDescriptor ,
505493 domain string ,
506494 domainDescriptors map [string ][]* rlsconfv3.RateLimitDescriptor ,
507- includeShared bool ,
508495) {
509- if ! isValidGlobalRateLimit (route ) || len ( serviceDescriptors ) == 0 {
496+ if ! isValidGlobalRateLimit (route ) || descriptor == nil {
510497 return
511498 }
512499
513- for i , rule := range route .Traffic .RateLimit .Global .Rules {
514- if i >= len (serviceDescriptors ) || (includeShared != isRuleShared (rule )) {
515- continue
516- }
517-
518- var descriptorKey string
519- if isRuleShared (rule ) {
520- descriptorKey = rule .Name
521- } else {
522- descriptorKey = getRouteDescriptor (route .Name )
523- }
500+ var descriptorKey string
501+ if isRuleShared (rule ) {
502+ descriptorKey = rule .Name
503+ } else {
504+ descriptorKey = getRouteDescriptor (route .Name )
505+ }
524506
525- // Find or create descriptor in domainDescriptors[domain]
526- var descriptorRule * rlsconfv3.RateLimitDescriptor
527- found := false
528- for _ , d := range domainDescriptors [domain ] {
529- if d .Key == descriptorKey {
530- descriptorRule = d
531- found = true
532- break
533- }
534- }
535- if ! found {
536- descriptorRule = & rlsconfv3.RateLimitDescriptor {Key : descriptorKey , Value : descriptorKey }
537- domainDescriptors [domain ] = append (domainDescriptors [domain ], descriptorRule )
507+ // Find or create descriptor in domainDescriptors[domain]
508+ var descriptorRule * rlsconfv3.RateLimitDescriptor
509+ found := false
510+ for _ , d := range domainDescriptors [domain ] {
511+ if d .Key == descriptorKey {
512+ descriptorRule = d
513+ found = true
514+ break
538515 }
516+ }
517+ if ! found {
518+ descriptorRule = & rlsconfv3.RateLimitDescriptor {Key : descriptorKey , Value : descriptorKey }
519+ domainDescriptors [domain ] = append (domainDescriptors [domain ], descriptorRule )
520+ }
539521
540- // Ensure no duplicate descriptors
541- alreadyExists := false
542- for _ , existing := range descriptorRule .Descriptors {
543- if descriptorsEqual (existing , serviceDescriptors [i ]) {
544- alreadyExists = true
545- break
546- }
547- }
548- if ! alreadyExists {
549- descriptorRule .Descriptors = append (descriptorRule .Descriptors , serviceDescriptors [i ])
522+ // Ensure no duplicate descriptors
523+ alreadyExists := false
524+ for _ , existing := range descriptorRule .Descriptors {
525+ if descriptorsEqual (existing , descriptor ) {
526+ alreadyExists = true
527+ break
550528 }
551529 }
530+ if ! alreadyExists {
531+ descriptorRule .Descriptors = append (descriptorRule .Descriptors , descriptor )
532+ }
552533}
553534
554535// isSharedRateLimit checks if a route has at least one shared rate limit rule.
@@ -579,16 +560,6 @@ func isRuleShared(rule *ir.RateLimitRule) bool {
579560 return rule != nil && rule .Shared != nil && * rule .Shared
580561}
581562
582- // Helper function to check if a specific rule in a route is shared
583- func isRuleAtIndexShared (route * ir.HTTPRoute , ruleIndex int ) bool {
584- if route == nil || route .Traffic == nil || route .Traffic .RateLimit == nil ||
585- route .Traffic .RateLimit .Global == nil || len (route .Traffic .RateLimit .Global .Rules ) <= ruleIndex || ruleIndex < 0 {
586- return false
587- }
588-
589- return isRuleShared (route .Traffic .RateLimit .Global .Rules [ruleIndex ])
590- }
591-
592563// Helper function to map a global rule index to a domain-specific rule index
593564// This ensures that both shared and non-shared rules have indices starting from 0 in their own domains.
594565func getDomainRuleIndex (rules []* ir.RateLimitRule , globalRuleIdx int , ruleIsShared bool ) int {
@@ -710,19 +681,8 @@ func buildRateLimitServiceDescriptors(route *ir.HTTPRoute) []*rlsconfv3.RateLimi
710681 // 3) No Match (apply to all traffic)
711682 if ! rule .IsMatchSet () {
712683 pbDesc := new (rlsconfv3.RateLimitDescriptor )
713-
714- // Determine if we should use the shared rate limit key (rule-based) or a generic route key
715- if isRuleAtIndexShared (route , rIdx ) {
716- // For shared rate limits, use rule name
717- pbDesc .Key = rule .Name
718- pbDesc .Value = rule .Name
719- } else {
720- // Use generic key for non-shared rate limits, with prefix for uniqueness
721- descriptorKey := getRouteRuleDescriptor (domainRuleIdx , - 1 )
722- pbDesc .Key = descriptorKey
723- pbDesc .Value = pbDesc .Key
724- }
725-
684+ pbDesc .Key = getRouteRuleDescriptor (domainRuleIdx , - 1 )
685+ pbDesc .Value = getRouteRuleDescriptor (domainRuleIdx , - 1 )
726686 head = pbDesc
727687 cur = head
728688 }
@@ -735,11 +695,6 @@ func buildRateLimitServiceDescriptors(route *ir.HTTPRoute) []*rlsconfv3.RateLimi
735695 return pbDescriptors
736696}
737697
738- // getDomainSharedName returns the shared domain (stripped policy name), stripRuleIndexSuffix is used to remove the rule index suffix.
739- func getDomainSharedName (route * ir.HTTPRoute ) string {
740- return stripRuleIndexSuffix (route .Traffic .RateLimit .Global .Rules [0 ].Name )
741- }
742-
743698func getRouteRuleDescriptor (ruleIndex , matchIndex int ) string {
744699 return "rule-" + strconv .Itoa (ruleIndex ) + "-match-" + strconv .Itoa (matchIndex )
745700}
0 commit comments