feat(k8s): add rate limit annotations.
This commit is contained in:
parent
4c0d6e211b
commit
1c495d7ea4
|
@ -2,7 +2,9 @@ package kubernetes
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"testing"
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/containous/flaeg"
|
||||||
"github.com/containous/traefik/tls"
|
"github.com/containous/traefik/tls"
|
||||||
"github.com/containous/traefik/types"
|
"github.com/containous/traefik/types"
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
|
@ -252,6 +254,57 @@ func errorBackend(backend string) func(*types.ErrorPage) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func rateLimit(opts ...func(*types.RateLimit)) func(*types.Frontend) {
|
||||||
|
return func(f *types.Frontend) {
|
||||||
|
if f.RateLimit == nil {
|
||||||
|
f.RateLimit = &types.RateLimit{}
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, opt := range opts {
|
||||||
|
opt(f.RateLimit)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func rateExtractorFunc(exp string) func(*types.RateLimit) {
|
||||||
|
return func(limit *types.RateLimit) {
|
||||||
|
limit.ExtractorFunc = exp
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func rateSet(name string, opts ...func(*types.Rate)) func(*types.RateLimit) {
|
||||||
|
return func(limit *types.RateLimit) {
|
||||||
|
if limit.RateSet == nil {
|
||||||
|
limit.RateSet = make(map[string]*types.Rate)
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(name) > 0 {
|
||||||
|
limit.RateSet[name] = &types.Rate{}
|
||||||
|
for _, opt := range opts {
|
||||||
|
opt(limit.RateSet[name])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func limitAverage(avg int64) func(*types.Rate) {
|
||||||
|
return func(rate *types.Rate) {
|
||||||
|
rate.Average = avg
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func limitBurst(burst int64) func(*types.Rate) {
|
||||||
|
return func(rate *types.Rate) {
|
||||||
|
rate.Burst = burst
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func limitPeriod(period time.Duration) func(*types.Rate) {
|
||||||
|
return func(rate *types.Rate) {
|
||||||
|
rate.Period = flaeg.Duration(period)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func passTLSCert() func(*types.Frontend) {
|
func passTLSCert() func(*types.Frontend) {
|
||||||
return func(f *types.Frontend) {
|
return func(f *types.Frontend) {
|
||||||
f.PassTLSCert = true
|
f.PassTLSCert = true
|
||||||
|
|
|
@ -234,6 +234,7 @@ func (p *Provider) loadIngresses(k8sClient Client) (*types.Configuration, error)
|
||||||
EntryPoints: entryPoints,
|
EntryPoints: entryPoints,
|
||||||
Headers: getHeader(i),
|
Headers: getHeader(i),
|
||||||
Errors: errorPages,
|
Errors: errorPages,
|
||||||
|
RateLimit: getRateLimit(i),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -507,6 +508,19 @@ func getFrontendRedirect(i *v1beta1.Ingress) *types.Redirect {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func getBuffering(service *v1.Service) *types.Buffering {
|
||||||
|
if label.HasPrefix(service.Annotations, label.TraefikBackendBuffering) {
|
||||||
|
return &types.Buffering{
|
||||||
|
MaxRequestBodyBytes: label.GetInt64Value(service.Annotations, label.TraefikBackendBufferingMaxRequestBodyBytes, 0),
|
||||||
|
MemRequestBodyBytes: label.GetInt64Value(service.Annotations, label.TraefikBackendBufferingMemRequestBodyBytes, 0),
|
||||||
|
MaxResponseBodyBytes: label.GetInt64Value(service.Annotations, label.TraefikBackendBufferingMaxResponseBodyBytes, 0),
|
||||||
|
MemResponseBodyBytes: label.GetInt64Value(service.Annotations, label.TraefikBackendBufferingMemResponseBodyBytes, 0),
|
||||||
|
RetryExpression: label.GetStringValue(service.Annotations, label.TraefikBackendBufferingRetryExpression, ""),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func getLoadBalancer(service *v1.Service) *types.LoadBalancer {
|
func getLoadBalancer(service *v1.Service) *types.LoadBalancer {
|
||||||
loadBalancer := &types.LoadBalancer{
|
loadBalancer := &types.LoadBalancer{
|
||||||
Method: "wrr",
|
Method: "wrr",
|
||||||
|
@ -564,14 +578,11 @@ func getHeader(i *v1beta1.Ingress) *types.Headers {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func getBuffering(service *v1.Service) *types.Buffering {
|
func getRateLimit(i *v1beta1.Ingress) *types.RateLimit {
|
||||||
if label.HasPrefix(service.Annotations, label.TraefikBackendBuffering) {
|
if rlExtractFunc := i.Annotations[label.TraefikFrontendRateLimitExtractorFunc]; len(rlExtractFunc) > 0 {
|
||||||
return &types.Buffering{
|
return &types.RateLimit{
|
||||||
MaxRequestBodyBytes: label.GetInt64Value(service.Annotations, label.TraefikBackendBufferingMaxRequestBodyBytes, 0),
|
ExtractorFunc: rlExtractFunc,
|
||||||
MemRequestBodyBytes: label.GetInt64Value(service.Annotations, label.TraefikBackendBufferingMemRequestBodyBytes, 0),
|
RateSet: label.ParseRateSets(i.Annotations, label.Prefix+label.BaseFrontendRateLimit, label.RegexpFrontendRateLimit),
|
||||||
MaxResponseBodyBytes: label.GetInt64Value(service.Annotations, label.TraefikBackendBufferingMaxResponseBodyBytes, 0),
|
|
||||||
MemResponseBodyBytes: label.GetInt64Value(service.Annotations, label.TraefikBackendBufferingMemResponseBodyBytes, 0),
|
|
||||||
RetryExpression: label.GetStringValue(service.Annotations, label.TraefikBackendBufferingRetryExpression, ""),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
|
|
|
@ -5,6 +5,7 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"reflect"
|
"reflect"
|
||||||
"testing"
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/containous/traefik/provider/label"
|
"github.com/containous/traefik/provider/label"
|
||||||
"github.com/containous/traefik/tls"
|
"github.com/containous/traefik/tls"
|
||||||
|
@ -685,6 +686,22 @@ func TestIngressAnnotations(t *testing.T) {
|
||||||
iPaths(onePath(iPath("/errorpages"), iBackend("service1", intstr.FromInt(80))))),
|
iPaths(onePath(iPath("/errorpages"), iBackend("service1", intstr.FromInt(80))))),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
buildIngress(
|
||||||
|
iNamespace("testing"),
|
||||||
|
iAnnotation(annotationKubernetesIngressClass, "traefik"),
|
||||||
|
iAnnotation(label.TraefikFrontendRateLimitExtractorFunc, "client.ip"),
|
||||||
|
iAnnotation(label.Prefix+label.BaseFrontendRateLimit+"foo."+label.SuffixRateLimitPeriod, "6"),
|
||||||
|
iAnnotation(label.Prefix+label.BaseFrontendRateLimit+"foo."+label.SuffixRateLimitAverage, "12"),
|
||||||
|
iAnnotation(label.Prefix+label.BaseFrontendRateLimit+"foo."+label.SuffixRateLimitBurst, "18"),
|
||||||
|
iAnnotation(label.Prefix+label.BaseFrontendRateLimit+"bar."+label.SuffixRateLimitPeriod, "3"),
|
||||||
|
iAnnotation(label.Prefix+label.BaseFrontendRateLimit+"bar."+label.SuffixRateLimitAverage, "6"),
|
||||||
|
iAnnotation(label.Prefix+label.BaseFrontendRateLimit+"bar."+label.SuffixRateLimitBurst, "9"),
|
||||||
|
iRules(
|
||||||
|
iRule(
|
||||||
|
iHost("rate-limit"),
|
||||||
|
iPaths(onePath(iPath("/ratelimit"), iBackend("service1", intstr.FromInt(80))))),
|
||||||
|
),
|
||||||
|
),
|
||||||
}
|
}
|
||||||
|
|
||||||
services := []*v1.Service{
|
services := []*v1.Service{
|
||||||
|
@ -785,6 +802,12 @@ func TestIngressAnnotations(t *testing.T) {
|
||||||
server("http://example.com", weight(1))),
|
server("http://example.com", weight(1))),
|
||||||
lbMethod("wrr"),
|
lbMethod("wrr"),
|
||||||
),
|
),
|
||||||
|
backend("rate-limit/ratelimit",
|
||||||
|
servers(
|
||||||
|
server("http://example.com", weight(1)),
|
||||||
|
server("http://example.com", weight(1))),
|
||||||
|
lbMethod("wrr"),
|
||||||
|
),
|
||||||
),
|
),
|
||||||
frontends(
|
frontends(
|
||||||
frontend("foo/bar",
|
frontend("foo/bar",
|
||||||
|
@ -863,6 +886,16 @@ func TestIngressAnnotations(t *testing.T) {
|
||||||
route("/errorpages", "PathPrefix:/errorpages"),
|
route("/errorpages", "PathPrefix:/errorpages"),
|
||||||
route("error-pages", "Host:error-pages")),
|
route("error-pages", "Host:error-pages")),
|
||||||
),
|
),
|
||||||
|
frontend("rate-limit/ratelimit",
|
||||||
|
headers(),
|
||||||
|
passHostHeader(),
|
||||||
|
rateLimit(rateExtractorFunc("client.ip"),
|
||||||
|
rateSet("foo", limitPeriod(6*time.Second), limitAverage(12), limitBurst(18)),
|
||||||
|
rateSet("bar", limitPeriod(3*time.Second), limitAverage(6), limitBurst(9))),
|
||||||
|
routes(
|
||||||
|
route("/ratelimit", "PathPrefix:/ratelimit"),
|
||||||
|
route("rate-limit", "Host:rate-limit")),
|
||||||
|
),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
@ -75,6 +75,18 @@
|
||||||
{{end}}
|
{{end}}
|
||||||
{{end}}
|
{{end}}
|
||||||
|
|
||||||
|
{{ if $frontend.RateLimit }}
|
||||||
|
[frontends."frontend-{{$frontendName}}".rateLimit]
|
||||||
|
extractorFunc = "{{ $frontend.RateLimit.ExtractorFunc }}"
|
||||||
|
[frontends."frontend-{{$frontendName}}".rateLimit.rateSet]
|
||||||
|
{{ range $limitName, $limit := $frontend.RateLimit.RateSet }}
|
||||||
|
[frontends."frontend-{{$frontendName}}".rateLimit.rateSet.{{ $limitName }}]
|
||||||
|
period = "{{ $limit.Period }}"
|
||||||
|
average = {{ $limit.Average }}
|
||||||
|
burst = {{ $limit.Burst }}
|
||||||
|
{{end}}
|
||||||
|
{{end}}
|
||||||
|
|
||||||
{{if $frontend.Headers }}
|
{{if $frontend.Headers }}
|
||||||
[frontends."{{$frontendName}}".headers]
|
[frontends."{{$frontendName}}".headers]
|
||||||
SSLRedirect = {{$frontend.Headers.SSLRedirect}}
|
SSLRedirect = {{$frontend.Headers.SSLRedirect}}
|
||||||
|
|
Loading…
Reference in a new issue