From ab32e108a15fbd0227770c5555ffa5858adfe38f Mon Sep 17 00:00:00 2001 From: Abbas Gheydi Date: Mon, 15 Apr 2024 10:34:08 +0330 Subject: [PATCH] feat: add rls rules --- go.mod | 13 +++++++---- go.sum | 15 ++++++++++++ internal/webhook/rule_rls.go | 45 ++++++++++++++++++++++++++++++++++++ internal/webhook/validate.go | 31 +++++++++++++++++++++++++ 4 files changed, 100 insertions(+), 4 deletions(-) create mode 100644 internal/webhook/rule_rls.go diff --git a/go.mod b/go.mod index ac594b4..23c04f2 100644 --- a/go.mod +++ b/go.mod @@ -9,6 +9,7 @@ require ( github.com/onsi/ginkgo/v2 v2.13.0 github.com/onsi/gomega v1.29.0 github.com/projectcontour/contour v1.27.0 + github.com/snapp-incubator/contour-global-ratelimit-operator v1.0.2 github.com/spf13/viper v1.15.0 github.com/stretchr/testify v1.8.4 k8s.io/api v0.28.3 @@ -25,11 +26,15 @@ require ( github.com/beorn7/perks v1.0.1 // indirect github.com/blang/semver/v4 v4.0.0 // indirect github.com/cenkalti/backoff/v4 v4.2.1 // indirect + github.com/census-instrumentation/opencensus-proto v0.4.1 // indirect github.com/cespare/xxhash/v2 v2.2.0 // indirect + github.com/cncf/xds/go v0.0.0-20230607035331-e9ce68804cb4 // indirect github.com/coreos/go-semver v0.3.1 // indirect github.com/coreos/go-systemd/v22 v22.5.0 // indirect github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect github.com/emicklei/go-restful/v3 v3.11.0 // indirect + github.com/envoyproxy/go-control-plane v0.11.2-0.20231019082134-6e4589f570e1 // indirect + github.com/envoyproxy/protoc-gen-validate v1.0.2 // indirect github.com/evanphx/json-patch v5.6.0+incompatible // indirect github.com/evanphx/json-patch/v5 v5.6.0 // indirect github.com/felixge/httpsnoop v1.0.3 // indirect @@ -51,7 +56,7 @@ require ( github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1 // indirect github.com/google/uuid v1.4.0 // indirect github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0 // indirect - github.com/grpc-ecosystem/grpc-gateway/v2 v2.7.0 // indirect + github.com/grpc-ecosystem/grpc-gateway/v2 v2.16.0 // indirect github.com/hashicorp/hcl v1.0.1-vault-5 // indirect github.com/imdario/mergo v0.3.12 // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect @@ -94,19 +99,19 @@ require ( go.opentelemetry.io/otel/metric v0.31.0 // indirect go.opentelemetry.io/otel/sdk v1.10.0 // indirect go.opentelemetry.io/otel/trace v1.10.0 // indirect - go.opentelemetry.io/proto/otlp v0.19.0 // indirect + go.opentelemetry.io/proto/otlp v1.0.0 // indirect go.uber.org/multierr v1.11.0 // indirect go.uber.org/zap v1.25.0 // indirect golang.org/x/crypto v0.14.0 // indirect golang.org/x/exp v0.0.0-20230905200255-921286631fa9 // indirect golang.org/x/net v0.17.0 // indirect golang.org/x/oauth2 v0.13.0 // indirect - golang.org/x/sync v0.3.0 // indirect + golang.org/x/sync v0.4.0 // indirect golang.org/x/sys v0.13.0 // indirect golang.org/x/term v0.13.0 // indirect golang.org/x/text v0.13.0 // indirect golang.org/x/time v0.3.0 // indirect - golang.org/x/tools v0.13.0 // indirect + golang.org/x/tools v0.14.0 // indirect gomodules.xyz/jsonpatch/v2 v2.4.0 // indirect google.golang.org/appengine v1.6.7 // indirect google.golang.org/genproto v0.0.0-20230822172742-b8732ec3820d // indirect diff --git a/go.sum b/go.sum index 3be4e76..87cca75 100644 --- a/go.sum +++ b/go.sum @@ -61,6 +61,8 @@ github.com/blang/semver/v4 v4.0.0/go.mod h1:IbckMUScFkM3pff0VJDNKRiT6TG/YpiHIM2y github.com/cenkalti/backoff/v4 v4.2.1 h1:y4OZtCnogmCPw98Zjyt5a6+QwPLGkiQsYW5oUqylYbM= github.com/cenkalti/backoff/v4 v4.2.1/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= +github.com/census-instrumentation/opencensus-proto v0.4.1 h1:iKLQ0xPNFxR/2hzXZMrBo8f1j86j5WHzznCCQxV/b8g= +github.com/census-instrumentation/opencensus-proto v0.4.1/go.mod h1:4T9NM4+4Vw91VeyqjLS6ao50K5bOcLKN6Q42XnYaRYw= github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44= @@ -100,6 +102,8 @@ github.com/envoyproxy/go-control-plane v0.9.7/go.mod h1:cwu0lG7PUMfa9snN8LXBig5y github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= github.com/envoyproxy/go-control-plane v0.9.9-0.20210512163311-63b5d3c536b0/go.mod h1:hliV/p42l8fGbc6Y9bQ70uLwIvmJyVE5k4iMKlh8wCQ= github.com/envoyproxy/go-control-plane v0.9.10-0.20210907150352-cf90f659a021/go.mod h1:AFq3mo9L8Lqqiid3OhADV3RfLJnjiw63cSpi+fDTRC0= +github.com/envoyproxy/go-control-plane v0.11.2-0.20231019082134-6e4589f570e1 h1:i/XN+pZrv2iM+Czc4o4tP6UzUJoOxjNI9gQdE1vIjd0= +github.com/envoyproxy/go-control-plane v0.11.2-0.20231019082134-6e4589f570e1/go.mod h1:3X10o7QcAVxP4y/hnTLgkXLwuZV2DxAEh6uaYD5PoxI= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= github.com/envoyproxy/protoc-gen-validate v1.0.2 h1:QkIBuU5k+x7/QXPvPPnWXWlCdaBFApVqftFV6k087DA= github.com/envoyproxy/protoc-gen-validate v1.0.2/go.mod h1:GpiZQP3dDbg4JouG/NNS7QWXpgx6x8QiMKdmN72jogE= @@ -230,6 +234,8 @@ github.com/grpc-ecosystem/grpc-gateway v1.16.0 h1:gmcG1KaJ57LophUzW0Hy8NmPhnMZb4 github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= github.com/grpc-ecosystem/grpc-gateway/v2 v2.7.0 h1:BZHcxBETFHIdVyhyEfOvn/RdU/QGdLI4y34qQGjGWO0= github.com/grpc-ecosystem/grpc-gateway/v2 v2.7.0/go.mod h1:hgWBS7lorOAVIJEQMi4ZsPv9hVvWI6+ch50m39Pf2Ks= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.16.0 h1:YBftPWNWd4WwGqtY2yeZL2ef8rHAxPBD8KFhJpmcqms= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.16.0/go.mod h1:YN5jB8ie0yfIUg6VvR9Kz84aCaG7AsGZnLjhHbUqwPg= github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hashicorp/hcl v1.0.1-vault-5 h1:kI3hhbbyzr4dldA8UdTb7ZlVVlI2DACdCfz31RPDgJM= @@ -319,6 +325,10 @@ github.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncj github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ= github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= +github.com/snapp-incubator/contour-global-ratelimit-operator v1.0.1 h1:cdECcT1SesEWWmtdPpdnjmgNZjyDQrNiSK4TuSb4DDQ= +github.com/snapp-incubator/contour-global-ratelimit-operator v1.0.1/go.mod h1:0pJRVtrObn6sZ7vv7+kdfjRfP+VsYFW/8lThEevDnjA= +github.com/snapp-incubator/contour-global-ratelimit-operator v1.0.2 h1:R2OIOK4mntXHMnA+btaAiFgSqdpKgxQ+mj/oPW/XV7w= +github.com/snapp-incubator/contour-global-ratelimit-operator v1.0.2/go.mod h1:0pJRVtrObn6sZ7vv7+kdfjRfP+VsYFW/8lThEevDnjA= github.com/soheilhy/cmux v0.1.5 h1:jjzc5WVemNEDTLwv9tlmemhC73tI08BNOIGwBOo10Js= github.com/soheilhy/cmux v0.1.5/go.mod h1:T7TcVDs9LWfQgPlPsdngu6I6QIoyIFZDDC6sNE1GqG0= github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= @@ -409,6 +419,8 @@ go.opentelemetry.io/otel/trace v1.10.0/go.mod h1:Sij3YYczqAdz+EhmGhE6TpTxUO5/F/A go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI= go.opentelemetry.io/proto/otlp v0.19.0 h1:IVN6GR+mhC4s5yfcTbmzHYODqvWAp3ZedA2SJPI1Nnw= go.opentelemetry.io/proto/otlp v0.19.0/go.mod h1:H7XAot3MsfNsj7EXtrA2q5xSNQ10UqI405h3+duxN4U= +go.opentelemetry.io/proto/otlp v1.0.0 h1:T0TX0tmXU8a3CbNXzEKGeU5mIVOdf0oykP+u2lIVU/I= +go.opentelemetry.io/proto/otlp v1.0.0/go.mod h1:Sy6pihPLfYHkr3NkUbEhGHFhINUSI/v80hjKIs5JXpM= go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= go.uber.org/goleak v1.1.11/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ= go.uber.org/goleak v1.2.1 h1:NBol2c7O1ZokfZ0LEU9K6Whx/KnwvepVetCUhtKja4A= @@ -525,6 +537,8 @@ golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.3.0 h1:ftCYgMx6zT/asHUrPw8BLLscYtGznsLAnjq5RH9P66E= golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y= +golang.org/x/sync v0.4.0 h1:zxkM55ReGkDlKSM+Fu41A+zmbZuaPVbGMzvvdUPznYQ= +golang.org/x/sync v0.4.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -638,6 +652,7 @@ golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0= golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.13.0 h1:Iey4qkscZuv0VvIt8E0neZjtPVQFSc870HQ448QgEmQ= golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58= +golang.org/x/tools v0.14.0/go.mod h1:uYBEerGOWcJyEORxN+Ek8+TT266gXkNlHdJBwexUsBg= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= diff --git a/internal/webhook/rule_rls.go b/internal/webhook/rule_rls.go new file mode 100644 index 0000000..1529963 --- /dev/null +++ b/internal/webhook/rule_rls.go @@ -0,0 +1,45 @@ +package webhook + +import ( + "fmt" + "net/http" + + "github.com/snapp-incubator/contour-global-ratelimit-operator/pkg/rlsparser" + admissionv1 "k8s.io/api/admission/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +type rlsValidator struct { + next checker +} + +func (e *rlsValidator) acceptWithWarning(message string) (*admissionv1.AdmissionResponse, *httpErr) { + message = fmt.Sprint("Rate Limit Config Error: ", message) + + messageMaxLenth := 120 + if lenMsg := len(message); lenMsg < 120 { + messageMaxLenth = lenMsg + } + + return &admissionv1.AdmissionResponse{Allowed: true, Warnings: []string{message[:messageMaxLenth]}, Result: &metav1.Status{ + Code: http.StatusAccepted, + Message: message, + }}, nil +} +func (e *rlsValidator) check(checkrequest *checkRequest) (*admissionv1.AdmissionResponse, *httpErr) { + // check if there is any error in parsing rls configs in HTTPProxy Object + _, _, err := rlsparser.ParseGlobalRateLimit(checkrequest.newObj) + if err != nil { + return e.acceptWithWarning(err.Error()) + } + + if e.next != nil { + return e.next.check(checkrequest) + } + + return &admissionv1.AdmissionResponse{Allowed: true}, nil +} + +func (e *rlsValidator) setNext(c checker) { + e.next = c +} diff --git a/internal/webhook/validate.go b/internal/webhook/validate.go index a86c599..40f8e6b 100644 --- a/internal/webhook/validate.go +++ b/internal/webhook/validate.go @@ -57,6 +57,7 @@ func validateV1(ar admissionv1.AdmissionReview, cache *cache.Cache) (*admissionv dryRun: ar.Request.DryRun, cache: cache, } + warningValidatorRlsRules := rlsValidator{} switch ar.Request.Operation { case admissionv1.Create: @@ -66,6 +67,8 @@ func validateV1(ar admissionv1.AdmissionReview, cache *cache.Cache) (*admissionv cicnoc.setNext(&cfoc) response, err := cicnoc.check(cr) + //warning rules + response, err = validateWariningRules(response, err, cr, &warningValidatorRlsRules) return response, err @@ -76,6 +79,8 @@ func validateV1(ar admissionv1.AdmissionReview, cache *cache.Cache) (*admissionv cicnou.setNext(&cfou) response, err := cicnou.check(cr) + //warning rules + response, err = validateWariningRules(response, err, cr, &warningValidatorRlsRules) return response, err @@ -93,3 +98,29 @@ func validateV1(ar admissionv1.AdmissionReview, cache *cache.Cache) (*admissionv return nil, &httpErr{code: http.StatusBadRequest, message: "operation being performed on the requested resource must be one of CREATE, UPDATE or DELETE"} } + +func validateWariningRules(response *admissionv1.AdmissionResponse, err *httpErr, request *checkRequest, checkers ...checker) (*admissionv1.AdmissionResponse, *httpErr) { + //If the request is rejected during rule checking, then do not process the warning rules. + if !response.Allowed { + return response, err + } + + msg := make([]string, 0) + // Combine all warining msg together + for _, c := range checkers { + resp, _ := c.check(request) + if len(resp.Warnings) > 0 { + msg = append(msg, resp.Warnings...) + } + } + + if wariningResponseCount := len(msg); wariningResponseCount == 0 { + //There isn't any warning + return &admissionv1.AdmissionResponse{Allowed: true}, nil + } + //return all warnings + return &admissionv1.AdmissionResponse{Allowed: true, Warnings: msg, Result: &metav1.Status{ + Code: http.StatusAccepted, + Message: fmt.Sprint(msg), + }}, nil +}