diff --git a/provider/kubernetes/kubernetes.go b/provider/kubernetes/kubernetes.go index 1aa43517e..09ffdefdc 100644 --- a/provider/kubernetes/kubernetes.go +++ b/provider/kubernetes/kubernetes.go @@ -29,11 +29,13 @@ var _ provider.Provider = (*Provider)(nil) const ( annotationFrontendRuleType = "traefik.frontend.rule.type" 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" ) @@ -153,6 +155,7 @@ func (p *Provider) loadIngresses(k8sClient Client) (*types.Configuration, error) log.Warn("Error in ingress: HTTP is nil") continue } + for _, pa := range r.HTTP.Paths { if _, exists := templateObjects.Backends[r.Host+pa.Path]; !exists { templateObjects.Backends[r.Host+pa.Path] = &types.Backend{ @@ -213,14 +216,10 @@ func (p *Provider) loadIngresses(k8sClient Client) (*types.Configuration, error) } } - if len(pa.Path) > 0 { - ruleType := i.Annotations[annotationFrontendRuleType] - if ruleType == "" { - ruleType = ruleTypePathPrefix - } - + rule := getRuleForPath(pa, i) + if rule != "" { templateObjects.Frontends[r.Host+pa.Path].Routes[pa.Path] = types.Route{ - Rule: ruleType + ":" + pa.Path, + Rule: rule, } } @@ -241,9 +240,11 @@ func (p *Provider) loadIngresses(k8sClient Client) (*types.Configuration, error) Expression: expression, } } + if service.Annotations["traefik.backend.loadbalancer.method"] == "drr" { templateObjects.Backends[r.Host+pa.Path].LoadBalancer.Method = "drr" } + if service.Annotations["traefik.backend.loadbalancer.sticky"] == "true" { templateObjects.Backends[r.Host+pa.Path].LoadBalancer.Sticky = true } @@ -254,6 +255,7 @@ func (p *Provider) loadIngresses(k8sClient Client) (*types.Configuration, error) if port.Port == 443 { protocol = "https" } + if service.Spec.Type == "ExternalName" { url := protocol + "://" + service.Spec.ExternalName name := url @@ -302,6 +304,25 @@ func (p *Provider) loadIngresses(k8sClient Client) (*types.Configuration, error) return &templateObjects, nil } +func getRuleForPath(pa v1beta1.HTTPIngressPath, i *v1beta1.Ingress) string { + if len(pa.Path) == 0 { + return "" + } + + ruleType := i.Annotations[annotationFrontendRuleType] + if ruleType == "" { + ruleType = ruleTypePathPrefix + } + + rule := ruleType + ":" + pa.Path + + if rewriteTarget := i.Annotations[annotationKubernetesRewriteTarget]; rewriteTarget != "" { + rule = ruleTypeReplacePath + ":" + rewriteTarget + } + + return rule +} + func handleBasicAuthConfig(i *v1beta1.Ingress, k8sClient Client) ([]string, error) { authType, exists := i.Annotations[annotationKubernetesAuthType] if !exists { diff --git a/provider/kubernetes/kubernetes_test.go b/provider/kubernetes/kubernetes_test.go index 73f7dfe1c..75eb02e64 100644 --- a/provider/kubernetes/kubernetes_test.go +++ b/provider/kubernetes/kubernetes_test.go @@ -9,6 +9,7 @@ import ( "github.com/containous/traefik/types" "github.com/davecgh/go-spew/spew" + "github.com/stretchr/testify/assert" "k8s.io/client-go/pkg/api/v1" "k8s.io/client-go/pkg/apis/extensions/v1beta1" "k8s.io/client-go/pkg/util/intstr" @@ -1555,6 +1556,33 @@ func TestIngressAnnotations(t *testing.T) { }, }, }, + }, { + ObjectMeta: v1.ObjectMeta{ + Namespace: "testing", + Annotations: map[string]string{ + "ingress.kubernetes.io/rewrite-target": "/", + }, + }, + Spec: v1beta1.IngressSpec{ + Rules: []v1beta1.IngressRule{ + { + Host: "rewrite", + IngressRuleValue: v1beta1.IngressRuleValue{ + HTTP: &v1beta1.HTTPIngressRuleValue{ + Paths: []v1beta1.HTTPIngressPath{ + { + Path: "/api", + Backend: v1beta1.IngressBackend{ + ServiceName: "service1", + ServicePort: intstr.FromInt(80), + }, + }, + }, + }, + }, + }, + }, + }, }, } services := []*v1.Service{ @@ -1659,6 +1687,19 @@ func TestIngressAnnotations(t *testing.T) { Method: "wrr", }, }, + "rewrite/api": { + Servers: map[string]types.Server{ + "http://example.com": { + URL: "http://example.com", + Weight: 1, + }, + }, + CircuitBreaker: nil, + LoadBalancer: &types.LoadBalancer{ + Sticky: false, + Method: "wrr", + }, + }, }, Frontends: map[string]*types.Frontend{ "foo/bar": { @@ -1718,15 +1759,23 @@ func TestIngressAnnotations(t *testing.T) { }, }, }, + "rewrite/api": { + Backend: "rewrite/api", + PassHostHeader: true, + Routes: map[string]types.Route{ + "/api": { + Rule: "ReplacePath:/", + }, + "rewrite": { + Rule: "Host:rewrite", + }, + }, + Priority: len("/api"), + }, }, } - actualJSON, _ := json.Marshal(actual) - expectedJSON, _ := json.Marshal(expected) - - if !reflect.DeepEqual(actual, expected) { - t.Fatalf("expected %+v, got %+v", string(expectedJSON), string(actualJSON)) - } + assert.Equal(t, expected, actual) } func TestInvalidPassHostHeaderValue(t *testing.T) {