Skip to content

Commit

Permalink
fix: add route descriptor prefix for ratelimit xds translator (#2234)
Browse files Browse the repository at this point in the history
* add route descriptor prefix for each xds route under listener

Signed-off-by: sh2 <shawnhxh@outlook.com>

* simplify route and route rule descriptor keys and values

Signed-off-by: sh2 <shawnhxh@outlook.com>

---------

Signed-off-by: sh2 <shawnhxh@outlook.com>
Co-authored-by: Xunzhuo <bitliu@tencent.com>
Co-authored-by: zirain <zirain2009@gmail.com>
  • Loading branch information
3 people authored Dec 19, 2023
1 parent b9e75af commit 769849e
Show file tree
Hide file tree
Showing 17 changed files with 348 additions and 139 deletions.
80 changes: 54 additions & 26 deletions internal/xds/translator/ratelimit.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ const (
// rateLimitClientTLSCACertFilename is the ratelimit ca cert file.
rateLimitClientTLSCACertFilename = "/certs/ca.crt"
)

const (
// Use `draft RFC Version 03 <https://tools.ietf.org/id/draft-polli-ratelimit-headers-03.html>` by default,
// where 3 headers will be added:
Expand Down Expand Up @@ -88,7 +89,6 @@ func (t *Translator) isRateLimitPresent(irListener *ir.HTTPListener) bool {
}

func (t *Translator) buildRateLimitFilter(irListener *ir.HTTPListener) *hcmv3.HttpFilter {

rateLimitFilterProto := &ratelimitfilterv3.RateLimit{
Domain: getRateLimitDomain(irListener),
RateLimitService: &ratelimitv3.RateLimitServiceConfig{
Expand Down Expand Up @@ -138,16 +138,27 @@ func patchRouteWithRateLimit(xdsRouteAction *routev3.RouteAction, irRoute *ir.HT
}

func buildRouteRateLimits(descriptorPrefix string, global *ir.GlobalRateLimit) []*routev3.RateLimit {
rateLimits := []*routev3.RateLimit{}
var rateLimits []*routev3.RateLimit

// Route descriptor for each route rule action
routeDescriptor := &routev3.RateLimit_Action{
ActionSpecifier: &routev3.RateLimit_Action_GenericKey_{
GenericKey: &routev3.RateLimit_Action_GenericKey{
DescriptorKey: getRouteDescriptor(descriptorPrefix),
DescriptorValue: getRouteDescriptor(descriptorPrefix),
},
},
}

// Rules are ORed
for rIdx, rule := range global.Rules {
rlActions := []*routev3.RateLimit_Action{}
var rlActions []*routev3.RateLimit_Action
// Matches are ANDed
for mIdx, match := range rule.HeaderMatches {
// Case for distinct match
if match.Distinct {
// Setup RequestHeader actions
descriptorKey := getRateLimitDescriptorKey(descriptorPrefix, rIdx, mIdx)
descriptorKey := getRouteRuleDescriptor(rIdx, mIdx)
action := &routev3.RateLimit_Action{
ActionSpecifier: &routev3.RateLimit_Action_RequestHeaders_{
RequestHeaders: &routev3.RateLimit_Action_RequestHeaders{
Expand All @@ -156,11 +167,11 @@ func buildRouteRateLimits(descriptorPrefix string, global *ir.GlobalRateLimit) [
},
},
}
rlActions = append(rlActions, action)
rlActions = append(rlActions, routeDescriptor, action)
} else {
// Setup HeaderValueMatch actions
descriptorKey := getRateLimitDescriptorKey(descriptorPrefix, rIdx, mIdx)
descriptorVal := getRateLimitDescriptorValue(descriptorPrefix, rIdx, mIdx)
descriptorKey := getRouteRuleDescriptor(rIdx, mIdx)
descriptorVal := getRouteRuleDescriptor(rIdx, mIdx)
headerMatcher := &routev3.HeaderMatcher{
Name: match.Name,
HeaderMatchSpecifier: &routev3.HeaderMatcher_StringMatch{
Expand All @@ -179,7 +190,7 @@ func buildRouteRateLimits(descriptorPrefix string, global *ir.GlobalRateLimit) [
},
},
}
rlActions = append(rlActions, action)
rlActions = append(rlActions, routeDescriptor, action)
}
}

Expand Down Expand Up @@ -214,7 +225,7 @@ func buildRouteRateLimits(descriptorPrefix string, global *ir.GlobalRateLimit) [
MaskedRemoteAddress: mra,
},
}
rlActions = append(rlActions, action)
rlActions = append(rlActions, routeDescriptor, action)

// Setup RemoteAddress action if distinct match is set
if rule.CIDRMatch.Distinct {
Expand All @@ -235,12 +246,12 @@ func buildRouteRateLimits(descriptorPrefix string, global *ir.GlobalRateLimit) [
action := &routev3.RateLimit_Action{
ActionSpecifier: &routev3.RateLimit_Action_GenericKey_{
GenericKey: &routev3.RateLimit_Action_GenericKey{
DescriptorKey: getRateLimitDescriptorKey(descriptorPrefix, rIdx, -1),
DescriptorValue: getRateLimitDescriptorValue(descriptorPrefix, rIdx, -1),
DescriptorKey: getRouteRuleDescriptor(rIdx, -1),
DescriptorValue: getRouteRuleDescriptor(rIdx, -1),
},
},
}
rlActions = append(rlActions, action)
rlActions = append(rlActions, routeDescriptor, action)
}

rateLimit := &routev3.RateLimit{Actions: rlActions}
Expand All @@ -264,12 +275,29 @@ func GetRateLimitServiceConfigStr(pbCfg *rlsconfv3.RateLimitConfig) (string, err
// BuildRateLimitServiceConfig builds the rate limit service configuration based on
// https://github.com/envoyproxy/ratelimit#the-configuration-format
func BuildRateLimitServiceConfig(irListener *ir.HTTPListener) *rlsconfv3.RateLimitConfig {
pbDescriptors := make([]*rlsconfv3.RateLimitDescriptor, 0, 1)
pbDescriptors := make([]*rlsconfv3.RateLimitDescriptor, 0, len(irListener.Routes))

for _, route := range irListener.Routes {
if route.RateLimit != nil && route.RateLimit.Global != nil {
serviceDescriptors := buildRateLimitServiceDescriptors(route.Name, route.RateLimit.Global)
pbDescriptors = append(pbDescriptors, serviceDescriptors...)
serviceDescriptors := buildRateLimitServiceDescriptors(route.RateLimit.Global)

// Get route rule descriptors within each route.
//
// An example of route descriptor looks like this:
//
// descriptors:
// - key: ${RouteDescriptor}
// value: ${RouteDescriptor}
// descriptors:
// - key: ${RouteRuleDescriptor}
// value: ${RouteRuleDescriptor}
//
routeDescriptor := &rlsconfv3.RateLimitDescriptor{
Key: getRouteDescriptor(route.Name),
Value: getRouteDescriptor(route.Name),
Descriptors: serviceDescriptors,
}
pbDescriptors = append(pbDescriptors, routeDescriptor)
}
}

Expand All @@ -284,16 +312,16 @@ func BuildRateLimitServiceConfig(irListener *ir.HTTPListener) *rlsconfv3.RateLim
}

// buildRateLimitServiceDescriptors creates the rate limit service pb descriptors based on the global rate limit IR config.
func buildRateLimitServiceDescriptors(descriptorPrefix string, global *ir.GlobalRateLimit) []*rlsconfv3.RateLimitDescriptor {
pbDescriptors := make([]*rlsconfv3.RateLimitDescriptor, 0, 1)
func buildRateLimitServiceDescriptors(global *ir.GlobalRateLimit) []*rlsconfv3.RateLimitDescriptor {
pbDescriptors := make([]*rlsconfv3.RateLimitDescriptor, 0, len(global.Rules))

for rIdx, rule := range global.Rules {
var head, cur *rlsconfv3.RateLimitDescriptor
if !rule.IsMatchSet() {
pbDesc := new(rlsconfv3.RateLimitDescriptor)
// GenericKey case
pbDesc.Key = getRateLimitDescriptorKey(descriptorPrefix, rIdx, -1)
pbDesc.Value = getRateLimitDescriptorValue(descriptorPrefix, rIdx, -1)
pbDesc.Key = getRouteRuleDescriptor(rIdx, -1)
pbDesc.Value = getRouteRuleDescriptor(rIdx, -1)
rateLimit := rlsconfv3.RateLimitPolicy{
RequestsPerUnit: uint32(rule.Limit.Requests),
Unit: rlsconfv3.RateLimitUnit(rlsconfv3.RateLimitUnit_value[strings.ToUpper(string(rule.Limit.Unit))]),
Expand All @@ -308,11 +336,11 @@ func buildRateLimitServiceDescriptors(descriptorPrefix string, global *ir.Global
// Case for distinct match
if match.Distinct {
// RequestHeader case
pbDesc.Key = getRateLimitDescriptorKey(descriptorPrefix, rIdx, mIdx)
pbDesc.Key = getRouteRuleDescriptor(rIdx, mIdx)
} else {
// HeaderValueMatch case
pbDesc.Key = getRateLimitDescriptorKey(descriptorPrefix, rIdx, mIdx)
pbDesc.Value = getRateLimitDescriptorValue(descriptorPrefix, rIdx, mIdx)
pbDesc.Key = getRouteRuleDescriptor(rIdx, mIdx)
pbDesc.Value = getRouteRuleDescriptor(rIdx, mIdx)
}

// Add the ratelimit values to the last descriptor
Expand Down Expand Up @@ -453,12 +481,12 @@ func (t *Translator) createRateLimitServiceCluster(tCtx *types.ResourceVersionTa
return nil
}

func getRateLimitDescriptorKey(prefix string, ruleIndex, matchIndex int) string {
return prefix + "-key-rule-" + strconv.Itoa(ruleIndex) + "-match-" + strconv.Itoa(matchIndex)
func getRouteRuleDescriptor(ruleIndex, matchIndex int) string {
return "rule-" + strconv.Itoa(ruleIndex) + "-match-" + strconv.Itoa(matchIndex)
}

func getRateLimitDescriptorValue(prefix string, ruleIndex, matchIndex int) string {
return prefix + "-value-rule-" + strconv.Itoa(ruleIndex) + "-match-" + strconv.Itoa(matchIndex)
func getRouteDescriptor(routeName string) string {
return routeName
}

func getRateLimitServiceClusterName() string {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,10 @@ routes:
cidr: "192.168.0.0/16"
ipv6: false
maskLen: 16
distinct: true
limit:
requests: 5
unit: second
distinct: true
pathMatch:
exact: "foo/bar"
destination:
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
name: "first-listener"
address: "0.0.0.0"
port: 10080
hostnames:
- "*"
routes:
- name: "first-route"
rateLimit:
global:
rules:
- cidrMatch:
cidr: "192.168.0.10/32"
ipv6: false
maskLen: 32
limit:
requests: 15
unit: Hour
pathMatch:
exact: "foo/bar"
destination:
name: "first-route-dest"
settings:
- endpoints:
- host: "1.2.3.4"
port: 50000
- name: "second-route"
rateLimit:
global:
rules:
- cidrMatch:
cidr: "192.168.0.10/32"
ipv6: false
maskLen: 32
limit:
requests: 300
unit: Hour
pathMatch:
exact: "foo/bar"
destination:
name: "second-route-dest"
settings:
- endpoints:
- host: "4.5.6.7"
port: 50001
Original file line number Diff line number Diff line change
@@ -1,13 +1,19 @@
domain: first-listener
descriptors:
- key: first-route-key-rule-0-match-0
value: ""
rate_limit:
requests_per_unit: 5
unit: SECOND
unlimited: false
name: ""
replaces: []
descriptors: []
- key: first-route
value: first-route
rate_limit: null
descriptors:
- key: rule-0-match-0
value: ""
rate_limit:
requests_per_unit: 5
unit: SECOND
unlimited: false
name: ""
replaces: []
descriptors: []
shadow_mode: false
detailed_metric: false
shadow_mode: false
detailed_metric: false
Original file line number Diff line number Diff line change
@@ -1,16 +1,24 @@
domain: first-listener
descriptors:
- key: masked_remote_address
value: 192.168.0.0/16
- key: first-route
value: first-route
rate_limit: null
descriptors:
- key: remote_address
rate_limit:
requests_per_unit: 5
unit: SECOND
unlimited: false
name: ""
replaces: []
descriptors: []
- key: masked_remote_address
value: 192.168.0.0/16
rate_limit: null
descriptors:
- key: remote_address
value: ""
rate_limit:
requests_per_unit: 5
unit: SECOND
unlimited: false
name: ""
replaces: []
descriptors: []
shadow_mode: false
detailed_metric: false
shadow_mode: false
detailed_metric: false
shadow_mode: false
Expand Down
Original file line number Diff line number Diff line change
@@ -1,13 +1,19 @@
domain: first-listener
descriptors:
- key: first-route-key-rule-0-match--1
value: first-route-value-rule-0-match--1
rate_limit:
requests_per_unit: 5
unit: SECOND
unlimited: false
name: ""
replaces: []
descriptors: []
- key: first-route
value: first-route
rate_limit: null
descriptors:
- key: rule-0-match--1
value: rule-0-match--1
rate_limit:
requests_per_unit: 5
unit: SECOND
unlimited: false
name: ""
replaces: []
descriptors: []
shadow_mode: false
detailed_metric: false
shadow_mode: false
detailed_metric: false
Original file line number Diff line number Diff line change
@@ -1,13 +1,19 @@
domain: first-listener
descriptors:
- key: masked_remote_address
value: 192.168.0.0/16
rate_limit:
requests_per_unit: 5
unit: SECOND
unlimited: false
name: ""
replaces: []
descriptors: []
- key: first-route
value: first-route
rate_limit: null
descriptors:
- key: masked_remote_address
value: 192.168.0.0/16
rate_limit:
requests_per_unit: 5
unit: SECOND
unlimited: false
name: ""
replaces: []
descriptors: []
shadow_mode: false
detailed_metric: false
shadow_mode: false
detailed_metric: false
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
domain: first-listener
descriptors:
- key: first-route
value: first-route
rate_limit: null
descriptors:
- key: masked_remote_address
value: 192.168.0.10/32
rate_limit:
requests_per_unit: 15
unit: HOUR
unlimited: false
name: ""
replaces: []
descriptors: []
shadow_mode: false
detailed_metric: false
shadow_mode: false
detailed_metric: false
- key: second-route
value: second-route
rate_limit: null
descriptors:
- key: masked_remote_address
value: 192.168.0.10/32
rate_limit:
requests_per_unit: 300
unit: HOUR
unlimited: false
name: ""
replaces: []
descriptors: []
shadow_mode: false
detailed_metric: false
shadow_mode: false
detailed_metric: false
Loading

0 comments on commit 769849e

Please sign in to comment.