1
0
Fork 0

Merge tag 'v1.7.4' into master

This commit is contained in:
Fernandez Ludovic 2018-10-30 12:34:00 +01:00
commit d3ae88f108
154 changed files with 4356 additions and 1285 deletions

View file

@ -7,43 +7,45 @@ import (
)
const (
annotationKubernetesIngressClass = "kubernetes.io/ingress.class"
annotationKubernetesAuthRealm = "ingress.kubernetes.io/auth-realm"
annotationKubernetesAuthType = "ingress.kubernetes.io/auth-type"
annotationKubernetesAuthSecret = "ingress.kubernetes.io/auth-secret"
annotationKubernetesAuthHeaderField = "ingress.kubernetes.io/auth-header-field"
annotationKubernetesAuthForwardResponseHeaders = "ingress.kubernetes.io/auth-response-headers"
annotationKubernetesAuthRemoveHeader = "ingress.kubernetes.io/auth-remove-header"
annotationKubernetesAuthForwardURL = "ingress.kubernetes.io/auth-url"
annotationKubernetesAuthForwardTrustHeaders = "ingress.kubernetes.io/auth-trust-headers"
annotationKubernetesAuthForwardTLSSecret = "ingress.kubernetes.io/auth-tls-secret"
annotationKubernetesAuthForwardTLSInsecure = "ingress.kubernetes.io/auth-tls-insecure"
annotationKubernetesRewriteTarget = "ingress.kubernetes.io/rewrite-target"
annotationKubernetesWhiteListSourceRange = "ingress.kubernetes.io/whitelist-source-range"
annotationKubernetesWhiteListIPStrategy = "ingress.kubernetes.io/whitelist-ipstrategy"
annotationKubernetesWhiteListIPStrategyDepth = "ingress.kubernetes.io/whitelist-ipstrategy-depth"
annotationKubernetesWhiteListIPStrategyExcludedIPs = "ingress.kubernetes.io/whitelist-ipstrategy-excluded-ips"
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"
annotationKubernetesRedirectPermanent = "ingress.kubernetes.io/redirect-permanent"
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"
annotationKubernetesAppRoot = "ingress.kubernetes.io/app-root"
annotationKubernetesServiceWeights = "ingress.kubernetes.io/service-weights"
annotationKubernetesRequestModifier = "ingress.kubernetes.io/request-modifier"
annotationKubernetesIngressClass = "kubernetes.io/ingress.class"
annotationKubernetesAuthRealm = "ingress.kubernetes.io/auth-realm"
annotationKubernetesAuthType = "ingress.kubernetes.io/auth-type"
annotationKubernetesAuthSecret = "ingress.kubernetes.io/auth-secret"
annotationKubernetesAuthHeaderField = "ingress.kubernetes.io/auth-header-field"
annotationKubernetesAuthForwardResponseHeaders = "ingress.kubernetes.io/auth-response-headers"
annotationKubernetesAuthRemoveHeader = "ingress.kubernetes.io/auth-remove-header"
annotationKubernetesAuthForwardURL = "ingress.kubernetes.io/auth-url"
annotationKubernetesAuthForwardTrustHeaders = "ingress.kubernetes.io/auth-trust-headers"
annotationKubernetesAuthForwardTLSSecret = "ingress.kubernetes.io/auth-tls-secret"
annotationKubernetesAuthForwardTLSInsecure = "ingress.kubernetes.io/auth-tls-insecure"
annotationKubernetesRewriteTarget = "ingress.kubernetes.io/rewrite-target"
annotationKubernetesWhiteListSourceRange = "ingress.kubernetes.io/whitelist-source-range"
annotationKubernetesWhiteListIPStrategy = "ingress.kubernetes.io/whitelist-ipstrategy"
annotationKubernetesWhiteListIPStrategyDepth = "ingress.kubernetes.io/whitelist-ipstrategy-depth"
annotationKubernetesWhiteListIPStrategyExcludedIPs = "ingress.kubernetes.io/whitelist-ipstrategy-excluded-ips"
annotationKubernetesPreserveHost = "ingress.kubernetes.io/preserve-host"
annotationKubernetesPassTLSCert = "ingress.kubernetes.io/pass-tls-cert" // Deprecated
annotationKubernetesPassTLSClientCert = "ingress.kubernetes.io/pass-client-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"
annotationKubernetesRedirectPermanent = "ingress.kubernetes.io/redirect-permanent"
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"
annotationKubernetesResponseForwardingFlushInterval = "ingress.kubernetes.io/responseforwarding-flushinterval"
annotationKubernetesAppRoot = "ingress.kubernetes.io/app-root"
annotationKubernetesServiceWeights = "ingress.kubernetes.io/service-weights"
annotationKubernetesRequestModifier = "ingress.kubernetes.io/request-modifier"
annotationKubernetesSSLForceHost = "ingress.kubernetes.io/ssl-force-host"
annotationKubernetesSSLRedirect = "ingress.kubernetes.io/ssl-redirect"

View file

@ -93,6 +93,13 @@ func circuitBreaker(exp string) func(*types.Backend) {
}
}
func responseForwarding(interval string) func(*types.Backend) {
return func(b *types.Backend) {
b.ResponseForwarding = &types.ResponseForwarding{}
b.ResponseForwarding.FlushInterval = interval
}
}
func buffering(opts ...func(*types.Buffering)) func(*types.Backend) {
return func(b *types.Backend) {
if b.Buffering == nil {
@ -398,12 +405,34 @@ func limitPeriod(period time.Duration) func(*types.Rate) {
}
}
// Deprecated
func passTLSCert() func(*types.Frontend) {
return func(f *types.Frontend) {
f.PassTLSCert = true
}
}
func passTLSClientCert() func(*types.Frontend) {
return func(f *types.Frontend) {
f.PassTLSClientCert = &types.TLSClientHeaders{
PEM: true,
Infos: &types.TLSClientCertificateInfos{
NotAfter: true,
NotBefore: true,
Subject: &types.TLSCLientCertificateSubjectInfos{
Country: true,
Province: true,
Locality: true,
Organization: true,
CommonName: true,
SerialNumber: true,
},
Sans: true,
},
}
}
}
func routes(opts ...func(*types.Route) string) func(*types.Frontend) {
return func(f *types.Frontend) {
f.Routes = make(map[string]types.Route)

View file

@ -43,6 +43,7 @@ const (
traefikDefaultIngressClass = "traefik"
defaultBackendName = "global-default-backend"
defaultFrontendName = "global-default-frontend"
defaultFrontendRule = "PathPrefix:/"
allowedProtocolHTTPS = "https"
allowedProtocolH2C = "h2c"
)
@ -61,7 +62,7 @@ type Provider struct {
Token string `description:"Kubernetes bearer token (not needed for in-cluster client)"`
CertAuthFilePath string `description:"Kubernetes certificate authority file path (not needed for in-cluster client)"`
DisablePassHostHeaders bool `description:"Kubernetes disable PassHost Headers" export:"true"`
EnablePassTLSCert bool `description:"Kubernetes enable Pass TLS Client Certs" export:"true"`
EnablePassTLSCert bool `description:"Kubernetes enable Pass TLS Client Certs" export:"true"` // Deprecated
Namespaces Namespaces `description:"Kubernetes namespaces" export:"true"`
LabelSelector string `description:"Kubernetes Ingress label selector to use" export:"true"`
IngressClass string `description:"Value of kubernetes.io/ingress.class annotation to watch for" export:"true"`
@ -238,6 +239,11 @@ func (p *Provider) loadIngresses(k8sClient Client) (*types.Configuration, error)
}
baseName := r.Host + pa.Path
if len(baseName) == 0 {
baseName = pa.Backend.ServiceName
}
if priority > 0 {
baseName = strconv.Itoa(priority) + "-" + baseName
}
@ -269,22 +275,23 @@ func (p *Provider) loadIngresses(k8sClient Client) (*types.Configuration, error)
}
passHostHeader := getBoolValue(i.Annotations, annotationKubernetesPreserveHost, !p.DisablePassHostHeaders)
passTLSCert := getBoolValue(i.Annotations, annotationKubernetesPassTLSCert, p.EnablePassTLSCert)
passTLSCert := getBoolValue(i.Annotations, annotationKubernetesPassTLSCert, p.EnablePassTLSCert) // Deprecated
entryPoints := getSliceStringValue(i.Annotations, annotationKubernetesFrontendEntryPoints)
frontend = &types.Frontend{
Backend: baseName,
PassHostHeader: passHostHeader,
PassTLSCert: passTLSCert,
Routes: make(map[string]types.Route),
Priority: priority,
WhiteList: getWhiteList(i),
Redirect: getFrontendRedirect(i, baseName, pa.Path),
EntryPoints: entryPoints,
Headers: getHeader(i),
Errors: getErrorPages(i),
RateLimit: getRateLimit(i),
Auth: auth,
Backend: baseName,
PassHostHeader: passHostHeader,
PassTLSCert: passTLSCert,
PassTLSClientCert: getPassTLSClientCert(i),
Routes: make(map[string]types.Route),
Priority: priority,
WhiteList: getWhiteList(i),
Redirect: getFrontendRedirect(i, baseName, pa.Path),
EntryPoints: entryPoints,
Headers: getHeader(i),
Errors: getErrorPages(i),
RateLimit: getRateLimit(i),
Auth: auth,
}
}
@ -319,11 +326,18 @@ func (p *Provider) loadIngresses(k8sClient Client) (*types.Configuration, error)
}
}
if len(frontend.Routes) == 0 {
frontend.Routes["/"] = types.Route{
Rule: defaultFrontendRule,
}
}
templateObjects.Frontends[baseName] = frontend
templateObjects.Backends[baseName].CircuitBreaker = getCircuitBreaker(service)
templateObjects.Backends[baseName].LoadBalancer = getLoadBalancer(service)
templateObjects.Backends[baseName].MaxConn = getMaxConn(service)
templateObjects.Backends[baseName].Buffering = getBuffering(service)
templateObjects.Backends[baseName].ResponseForwarding = getResponseForwarding(service)
protocol := label.DefaultProtocol
@ -481,6 +495,7 @@ func (p *Provider) addGlobalBackend(cl Client, i *extensionsv1beta1.Ingress, tem
templateObjects.Backends[defaultBackendName].LoadBalancer = getLoadBalancer(service)
templateObjects.Backends[defaultBackendName].MaxConn = getMaxConn(service)
templateObjects.Backends[defaultBackendName].Buffering = getBuffering(service)
templateObjects.Backends[defaultBackendName].ResponseForwarding = getResponseForwarding(service)
endpoints, exists, err := cl.GetEndpoints(service.Namespace, service.Name)
if err != nil {
@ -520,26 +535,27 @@ func (p *Provider) addGlobalBackend(cl Client, i *extensionsv1beta1.Ingress, tem
}
passHostHeader := getBoolValue(i.Annotations, annotationKubernetesPreserveHost, !p.DisablePassHostHeaders)
passTLSCert := getBoolValue(i.Annotations, annotationKubernetesPassTLSCert, p.EnablePassTLSCert)
passTLSCert := getBoolValue(i.Annotations, annotationKubernetesPassTLSCert, p.EnablePassTLSCert) // Deprecated
priority := getIntValue(i.Annotations, annotationKubernetesPriority, 0)
entryPoints := getSliceStringValue(i.Annotations, annotationKubernetesFrontendEntryPoints)
templateObjects.Frontends[defaultFrontendName] = &types.Frontend{
Backend: defaultBackendName,
PassHostHeader: passHostHeader,
PassTLSCert: passTLSCert,
Routes: make(map[string]types.Route),
Priority: priority,
WhiteList: getWhiteList(i),
Redirect: getFrontendRedirect(i, defaultFrontendName, "/"),
EntryPoints: entryPoints,
Headers: getHeader(i),
Errors: getErrorPages(i),
RateLimit: getRateLimit(i),
Backend: defaultBackendName,
PassHostHeader: passHostHeader,
PassTLSCert: passTLSCert,
PassTLSClientCert: getPassTLSClientCert(i),
Routes: make(map[string]types.Route),
Priority: priority,
WhiteList: getWhiteList(i),
Redirect: getFrontendRedirect(i, defaultFrontendName, "/"),
EntryPoints: entryPoints,
Headers: getHeader(i),
Errors: getErrorPages(i),
RateLimit: getRateLimit(i),
}
templateObjects.Frontends[defaultFrontendName].Routes["/"] = types.Route{
Rule: "PathPrefix:/",
Rule: defaultFrontendRule,
}
return nil
@ -578,6 +594,7 @@ func getRuleForPath(pa extensionsv1beta1.HTTPIngressPath, i *extensionsv1beta1.I
rules = append(rules, rule)
}
return strings.Join(rules, ";"), nil
}
@ -951,6 +968,17 @@ func getIPStrategy(annotations map[string]string) *types.IPStrategy {
}
}
func getResponseForwarding(service *corev1.Service) *types.ResponseForwarding {
flushIntervalValue := getStringValue(service.Annotations, annotationKubernetesResponseForwardingFlushInterval, "")
if len(flushIntervalValue) == 0 {
return nil
}
return &types.ResponseForwarding{
FlushInterval: flushIntervalValue,
}
}
func getBuffering(service *corev1.Service) *types.Buffering {
var buffering *types.Buffering
@ -1081,6 +1109,22 @@ func getRateLimit(i *extensionsv1beta1.Ingress) *types.RateLimit {
return rateLimit
}
func getPassTLSClientCert(i *extensionsv1beta1.Ingress) *types.TLSClientHeaders {
var passTLSClientCert *types.TLSClientHeaders
passRaw := getStringValue(i.Annotations, annotationKubernetesPassTLSClientCert, "")
if len(passRaw) > 0 {
passTLSClientCert = &types.TLSClientHeaders{}
err := yaml.Unmarshal([]byte(passRaw), passTLSClientCert)
if err != nil {
log.Error(err)
return nil
}
}
return passTLSClientCert
}
func templateSafeString(value string) error {
_, err := strconv.Unquote(`"` + value + `"`)
return err

View file

@ -49,6 +49,11 @@ func TestLoadIngresses(t *testing.T) {
onePath(iBackend("service7", intstr.FromInt(80))),
),
),
iRule(iHost(""),
iPaths(
onePath(iBackend("service8", intstr.FromInt(80))),
),
),
),
),
}
@ -117,6 +122,14 @@ func TestLoadIngresses(t *testing.T) {
clusterIP("10.0.0.7"),
sPorts(sPort(80, ""))),
),
buildService(
sName("service8"),
sNamespace("testing"),
sUID("8"),
sSpec(
clusterIP("10.0.0.8"),
sPorts(sPort(80, ""))),
),
}
endpoints := []*corev1.Endpoints{
@ -164,6 +177,14 @@ func TestLoadIngresses(t *testing.T) {
eAddresses(eAddress("10.10.0.7")),
ePorts(ePort(80, ""))),
),
buildEndpoint(
eNamespace("testing"),
eName("service8"),
eUID("8"),
subset(
eAddresses(eAddress("10.10.0.8")),
ePorts(ePort(80, ""))),
),
}
watchChan := make(chan interface{})
@ -217,6 +238,12 @@ func TestLoadIngresses(t *testing.T) {
server("http://10.10.0.7:80", weight(1)),
),
),
backend("service8",
lbMethod("wrr"),
servers(
server("http://10.10.0.8:80", weight(1)),
),
),
),
frontends(
frontend("foo/bar",
@ -247,6 +274,10 @@ func TestLoadIngresses(t *testing.T) {
passHostHeader(),
routes(route("*.service7", "HostRegexp:{subdomain:[A-Za-z0-9-_]+}.service7")),
),
frontend("service8",
passHostHeader(),
routes(route("/", "PathPrefix:/")),
),
),
)
assert.Equal(t, expected, actual)
@ -696,6 +727,7 @@ func TestGetPassHostHeader(t *testing.T) {
assert.Equal(t, expected, actual)
}
// Deprecated
func TestGetPassTLSCert(t *testing.T) {
ingresses := []*extensionsv1beta1.Ingress{
buildIngress(iNamespace("awesome"),
@ -875,6 +907,9 @@ func TestServiceAnnotations(t *testing.T) {
iRule(
iHost("max-conn"),
iPaths(onePath(iBackend("service4", intstr.FromInt(804))))),
iRule(
iHost("flush"),
iPaths(onePath(iBackend("service5", intstr.FromInt(805))))),
),
),
}
@ -924,6 +959,15 @@ retryexpression: IsNetworkError() && Attempts() <= 2
clusterIP("10.0.0.4"),
sPorts(sPort(804, "http"))),
),
buildService(
sName("service5"),
sNamespace("testing"),
sUID("5"),
sAnnotation(annotationKubernetesResponseForwardingFlushInterval, "10ms"),
sSpec(
clusterIP("10.0.0.5"),
sPorts(sPort(80, ""))),
),
}
endpoints := []*corev1.Endpoints{
@ -971,6 +1015,17 @@ retryexpression: IsNetworkError() && Attempts() <= 2
eAddresses(eAddress("10.4.0.2")),
ePorts(ePort(8080, "http"))),
),
buildEndpoint(
eNamespace("testing"),
eName("service5"),
eUID("5"),
subset(
eAddresses(eAddress("10.4.0.1")),
ePorts(ePort(8080, "http"))),
subset(
eAddresses(eAddress("10.4.0.2")),
ePorts(ePort(8080, "http"))),
),
}
watchChan := make(chan interface{})
@ -994,6 +1049,11 @@ retryexpression: IsNetworkError() && Attempts() <= 2
lbMethod("drr"),
circuitBreaker("NetworkErrorRatio() > 0.5"),
),
backend("flush",
servers(),
lbMethod("wrr"),
responseForwarding("10ms"),
),
backend("bar",
servers(
server("http://10.15.0.1:8080", weight(1)),
@ -1039,6 +1099,10 @@ retryexpression: IsNetworkError() && Attempts() <= 2
passHostHeader(),
routes(
route("max-conn", "Host:max-conn"))),
frontend("flush",
passHostHeader(),
routes(
route("flush", "Host:flush"))),
),
)
@ -1069,6 +1133,20 @@ func TestIngressAnnotations(t *testing.T) {
buildIngress(
iNamespace("testing"),
iAnnotation(annotationKubernetesPassTLSCert, "true"),
iAnnotation(annotationKubernetesPassTLSClientCert, `
pem: true
infos:
notafter: true
notbefore: true
sans: true
subject:
country: true
province: true
locality: true
organization: true
commonname: true
serialnumber: true
`),
iAnnotation(annotationKubernetesIngressClass, traefikDefaultRealm),
iRules(
iRule(
@ -1484,13 +1562,7 @@ rateset:
),
frontend("other/sslstuff",
passHostHeader(),
passTLSCert(),
routes(
route("/sslstuff", "PathPrefix:/sslstuff"),
route("other", "Host:other")),
),
frontend("other/sslstuff",
passHostHeader(),
passTLSClientCert(),
passTLSCert(),
routes(
route("/sslstuff", "PathPrefix:/sslstuff"),