diff --git a/go.mod b/go.mod index 19bf24aa742c..a802f59cca82 100644 --- a/go.mod +++ b/go.mod @@ -5,6 +5,7 @@ go 1.19 require ( github.com/cncf/xds/go v0.0.0-20220314180256-7f1daf1720fc github.com/envoyproxy/go-control-plane v0.10.3-0.20221028143534-ed9652aebfd9 + github.com/envoyproxy/ratelimit v1.4.1-0.20230109191524-5f3f5a4cf573 github.com/go-logr/logr v1.2.3 github.com/go-logr/zapr v1.2.3 github.com/google/go-cmp v0.5.9 @@ -17,6 +18,7 @@ require ( golang.org/x/exp v0.0.0-20220722155223-a9213eeb770e google.golang.org/grpc v1.51.0 google.golang.org/protobuf v1.28.1 + gopkg.in/yaml.v3 v3.0.1 k8s.io/api v0.26.0 k8s.io/apimachinery v0.26.0 k8s.io/client-go v0.26.0 @@ -45,12 +47,14 @@ require ( github.com/golang/protobuf v1.5.2 // indirect github.com/google/gnostic v0.5.7-v3refs // indirect github.com/google/gofuzz v1.1.0 // indirect - github.com/google/uuid v1.1.2 // indirect + github.com/google/uuid v1.3.0 // indirect github.com/hashicorp/errwrap v1.1.0 // indirect github.com/imdario/mergo v0.3.12 // indirect github.com/inconshreveable/mousetrap v1.0.1 // indirect github.com/josharian/intern v1.0.0 // indirect github.com/json-iterator/go v1.1.12 // indirect + github.com/kelseyhightower/envconfig v1.4.0 // indirect + github.com/lyft/gostats v0.4.1 // indirect github.com/mailru/easyjson v0.7.6 // indirect github.com/matttproud/golang_protobuf_extensions v1.0.2 // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect @@ -62,11 +66,12 @@ require ( github.com/prometheus/client_model v0.3.0 // indirect github.com/prometheus/common v0.37.0 // indirect github.com/prometheus/procfs v0.8.0 // indirect + github.com/sirupsen/logrus v1.8.1 // indirect github.com/spf13/pflag v1.0.5 // indirect github.com/tsaarni/x500dn v0.0.0-20210331182804-14283c7f5a16 // indirect go.uber.org/atomic v1.7.0 // indirect go.uber.org/multierr v1.6.0 // indirect - golang.org/x/net v0.3.1-0.20221206200815-1e63c2f08a10 // indirect + golang.org/x/net v0.4.0 // indirect golang.org/x/oauth2 v0.0.0-20220223155221-ee480838109b // indirect golang.org/x/sys v0.3.0 // indirect golang.org/x/term v0.3.0 // indirect @@ -77,7 +82,6 @@ require ( google.golang.org/genproto v0.0.0-20220505152158-f39f71e6c8f3 // indirect gopkg.in/inf.v0 v0.9.1 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect - gopkg.in/yaml.v3 v3.0.1 // indirect k8s.io/apiextensions-apiserver v0.26.0 // indirect k8s.io/component-base v0.26.0 // indirect k8s.io/klog/v2 v2.80.1 // indirect diff --git a/go.sum b/go.sum index f20201f2c7ab..40c15375bf4b 100644 --- a/go.sum +++ b/go.sum @@ -89,6 +89,8 @@ github.com/envoyproxy/go-control-plane v0.10.3-0.20221028143534-ed9652aebfd9/go. github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= github.com/envoyproxy/protoc-gen-validate v0.6.7 h1:qcZcULcd/abmQg6dwigimCNEyi4gg31M/xaciQlDml8= github.com/envoyproxy/protoc-gen-validate v0.6.7/go.mod h1:dyJXwwfPK2VSqiB9Klm1J6romD608Ba7Hij42vrOBCo= +github.com/envoyproxy/ratelimit v1.4.1-0.20230109191524-5f3f5a4cf573 h1:R0q3SnW2O9CnSHt9Mx6I/v3nz3HH3xzqeOQ+zSpszpA= +github.com/envoyproxy/ratelimit v1.4.1-0.20230109191524-5f3f5a4cf573/go.mod h1:J780gPM5tWaDEY2n+A7q744I9Bot8OJuBHZbwCfDpVc= github.com/evanphx/json-patch v0.5.2/go.mod h1:ZWS5hhDbVDyob71nXKNL0+PWn6ToqBHMikGIFbs31qQ= github.com/evanphx/json-patch v4.12.0+incompatible h1:4onqiflcdA9EOZ4RxV643DvftH5pOlLGNtQ5lPWQu84= github.com/evanphx/json-patch v4.12.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= @@ -190,8 +192,9 @@ github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hf github.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= -github.com/google/uuid v1.1.2 h1:EVhdT+1Kseyi1/pUmXKaFxYsDNy9RQYkMWRH68J/W7Y= github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= +github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= @@ -219,6 +222,8 @@ github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1 github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk= github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM= +github.com/kelseyhightower/envconfig v1.4.0 h1:Im6hONhd3pLkfDFsbRgu68RDNkGF1r3dvMUtDTo2cv8= +github.com/kelseyhightower/envconfig v1.4.0/go.mod h1:cccZRl6mQpaq41TPp5QxidR+Sa3axMbJDNb//FQX6Gg= github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= @@ -231,6 +236,8 @@ github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= +github.com/lyft/gostats v0.4.1 h1:oR6p4HRCGxt0nUntmZIWmYMgyothBi3eZH2A71vRjsc= +github.com/lyft/gostats v0.4.1/go.mod h1:Tpx2xRzz4t+T2Tx0xdVgIoBdR2UMVz+dKnE3X01XSd8= github.com/lyft/protoc-gen-star v0.6.0/go.mod h1:TGAoBVkt8w7MPG72TrKIu85MIdXwDuzJYeZuUPFPNwA= github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= @@ -294,6 +301,7 @@ github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPx github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88= github.com/sirupsen/logrus v1.8.1 h1:dJKuHgqk1NNQlqoA6BTlM1Wf9DOH3NBjQyu0h9+AZZE= +github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= github.com/spf13/afero v1.3.3/go.mod h1:5KUK8ByomD5Ti5Artl0RtHeI5pTF7MIDuXL3yY520V4= github.com/spf13/afero v1.6.0/go.mod h1:Ai8FlHk4v/PARR026UzYexafAt9roJ7LcLMAmO6Z93I= @@ -428,8 +436,8 @@ golang.org/x/net v0.0.0-20210813160813-60bc85c4be6d/go.mod h1:9nx3DQGgdP8bBQD5qx golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= -golang.org/x/net v0.3.1-0.20221206200815-1e63c2f08a10 h1:Frnccbp+ok2GkUS2tC84yAq/U9Vg+0sIO7aRL3T4Xnc= -golang.org/x/net v0.3.1-0.20221206200815-1e63c2f08a10/go.mod h1:MBQ8lrhLObU/6UmLb4fmbmk5OcyYmqtbGd/9yIeKjEE= +golang.org/x/net v0.4.0 h1:Q5QPcMlvfxFTAPV0+07Xz/MpK9NTXu2VDUuy0FeMfaU= +golang.org/x/net v0.4.0/go.mod h1:MBQ8lrhLObU/6UmLb4fmbmk5OcyYmqtbGd/9yIeKjEE= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= @@ -464,6 +472,7 @@ golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200106162015-b016eb3dc98e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= diff --git a/internal/xds/translator/ratelimit.go b/internal/xds/translator/ratelimit.go index 5465fab3b486..ea3956d5da14 100644 --- a/internal/xds/translator/ratelimit.go +++ b/internal/xds/translator/ratelimit.go @@ -17,6 +17,7 @@ import ( ratelimitfilter "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/http/ratelimit/v3" hcm "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/network/http_connection_manager/v3" wkt "github.com/envoyproxy/go-control-plane/pkg/wellknown" + ratelimitserviceconfig "github.com/envoyproxy/ratelimit/src/config" "google.golang.org/protobuf/types/known/anypb" "google.golang.org/protobuf/types/known/durationpb" "google.golang.org/protobuf/types/known/wrapperspb" @@ -165,6 +166,86 @@ func buildRouteRateLimits(descriptorPrefix string, global *ir.GlobalRateLimit) [ return rateLimits } +// BuildRateLimitServiceConfig builds the rate limit service configuration based on +// https://github.com/envoyproxy/ratelimit#the-configuration-format +func BuildRateLimitServiceConfig(irListener *ir.HTTPListener) *ratelimitserviceconfig.YamlRoot { + yamlDescs := make([]ratelimitserviceconfig.YamlDescriptor, 0, 1) + + for _, route := range irListener.Routes { + if route.RateLimit != nil && route.RateLimit.Global != nil { + descs := buildRateLimitServiceDescriptors(route.Name, route.RateLimit.Global) + yamlDescs = append(yamlDescs, descs...) + } + } + + if len(yamlDescs) == 0 { + return nil + } + + return &ratelimitserviceconfig.YamlRoot{ + Domain: getRateLimitDomain(irListener), + Descriptors: yamlDescs, + } +} + +// buildRateLimitServiceDescriptors creates the rate limit service yaml descriptors based on the global rate limit IR config. +func buildRateLimitServiceDescriptors(descriptorPrefix string, global *ir.GlobalRateLimit) []ratelimitserviceconfig.YamlDescriptor { + yamlDescs := make([]ratelimitserviceconfig.YamlDescriptor, 0, 1) + + for rIdx, rule := range global.Rules { + var head, cur *ratelimitserviceconfig.YamlDescriptor + if len(rule.HeaderMatches) == 0 { + yamlDesc := new(ratelimitserviceconfig.YamlDescriptor) + // GenericKey case + yamlDesc.Key = getRateLimitDescriptorKey(descriptorPrefix, rIdx, -1) + yamlDesc.Value = getRateLimitDescriptorValue(descriptorPrefix, rIdx, -1) + rateLimit := ratelimitserviceconfig.YamlRateLimit{ + RequestsPerUnit: rule.Limit.Requests, + Unit: string(rule.Limit.Unit), + } + yamlDesc.RateLimit = &rateLimit + + head = yamlDesc + cur = head + } + + for mIdx, match := range rule.HeaderMatches { + yamlDesc := new(ratelimitserviceconfig.YamlDescriptor) + // Case for distinct match + if match.Distinct { + // RequestHeader case + yamlDesc.Key = getRateLimitDescriptorKey(descriptorPrefix, rIdx, mIdx) + } else { + // HeaderValueMatch case + yamlDesc.Key = getRateLimitDescriptorKey(descriptorPrefix, rIdx, mIdx) + yamlDesc.Value = getRateLimitDescriptorValue(descriptorPrefix, rIdx, mIdx) + + } + + // Add the ratelimit values to the last descriptor + if mIdx == len(rule.HeaderMatches)-1 { + rateLimit := ratelimitserviceconfig.YamlRateLimit{ + RequestsPerUnit: rule.Limit.Requests, + Unit: string(rule.Limit.Unit), + } + yamlDesc.RateLimit = &rateLimit + } + + if mIdx == 0 { + head = yamlDesc + } else { + cur.Descriptors = []ratelimitserviceconfig.YamlDescriptor{*yamlDesc} + } + + cur = yamlDesc + } + + yamlDescs = append(yamlDescs, *head) + } + + return yamlDescs +} + func buildRateLimitServiceCluster(irListener *ir.HTTPListener) *cluster.Cluster { // Return early if rate limits dont exist. if !isRateLimitPresent(irListener) { diff --git a/internal/xds/translator/testdata/in/ratelimit-config/distinct-match.yaml b/internal/xds/translator/testdata/in/ratelimit-config/distinct-match.yaml new file mode 100644 index 000000000000..109b76ce9202 --- /dev/null +++ b/internal/xds/translator/testdata/in/ratelimit-config/distinct-match.yaml @@ -0,0 +1,21 @@ +name: "first-listener" +address: "0.0.0.0" +port: 10080 +hostnames: +- "*" +routes: +- name: "first-route" + rateLimit: + global: + rules: + - headerMatches: + - name: "x-user-id" + distinct: true + limit: + requests: 5 + unit: second + pathMatch: + exact: "foo/bar" + destinations: + - host: "1.2.3.4" + port: 50000 diff --git a/internal/xds/translator/testdata/in/ratelimit-config/empty-header-matches.yaml b/internal/xds/translator/testdata/in/ratelimit-config/empty-header-matches.yaml new file mode 100644 index 000000000000..5e7faec8b6c7 --- /dev/null +++ b/internal/xds/translator/testdata/in/ratelimit-config/empty-header-matches.yaml @@ -0,0 +1,18 @@ +name: "first-listener" +address: "0.0.0.0" +port: 10080 +hostnames: +- "*" +routes: +- name: "first-route" + rateLimit: + global: + rules: + - limit: + requests: 5 + unit: second + pathMatch: + exact: "foo/bar" + destinations: + - host: "1.2.3.4" + port: 50000 diff --git a/internal/xds/translator/testdata/in/ratelimit-config/multiple-matches.yaml b/internal/xds/translator/testdata/in/ratelimit-config/multiple-matches.yaml new file mode 100644 index 000000000000..fc0ec0f7483f --- /dev/null +++ b/internal/xds/translator/testdata/in/ratelimit-config/multiple-matches.yaml @@ -0,0 +1,23 @@ +name: "first-listener" +address: "0.0.0.0" +port: 10080 +hostnames: +- "*" +routes: +- name: "first-route" + rateLimit: + global: + rules: + - headerMatches: + - name: "x-user-id" + exact: "one" + - name: "x-user-id" + exact: "two" + limit: + requests: 5 + unit: second + pathMatch: + exact: "foo/bar" + destinations: + - host: "1.2.3.4" + port: 50000 diff --git a/internal/xds/translator/testdata/in/ratelimit-config/multiple-routes.yaml b/internal/xds/translator/testdata/in/ratelimit-config/multiple-routes.yaml new file mode 100644 index 000000000000..a158cb7d9fa5 --- /dev/null +++ b/internal/xds/translator/testdata/in/ratelimit-config/multiple-routes.yaml @@ -0,0 +1,33 @@ +name: "first-listener" +address: "0.0.0.0" +port: 10080 +hostnames: +- "*" +routes: +- name: "first-route" + rateLimit: + global: + rules: + - headerMatches: + - name: "x-user-id" + exact: "one" + limit: + requests: 5 + unit: second + pathMatch: + exact: "foo/baz" +- name: "second-route" + rateLimit: + global: + rules: + - headerMatches: + - name: "x-user-id" + exact: "two" + limit: + requests: 10 + unit: second + pathMatch: + exact: "foo/bar" + destinations: + - host: "1.2.3.4" + port: 50000 diff --git a/internal/xds/translator/testdata/in/ratelimit-config/multiple-rules.yaml b/internal/xds/translator/testdata/in/ratelimit-config/multiple-rules.yaml new file mode 100644 index 000000000000..42b297f26622 --- /dev/null +++ b/internal/xds/translator/testdata/in/ratelimit-config/multiple-rules.yaml @@ -0,0 +1,27 @@ +name: "first-listener" +address: "0.0.0.0" +port: 10080 +hostnames: +- "*" +routes: +- name: "first-route" + rateLimit: + global: + rules: + - headerMatches: + - name: "x-user-id" + exact: "one" + limit: + requests: 5 + unit: second + - headerMatches: + - name: "x-user-id" + exact: "two" + limit: + requests: 10 + unit: second + pathMatch: + exact: "foo/bar" + destinations: + - host: "1.2.3.4" + port: 50000 diff --git a/internal/xds/translator/testdata/in/ratelimit-config/value-match.yaml b/internal/xds/translator/testdata/in/ratelimit-config/value-match.yaml new file mode 100644 index 000000000000..1a3b847bf115 --- /dev/null +++ b/internal/xds/translator/testdata/in/ratelimit-config/value-match.yaml @@ -0,0 +1,21 @@ +name: "first-listener" +address: "0.0.0.0" +port: 10080 +hostnames: +- "*" +routes: +- name: "first-route" + rateLimit: + global: + rules: + - headerMatches: + - name: "x-user-id" + exact: "one" + limit: + requests: 5 + unit: second + pathMatch: + exact: "foo/bar" + destinations: + - host: "1.2.3.4" + port: 50000 diff --git a/internal/xds/translator/testdata/out/ratelimit-config/distinct-match.yaml b/internal/xds/translator/testdata/out/ratelimit-config/distinct-match.yaml new file mode 100644 index 000000000000..ce9ff6e51c2f --- /dev/null +++ b/internal/xds/translator/testdata/out/ratelimit-config/distinct-match.yaml @@ -0,0 +1,12 @@ +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: [] + shadow_mode: false diff --git a/internal/xds/translator/testdata/out/ratelimit-config/empty-header-matches.yaml b/internal/xds/translator/testdata/out/ratelimit-config/empty-header-matches.yaml new file mode 100644 index 000000000000..93526cca6f58 --- /dev/null +++ b/internal/xds/translator/testdata/out/ratelimit-config/empty-header-matches.yaml @@ -0,0 +1,12 @@ +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: [] + shadow_mode: false diff --git a/internal/xds/translator/testdata/out/ratelimit-config/multiple-matches.yaml b/internal/xds/translator/testdata/out/ratelimit-config/multiple-matches.yaml new file mode 100644 index 000000000000..c259ebe81548 --- /dev/null +++ b/internal/xds/translator/testdata/out/ratelimit-config/multiple-matches.yaml @@ -0,0 +1,17 @@ +domain: first-listener +descriptors: + - key: first-route-key-rule-0-match-0 + value: first-route-value-rule-0-match-0 + rate_limit: null + 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: [] + shadow_mode: false + shadow_mode: false diff --git a/internal/xds/translator/testdata/out/ratelimit-config/multiple-routes.yaml b/internal/xds/translator/testdata/out/ratelimit-config/multiple-routes.yaml new file mode 100644 index 000000000000..4207669b2c7f --- /dev/null +++ b/internal/xds/translator/testdata/out/ratelimit-config/multiple-routes.yaml @@ -0,0 +1,22 @@ +domain: first-listener +descriptors: + - key: first-route-key-rule-0-match-0 + value: first-route-value-rule-0-match-0 + rate_limit: + requests_per_unit: 5 + unit: second + unlimited: false + name: "" + replaces: [] + descriptors: [] + shadow_mode: false + - key: second-route-key-rule-0-match-0 + value: second-route-value-rule-0-match-0 + rate_limit: + requests_per_unit: 10 + unit: second + unlimited: false + name: "" + replaces: [] + descriptors: [] + shadow_mode: false diff --git a/internal/xds/translator/testdata/out/ratelimit-config/multiple-rules.yaml b/internal/xds/translator/testdata/out/ratelimit-config/multiple-rules.yaml new file mode 100644 index 000000000000..ab2ba6733697 --- /dev/null +++ b/internal/xds/translator/testdata/out/ratelimit-config/multiple-rules.yaml @@ -0,0 +1,22 @@ +domain: first-listener +descriptors: + - key: first-route-key-rule-0-match-0 + value: first-route-value-rule-0-match-0 + rate_limit: + requests_per_unit: 5 + unit: second + unlimited: false + name: "" + replaces: [] + descriptors: [] + shadow_mode: false + - key: first-route-key-rule-1-match-0 + value: first-route-value-rule-1-match-0 + rate_limit: + requests_per_unit: 10 + unit: second + unlimited: false + name: "" + replaces: [] + descriptors: [] + shadow_mode: false diff --git a/internal/xds/translator/testdata/out/ratelimit-config/value-match.yaml b/internal/xds/translator/testdata/out/ratelimit-config/value-match.yaml new file mode 100644 index 000000000000..25b4fce40830 --- /dev/null +++ b/internal/xds/translator/testdata/out/ratelimit-config/value-match.yaml @@ -0,0 +1,12 @@ +domain: first-listener +descriptors: + - key: first-route-key-rule-0-match-0 + value: first-route-value-rule-0-match-0 + rate_limit: + requests_per_unit: 5 + unit: second + unlimited: false + name: "" + replaces: [] + descriptors: [] + shadow_mode: false diff --git a/internal/xds/translator/translator_test.go b/internal/xds/translator/translator_test.go index ca32fcbc5ece..4b9b011232c4 100644 --- a/internal/xds/translator/translator_test.go +++ b/internal/xds/translator/translator_test.go @@ -11,14 +11,15 @@ import ( "path/filepath" "testing" + "github.com/envoyproxy/gateway/internal/ir" "github.com/envoyproxy/go-control-plane/pkg/cache/types" resource "github.com/envoyproxy/go-control-plane/pkg/resource/v3" + ratelimitserviceconfig "github.com/envoyproxy/ratelimit/src/config" "github.com/stretchr/testify/require" "google.golang.org/protobuf/encoding/protojson" "google.golang.org/protobuf/proto" + goyaml "gopkg.in/yaml.v3" "sigs.k8s.io/yaml" - - "github.com/envoyproxy/gateway/internal/ir" ) var ( @@ -28,7 +29,7 @@ var ( inFiles embed.FS ) -func TestTranslate(t *testing.T) { +func TestTranslateXds(t *testing.T) { testCases := []struct { name string requireSecrets bool @@ -129,6 +130,40 @@ func TestTranslate(t *testing.T) { } } +func TestTranslateRateLimitConfig(t *testing.T) { + testCases := []struct { + name string + }{ + { + name: "empty-header-matches", + }, + { + name: "distinct-match", + }, + { + name: "value-match", + }, + { + name: "multiple-matches", + }, + { + name: "multiple-rules", + }, + { + name: "multiple-routes", + }, + } + + for _, tc := range testCases { + tc := tc + t.Run(tc.name, func(t *testing.T) { + in := requireXdsIRListenerFromInputTestData(t, "ratelimit-config", tc.name+".yaml") + out := BuildRateLimitServiceConfig(in) + require.Equal(t, requireTestDataOutFile(t, "ratelimit-config", tc.name+".yaml"), requireYamlRootToYAMLString(t, out)) + }) + } +} + func requireXdsIRFromInputTestData(t *testing.T, name ...string) *ir.Xds { t.Helper() elems := append([]string{"testdata", "in"}, name...) @@ -140,6 +175,17 @@ func requireXdsIRFromInputTestData(t *testing.T, name ...string) *ir.Xds { return ir } +func requireXdsIRListenerFromInputTestData(t *testing.T, name ...string) *ir.HTTPListener { + t.Helper() + elems := append([]string{"testdata", "in"}, name...) + content, err := inFiles.ReadFile(filepath.Join(elems...)) + require.NoError(t, err) + listener := &ir.HTTPListener{} + err = yaml.Unmarshal(content, listener) + require.NoError(t, err) + return listener +} + func requireTestDataOutFile(t *testing.T, name ...string) string { t.Helper() elems := append([]string{"testdata", "out"}, name...) @@ -148,6 +194,12 @@ func requireTestDataOutFile(t *testing.T, name ...string) string { return string(content) } +func requireYamlRootToYAMLString(t *testing.T, yamlRoot *ratelimitserviceconfig.YamlRoot) string { + data, err := goyaml.Marshal(*yamlRoot) + require.NoError(t, err) + return string(data) +} + func requireResourcesToYAMLString(t *testing.T, resources []types.Resource) string { jsonBytes, err := marshalResourcesToJSON(resources) require.NoError(t, err)