From 020a8e31ab54e6e854a60dbfbedfa2a435248e7c Mon Sep 17 00:00:00 2001 From: Kristian Klausen Date: Mon, 27 Feb 2017 00:25:54 +0100 Subject: [PATCH 1/4] kv: Ignore backend servers with no url MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Currently with a kv tree like: /traefik/backends/b1/servers/ẁeb1 /traefik/backends/b1/servers/web2 /traefik/backends/b1/servers/web2/url Traefik would try to forward traffic to web1, which is impossible as traefik doesn't know the url of web1. This commit solve that, by ignoring backend server with no url "key" when generating the config. This is very useful, for people who use etcd TTL feature. They can then just "renew" the url key every X second, and if the server goes down, it is automatic removed from traefik after the TTL. --- provider/kv.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/provider/kv.go b/provider/kv.go index 6885d09b3..a6c171a9a 100644 --- a/provider/kv.go +++ b/provider/kv.go @@ -162,6 +162,9 @@ func (provider *Kv) list(keys ...string) []string { func (provider *Kv) listServers(backend string) []string { serverNames := provider.list(backend, "/servers/") return fun.Filter(func(serverName string) bool { + if _, err := provider.kvclient.Get(fmt.Sprint(serverName, "/url")); err != nil { + return false + } return provider.checkConstraints(serverName, "/tags") }, serverNames).([]string) } From c864d8027013426beda0c329d91ff6cf48bc5c83 Mon Sep 17 00:00:00 2001 From: Kristian Klausen Date: Tue, 7 Mar 2017 15:56:35 +0100 Subject: [PATCH 2/4] kv: Add test for server without url key --- provider/kv_test.go | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/provider/kv_test.go b/provider/kv_test.go index bba8fab47..a6de2742f 100644 --- a/provider/kv_test.go +++ b/provider/kv_test.go @@ -304,7 +304,7 @@ func (s *Mock) Get(key string) (*store.KVPair, error) { return kvPair, nil } } - return nil, nil + return nil, store.ErrKeyNotFound } func (s *Mock) Delete(key string) error { @@ -410,6 +410,14 @@ func TestKVLoadConfig(t *testing.T) { Key: "traefik/backends/backend.with.dot.too/servers/server.with.dot/weight", Value: []byte("0"), }, + { + Key: "traefik/backends/backend.with.dot.too/servers/server.with.dot.without.url", + Value: []byte(""), + }, + { + Key: "traefik/backends/backend.with.dot.too/servers/server.with.dot.without.url/weight", + Value: []byte("0"), + }, }, }, } From f621a46a2ef918f87588eb28710902392c6dacbd Mon Sep 17 00:00:00 2001 From: Kristian Klausen Date: Tue, 7 Mar 2017 17:11:43 +0100 Subject: [PATCH 3/4] kv: Log error when checking existence of server url key --- provider/kv.go | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/provider/kv.go b/provider/kv.go index a6c171a9a..9c99781c7 100644 --- a/provider/kv.go +++ b/provider/kv.go @@ -162,7 +162,11 @@ func (provider *Kv) list(keys ...string) []string { func (provider *Kv) listServers(backend string) []string { serverNames := provider.list(backend, "/servers/") return fun.Filter(func(serverName string) bool { - if _, err := provider.kvclient.Get(fmt.Sprint(serverName, "/url")); err != nil { + key := fmt.Sprint(serverName, "/url") + if _, err := provider.kvclient.Get(key); err != nil { + if err != store.ErrKeyNotFound { + log.Errorf("Failed to retrieve value for key %s: %s", key, err) + } return false } return provider.checkConstraints(serverName, "/tags") From b4dfb7223b8348a963f5a37e708933d8db0e613c Mon Sep 17 00:00:00 2001 From: Kristian Klausen Date: Sun, 12 Mar 2017 00:22:39 +0100 Subject: [PATCH 4/4] kv: Extend test with support for specifying custom error for Get/List --- provider/kv_test.go | 24 +++++++++++++++++------- 1 file changed, 17 insertions(+), 7 deletions(-) diff --git a/provider/kv_test.go b/provider/kv_test.go index a6de2742f..961eed784 100644 --- a/provider/kv_test.go +++ b/provider/kv_test.go @@ -96,7 +96,9 @@ func TestKvList(t *testing.T) { // Error case provider := &Kv{ kvclient: &Mock{ - Error: true, + Error: KvError{ + List: store.ErrKeyNotFound, + }, }, } actual := provider.list("anything") @@ -187,7 +189,9 @@ func TestKvGet(t *testing.T) { // Error case provider := &Kv{ kvclient: &Mock{ - Error: true, + Error: KvError{ + Get: store.ErrKeyNotFound, + }, }, } actual := provider.get("", "anything") @@ -284,9 +288,15 @@ func TestKvWatchTree(t *testing.T) { } } +// Override Get/List to return a error +type KvError struct { + Get error + List error +} + // Extremely limited mock store so we can test initialization type Mock struct { - Error bool + Error KvError KVPairs []*store.KVPair WatchTreeMethod func() <-chan []*store.KVPair } @@ -296,8 +306,8 @@ func (s *Mock) Put(key string, value []byte, opts *store.WriteOptions) error { } func (s *Mock) Get(key string) (*store.KVPair, error) { - if s.Error { - return nil, errors.New("Error") + if err := s.Error.Get; err != nil { + return nil, err } for _, kvPair := range s.KVPairs { if kvPair.Key == key { @@ -333,8 +343,8 @@ func (s *Mock) NewLock(key string, options *store.LockOptions) (store.Locker, er // List mock func (s *Mock) List(prefix string) ([]*store.KVPair, error) { - if s.Error { - return nil, errors.New("Error") + if err := s.Error.List; err != nil { + return nil, err } kv := []*store.KVPair{} for _, kvPair := range s.KVPairs {