From 7c80b9a69289d8284f5ab5f7193b0e9bd1a075bf Mon Sep 17 00:00:00 2001 From: Fernandez Ludovic Date: Fri, 26 Jan 2018 13:29:42 +0100 Subject: [PATCH] refactor(k8s): new annotations style. --- provider/kubernetes/annotations.go | 117 +++++++++++++ provider/kubernetes/kubernetes.go | 230 ++++++++++++------------- provider/kubernetes/kubernetes_test.go | 81 +++++---- 3 files changed, 278 insertions(+), 150 deletions(-) create mode 100644 provider/kubernetes/annotations.go diff --git a/provider/kubernetes/annotations.go b/provider/kubernetes/annotations.go new file mode 100644 index 000000000..4a268b4f8 --- /dev/null +++ b/provider/kubernetes/annotations.go @@ -0,0 +1,117 @@ +package kubernetes + +import ( + "github.com/containous/traefik/provider/label" +) + +const ( + annotationKubernetesIngressClass = "kubernetes.io/ingress.class" + annotationKubernetesAuthRealm = "ingress.kubernetes.io/auth-realm" + annotationKubernetesAuthType = "ingress.kubernetes.io/auth-type" + annotationKubernetesAuthSecret = "ingress.kubernetes.io/auth-secret" + annotationKubernetesRewriteTarget = "ingress.kubernetes.io/rewrite-target" + annotationKubernetesWhitelistSourceRange = "ingress.kubernetes.io/whitelist-source-range" + annotationKubernetesPreserveHost = "ingress.kubernetes.io/preserve-host" + annotationKubernetesPassTLSCert = "ingress.kubernetes.io/pass-tls-cert" + annotationKubernetesFrontendEntryPoints = "ingress.kubernetes.io/frontend-entry-points" + annotationKubernetesPriority = "ingress.kubernetes.io/priority" + annotationKubernetesCircuitBreakerExpression = "ingress.kubernetes.io/circuit-breaker-expression" + annotationKubernetesLoadBalancerMethod = "ingress.kubernetes.io/load-balancer-method" + annotationKubernetesAffinity = "ingress.kubernetes.io/affinity" + annotationKubernetesSessionCookieName = "ingress.kubernetes.io/session-cookie-name" + annotationKubernetesRuleType = "ingress.kubernetes.io/rule-type" + annotationKubernetesRedirectEntryPoint = "ingress.kubernetes.io/redirect-entry-point" + annotationKubernetesRedirectRegex = "ingress.kubernetes.io/redirect-regex" + annotationKubernetesRedirectReplacement = "ingress.kubernetes.io/redirect-replacement" + annotationKubernetesMaxConnAmount = "ingress.kubernetes.io/max-conn-amount" + annotationKubernetesMaxConnExtractorFunc = "ingress.kubernetes.io/max-conn-extractor-func" + annotationKubernetesRateLimit = "ingress.kubernetes.io/rate-limit" + annotationKubernetesErrorPages = "ingress.kubernetes.io/error-pages" + annotationKubernetesBuffering = "ingress.kubernetes.io/buffering" + + annotationKubernetesSSLRedirect = "ingress.kubernetes.io/ssl-redirect" + annotationKubernetesHSTSMaxAge = "ingress.kubernetes.io/hsts-max-age" + annotationKubernetesHSTSIncludeSubdomains = "ingress.kubernetes.io/hsts-include-subdomains" + annotationKubernetesCustomRequestHeaders = "ingress.kubernetes.io/custom-request-headers" + annotationKubernetesCustomResponseHeaders = "ingress.kubernetes.io/custom-response-headers" + annotationKubernetesAllowedHosts = "ingress.kubernetes.io/allowed-hosts" + annotationKubernetesProxyHeaders = "ingress.kubernetes.io/proxy-headers" + annotationKubernetesSSLTemporaryRedirect = "ingress.kubernetes.io/ssl-temporary-redirect" + annotationKubernetesSSLHost = "ingress.kubernetes.io/ssl-host" + annotationKubernetesSSLProxyHeaders = "ingress.kubernetes.io/ssl-proxy-headers" + annotationKubernetesHSTSPreload = "ingress.kubernetes.io/hsts-preload" + annotationKubernetesForceHSTSHeader = "ingress.kubernetes.io/force-hsts" + annotationKubernetesFrameDeny = "ingress.kubernetes.io/frame-deny" + annotationKubernetesCustomFrameOptionsValue = "ingress.kubernetes.io/custom-frame-options-value" + annotationKubernetesContentTypeNosniff = "ingress.kubernetes.io/content-type-nosniff" + annotationKubernetesBrowserXSSFilter = "ingress.kubernetes.io/browser-xss-filter" + annotationKubernetesContentSecurityPolicy = "ingress.kubernetes.io/content-security-policy" + annotationKubernetesPublicKey = "ingress.kubernetes.io/public-key" + annotationKubernetesReferrerPolicy = "ingress.kubernetes.io/referrer-policy" + annotationKubernetesIsDevelopment = "ingress.kubernetes.io/is-development" +) + +// TODO [breaking] remove label support +var compatibilityMapping = map[string]string{ + annotationKubernetesPreserveHost: "traefik.frontend.passHostHeader", + annotationKubernetesPassTLSCert: "traefik.frontend.passTLSCert", + annotationKubernetesFrontendEntryPoints: "traefik.frontend.entryPoints", + annotationKubernetesPriority: "traefik.frontend.priority", + annotationKubernetesCircuitBreakerExpression: "traefik.backend.circuitbreaker", + annotationKubernetesLoadBalancerMethod: "traefik.backend.loadbalancer.method", + annotationKubernetesAffinity: "traefik.backend.loadbalancer.stickiness", + annotationKubernetesSessionCookieName: "traefik.backend.loadbalancer.stickiness.cookieName", + annotationKubernetesRuleType: "traefik.frontend.rule.type", + annotationKubernetesRedirectEntryPoint: "traefik.frontend.redirect.entrypoint", + annotationKubernetesRedirectRegex: "traefik.frontend.redirect.regex", + annotationKubernetesRedirectReplacement: "traefik.frontend.redirect.replacement", +} + +func getAnnotationName(annotations map[string]string, name string) string { + if _, ok := annotations[name]; ok { + return name + } + + if _, ok := annotations[label.Prefix+name]; ok { + return name + } + + // TODO [breaking] remove label support + if lbl, compat := compatibilityMapping[name]; compat { + if _, ok := annotations[lbl]; ok { + return name + } + } + + return name +} + +func getStringValue(annotations map[string]string, annotation string, defaultValue string) string { + annotationName := getAnnotationName(annotations, annotation) + return label.GetStringValue(annotations, annotationName, defaultValue) +} + +func getBoolValue(annotations map[string]string, annotation string, defaultValue bool) bool { + annotationName := getAnnotationName(annotations, annotation) + return label.GetBoolValue(annotations, annotationName, defaultValue) +} + +func getIntValue(annotations map[string]string, annotation string, defaultValue int) int { + annotationName := getAnnotationName(annotations, annotation) + return label.GetIntValue(annotations, annotationName, defaultValue) +} + +func getInt64Value(annotations map[string]string, annotation string, defaultValue int64) int64 { + annotationName := getAnnotationName(annotations, annotation) + return label.GetInt64Value(annotations, annotationName, defaultValue) +} + +func getSliceStringValue(annotations map[string]string, annotation string) []string { + annotationName := getAnnotationName(annotations, annotation) + return label.GetSliceStringValue(annotations, annotationName) +} + +func getMapValue(annotations map[string]string, annotation string) map[string]string { + annotationName := getAnnotationName(annotations, annotation) + return label.GetMapValue(annotations, annotationName) +} diff --git a/provider/kubernetes/kubernetes.go b/provider/kubernetes/kubernetes.go index a31231c13..b7a66ff57 100644 --- a/provider/kubernetes/kubernetes.go +++ b/provider/kubernetes/kubernetes.go @@ -21,6 +21,7 @@ import ( "github.com/containous/traefik/safe" "github.com/containous/traefik/tls" "github.com/containous/traefik/types" + "gopkg.in/yaml.v2" "k8s.io/client-go/pkg/api/v1" "k8s.io/client-go/pkg/apis/extensions/v1beta1" "k8s.io/client-go/pkg/util/intstr" @@ -32,36 +33,9 @@ const ( ruleTypePathPrefix = "PathPrefix" ruleTypeReplacePath = "ReplacePath" - annotationKubernetesIngressClass = "kubernetes.io/ingress.class" - annotationKubernetesAuthRealm = "ingress.kubernetes.io/auth-realm" - annotationKubernetesAuthType = "ingress.kubernetes.io/auth-type" - annotationKubernetesAuthSecret = "ingress.kubernetes.io/auth-secret" - annotationKubernetesRewriteTarget = "ingress.kubernetes.io/rewrite-target" - annotationKubernetesWhitelistSourceRange = "ingress.kubernetes.io/whitelist-source-range" - annotationKubernetesSSLRedirect = "ingress.kubernetes.io/ssl-redirect" - annotationKubernetesHSTSMaxAge = "ingress.kubernetes.io/hsts-max-age" - annotationKubernetesHSTSIncludeSubdomains = "ingress.kubernetes.io/hsts-include-subdomains" - annotationKubernetesCustomRequestHeaders = "ingress.kubernetes.io/custom-request-headers" - annotationKubernetesCustomResponseHeaders = "ingress.kubernetes.io/custom-response-headers" - annotationKubernetesAllowedHosts = "ingress.kubernetes.io/allowed-hosts" - annotationKubernetesProxyHeaders = "ingress.kubernetes.io/proxy-headers" - annotationKubernetesSSLTemporaryRedirect = "ingress.kubernetes.io/ssl-temporary-redirect" - annotationKubernetesSSLHost = "ingress.kubernetes.io/ssl-host" - annotationKubernetesSSLProxyHeaders = "ingress.kubernetes.io/ssl-proxy-headers" - annotationKubernetesHSTSPreload = "ingress.kubernetes.io/hsts-preload" - annotationKubernetesForceHSTSHeader = "ingress.kubernetes.io/force-hsts" - annotationKubernetesFrameDeny = "ingress.kubernetes.io/frame-deny" - annotationKubernetesCustomFrameOptionsValue = "ingress.kubernetes.io/custom-frame-options-value" - annotationKubernetesContentTypeNosniff = "ingress.kubernetes.io/content-type-nosniff" - annotationKubernetesBrowserXSSFilter = "ingress.kubernetes.io/browser-xss-filter" - annotationKubernetesContentSecurityPolicy = "ingress.kubernetes.io/content-security-policy" - annotationKubernetesPublicKey = "ingress.kubernetes.io/public-key" - annotationKubernetesReferrerPolicy = "ingress.kubernetes.io/referrer-policy" - annotationKubernetesIsDevelopment = "ingress.kubernetes.io/is-development" + traefikDefaultRealm = "traefik" ) -const traefikDefaultRealm = "traefik" - // Provider holds configurations of the provider. type Provider struct { provider.BaseProvider `mapstructure:",squash" export:"true"` @@ -170,7 +144,8 @@ func (p *Provider) loadIngresses(k8sClient Client) (*types.Configuration, error) } for _, i := range ingresses { - ingressClass := i.Annotations[annotationKubernetesIngressClass] + annotationIngressClass := getAnnotationName(i.Annotations, annotationKubernetesIngressClass) + ingressClass := i.Annotations[annotationIngressClass] if !shouldProcessIngress(ingressClass) { continue @@ -200,8 +175,9 @@ func (p *Provider) loadIngresses(k8sClient Client) (*types.Configuration, error) } } - if realm := i.Annotations[annotationKubernetesAuthRealm]; realm != "" && realm != traefikDefaultRealm { - log.Errorf("Value for annotation %q on ingress %s/%s invalid: no realm customization supported", annotationKubernetesAuthRealm, i.Namespace, i.Name) + annotationAuthRealm := getAnnotationName(i.Annotations, annotationKubernetesAuthRealm) + if realm := i.Annotations[annotationAuthRealm]; realm != "" && realm != traefikDefaultRealm { + log.Errorf("Value for annotation %q on ingress %s/%s invalid: no realm customization supported", annotationAuthRealm, i.Namespace, i.Name) delete(templateObjects.Backends, baseName) continue } @@ -213,12 +189,11 @@ func (p *Provider) loadIngresses(k8sClient Client) (*types.Configuration, error) continue } - passHostHeader := label.GetBoolValue(i.Annotations, label.TraefikFrontendPassHostHeader, !p.DisablePassHostHeaders) - passTLSCert := label.GetBoolValue(i.Annotations, label.TraefikFrontendPassTLSCert, p.EnablePassTLSCert) - priority := label.GetIntValue(i.Annotations, label.TraefikFrontendPriority, 0) - entryPoints := label.GetSliceStringValue(i.Annotations, label.TraefikFrontendEntryPoints) - whitelistSourceRange := label.GetSliceStringValue(i.Annotations, annotationKubernetesWhitelistSourceRange) - errorPages := label.ParseErrorPages(i.Annotations, label.Prefix+label.BaseFrontendErrorPage, label.RegexpFrontendErrorPage) + passHostHeader := getBoolValue(i.Annotations, annotationKubernetesPreserveHost, !p.DisablePassHostHeaders) + passTLSCert := getBoolValue(i.Annotations, annotationKubernetesPassTLSCert, p.EnablePassTLSCert) + priority := getIntValue(i.Annotations, annotationKubernetesPriority, 0) + entryPoints := getSliceStringValue(i.Annotations, annotationKubernetesFrontendEntryPoints) + whitelistSourceRange := getSliceStringValue(i.Annotations, annotationKubernetesWhitelistSourceRange) templateObjects.Frontends[baseName] = &types.Frontend{ Backend: baseName, @@ -231,7 +206,7 @@ func (p *Provider) loadIngresses(k8sClient Client) (*types.Configuration, error) Redirect: getFrontendRedirect(i), EntryPoints: entryPoints, Headers: getHeader(i), - Errors: errorPages, + Errors: getErrorPages(i), RateLimit: getRateLimit(i), } } @@ -262,19 +237,11 @@ func (p *Provider) loadIngresses(k8sClient Client) (*types.Configuration, error) continue } - if expression := service.Annotations[label.TraefikBackendCircuitBreaker]; expression != "" { - templateObjects.Backends[baseName].CircuitBreaker = &types.CircuitBreaker{ - Expression: expression, - } - } - + templateObjects.Backends[baseName].CircuitBreaker = getCircuitBreaker(service) templateObjects.Backends[baseName].LoadBalancer = getLoadBalancer(service) + templateObjects.Backends[baseName].MaxConn = getMaxConn(service) templateObjects.Backends[baseName].Buffering = getBuffering(service) - if maxConn := getMaxConn(service); maxConn != nil { - templateObjects.Backends[baseName].MaxConn = maxConn - } - protocol := label.DefaultProtocol for _, port := range service.Spec.Ports { if equalPorts(port, pa.Backend.ServicePort) { @@ -344,14 +311,10 @@ func getRuleForPath(pa v1beta1.HTTPIngressPath, i *v1beta1.Ingress) string { return "" } - ruleType := i.Annotations[label.TraefikFrontendRuleType] - if ruleType == "" { - ruleType = ruleTypePathPrefix - } - + ruleType := getStringValue(i.Annotations, annotationKubernetesRuleType, ruleTypePathPrefix) rules := []string{ruleType + ":" + pa.Path} - if rewriteTarget := i.Annotations[annotationKubernetesRewriteTarget]; rewriteTarget != "" { + if rewriteTarget := getStringValue(i.Annotations, annotationKubernetesRewriteTarget, ""); rewriteTarget != "" { rules = append(rules, ruleTypeReplacePath+":"+rewriteTarget) } @@ -366,7 +329,8 @@ func getRuleForHost(host string) string { } func handleBasicAuthConfig(i *v1beta1.Ingress, k8sClient Client) ([]string, error) { - authType, exists := i.Annotations[annotationKubernetesAuthType] + annotationAuthType := getAnnotationName(i.Annotations, annotationKubernetesAuthType) + authType, exists := i.Annotations[annotationAuthType] if !exists { return nil, nil } @@ -375,7 +339,7 @@ func handleBasicAuthConfig(i *v1beta1.Ingress, k8sClient Client) ([]string, erro return nil, fmt.Errorf("unsupported auth-type on annotation ingress.kubernetes.io/auth-type: %q", authType) } - authSecret := i.Annotations[annotationKubernetesAuthSecret] + authSecret := getStringValue(i.Annotations, annotationKubernetesAuthSecret, "") if authSecret == "" { return nil, errors.New("auth-secret annotation ingress.kubernetes.io/auth-secret must be set") } @@ -443,10 +407,11 @@ func getTLS(ingress *v1beta1.Ingress, k8sClient Client) ([]*tls.Configuration, e missingEntries = append(missingEntries, "tls.key") } if len(missingEntries) > 0 { - return nil, fmt.Errorf("secret %s/%s is missing the following TLS data entries: %s", ingress.Namespace, t.SecretName, strings.Join(missingEntries, ", ")) + return nil, fmt.Errorf("secret %s/%s is missing the following TLS data entries: %s", + ingress.Namespace, t.SecretName, strings.Join(missingEntries, ", ")) } - entryPoints := label.GetSliceStringValue(ingress.Annotations, label.TraefikFrontendEntryPoints) + entryPoints := getSliceStringValue(ingress.Annotations, annotationKubernetesFrontendEntryPoints) tlsConfig := &tls.Configuration{ EntryPoints: entryPoints, @@ -491,36 +456,40 @@ func shouldProcessIngress(ingressClass string) bool { } func getFrontendRedirect(i *v1beta1.Ingress) *types.Redirect { - frontendRedirectEntryPoint, ok := i.Annotations[label.TraefikFrontendRedirectEntryPoint] - frep := ok && len(frontendRedirectEntryPoint) > 0 - - frontendRedirectRegex, ok := i.Annotations[label.TraefikFrontendRedirectRegex] - frrg := ok && len(frontendRedirectRegex) > 0 - - frontendRedirectReplacement, ok := i.Annotations[label.TraefikFrontendRedirectReplacement] - frrp := ok && len(frontendRedirectReplacement) > 0 - - if frep || frrg && frrp { + redirectEntryPoint := getStringValue(i.Annotations, annotationKubernetesRedirectEntryPoint, "") + if len(redirectEntryPoint) > 0 { return &types.Redirect{ - EntryPoint: frontendRedirectEntryPoint, - Regex: frontendRedirectRegex, - Replacement: frontendRedirectReplacement, + EntryPoint: redirectEntryPoint, } } + + redirectRegex := getStringValue(i.Annotations, annotationKubernetesRedirectRegex, "") + redirectReplacement := getStringValue(i.Annotations, annotationKubernetesRedirectReplacement, "") + if len(redirectRegex) > 0 && len(redirectReplacement) > 0 { + return &types.Redirect{ + Regex: redirectRegex, + Replacement: redirectReplacement, + } + } + 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, ""), + var buffering *types.Buffering + + bufferingRaw := getStringValue(service.Annotations, annotationKubernetesBuffering, "") + + if len(bufferingRaw) > 0 { + buffering = &types.Buffering{} + err := yaml.Unmarshal([]byte(bufferingRaw), buffering) + if err != nil { + log.Error(err) + return nil } } - return nil + + return buffering } func getLoadBalancer(service *v1.Service) *types.LoadBalancer { @@ -528,12 +497,12 @@ func getLoadBalancer(service *v1.Service) *types.LoadBalancer { Method: "wrr", } - if service.Annotations[label.TraefikBackendLoadBalancerMethod] == "drr" { + if getStringValue(service.Annotations, annotationKubernetesLoadBalancerMethod, "") == "drr" { loadBalancer.Method = "drr" } if sticky := service.Annotations[label.TraefikBackendLoadBalancerSticky]; len(sticky) > 0 { - log.Warnf("Deprecated configuration found: %s. Please use %s.", label.TraefikBackendLoadBalancerSticky, label.TraefikBackendLoadBalancerStickiness) + log.Warnf("Deprecated configuration found: %s. Please use %s.", label.TraefikBackendLoadBalancerSticky, annotationKubernetesAffinity) loadBalancer.Sticky = strings.EqualFold(strings.TrimSpace(sticky), "true") } @@ -545,9 +514,9 @@ func getLoadBalancer(service *v1.Service) *types.LoadBalancer { } func getStickiness(service *v1.Service) *types.Stickiness { - if service.Annotations[label.TraefikBackendLoadBalancerStickiness] == "true" { + if getBoolValue(service.Annotations, annotationKubernetesAffinity, false) { stickiness := &types.Stickiness{} - if cookieName := service.Annotations[label.TraefikBackendLoadBalancerStickinessCookieName]; len(cookieName) > 0 { + if cookieName := getStringValue(service.Annotations, annotationKubernetesSessionCookieName, ""); len(cookieName) > 0 { stickiness.CookieName = cookieName } return stickiness @@ -557,26 +526,26 @@ func getStickiness(service *v1.Service) *types.Stickiness { func getHeader(i *v1beta1.Ingress) *types.Headers { headers := &types.Headers{ - CustomRequestHeaders: label.GetMapValue(i.Annotations, annotationKubernetesCustomRequestHeaders), - CustomResponseHeaders: label.GetMapValue(i.Annotations, annotationKubernetesCustomResponseHeaders), - AllowedHosts: label.GetSliceStringValue(i.Annotations, annotationKubernetesAllowedHosts), - HostsProxyHeaders: label.GetSliceStringValue(i.Annotations, annotationKubernetesProxyHeaders), - SSLRedirect: label.GetBoolValue(i.Annotations, annotationKubernetesSSLRedirect, false), - SSLTemporaryRedirect: label.GetBoolValue(i.Annotations, annotationKubernetesSSLTemporaryRedirect, false), - SSLHost: label.GetStringValue(i.Annotations, annotationKubernetesSSLHost, ""), - SSLProxyHeaders: label.GetMapValue(i.Annotations, annotationKubernetesSSLProxyHeaders), - STSSeconds: label.GetInt64Value(i.Annotations, annotationKubernetesHSTSMaxAge, 0), - STSIncludeSubdomains: label.GetBoolValue(i.Annotations, annotationKubernetesHSTSIncludeSubdomains, false), - STSPreload: label.GetBoolValue(i.Annotations, annotationKubernetesHSTSPreload, false), - ForceSTSHeader: label.GetBoolValue(i.Annotations, annotationKubernetesForceHSTSHeader, false), - FrameDeny: label.GetBoolValue(i.Annotations, annotationKubernetesFrameDeny, false), - CustomFrameOptionsValue: label.GetStringValue(i.Annotations, annotationKubernetesCustomFrameOptionsValue, ""), - ContentTypeNosniff: label.GetBoolValue(i.Annotations, annotationKubernetesContentTypeNosniff, false), - BrowserXSSFilter: label.GetBoolValue(i.Annotations, annotationKubernetesBrowserXSSFilter, false), - ContentSecurityPolicy: label.GetStringValue(i.Annotations, annotationKubernetesContentSecurityPolicy, ""), - PublicKey: label.GetStringValue(i.Annotations, annotationKubernetesPublicKey, ""), - ReferrerPolicy: label.GetStringValue(i.Annotations, annotationKubernetesReferrerPolicy, ""), - IsDevelopment: label.GetBoolValue(i.Annotations, annotationKubernetesIsDevelopment, false), + CustomRequestHeaders: getMapValue(i.Annotations, annotationKubernetesCustomRequestHeaders), + CustomResponseHeaders: getMapValue(i.Annotations, annotationKubernetesCustomResponseHeaders), + AllowedHosts: getSliceStringValue(i.Annotations, annotationKubernetesAllowedHosts), + HostsProxyHeaders: getSliceStringValue(i.Annotations, annotationKubernetesProxyHeaders), + SSLRedirect: getBoolValue(i.Annotations, annotationKubernetesSSLRedirect, false), + SSLTemporaryRedirect: getBoolValue(i.Annotations, annotationKubernetesSSLTemporaryRedirect, false), + SSLHost: getStringValue(i.Annotations, annotationKubernetesSSLHost, ""), + SSLProxyHeaders: getMapValue(i.Annotations, annotationKubernetesSSLProxyHeaders), + STSSeconds: getInt64Value(i.Annotations, annotationKubernetesHSTSMaxAge, 0), + STSIncludeSubdomains: getBoolValue(i.Annotations, annotationKubernetesHSTSIncludeSubdomains, false), + STSPreload: getBoolValue(i.Annotations, annotationKubernetesHSTSPreload, false), + ForceSTSHeader: getBoolValue(i.Annotations, annotationKubernetesForceHSTSHeader, false), + FrameDeny: getBoolValue(i.Annotations, annotationKubernetesFrameDeny, false), + CustomFrameOptionsValue: getStringValue(i.Annotations, annotationKubernetesCustomFrameOptionsValue, ""), + ContentTypeNosniff: getBoolValue(i.Annotations, annotationKubernetesContentTypeNosniff, false), + BrowserXSSFilter: getBoolValue(i.Annotations, annotationKubernetesBrowserXSSFilter, false), + ContentSecurityPolicy: getStringValue(i.Annotations, annotationKubernetesContentSecurityPolicy, ""), + PublicKey: getStringValue(i.Annotations, annotationKubernetesPublicKey, ""), + ReferrerPolicy: getStringValue(i.Annotations, annotationKubernetesReferrerPolicy, ""), + IsDevelopment: getBoolValue(i.Annotations, annotationKubernetesIsDevelopment, false), } if !headers.HasSecureHeadersDefined() && !headers.HasCustomHeadersDefined() { @@ -586,19 +555,9 @@ func getHeader(i *v1beta1.Ingress) *types.Headers { return headers } -func getRateLimit(i *v1beta1.Ingress) *types.RateLimit { - if rlExtractFunc := i.Annotations[label.TraefikFrontendRateLimitExtractorFunc]; len(rlExtractFunc) > 0 { - return &types.RateLimit{ - ExtractorFunc: rlExtractFunc, - RateSet: label.ParseRateSets(i.Annotations, label.Prefix+label.BaseFrontendRateLimit, label.RegexpFrontendRateLimit), - } - } - return nil -} - func getMaxConn(service *v1.Service) *types.MaxConn { - amount := label.GetInt64Value(service.Annotations, label.TraefikBackendMaxConnAmount, -1) - extractorFunc := service.Annotations[label.TraefikBackendMaxConnExtractorFunc] + amount := getInt64Value(service.Annotations, annotationKubernetesMaxConnAmount, -1) + extractorFunc := getStringValue(service.Annotations, annotationKubernetesMaxConnExtractorFunc, "") if amount >= 0 && len(extractorFunc) > 0 { return &types.MaxConn{ ExtractorFunc: extractorFunc, @@ -607,3 +566,44 @@ func getMaxConn(service *v1.Service) *types.MaxConn { } return nil } + +func getCircuitBreaker(service *v1.Service) *types.CircuitBreaker { + if expression := getStringValue(service.Annotations, annotationKubernetesCircuitBreakerExpression, ""); expression != "" { + return &types.CircuitBreaker{ + Expression: expression, + } + } + return nil +} + +func getErrorPages(i *v1beta1.Ingress) map[string]*types.ErrorPage { + var errorPages map[string]*types.ErrorPage + + pagesRaw := getStringValue(i.Annotations, annotationKubernetesErrorPages, "") + if len(pagesRaw) > 0 { + errorPages = make(map[string]*types.ErrorPage) + err := yaml.Unmarshal([]byte(pagesRaw), errorPages) + if err != nil { + log.Error(err) + return nil + } + } + + return errorPages +} + +func getRateLimit(i *v1beta1.Ingress) *types.RateLimit { + var rateLimit *types.RateLimit + + rateRaw := getStringValue(i.Annotations, annotationKubernetesRateLimit, "") + if len(rateRaw) > 0 { + rateLimit = &types.RateLimit{} + err := yaml.Unmarshal([]byte(rateRaw), rateLimit) + if err != nil { + log.Error(err) + return nil + } + } + + return rateLimit +} diff --git a/provider/kubernetes/kubernetes_test.go b/provider/kubernetes/kubernetes_test.go index 124e1f8e3..9c074d87f 100644 --- a/provider/kubernetes/kubernetes_test.go +++ b/provider/kubernetes/kubernetes_test.go @@ -203,7 +203,7 @@ func TestRuleType(t *testing.T) { if test.ingressRuleType != "" { ingress.Annotations = map[string]string{ - label.TraefikFrontendRuleType: test.ingressRuleType, + annotationKubernetesRuleType: test.ingressRuleType, } } @@ -440,8 +440,8 @@ func TestServiceAnnotations(t *testing.T) { sName("service1"), sNamespace("testing"), sUID("1"), - sAnnotation(label.TraefikBackendCircuitBreaker, "NetworkErrorRatio() > 0.5"), - sAnnotation(label.TraefikBackendLoadBalancerMethod, "drr"), + sAnnotation(annotationKubernetesCircuitBreakerExpression, "NetworkErrorRatio() > 0.5"), + sAnnotation(annotationKubernetesLoadBalancerMethod, "drr"), sSpec( clusterIP("10.0.0.1"), sPorts(sPort(80, ""))), @@ -450,7 +450,7 @@ func TestServiceAnnotations(t *testing.T) { sName("service2"), sNamespace("testing"), sUID("2"), - sAnnotation(label.TraefikBackendCircuitBreaker, ""), + sAnnotation(annotationKubernetesCircuitBreakerExpression, ""), sAnnotation(label.TraefikBackendLoadBalancerSticky, "true"), sSpec( clusterIP("10.0.0.2"), @@ -460,11 +460,13 @@ func TestServiceAnnotations(t *testing.T) { sName("service3"), sNamespace("testing"), sUID("3"), - sAnnotation(label.TraefikBackendBufferingMaxRequestBodyBytes, "10485760"), - sAnnotation(label.TraefikBackendBufferingMemRequestBodyBytes, "2097152"), - sAnnotation(label.TraefikBackendBufferingMaxResponseBodyBytes, "10485760"), - sAnnotation(label.TraefikBackendBufferingMemResponseBodyBytes, "2097152"), - sAnnotation(label.TraefikBackendBufferingRetryExpression, "IsNetworkError() && Attempts() <= 2"), + sAnnotation(annotationKubernetesBuffering, ` +maxrequestbodybytes: 10485760 +memrequestbodybytes: 2097153 +maxresponsebodybytes: 10485761 +memresponsebodybytes: 2097152 +retryexpression: IsNetworkError() && Attempts() <= 2 +`), sSpec( clusterIP("10.0.0.3"), sPorts(sPort(803, ""))), @@ -473,8 +475,8 @@ func TestServiceAnnotations(t *testing.T) { sName("service4"), sNamespace("testing"), sUID("4"), - sAnnotation(label.TraefikBackendMaxConnExtractorFunc, "client.ip"), - sAnnotation(label.TraefikBackendMaxConnAmount, "6"), + sAnnotation(annotationKubernetesMaxConnExtractorFunc, "client.ip"), + sAnnotation(annotationKubernetesMaxConnAmount, "6"), sSpec( clusterIP("10.0.0.4"), sPorts(sPort(804, ""))), @@ -562,8 +564,8 @@ func TestServiceAnnotations(t *testing.T) { lbMethod("wrr"), buffering( maxRequestBodyBytes(10485760), - memRequestBodyBytes(2097152), - maxResponseBodyBytes(10485760), + memRequestBodyBytes(2097153), + maxResponseBodyBytes(10485761), memResponseBodyBytes(2097152), retrying("IsNetworkError() && Attempts() <= 2"), ), @@ -588,7 +590,6 @@ func TestServiceAnnotations(t *testing.T) { passHostHeader(), routes(route("bar", "Host:bar"))), frontend("baz", - headers(), passHostHeader(), routes(route("baz", "Host:baz"))), frontend("max-conn", @@ -605,7 +606,7 @@ func TestIngressAnnotations(t *testing.T) { ingresses := []*v1beta1.Ingress{ buildIngress( iNamespace("testing"), - iAnnotation(label.TraefikFrontendPassHostHeader, "false"), + iAnnotation(annotationKubernetesPreserveHost, "false"), iRules( iRule( iHost("foo"), @@ -614,7 +615,7 @@ func TestIngressAnnotations(t *testing.T) { ), buildIngress( iNamespace("testing"), - iAnnotation(label.TraefikFrontendPassHostHeader, "true"), + iAnnotation(annotationKubernetesPreserveHost, "true"), iAnnotation(annotationKubernetesIngressClass, "traefik"), iRules( iRule( @@ -624,7 +625,7 @@ func TestIngressAnnotations(t *testing.T) { ), buildIngress( iNamespace("testing"), - iAnnotation(label.TraefikFrontendPassTLSCert, "true"), + iAnnotation(annotationKubernetesPassTLSCert, "true"), iAnnotation(annotationKubernetesIngressClass, "traefik"), iRules( iRule( @@ -634,7 +635,7 @@ func TestIngressAnnotations(t *testing.T) { ), buildIngress( iNamespace("testing"), - iAnnotation(label.TraefikFrontendEntryPoints, "http,https"), + iAnnotation(annotationKubernetesFrontendEntryPoints, "http,https"), iAnnotation(annotationKubernetesIngressClass, "traefik"), iRules( iRule( @@ -692,7 +693,7 @@ func TestIngressAnnotations(t *testing.T) { buildIngress( iNamespace("testing"), iAnnotation(annotationKubernetesIngressClass, "traefik"), - iAnnotation(label.TraefikFrontendRedirectEntryPoint, "https"), + iAnnotation(annotationKubernetesRedirectEntryPoint, "https"), iRules( iRule( iHost("redirect"), @@ -702,9 +703,14 @@ func TestIngressAnnotations(t *testing.T) { buildIngress( iNamespace("testing"), iAnnotation(annotationKubernetesIngressClass, "traefik"), - iAnnotation(label.Prefix+label.BaseFrontendErrorPage+"foo."+label.SuffixErrorPageQuery, "/bar"), - iAnnotation(label.Prefix+label.BaseFrontendErrorPage+"foo."+label.SuffixErrorPageStatus, "123,456"), - iAnnotation(label.Prefix+label.BaseFrontendErrorPage+"foo."+label.SuffixErrorPageBackend, "bar"), + iAnnotation(annotationKubernetesErrorPages, ` +foo: + status: + - "123" + - "456" + backend: bar + query: /bar +`), iRules( iRule( iHost("error-pages"), @@ -714,13 +720,18 @@ func TestIngressAnnotations(t *testing.T) { 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"), + iAnnotation(annotationKubernetesRateLimit, ` +extractorfunc: client.ip +rateset: + bar: + period: 3s + average: 6 + burst: 9 + foo: + period: 6s + average: 12 + burst: 18 +`), iRules( iRule( iHost("rate-limit"), @@ -992,7 +1003,7 @@ func TestPriorityHeaderValue(t *testing.T) { ingresses := []*v1beta1.Ingress{ buildIngress( iNamespace("testing"), - iAnnotation(label.TraefikFrontendPriority, "1337"), + iAnnotation(annotationKubernetesPriority, "1337"), iRules( iRule( iHost("foo"), @@ -1052,7 +1063,7 @@ func TestInvalidPassTLSCertValue(t *testing.T) { ingresses := []*v1beta1.Ingress{ buildIngress( iNamespace("testing"), - iAnnotation(label.TraefikFrontendPassTLSCert, "herpderp"), + iAnnotation(annotationKubernetesPassTLSCert, "herpderp"), iRules( iRule( iHost("foo"), @@ -1109,7 +1120,7 @@ func TestInvalidPassHostHeaderValue(t *testing.T) { ingresses := []*v1beta1.Ingress{ buildIngress( iNamespace("testing"), - iAnnotation(label.TraefikFrontendPassHostHeader, "herpderp"), + iAnnotation(annotationKubernetesPreserveHost, "herpderp"), iRules( iRule( iHost("foo"), @@ -1405,7 +1416,7 @@ func TestTLSSecretLoad(t *testing.T) { ingresses := []*v1beta1.Ingress{ buildIngress( iNamespace("testing"), - iAnnotation(label.TraefikFrontendEntryPoints, "ep1,ep2"), + iAnnotation(annotationKubernetesFrontendEntryPoints, "ep1,ep2"), iRules( iRule(iHost("example.com"), iPaths( onePath(iBackend("example-com", intstr.FromInt(80))), @@ -1420,7 +1431,7 @@ func TestTLSSecretLoad(t *testing.T) { ), buildIngress( iNamespace("testing"), - iAnnotation(label.TraefikFrontendEntryPoints, "ep3"), + iAnnotation(annotationKubernetesFrontendEntryPoints, "ep3"), iRules( iRule(iHost("example.fail"), iPaths( onePath(iBackend("example-fail", intstr.FromInt(80))), @@ -1651,7 +1662,7 @@ func TestGetTLS(t *testing.T) { desc: "pass the endpoints defined in the annotation to the certificate", ingress: buildIngress( iNamespace("testing"), - iAnnotation(label.TraefikFrontendEntryPoints, "https,api-secure"), + iAnnotation(annotationKubernetesFrontendEntryPoints, "https,api-secure"), iRules(iRule(iHost("example.com"))), iTLSes(iTLS("test-secret")), ),