From 5923d22379cb8cd335f7d88a6ea8b5118dc18e37 Mon Sep 17 00:00:00 2001 From: Ed Robinson Date: Wed, 18 May 2016 16:46:19 +0100 Subject: [PATCH 1/3] The referenced k8s service(s) must be in namespace By design k8s ingress is only designed to ballance services from within the namespace of the ingress. This is disscuessed a little in https://github.com/kubernetes/kubernetes/issues/17088. For now traefik should only reference the services in the current namespace. For me this was a confusing change of behaviour from the reference implimentations, as I have services with the same name in each namespace. --- provider/kubernetes.go | 2 +- provider/kubernetes_test.go | 188 ++++++++++++++++++++++++++++++++---- 2 files changed, 172 insertions(+), 18 deletions(-) diff --git a/provider/kubernetes.go b/provider/kubernetes.go index a039e0523..3f2d1f492 100644 --- a/provider/kubernetes.go +++ b/provider/kubernetes.go @@ -186,7 +186,7 @@ func (provider *Kubernetes) loadIngresses(k8sClient k8s.Client) (*types.Configur } } services, err := k8sClient.GetServices(func(service k8s.Service) bool { - return service.Name == pa.Backend.ServiceName + return service.ObjectMeta.Namespace == i.ObjectMeta.Namespace && service.Name == pa.Backend.ServiceName }) if err != nil { log.Errorf("Error retrieving services: %v", err) diff --git a/provider/kubernetes_test.go b/provider/kubernetes_test.go index 95c4d8178..ccd0818d6 100644 --- a/provider/kubernetes_test.go +++ b/provider/kubernetes_test.go @@ -394,6 +394,9 @@ func TestRuleType(t *testing.T) { func TestGetPassHostHeader(t *testing.T) { ingresses := []k8s.Ingress{{ + ObjectMeta: k8s.ObjectMeta{ + Namespace: "awesome", + }, Spec: k8s.IngressSpec{ Rules: []k8s.IngressRule{ { @@ -418,8 +421,9 @@ func TestGetPassHostHeader(t *testing.T) { services := []k8s.Service{ { ObjectMeta: k8s.ObjectMeta{ - Name: "service1", - UID: "1", + Name: "service1", + Namespace: "awesome", + UID: "1", }, Spec: k8s.ServiceSpec{ ClusterIP: "10.0.0.1", @@ -479,6 +483,112 @@ func TestGetPassHostHeader(t *testing.T) { } } +func TestOnlyReferencesServicesFromOwnNamespace(t *testing.T) { + ingresses := []k8s.Ingress{ + { + ObjectMeta: k8s.ObjectMeta{ + Namespace: "awesome", + }, + Spec: k8s.IngressSpec{ + Rules: []k8s.IngressRule{ + { + Host: "foo", + IngressRuleValue: k8s.IngressRuleValue{ + HTTP: &k8s.HTTPIngressRuleValue{ + Paths: []k8s.HTTPIngressPath{ + { + Backend: k8s.IngressBackend{ + ServiceName: "service", + ServicePort: k8s.FromInt(80), + }, + }, + }, + }, + }, + }, + }, + }, + }, + } + services := []k8s.Service{ + { + ObjectMeta: k8s.ObjectMeta{ + Name: "service", + UID: "1", + Namespace: "awesome", + }, + Spec: k8s.ServiceSpec{ + ClusterIP: "10.0.0.1", + Ports: []k8s.ServicePort{ + { + Name: "http", + Port: 80, + }, + }, + }, + }, + { + ObjectMeta: k8s.ObjectMeta{ + Name: "service", + UID: "2", + Namespace: "not-awesome", + }, + Spec: k8s.ServiceSpec{ + ClusterIP: "10.0.0.2", + Ports: []k8s.ServicePort{ + { + Name: "http", + Port: 80, + }, + }, + }, + }, + } + watchChan := make(chan interface{}) + client := clientMock{ + ingresses: ingresses, + services: services, + watchChan: watchChan, + } + provider := Kubernetes{} + actual, err := provider.loadIngresses(client) + if err != nil { + t.Fatalf("error %+v", err) + } + + expected := &types.Configuration{ + Backends: map[string]*types.Backend{ + "foo": { + Servers: map[string]types.Server{ + "1": { + URL: "http://10.0.0.1:80", + Weight: 1, + }, + }, + CircuitBreaker: nil, + LoadBalancer: nil, + }, + }, + Frontends: map[string]*types.Frontend{ + "foo": { + Backend: "foo", + PassHostHeader: true, + Routes: map[string]types.Route{ + "foo": { + Rule: "Host:foo", + }, + }, + }, + }, + } + actualJSON, _ := json.Marshal(actual) + expectedJSON, _ := json.Marshal(expected) + + if !reflect.DeepEqual(actual, expected) { + t.Fatalf("expected %+v, got %+v", string(expectedJSON), string(actualJSON)) + } +} + func TestLoadNamespacedIngresses(t *testing.T) { ingresses := []k8s.Ingress{ { @@ -556,8 +666,9 @@ func TestLoadNamespacedIngresses(t *testing.T) { services := []k8s.Service{ { ObjectMeta: k8s.ObjectMeta{ - Name: "service1", - UID: "1", + Namespace: "awesome", + Name: "service1", + UID: "1", }, Spec: k8s.ServiceSpec{ ClusterIP: "10.0.0.1", @@ -571,8 +682,25 @@ func TestLoadNamespacedIngresses(t *testing.T) { }, { ObjectMeta: k8s.ObjectMeta{ - Name: "service2", - UID: "2", + Name: "service1", + Namespace: "not-awesome", + UID: "1", + }, + Spec: k8s.ServiceSpec{ + ClusterIP: "10.0.0.1", + Ports: []k8s.ServicePort{ + { + Name: "http", + Port: 801, + }, + }, + }, + }, + { + ObjectMeta: k8s.ObjectMeta{ + Name: "service2", + Namespace: "awesome", + UID: "2", }, Spec: k8s.ServiceSpec{ ClusterIP: "10.0.0.2", @@ -585,8 +713,9 @@ func TestLoadNamespacedIngresses(t *testing.T) { }, { ObjectMeta: k8s.ObjectMeta{ - Name: "service3", - UID: "3", + Name: "service3", + Namespace: "awesome", + UID: "3", }, Spec: k8s.ServiceSpec{ ClusterIP: "10.0.0.3", @@ -774,8 +903,9 @@ func TestLoadMultipleNamespacedIngresses(t *testing.T) { services := []k8s.Service{ { ObjectMeta: k8s.ObjectMeta{ - Name: "service1", - UID: "1", + Name: "service1", + Namespace: "awesome", + UID: "1", }, Spec: k8s.ServiceSpec{ ClusterIP: "10.0.0.1", @@ -789,8 +919,25 @@ func TestLoadMultipleNamespacedIngresses(t *testing.T) { }, { ObjectMeta: k8s.ObjectMeta{ - Name: "service2", - UID: "2", + Namespace: "somewhat-awesome", + Name: "service1", + UID: "17", + }, + Spec: k8s.ServiceSpec{ + ClusterIP: "10.0.0.4", + Ports: []k8s.ServicePort{ + { + Name: "http", + Port: 801, + }, + }, + }, + }, + { + ObjectMeta: k8s.ObjectMeta{ + Namespace: "awesome", + Name: "service2", + UID: "2", }, Spec: k8s.ServiceSpec{ ClusterIP: "10.0.0.2", @@ -803,8 +950,9 @@ func TestLoadMultipleNamespacedIngresses(t *testing.T) { }, { ObjectMeta: k8s.ObjectMeta{ - Name: "service3", - UID: "3", + Namespace: "awesome", + Name: "service3", + UID: "3", }, Spec: k8s.ServiceSpec{ ClusterIP: "10.0.0.3", @@ -859,8 +1007,8 @@ func TestLoadMultipleNamespacedIngresses(t *testing.T) { }, "awesome/quix": { Servers: map[string]types.Server{ - "1": { - URL: "http://10.0.0.1:801", + "17": { + URL: "http://10.0.0.4:801", Weight: 1, }, }, @@ -931,7 +1079,13 @@ func (c clientMock) WatchIngresses(predicate func(k8s.Ingress) bool, stopCh <-ch return c.watchChan, make(chan error), nil } func (c clientMock) GetServices(predicate func(k8s.Service) bool) ([]k8s.Service, error) { - return c.services, nil + var services []k8s.Service + for _, service := range c.services { + if predicate(service) { + services = append(services, service) + } + } + return services, nil } func (c clientMock) WatchAll(stopCh <-chan bool) (chan interface{}, chan error, error) { return c.watchChan, make(chan error), nil From d13b755df2abc706bf5e61d88df8c54287c1c894 Mon Sep 17 00:00:00 2001 From: Ed Robinson Date: Wed, 18 May 2016 17:30:42 +0100 Subject: [PATCH 2/3] Allow k8s ports to be referenced by name as well as number --- provider/kubernetes.go | 15 +++++++++++++-- provider/kubernetes_test.go | 2 +- 2 files changed, 14 insertions(+), 3 deletions(-) diff --git a/provider/kubernetes.go b/provider/kubernetes.go index 3f2d1f492..4af905084 100644 --- a/provider/kubernetes.go +++ b/provider/kubernetes.go @@ -9,6 +9,7 @@ import ( "io" "io/ioutil" "os" + "strconv" "strings" "text/template" "time" @@ -200,12 +201,12 @@ func (provider *Kubernetes) loadIngresses(k8sClient k8s.Client) (*types.Configur for _, service := range services { protocol := "http" for _, port := range service.Spec.Ports { - if port.Port == pa.Backend.ServicePort.IntValue() { + if equalPorts(port, pa.Backend.ServicePort) { if port.Port == 443 { protocol = "https" } templateObjects.Backends[r.Host+pa.Path].Servers[string(service.UID)] = types.Server{ - URL: protocol + "://" + service.Spec.ClusterIP + ":" + pa.Backend.ServicePort.String(), + URL: protocol + "://" + service.Spec.ClusterIP + ":" + strconv.Itoa(port.Port), Weight: 1, } break @@ -218,6 +219,16 @@ func (provider *Kubernetes) loadIngresses(k8sClient k8s.Client) (*types.Configur return &templateObjects, nil } +func equalPorts(servicePort k8s.ServicePort, ingressPort k8s.IntOrString) bool { + if servicePort.Port == ingressPort.IntValue() { + return true + } + if servicePort.Name != "" && servicePort.Name == ingressPort.String() { + return true + } + return false +} + func (provider *Kubernetes) getPassHostHeader() bool { if provider.disablePassHostHeaders { return false diff --git a/provider/kubernetes_test.go b/provider/kubernetes_test.go index ccd0818d6..0b9b98d62 100644 --- a/provider/kubernetes_test.go +++ b/provider/kubernetes_test.go @@ -21,7 +21,7 @@ func TestLoadIngresses(t *testing.T) { Path: "/bar", Backend: k8s.IngressBackend{ ServiceName: "service1", - ServicePort: k8s.FromInt(801), + ServicePort: k8s.FromString("http"), }, }, }, From 153ab8f0fa593a735b1be4962ae2f88ce18bdb57 Mon Sep 17 00:00:00 2001 From: Vincent Demeester Date: Thu, 19 May 2016 21:11:30 +0200 Subject: [PATCH 3/3] Update engine-api to fix versions issues (#383) Updating an engine-api that has fixed some versioning issues (filters json marshalling) Signed-off-by: Vincent Demeester --- glide.lock | 4 ++-- glide.yaml | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/glide.lock b/glide.lock index 6e8fc624c..6c78fffae 100644 --- a/glide.lock +++ b/glide.lock @@ -86,7 +86,7 @@ imports: - utils - volume - name: github.com/docker/engine-api - version: fd7f99d354831e7e809386087e7ec3129fdb1520 + version: 3d3d0b6c9d2651aac27f416a6da0224c1875b3eb subpackages: - client - types @@ -168,7 +168,7 @@ imports: - name: github.com/kr/text version: 7cafcd837844e784b526369c9bce262804aebc60 - name: github.com/libkermit/docker - version: 9f5a90f8eb3c49bf56e81621f98b3cd86fe4139f + version: 3b5eb2973efff7af33cfb65141deaf4ed25c6d02 - name: github.com/libkermit/docker-check version: bb75a86b169c6c5d22c0ee98278124036f272d7b - name: github.com/magiconair/properties diff --git a/glide.yaml b/glide.yaml index 40d73569b..4c55bbb92 100644 --- a/glide.yaml +++ b/glide.yaml @@ -160,7 +160,7 @@ import: - package: github.com/libkermit/docker-check version: bb75a86b169c6c5d22c0ee98278124036f272d7b - package: github.com/libkermit/docker - version: 9f5a90f8eb3c49bf56e81621f98b3cd86fe4139f + version: 3b5eb2973efff7af33cfb65141deaf4ed25c6d02 - package: github.com/docker/libcompose version: 8ee7bcc364f7b8194581a3c6bd9fa019467c7873 - package: github.com/docker/distribution @@ -168,7 +168,7 @@ import: subpackages: - reference - package: github.com/docker/engine-api - version: fd7f99d354831e7e809386087e7ec3129fdb1520 + version: 3d3d0b6c9d2651aac27f416a6da0224c1875b3eb subpackages: - client - types