From 93b5410987183063020cd32ce0dba8c3c7196835 Mon Sep 17 00:00:00 2001 From: emile Date: Thu, 1 Oct 2015 12:04:25 +0200 Subject: [PATCH 1/3] libkv support https://github.com/EmileVauge/traefik/issues/25 https://github.com/EmileVauge/traefik/issues/9 --- Godeps/Godeps.json | 21 +++ Makefile | 3 + boltdb.go | 14 ++ configuration.go | 5 +- consul.go | 167 +----------------- docker.go | 18 +- etcd.go | 14 ++ file.go | 19 +- integration/basic_test.go | 6 +- integration/resources/compose/consul.yml | 10 +- kv.go | 195 +++++++++++++++++++++ marathon.go | 15 +- provider.go | 2 +- providerTemplates/{consul.tmpl => kv.tmpl} | 12 +- templates/configuration.tmpl | 2 +- tests/consul-config.sh | 32 ++-- traefik.go | 40 +++-- traefik.sample.toml | 2 +- web.go | 3 +- zk.go | 14 ++ 20 files changed, 346 insertions(+), 248 deletions(-) create mode 100644 boltdb.go create mode 100644 etcd.go create mode 100644 kv.go rename providerTemplates/{consul.tmpl => kv.tmpl} (71%) create mode 100644 zk.go diff --git a/Godeps/Godeps.json b/Godeps/Godeps.json index a5abdb1fb..8eb9d2194 100644 --- a/Godeps/Godeps.json +++ b/Godeps/Godeps.json @@ -26,6 +26,10 @@ "ImportPath": "github.com/alecthomas/units", "Rev": "6b4e7dc5e3143b85ea77909c72caf89416fc2915" }, + { + "ImportPath": "github.com/boltdb/bolt", + "Rev": "51f99c862475898df9773747d3accd05a7ca33c1" + }, { "ImportPath": "github.com/cenkalti/backoff", "Rev": "4dc77674aceaabba2c7e3da25d4c823edfb73f99" @@ -39,6 +43,19 @@ "Comment": "v0.1-70-gc7477ad", "Rev": "c7477ad8e330bef55bf1ebe300cf8aa67c492d1b" }, + { + "ImportPath": "github.com/coreos/go-etcd/etcd", + "Comment": "v2.0.0-11-gcc90c7b", + "Rev": "cc90c7b091275e606ad0ca7102a23fb2072f3f5e" + }, + { + "ImportPath": "github.com/davecgh/go-spew/spew", + "Rev": "2df174808ee097f90d259e432cc04442cf60be21" + }, + { + "ImportPath": "github.com/docker/libkv", + "Rev": "3732f7ff1b56057c3158f10bceb1e79133025373" + }, { "ImportPath": "github.com/docker/distribution", "Comment": "v2.0.0-467-g9038e48", @@ -313,6 +330,10 @@ "ImportPath": "github.com/mailgun/timetools", "Rev": "fd192d755b00c968d312d23f521eb0cdc6f66bd0" }, + { + "ImportPath": "github.com/samuel/go-zookeeper/zk", + "Rev": "fa6674abf3f4580b946a01bf7a1ce4ba8766205b" + }, { "ImportPath": "github.com/opencontainers/runc/libcontainer/user", "Comment": "v0.0.4-21-g4ab1324", diff --git a/Makefile b/Makefile index 2d8c7dec7..e3bd87189 100644 --- a/Makefile +++ b/Makefile @@ -47,6 +47,9 @@ validate-govet: build build: dist docker build -t "$(TRAEFIK_DEV_IMAGE)" -f build.Dockerfile . +build-no-cache: dist + docker build --no-cache -t "$(TRAEFIK_DEV_IMAGE)" -f build.Dockerfile . + shell: build $(DOCKER_RUN_TRAEFIK) /bin/bash diff --git a/boltdb.go b/boltdb.go new file mode 100644 index 000000000..663cd4aed --- /dev/null +++ b/boltdb.go @@ -0,0 +1,14 @@ +package main + +type BoltDbProvider struct { + Watch bool + Endpoint string + Prefix string + Filename string + KvProvider *KvProvider +} + +func (provider *BoltDbProvider) Provide(configurationChan chan<- configMessage) error { + provider.KvProvider = NewBoltDbProvider(provider) + return provider.KvProvider.provide(configurationChan) +} diff --git a/configuration.go b/configuration.go index 25c412848..22fc7b955 100644 --- a/configuration.go +++ b/configuration.go @@ -10,7 +10,6 @@ type GlobalConfiguration struct { GraceTimeOut int64 AccessLogsFile string TraefikLogsFile string - TraefikLogsStdout bool CertFile, KeyFile string LogLevel string Docker *DockerProvider @@ -18,6 +17,9 @@ type GlobalConfiguration struct { Web *WebProvider Marathon *MarathonProvider Consul *ConsulProvider + Etcd *EtcdProvider + Zookeeper *ZookepperProvider + Boltdb *BoltDbProvider } func NewGlobalConfiguration() *GlobalConfiguration { @@ -26,7 +28,6 @@ func NewGlobalConfiguration() *GlobalConfiguration { globalConfiguration.Port = ":80" globalConfiguration.GraceTimeOut = 10 globalConfiguration.LogLevel = "ERROR" - globalConfiguration.TraefikLogsStdout = true return globalConfiguration } diff --git a/consul.go b/consul.go index 344f8f1ec..9b1faa4f3 100644 --- a/consul.go +++ b/consul.go @@ -1,165 +1,14 @@ package main -import ( - "bytes" - "net/http" - "strings" - "text/template" - - "github.com/BurntSushi/toml" - "github.com/BurntSushi/ty/fun" - log "github.com/Sirupsen/logrus" - "github.com/hashicorp/consul/api" -) - -type Key struct { - Value string -} - type ConsulProvider struct { - Watch bool - Endpoint string - Prefix string - Filename string - consulClient *api.Client + Watch bool + Endpoint string + Prefix string + Filename string + KvProvider *KvProvider } -var kvClient *api.KV - -var ConsulFuncMap = template.FuncMap{ - "List": func(keys ...string) []string { - joinedKeys := strings.Join(keys, "") - keysPairs, _, err := kvClient.Keys(joinedKeys, "/", nil) - if err != nil { - log.Error("Error getting keys ", joinedKeys, err) - return nil - } - keysPairs = fun.Filter(func(key string) bool { - if key == joinedKeys { - return false - } - return true - }, keysPairs).([]string) - return keysPairs - }, - "Get": func(keys ...string) string { - joinedKeys := strings.Join(keys, "") - keyPair, _, err := kvClient.Get(joinedKeys, nil) - if err != nil { - log.Error("Error getting key ", joinedKeys, err) - return "" - } else if keyPair == nil { - return "" - } - return string(keyPair.Value) - }, - "Last": func(key string) string { - splittedKey := strings.Split(key, "/") - return splittedKey[len(splittedKey)-2] - }, -} - -func NewConsulProvider() *ConsulProvider { - consulProvider := new(ConsulProvider) - // default values - consulProvider.Watch = true - consulProvider.Prefix = "traefik" - - return consulProvider -} - -func (provider *ConsulProvider) Provide(configurationChan chan<- configMessage) { - config := &api.Config{ - Address: provider.Endpoint, - Scheme: "http", - HttpClient: http.DefaultClient, - } - consulClient, _ := api.NewClient(config) - provider.consulClient = consulClient - if provider.Watch { - keypairs, meta, err := consulClient.KV().Keys("", "", nil) - if keypairs == nil { - log.Error("Key was not found") - } else if err != nil { - log.Error("Error connecting to consul %s", err) - } else { - var waitIndex uint64 - waitIndex = meta.LastIndex - go func() { - for { - opts := api.QueryOptions{ - WaitIndex: waitIndex, - } - keypairs, meta, err := consulClient.KV().Keys("", "", &opts) - if keypairs == nil { - log.Error("Key was not found") - } else if err != nil { - log.Error("Error connecting to consul %s", err) - } else { - waitIndex = meta.LastIndex - configuration := provider.loadConsulConfig() - if configuration != nil { - configurationChan <- configMessage{"consul", configuration} - } - } - } - }() - } - } - configuration := provider.loadConsulConfig() - configurationChan <- configMessage{"consul", configuration} -} - -func (provider *ConsulProvider) loadConsulConfig() *Configuration { - configuration := new(Configuration) - services := []*api.CatalogService{} - kvClient = provider.consulClient.KV() - - servicesName, _, _ := provider.consulClient.Catalog().Services(nil) - for serviceName := range servicesName { - catalogServices, _, _ := provider.consulClient.Catalog().Service(serviceName, "", nil) - for _, catalogService := range catalogServices { - services = append(services, catalogService) - } - } - - templateObjects := struct { - Services []*api.CatalogService - }{ - services, - } - - tmpl := template.New(provider.Filename).Funcs(ConsulFuncMap) - if len(provider.Filename) > 0 { - _, err := tmpl.ParseFiles(provider.Filename) - if err != nil { - log.Error("Error reading file", err) - return nil - } - } else { - buf, err := Asset("providerTemplates/consul.tmpl") - if err != nil { - log.Error("Error reading file", err) - } - _, err = tmpl.Parse(string(buf)) - if err != nil { - log.Error("Error reading file", err) - return nil - } - } - - var buffer bytes.Buffer - - err := tmpl.Execute(&buffer, templateObjects) - if err != nil { - log.Error("Error with consul template:", err) - return nil - } - - if _, err := toml.Decode(buffer.String(), configuration); err != nil { - log.Error("Error creating consul configuration:", err) - return nil - } - - return configuration +func (provider *ConsulProvider) Provide(configurationChan chan<- configMessage) error { + provider.KvProvider = NewConsulProvider(provider) + return provider.KvProvider.provide(configurationChan) } diff --git a/docker.go b/docker.go index 4ae6c2209..5c73586c8 100644 --- a/docker.go +++ b/docker.go @@ -22,15 +22,6 @@ type DockerProvider struct { Domain string } -func NewDockerProvider() *DockerProvider { - dockerProvider := new(DockerProvider) - // default - dockerProvider.Watch = true - dockerProvider.Domain = "traefik" - - return dockerProvider -} - var DockerFuncMap = template.FuncMap{ "getBackend": func(container docker.Container) string { for key, value := range container.Config.Labels { @@ -65,13 +56,15 @@ var DockerFuncMap = template.FuncMap{ "getHost": getHost, } -func (provider *DockerProvider) Provide(configurationChan chan<- configMessage) { +func (provider *DockerProvider) Provide(configurationChan chan<- configMessage) error { if dockerClient, err := docker.NewClient(provider.Endpoint); err != nil { - log.Fatalf("Failed to create a client for docker, error: %s", err) + log.Errorf("Failed to create a client for docker, error: %s", err) + return err } else { err := dockerClient.Ping() if err != nil { - log.Fatalf("Docker connection error %+v", err) + log.Errorf("Docker connection error %+v", err) + return err } log.Debug("Docker connection established") if provider.Watch { @@ -108,6 +101,7 @@ func (provider *DockerProvider) Provide(configurationChan chan<- configMessage) configuration := provider.loadDockerConfig(dockerClient) configurationChan <- configMessage{"docker", configuration} } + return nil } func (provider *DockerProvider) loadDockerConfig(dockerClient *docker.Client) *Configuration { diff --git a/etcd.go b/etcd.go new file mode 100644 index 000000000..9c0bd68d7 --- /dev/null +++ b/etcd.go @@ -0,0 +1,14 @@ +package main + +type EtcdProvider struct { + Watch bool + Endpoint string + Prefix string + Filename string + KvProvider *KvProvider +} + +func (provider *EtcdProvider) Provide(configurationChan chan<- configMessage) error { + provider.KvProvider = NewEtcdProvider(provider) + return provider.KvProvider.provide(configurationChan) +} diff --git a/file.go b/file.go index 4cdd4704a..c567a5038 100644 --- a/file.go +++ b/file.go @@ -15,30 +15,21 @@ type FileProvider struct { Filename string } -func NewFileProvider() *FileProvider { - fileProvider := new(FileProvider) - // default values - fileProvider.Watch = true - - return fileProvider -} - -func (provider *FileProvider) Provide(configurationChan chan<- configMessage) { +func (provider *FileProvider) Provide(configurationChan chan<- configMessage) error { watcher, err := fsnotify.NewWatcher() if err != nil { log.Error("Error creating file watcher", err) - return + return err } defer watcher.Close() file, err := os.Open(provider.Filename) if err != nil { log.Error("Error opening file", err) - return + return err } defer file.Close() - done := make(chan bool) // Process events go func() { for { @@ -63,12 +54,12 @@ func (provider *FileProvider) Provide(configurationChan chan<- configMessage) { if err != nil { log.Error("Error adding file watcher", err) - return + return err } configuration := provider.LoadFileConfig(file.Name()) configurationChan <- configMessage{"file", configuration} - <-done + return nil } func (provider *FileProvider) LoadFileConfig(filename string) *Configuration { diff --git a/integration/basic_test.go b/integration/basic_test.go index 4ad3aafca..90abe2340 100644 --- a/integration/basic_test.go +++ b/integration/basic_test.go @@ -15,14 +15,14 @@ func (s *SimpleSuite) TestNoOrInexistentConfigShouldFail(c *check.C) { output, err := cmd.CombinedOutput() c.Assert(err, checker.NotNil) - c.Assert(string(output), checker.Contains, "Error reading file open traefik.toml: no such file or directory") + c.Assert(string(output), checker.Contains, "Error reading file: open traefik.toml: no such file or directory") nonExistentFile := "non/existent/file.toml" cmd = exec.Command(traefikBinary, nonExistentFile) output, err = cmd.CombinedOutput() c.Assert(err, checker.NotNil) - c.Assert(string(output), checker.Contains, fmt.Sprintf("Error reading file open %s: no such file or directory", nonExistentFile)) + c.Assert(string(output), checker.Contains, fmt.Sprintf("Error reading file: open %s: no such file or directory", nonExistentFile)) } func (s *SimpleSuite) TestInvalidConfigShouldFail(c *check.C) { @@ -30,7 +30,7 @@ func (s *SimpleSuite) TestInvalidConfigShouldFail(c *check.C) { output, err := cmd.CombinedOutput() c.Assert(err, checker.NotNil) - c.Assert(string(output), checker.Contains, "Error reading file Near line 1") + c.Assert(string(output), checker.Contains, "Error reading file: Near line 1") } func (s *SimpleSuite) TestSimpleDefaultConfig(c *check.C) { diff --git a/integration/resources/compose/consul.yml b/integration/resources/compose/consul.yml index 83f30b7a9..a2036443d 100644 --- a/integration/resources/compose/consul.yml +++ b/integration/resources/compose/consul.yml @@ -10,12 +10,4 @@ consul: - "8301" - "8301/udp" - "8302" - - "8302/udp" - -registrator: - image: gliderlabs/registrator:master - command: -internal consulkv://consul:8500/traefik - volumes: - - /var/run/docker.sock:/tmp/docker.sock - links: - - consul \ No newline at end of file + - "8302/udp" \ No newline at end of file diff --git a/kv.go b/kv.go new file mode 100644 index 000000000..4d3f7dd82 --- /dev/null +++ b/kv.go @@ -0,0 +1,195 @@ +/* +Copyright +*/ +package main + +import ( + "bytes" + "github.com/docker/libkv" + "github.com/docker/libkv/store/boltdb" + "github.com/docker/libkv/store/consul" + "github.com/docker/libkv/store/etcd" + "github.com/docker/libkv/store/zookeeper" + "strings" + "text/template" + + "errors" + "github.com/BurntSushi/toml" + "github.com/BurntSushi/ty/fun" + log "github.com/Sirupsen/logrus" + "github.com/docker/libkv/store" + "time" +) + +type KvProvider struct { + Watch bool + Endpoint string + Prefix string + Filename string + StoreType store.Backend + kvclient store.Store +} + +func NewConsulProvider(provider *ConsulProvider) *KvProvider { + kvProvider := new(KvProvider) + kvProvider.Watch = provider.Watch + kvProvider.Endpoint = provider.Endpoint + kvProvider.Prefix = provider.Prefix + kvProvider.Filename = provider.Filename + kvProvider.StoreType = store.CONSUL + return kvProvider +} + +func NewEtcdProvider(provider *EtcdProvider) *KvProvider { + kvProvider := new(KvProvider) + kvProvider.Watch = provider.Watch + kvProvider.Endpoint = provider.Endpoint + kvProvider.Prefix = provider.Prefix + kvProvider.Filename = provider.Filename + kvProvider.StoreType = store.ETCD + return kvProvider +} + +func NewZkProvider(provider *ZookepperProvider) *KvProvider { + kvProvider := new(KvProvider) + kvProvider.Watch = provider.Watch + kvProvider.Endpoint = provider.Endpoint + kvProvider.Prefix = provider.Prefix + kvProvider.Filename = provider.Filename + kvProvider.StoreType = store.ZK + return kvProvider +} + +func NewBoltDbProvider(provider *BoltDbProvider) *KvProvider { + kvProvider := new(KvProvider) + kvProvider.Watch = provider.Watch + kvProvider.Endpoint = provider.Endpoint + kvProvider.Prefix = provider.Prefix + kvProvider.Filename = provider.Filename + kvProvider.StoreType = store.BOLTDB + return kvProvider +} + +func (provider *KvProvider) provide(configurationChan chan<- configMessage) error { + switch provider.StoreType { + case store.CONSUL: + consul.Register() + case store.ETCD: + etcd.Register() + case store.ZK: + zookeeper.Register() + case store.BOLTDB: + boltdb.Register() + default: + return errors.New("Invalid kv store: " + string(provider.StoreType)) + } + kv, err := libkv.NewStore( + provider.StoreType, + []string{provider.Endpoint}, + &store.Config{ + ConnectionTimeout: 30 * time.Second, + }, + ) + if err != nil { + return err + } + if _, err := kv.List(""); err != nil { + return err + } + provider.kvclient = kv + if provider.Watch { + stopCh := make(chan struct{}) + chanKeys, err := kv.WatchTree(provider.Prefix, stopCh) + if err != nil { + return err + } + go func() { + for { + <-chanKeys + configuration := provider.loadConfig() + if configuration != nil { + configurationChan <- configMessage{string(provider.StoreType), configuration} + } + defer close(stopCh) + } + }() + } + configuration := provider.loadConfig() + configurationChan <- configMessage{string(provider.StoreType), configuration} + return nil +} + +func (provider *KvProvider) loadConfig() *Configuration { + configuration := new(Configuration) + templateObjects := struct { + Prefix string + }{ + provider.Prefix, + } + var KvFuncMap = template.FuncMap{ + "List": func(keys ...string) []string { + joinedKeys := strings.Join(keys, "") + keysPairs, err := provider.kvclient.List(joinedKeys) + if err != nil { + log.Error("Error getting keys: ", joinedKeys, err) + return nil + } + directoryKeys := make(map[string]string) + for _, key := range keysPairs { + directory := strings.Split(strings.TrimPrefix(key.Key, strings.TrimPrefix(joinedKeys, "/")), "/")[0] + directoryKeys[directory] = joinedKeys + directory + } + return fun.Values(directoryKeys).([]string) + }, + "Get": func(keys ...string) string { + joinedKeys := strings.Join(keys, "") + keyPair, err := provider.kvclient.Get(joinedKeys) + if err != nil { + log.Debug("Error getting key: ", joinedKeys, err) + return "" + } else if keyPair == nil { + return "" + } + return string(keyPair.Value) + }, + "Last": func(key string) string { + splittedKey := strings.Split(key, "/") + return splittedKey[len(splittedKey)-1] + }, + } + + tmpl := template.New(provider.Filename).Funcs(KvFuncMap) + if len(provider.Filename) > 0 { + _, err := tmpl.ParseFiles(provider.Filename) + if err != nil { + log.Error("Error reading file", err) + return nil + } + } else { + buf, err := Asset("providerTemplates/kv.tmpl") + if err != nil { + log.Error("Error reading file", err) + } + _, err = tmpl.Parse(string(buf)) + if err != nil { + log.Error("Error reading file", err) + return nil + } + } + + var buffer bytes.Buffer + + err := tmpl.Execute(&buffer, templateObjects) + if err != nil { + log.Error("Error with kv template:", err) + return nil + } + + if _, err := toml.Decode(buffer.String(), configuration); err != nil { + log.Error("Error creating kv configuration:", err) + log.Error(buffer.String()) + return nil + } + + return configuration +} diff --git a/marathon.go b/marathon.go index 9cb7fe12e..fc4f80e7d 100644 --- a/marathon.go +++ b/marathon.go @@ -21,16 +21,6 @@ type MarathonProvider struct { NetworkInterface string } -func NewMarathonProvider() *MarathonProvider { - marathonProvider := new(MarathonProvider) - // default values - marathonProvider.Watch = true - marathonProvider.Domain = "traefik" - marathonProvider.NetworkInterface = "eth0" - - return marathonProvider -} - var MarathonFuncMap = template.FuncMap{ "getPort": func(task marathon.Task) string { for _, port := range task.Ports { @@ -67,14 +57,14 @@ var MarathonFuncMap = template.FuncMap{ }, } -func (provider *MarathonProvider) Provide(configurationChan chan<- configMessage) { +func (provider *MarathonProvider) Provide(configurationChan chan<- configMessage) error { config := marathon.NewDefaultConfig() config.URL = provider.Endpoint config.EventsInterface = provider.NetworkInterface client, err := marathon.NewClient(config) if err != nil { log.Errorf("Failed to create a client for marathon, error: %s", err) - return + return err } provider.marathonClient = client update := make(marathon.EventsChannel, 5) @@ -97,6 +87,7 @@ func (provider *MarathonProvider) Provide(configurationChan chan<- configMessage configuration := provider.loadMarathonConfig() configurationChan <- configMessage{"marathon", configuration} + return nil } func (provider *MarathonProvider) loadMarathonConfig() *Configuration { diff --git a/provider.go b/provider.go index 680341827..df0bb7399 100644 --- a/provider.go +++ b/provider.go @@ -1,5 +1,5 @@ package main type Provider interface { - Provide(configurationChan chan<- configMessage) + Provide(configurationChan chan<- configMessage) error } diff --git a/providerTemplates/consul.tmpl b/providerTemplates/kv.tmpl similarity index 71% rename from providerTemplates/consul.tmpl rename to providerTemplates/kv.tmpl index e535e414e..b1833c91f 100644 --- a/providerTemplates/consul.tmpl +++ b/providerTemplates/kv.tmpl @@ -1,17 +1,17 @@ -{{$frontends := "frontends/" | List }} -{{$backends := "backends/" | List }} +{{$frontends := List .Prefix "/frontends/" }} +{{$backends := List .Prefix "/backends/"}} {{range $backends}} {{$backend := .}} -{{$servers := "servers/" | List $backend }} +{{$servers := List $backend "/servers/" }} -{{$circuitBreaker := Get . "circuitbreaker/" "expression"}} +{{$circuitBreaker := Get . "/circuitbreaker/" "expression"}} {{with $circuitBreaker}} [backends.{{Last $backend}}.circuitBreaker] expression = "{{$circuitBreaker}}" {{end}} -{{$loadBalancer := Get . "loadbalancer/" "method"}} +{{$loadBalancer := Get . "/loadbalancer/" "method"}} {{with $loadBalancer}} [backends.{{Last $backend}}.loadBalancer] method = "{{$loadBalancer}}" @@ -28,7 +28,7 @@ {{$frontend := Last .}} [frontends.{{$frontend}}] backend = "{{Get . "/backend"}}" - {{$routes := "routes/" | List .}} + {{$routes := List . "/routes/"}} {{range $routes}} [frontends.{{$frontend}}.routes.{{Last .}}] rule = "{{Get . "/rule"}}" diff --git a/templates/configuration.tmpl b/templates/configuration.tmpl index 5112fc12c..5b29dc38f 100644 --- a/templates/configuration.tmpl +++ b/templates/configuration.tmpl @@ -61,7 +61,7 @@ {{range $keyProviders, $valueProviders := .Configurations}} {{range $keyBackends, $valueBackends := $valueProviders.Backends}}
-
{{$keyBackends}}({{$keyProviders}})
+
{{$keyBackends}} - ({{$keyProviders}})
{{with $valueBackends.LoadBalancer}} diff --git a/tests/consul-config.sh b/tests/consul-config.sh index 0d0c1f5a9..e567f3319 100755 --- a/tests/consul-config.sh +++ b/tests/consul-config.sh @@ -1,25 +1,25 @@ #!/bin/sh # backend 1 -curl -i -H "Accept: application/json" -X PUT -d "NetworkErrorRatio() > 0.5" http://localhost:8500/v1/kv/backends/backend1/circuitbreaker/expression -curl -i -H "Accept: application/json" -X PUT -d "http://172.17.0.2:80" http://localhost:8500/v1/kv/backends/backend1/servers/server1/url -curl -i -H "Accept: application/json" -X PUT -d "10" http://localhost:8500/v1/kv/backends/backend1/servers/server1/weight -curl -i -H "Accept: application/json" -X PUT -d "http://172.17.0.3:80" http://localhost:8500/v1/kv/backends/backend1/servers/server2/url -curl -i -H "Accept: application/json" -X PUT -d "1" http://localhost:8500/v1/kv/backends/backend1/servers/server2/weight +curl -i -H "Accept: application/json" -X PUT -d "NetworkErrorRatio() > 0.5" http://localhost:8500/v1/kv/traefik/backends/backend1/circuitbreaker/expression +curl -i -H "Accept: application/json" -X PUT -d "http://172.17.0.2:80" http://localhost:8500/v1/kv/traefik/backends/backend1/servers/server1/url +curl -i -H "Accept: application/json" -X PUT -d "10" http://localhost:8500/v1/kv/traefik/backends/backend1/servers/server1/weight +curl -i -H "Accept: application/json" -X PUT -d "http://172.17.0.3:80" http://localhost:8500/v1/kv/traefik/backends/backend1/servers/server2/url +curl -i -H "Accept: application/json" -X PUT -d "1" http://localhost:8500/v1/kv/traefik/backends/backend1/servers/server2/weight # backend 2 -curl -i -H "Accept: application/json" -X PUT -d "drr" http://localhost:8500/v1/kv/backends/backend2/loadbalancer/method -curl -i -H "Accept: application/json" -X PUT -d "http://172.17.0.4:80" http://localhost:8500/v1/kv/backends/backend2/servers/server1/url -curl -i -H "Accept: application/json" -X PUT -d "1" http://localhost:8500/v1/kv/backends/backend2/servers/server1/weight -curl -i -H "Accept: application/json" -X PUT -d "http://172.17.0.5:80" http://localhost:8500/v1/kv/backends/backend2/servers/server2/url -curl -i -H "Accept: application/json" -X PUT -d "2" http://localhost:8500/v1/kv/backends/backend2/servers/server2/weight +curl -i -H "Accept: application/json" -X PUT -d "drr" http://localhost:8500/v1/kv/traefik/backends/backend2/loadbalancer/method +curl -i -H "Accept: application/json" -X PUT -d "http://172.17.0.4:80" http://localhost:8500/v1/kv/traefik/backends/backend2/servers/server1/url +curl -i -H "Accept: application/json" -X PUT -d "1" http://localhost:8500/v1/kv/traefik/backends/backend2/servers/server1/weight +curl -i -H "Accept: application/json" -X PUT -d "http://172.17.0.5:80" http://localhost:8500/v1/kv/traefik/backends/backend2/servers/server2/url +curl -i -H "Accept: application/json" -X PUT -d "2" http://localhost:8500/v1/kv/traefik/backends/backend2/servers/server2/weight # frontend 1 -curl -i -H "Accept: application/json" -X PUT -d "backend2" http://localhost:8500/v1/kv/frontends/frontend1/backend -curl -i -H "Accept: application/json" -X PUT -d "Host" http://localhost:8500/v1/kv/frontends/frontend1/routes/test_1/rule -curl -i -H "Accept: application/json" -X PUT -d "test.localhost" http://localhost:8500/v1/kv/frontends/frontend1/routes/test_1/value +curl -i -H "Accept: application/json" -X PUT -d "backend2" http://localhost:8500/v1/kv/traefik/frontends/frontend1/backend +curl -i -H "Accept: application/json" -X PUT -d "Host" http://localhost:8500/v1/kv/traefik/frontends/frontend1/routes/test_1/rule +curl -i -H "Accept: application/json" -X PUT -d "test.localhost" http://localhost:8500/v1/kv/traefik/frontends/frontend1/routes/test_1/value # frontend 2 -curl -i -H "Accept: application/json" -X PUT -d "backend1" http://localhost:8500/v1/kv/frontends/frontend2/backend -curl -i -H "Accept: application/json" -X PUT -d "Path" http://localhost:8500/v1/kv/frontends/frontend2/routes/test_2/rule -curl -i -H "Accept: application/json" -X PUT -d "/test" http://localhost:8500/v1/kv/frontends/frontend2/routes/test_2/value +curl -i -H "Accept: application/json" -X PUT -d "backend1" http://localhost:8500/v1/kv/traefik/frontends/frontend2/backend +curl -i -H "Accept: application/json" -X PUT -d "Path" http://localhost:8500/v1/kv/traefik/frontends/frontend2/routes/test_2/rule +curl -i -H "Accept: application/json" -X PUT -d "/test" http://localhost:8500/v1/kv/traefik/frontends/frontend2/routes/test_2/value diff --git a/traefik.go b/traefik.go index 00a2ae8a4..9b3452830 100644 --- a/traefik.go +++ b/traefik.go @@ -12,9 +12,11 @@ import ( "syscall" "time" + "errors" "github.com/BurntSushi/toml" log "github.com/Sirupsen/logrus" "github.com/codegangsta/negroni" + "github.com/davecgh/go-spew/spew" "github.com/emilevauge/traefik/middlewares" "github.com/gorilla/mux" "github.com/mailgun/manners" @@ -86,7 +88,7 @@ func main() { } else { log.SetFormatter(&log.TextFormatter{FullTimestamp: true, DisableSorting: true}) } - + log.Debugf("Global configuration loaded %s", spew.Sdump(globalConfiguration)) configurationRouter = LoadDefaultConfig(globalConfiguration) // listen new configurations from providers @@ -94,7 +96,8 @@ func main() { for { configMsg := <-configurationChan - log.Infof("Configuration receveived from provider %v: %+v", configMsg.providerName, configMsg.configuration) + log.Infof("Configuration receveived from provider %s: %#v", configMsg.providerName, configMsg.configuration) + log.Debugf("Configuration %s", spew.Sdump(configMsg.configuration)) if configMsg.configuration == nil { log.Info("Skipping empty configuration") } else if reflect.DeepEqual(currentConfigurations[configMsg.providerName], configMsg.configuration) { @@ -147,13 +150,25 @@ func main() { if globalConfiguration.Consul != nil { providers = append(providers, globalConfiguration.Consul) } + if globalConfiguration.Etcd != nil { + providers = append(providers, globalConfiguration.Etcd) + } + if globalConfiguration.Zookeeper != nil { + providers = append(providers, globalConfiguration.Zookeeper) + } + if globalConfiguration.Boltdb != nil { + providers = append(providers, globalConfiguration.Boltdb) + } // start providers for _, provider := range providers { log.Infof("Starting provider %v %+v", reflect.TypeOf(provider), provider) currentProvider := provider go func() { - currentProvider.Provide(configurationChan) + err := currentProvider.Provide(configurationChan) + if err != nil { + log.Errorf("Error starting provider %s", err) + } }() } @@ -176,6 +191,7 @@ func main() { func startServer(srv *manners.GracefulServer, globalConfiguration *GlobalConfiguration) { log.Info("Starting server") + log.Debugf("Server %s", spew.Sdump(srv)) if len(globalConfiguration.CertFile) > 0 && len(globalConfiguration.KeyFile) > 0 { err := srv.ListenAndServeTLS(globalConfiguration.CertFile, globalConfiguration.KeyFile) if err != nil { @@ -243,13 +259,16 @@ func LoadConfig(configurations configs, globalConfiguration *GlobalConfiguration log.Debugf("Creating backend %s", frontend.Backend) var lb http.Handler rr, _ := roundrobin.New(fwd) + if configuration.Backends[frontend.Backend] == nil { + return nil, errors.New("Backend not found: " + frontend.Backend) + } lbMethod, err := NewLoadBalancerMethod(configuration.Backends[frontend.Backend].LoadBalancer) if err != nil { configuration.Backends[frontend.Backend].LoadBalancer = &LoadBalancer{Method: "wrr"} } switch lbMethod { case drr: - log.Debugf("Creating load-balancer drr") + log.Infof("Creating load-balancer drr") rebalancer, _ := roundrobin.NewRebalancer(rr, roundrobin.RebalancerLogger(oxyLogger)) lb = rebalancer for serverName, server := range configuration.Backends[frontend.Backend].Servers { @@ -257,31 +276,31 @@ func LoadConfig(configurations configs, globalConfiguration *GlobalConfiguration if err != nil { return nil, err } - log.Debugf("Creating server %s %s", serverName, url.String()) + log.Infof("Creating server %s %s", serverName, url.String()) rebalancer.UpsertServer(url, roundrobin.Weight(server.Weight)) } case wrr: - log.Debugf("Creating load-balancer wrr") + log.Infof("Creating load-balancer wrr") lb = rr for serverName, server := range configuration.Backends[frontend.Backend].Servers { url, err := url.Parse(server.URL) if err != nil { return nil, err } - log.Debugf("Creating server %s %s", serverName, url.String()) + log.Infof("Creating server %s %s", serverName, url.String()) rr.UpsertServer(url, roundrobin.Weight(server.Weight)) } } var negroni = negroni.New() if configuration.Backends[frontend.Backend].CircuitBreaker != nil { - log.Debugf("Creating circuit breaker %s", configuration.Backends[frontend.Backend].CircuitBreaker.Expression) + log.Infof("Creating circuit breaker %s", configuration.Backends[frontend.Backend].CircuitBreaker.Expression) negroni.Use(middlewares.NewCircuitBreaker(lb, configuration.Backends[frontend.Backend].CircuitBreaker.Expression, cbreaker.Logger(oxyLogger))) } else { negroni.UseHandler(lb) } backends[frontend.Backend] = negroni } else { - log.Debugf("Reusing backend %s", frontend.Backend) + log.Infof("Reusing backend %s", frontend.Backend) } // stream.New(backends[frontend.Backend], stream.Retry("IsNetworkError() && Attempts() <= " + strconv.Itoa(globalConfiguration.Replay)), stream.Logger(oxyLogger)) @@ -306,8 +325,7 @@ func Invoke(any interface{}, name string, args ...interface{}) []reflect.Value { func LoadFileConfig(file string) *GlobalConfiguration { configuration := NewGlobalConfiguration() if _, err := toml.DecodeFile(file, configuration); err != nil { - log.Fatal("Error reading file ", err) + fmtlog.Fatalf("Error reading file: %s", err) } - log.Debugf("Global configuration loaded %+v", configuration) return configuration } diff --git a/traefik.sample.toml b/traefik.sample.toml index 7b7de5422..5194b1c8c 100644 --- a/traefik.sample.toml +++ b/traefik.sample.toml @@ -199,7 +199,7 @@ # # Optional # -# prefix = "traefik" +# prefix = "/traefik" # Override default configuration template. For advanced users :) # diff --git a/web.go b/web.go index 9e80dfafa..fb21eece4 100644 --- a/web.go +++ b/web.go @@ -20,7 +20,7 @@ type Page struct { Configurations configs } -func (provider *WebProvider) Provide(configurationChan chan<- configMessage) { +func (provider *WebProvider) Provide(configurationChan chan<- configMessage) error { systemRouter := mux.NewRouter() systemRouter.Methods("GET").Path("/").Handler(http.HandlerFunc(GetHTMLConfigHandler)) systemRouter.Methods("GET").Path("/health").Handler(http.HandlerFunc(GetHealthHandler)) @@ -67,6 +67,7 @@ func (provider *WebProvider) Provide(configurationChan chan<- configMessage) { } } }() + return nil } func GetConfigHandler(rw http.ResponseWriter, r *http.Request) { diff --git a/zk.go b/zk.go new file mode 100644 index 000000000..cdcde6fb9 --- /dev/null +++ b/zk.go @@ -0,0 +1,14 @@ +package main + +type ZookepperProvider struct { + Watch bool + Endpoint string + Prefix string + Filename string + KvProvider *KvProvider +} + +func (provider *ZookepperProvider) Provide(configurationChan chan<- configMessage) error { + provider.KvProvider = NewZkProvider(provider) + return provider.KvProvider.provide(configurationChan) +} From 0b3928dafbd147339343f13ab0bc8c99d486b31e Mon Sep 17 00:00:00 2001 From: emile Date: Sat, 3 Oct 2015 16:50:53 +0200 Subject: [PATCH 2/3] Add /api/providers API --- file.go | 46 ++++++++++++++++++++++------------------------ web.go | 6 ++++++ 2 files changed, 28 insertions(+), 24 deletions(-) diff --git a/file.go b/file.go index c567a5038..11a12cb0c 100644 --- a/file.go +++ b/file.go @@ -21,7 +21,6 @@ func (provider *FileProvider) Provide(configurationChan chan<- configMessage) er log.Error("Error creating file watcher", err) return err } - defer watcher.Close() file, err := os.Open(provider.Filename) if err != nil { @@ -30,31 +29,30 @@ func (provider *FileProvider) Provide(configurationChan chan<- configMessage) er } defer file.Close() - // Process events - go func() { - for { - select { - case event := <-watcher.Events: - if strings.Contains(event.Name, file.Name()) { - log.Debug("File event:", event) - configuration := provider.LoadFileConfig(file.Name()) - if configuration != nil { - configurationChan <- configMessage{"file", configuration} - } - } - case error := <-watcher.Errors: - log.Error("Watcher event error", error) - } - } - }() - if provider.Watch { + // Process events + go func() { + defer watcher.Close() + for { + select { + case event := <-watcher.Events: + if strings.Contains(event.Name, file.Name()) { + log.Debug("File event:", event) + configuration := provider.LoadFileConfig(file.Name()) + if configuration != nil { + configurationChan <- configMessage{"file", configuration} + } + } + case error := <-watcher.Errors: + log.Error("Watcher event error", error) + } + } + }() err = watcher.Add(filepath.Dir(file.Name())) - } - - if err != nil { - log.Error("Error adding file watcher", err) - return err + if err != nil { + log.Error("Error adding file watcher", err) + return err + } } configuration := provider.LoadFileConfig(file.Name()) diff --git a/web.go b/web.go index fb21eece4..f5a0d26c7 100644 --- a/web.go +++ b/web.go @@ -6,6 +6,7 @@ import ( "io/ioutil" "net/http" + "github.com/BurntSushi/ty/fun" log "github.com/Sirupsen/logrus" "github.com/elazarl/go-bindata-assetfs" "github.com/gorilla/mux" @@ -25,6 +26,7 @@ func (provider *WebProvider) Provide(configurationChan chan<- configMessage) err systemRouter.Methods("GET").Path("/").Handler(http.HandlerFunc(GetHTMLConfigHandler)) systemRouter.Methods("GET").Path("/health").Handler(http.HandlerFunc(GetHealthHandler)) systemRouter.Methods("GET").Path("/api").Handler(http.HandlerFunc(GetConfigHandler)) + systemRouter.Methods("GET").Path("/api/providers").Handler(http.HandlerFunc(GetProvidersHandler)) systemRouter.Methods("GET").Path("/api/{provider}").Handler(http.HandlerFunc(GetConfigHandler)) systemRouter.Methods("PUT").Path("/api/{provider}").Handler(http.HandlerFunc( func(rw http.ResponseWriter, r *http.Request) { @@ -82,6 +84,10 @@ func GetHealthHandler(rw http.ResponseWriter, r *http.Request) { templatesRenderer.JSON(rw, http.StatusOK, metrics.Data()) } +func GetProvidersHandler(rw http.ResponseWriter, r *http.Request) { + templatesRenderer.JSON(rw, http.StatusOK, fun.Keys(currentConfigurations)) +} + func GetBackendsHandler(rw http.ResponseWriter, r *http.Request) { vars := mux.Vars(r) providerId := vars["provider"] From ffbfc2d9017f734c22a9ef566ab37f4d2c660b21 Mon Sep 17 00:00:00 2001 From: emile Date: Sat, 3 Oct 2015 16:51:14 +0200 Subject: [PATCH 3/3] libkv docs --- Godeps/Godeps.json | 684 +++++++++++++++++++------------------------- README.md | 2 +- docs/ROADMAP.md | 35 --- docs/index.md | 238 ++++++++++----- traefik.sample.toml | 108 ++++++- web.go | 16 +- 6 files changed, 582 insertions(+), 501 deletions(-) delete mode 100644 docs/ROADMAP.md diff --git a/Godeps/Godeps.json b/Godeps/Godeps.json index 8eb9d2194..36e270c9b 100644 --- a/Godeps/Godeps.json +++ b/Godeps/Godeps.json @@ -1,386 +1,302 @@ { - "ImportPath": "github.com/emilevauge/traefik", - "GoVersion": "go1.4.2", - "Packages": [ - "./..." - ], - "Deps": [ - { - "ImportPath": "github.com/BurntSushi/toml", - "Rev": "bd2bdf7f18f849530ef7a1c29a4290217cab32a1" - }, - { - "ImportPath": "github.com/BurntSushi/ty", - "Rev": "6add9cd6ad42d389d6ead1dde60b4ad71e46fd74" - }, - { - "ImportPath": "github.com/Sirupsen/logrus", - "Comment": "v0.8.7", - "Rev": "418b41d23a1bf978c06faea5313ba194650ac088" - }, - { - "ImportPath": "github.com/alecthomas/template", - "Rev": "b867cc6ab45cece8143cfcc6fc9c77cf3f2c23c0" - }, - { - "ImportPath": "github.com/alecthomas/units", - "Rev": "6b4e7dc5e3143b85ea77909c72caf89416fc2915" - }, - { - "ImportPath": "github.com/boltdb/bolt", - "Rev": "51f99c862475898df9773747d3accd05a7ca33c1" - }, - { - "ImportPath": "github.com/cenkalti/backoff", - "Rev": "4dc77674aceaabba2c7e3da25d4c823edfb73f99" - }, - { - "ImportPath": "github.com/codahale/hdrhistogram", - "Rev": "954f16e8b9ef0e5d5189456aa4c1202758e04f17" - }, - { - "ImportPath": "github.com/codegangsta/negroni", - "Comment": "v0.1-70-gc7477ad", - "Rev": "c7477ad8e330bef55bf1ebe300cf8aa67c492d1b" - }, - { - "ImportPath": "github.com/coreos/go-etcd/etcd", - "Comment": "v2.0.0-11-gcc90c7b", - "Rev": "cc90c7b091275e606ad0ca7102a23fb2072f3f5e" - }, - { - "ImportPath": "github.com/davecgh/go-spew/spew", - "Rev": "2df174808ee097f90d259e432cc04442cf60be21" - }, - { - "ImportPath": "github.com/docker/libkv", - "Rev": "3732f7ff1b56057c3158f10bceb1e79133025373" - }, - { - "ImportPath": "github.com/docker/distribution", - "Comment": "v2.0.0-467-g9038e48", - "Rev": "9038e48c3b982f8e82281ea486f078a73731ac4e" - }, - { - "ImportPath": "github.com/docker/docker/api", - "Comment": "v1.4.1-5200-gf39987a", - "Rev": "f39987afe8d611407887b3094c03d6ba6a766a67" - }, - { - "ImportPath": "github.com/docker/docker/cliconfig", - "Comment": "v1.4.1-5200-gf39987a", - "Rev": "f39987afe8d611407887b3094c03d6ba6a766a67" - }, - { - "ImportPath": "github.com/docker/docker/daemon/network", - "Comment": "v1.4.1-5200-gf39987a", - "Rev": "f39987afe8d611407887b3094c03d6ba6a766a67" - }, - { - "ImportPath": "github.com/docker/docker/graph/tags", - "Comment": "v1.4.1-5200-gf39987a", - "Rev": "f39987afe8d611407887b3094c03d6ba6a766a67" - }, - { - "ImportPath": "github.com/docker/docker/image", - "Comment": "v1.4.1-5200-gf39987a", - "Rev": "f39987afe8d611407887b3094c03d6ba6a766a67" - }, - { - "ImportPath": "github.com/docker/docker/opts", - "Comment": "v1.4.1-5200-gf39987a", - "Rev": "f39987afe8d611407887b3094c03d6ba6a766a67" - }, - { - "ImportPath": "github.com/docker/docker/pkg/archive", - "Comment": "v1.4.1-5200-gf39987a", - "Rev": "f39987afe8d611407887b3094c03d6ba6a766a67" - }, - { - "ImportPath": "github.com/docker/docker/pkg/fileutils", - "Comment": "v1.4.1-5200-gf39987a", - "Rev": "f39987afe8d611407887b3094c03d6ba6a766a67" - }, - { - "ImportPath": "github.com/docker/docker/pkg/homedir", - "Comment": "v1.4.1-5200-gf39987a", - "Rev": "f39987afe8d611407887b3094c03d6ba6a766a67" - }, - { - "ImportPath": "github.com/docker/docker/pkg/httputils", - "Comment": "v1.4.1-5200-gf39987a", - "Rev": "f39987afe8d611407887b3094c03d6ba6a766a67" - }, - { - "ImportPath": "github.com/docker/docker/pkg/ioutils", - "Comment": "v1.4.1-5200-gf39987a", - "Rev": "f39987afe8d611407887b3094c03d6ba6a766a67" - }, - { - "ImportPath": "github.com/docker/docker/pkg/jsonmessage", - "Comment": "v1.4.1-5200-gf39987a", - "Rev": "f39987afe8d611407887b3094c03d6ba6a766a67" - }, - { - "ImportPath": "github.com/docker/docker/pkg/mflag", - "Comment": "v1.4.1-5200-gf39987a", - "Rev": "f39987afe8d611407887b3094c03d6ba6a766a67" - }, - { - "ImportPath": "github.com/docker/docker/pkg/nat", - "Comment": "v1.4.1-5200-gf39987a", - "Rev": "f39987afe8d611407887b3094c03d6ba6a766a67" - }, - { - "ImportPath": "github.com/docker/docker/pkg/parsers", - "Comment": "v1.4.1-5200-gf39987a", - "Rev": "f39987afe8d611407887b3094c03d6ba6a766a67" - }, - { - "ImportPath": "github.com/docker/docker/pkg/pools", - "Comment": "v1.4.1-5200-gf39987a", - "Rev": "f39987afe8d611407887b3094c03d6ba6a766a67" - }, - { - "ImportPath": "github.com/docker/docker/pkg/promise", - "Comment": "v1.4.1-5200-gf39987a", - "Rev": "f39987afe8d611407887b3094c03d6ba6a766a67" - }, - { - "ImportPath": "github.com/docker/docker/pkg/random", - "Comment": "v1.4.1-5200-gf39987a", - "Rev": "f39987afe8d611407887b3094c03d6ba6a766a67" - }, - { - "ImportPath": "github.com/docker/docker/pkg/stdcopy", - "Comment": "v1.4.1-5200-gf39987a", - "Rev": "f39987afe8d611407887b3094c03d6ba6a766a67" - }, - { - "ImportPath": "github.com/docker/docker/pkg/stringid", - "Comment": "v1.4.1-5200-gf39987a", - "Rev": "f39987afe8d611407887b3094c03d6ba6a766a67" - }, - { - "ImportPath": "github.com/docker/docker/pkg/symlink", - "Comment": "v1.4.1-5200-gf39987a", - "Rev": "f39987afe8d611407887b3094c03d6ba6a766a67" - }, - { - "ImportPath": "github.com/docker/docker/pkg/system", - "Comment": "v1.4.1-5200-gf39987a", - "Rev": "f39987afe8d611407887b3094c03d6ba6a766a67" - }, - { - "ImportPath": "github.com/docker/docker/pkg/tarsum", - "Comment": "v1.4.1-5200-gf39987a", - "Rev": "f39987afe8d611407887b3094c03d6ba6a766a67" - }, - { - "ImportPath": "github.com/docker/docker/pkg/term", - "Comment": "v1.4.1-5200-gf39987a", - "Rev": "f39987afe8d611407887b3094c03d6ba6a766a67" - }, - { - "ImportPath": "github.com/docker/docker/pkg/timeutils", - "Comment": "v1.4.1-5200-gf39987a", - "Rev": "f39987afe8d611407887b3094c03d6ba6a766a67" - }, - { - "ImportPath": "github.com/docker/docker/pkg/tlsconfig", - "Comment": "v1.4.1-5200-gf39987a", - "Rev": "f39987afe8d611407887b3094c03d6ba6a766a67" - }, - { - "ImportPath": "github.com/docker/docker/pkg/ulimit", - "Comment": "v1.4.1-5200-gf39987a", - "Rev": "f39987afe8d611407887b3094c03d6ba6a766a67" - }, - { - "ImportPath": "github.com/docker/docker/pkg/units", - "Comment": "v1.4.1-5200-gf39987a", - "Rev": "f39987afe8d611407887b3094c03d6ba6a766a67" - }, - { - "ImportPath": "github.com/docker/docker/pkg/urlutil", - "Comment": "v1.4.1-5200-gf39987a", - "Rev": "f39987afe8d611407887b3094c03d6ba6a766a67" - }, - { - "ImportPath": "github.com/docker/docker/pkg/useragent", - "Comment": "v1.4.1-5200-gf39987a", - "Rev": "f39987afe8d611407887b3094c03d6ba6a766a67" - }, - { - "ImportPath": "github.com/docker/docker/pkg/version", - "Comment": "v1.4.1-5200-gf39987a", - "Rev": "f39987afe8d611407887b3094c03d6ba6a766a67" - }, - { - "ImportPath": "github.com/docker/docker/registry", - "Comment": "v1.4.1-5200-gf39987a", - "Rev": "f39987afe8d611407887b3094c03d6ba6a766a67" - }, - { - "ImportPath": "github.com/docker/docker/runconfig", - "Comment": "v1.4.1-5200-gf39987a", - "Rev": "f39987afe8d611407887b3094c03d6ba6a766a67" - }, - { - "ImportPath": "github.com/docker/docker/utils", - "Comment": "v1.4.1-5200-gf39987a", - "Rev": "f39987afe8d611407887b3094c03d6ba6a766a67" - }, - { - "ImportPath": "github.com/docker/docker/volume", - "Comment": "v1.4.1-5200-gf39987a", - "Rev": "f39987afe8d611407887b3094c03d6ba6a766a67" - }, - { - "ImportPath": "github.com/docker/libcompose/docker", - "Rev": "aad672800904307e96a2c21cad1420f3080e0f35" - }, - { - "ImportPath": "github.com/docker/libcompose/logger", - "Rev": "aad672800904307e96a2c21cad1420f3080e0f35" - }, - { - "ImportPath": "github.com/docker/libcompose/lookup", - "Rev": "aad672800904307e96a2c21cad1420f3080e0f35" - }, - { - "ImportPath": "github.com/docker/libcompose/project", - "Rev": "aad672800904307e96a2c21cad1420f3080e0f35" - }, - { - "ImportPath": "github.com/docker/libcompose/utils", - "Rev": "aad672800904307e96a2c21cad1420f3080e0f35" - }, - { - "ImportPath": "github.com/docker/libtrust", - "Rev": "9cbd2a1374f46905c68a4eb3694a130610adc62a" - }, - { - "ImportPath": "github.com/elazarl/go-bindata-assetfs", - "Rev": "d5cac425555ca5cf00694df246e04f05e6a55150" - }, - { - "ImportPath": "github.com/flynn/go-shlex", - "Rev": "3f9db97f856818214da2e1057f8ad84803971cff" - }, - { - "ImportPath": "github.com/fsouza/go-dockerclient", - "Rev": "0239034d42f665efa17fd77c39f891c2f9f32922" - }, - { - "ImportPath": "github.com/gambol99/go-marathon", - "Rev": "0ba31bcb0d7633ba1888d744c42990eb15281cf1" - }, - { - "ImportPath": "github.com/gorilla/context", - "Rev": "215affda49addc4c8ef7e2534915df2c8c35c6cd" - }, - { - "ImportPath": "github.com/gorilla/handlers", - "Rev": "40694b40f4a928c062f56849989d3e9cd0570e5f" - }, - { - "ImportPath": "github.com/gorilla/mux", - "Rev": "f15e0c49460fd49eebe2bcc8486b05d1bef68d3a" - }, - { - "ImportPath": "github.com/hashicorp/consul/api", - "Comment": "v0.5.2-313-gde08067", - "Rev": "de080672fee9e6104572eeea89eccdca135bb918" - }, - { - "ImportPath": "github.com/mailgun/log", - "Rev": "44874009257d4d47ba9806f1b7f72a32a015e4d8" - }, - { - "ImportPath": "github.com/mailgun/manners", - "Comment": "0.3.1-30-g37136f7", - "Rev": "37136f736785d7c6aa3b9a27b4b2dd1028ca6d79" - }, - { - "ImportPath": "github.com/mailgun/oxy/cbreaker", - "Rev": "547c334d658398c05b346c0b79d8f47ba2e1473b" - }, - { - "ImportPath": "github.com/mailgun/oxy/forward", - "Rev": "547c334d658398c05b346c0b79d8f47ba2e1473b" - }, - { - "ImportPath": "github.com/mailgun/oxy/memmetrics", - "Rev": "547c334d658398c05b346c0b79d8f47ba2e1473b" - }, - { - "ImportPath": "github.com/mailgun/oxy/roundrobin", - "Rev": "547c334d658398c05b346c0b79d8f47ba2e1473b" - }, - { - "ImportPath": "github.com/mailgun/oxy/utils", - "Rev": "547c334d658398c05b346c0b79d8f47ba2e1473b" - }, - { - "ImportPath": "github.com/mailgun/predicate", - "Rev": "cb0bff91a7ab7cf7571e661ff883fc997bc554a3" - }, - { - "ImportPath": "github.com/mailgun/timetools", - "Rev": "fd192d755b00c968d312d23f521eb0cdc6f66bd0" - }, - { - "ImportPath": "github.com/samuel/go-zookeeper/zk", - "Rev": "fa6674abf3f4580b946a01bf7a1ce4ba8766205b" - }, - { - "ImportPath": "github.com/opencontainers/runc/libcontainer/user", - "Comment": "v0.0.4-21-g4ab1324", - "Rev": "4ab132458fc3e9dbeea624153e0331952dc4c8d5" - }, - { - "ImportPath": "github.com/samalba/dockerclient", - "Rev": "cfb489c624b635251a93e74e1e90eb0959c5367f" - }, - { - "ImportPath": "github.com/thoas/stats", - "Rev": "54ed61c2b47e263ae2f01b86837b0c4bd1da28e8" - }, - { - "ImportPath": "github.com/unrolled/render", - "Rev": "26b4e3aac686940fe29521545afad9966ddfc80c" - }, - { - "ImportPath": "github.com/vdemeester/shakers", - "Rev": "8fe734f75f3a70b651cbfbf8a55a009da09e8dc5" - }, - { - "ImportPath": "golang.org/x/net/context", - "Rev": "d9558e5c97f85372afee28cf2b6059d7d3818919" - }, - { - "ImportPath": "gopkg.in/alecthomas/kingpin.v2", - "Comment": "v2.0.12", - "Rev": "639879d6110b1b0409410c7b737ef0bb18325038" - }, - { - "ImportPath": "gopkg.in/check.v1", - "Rev": "11d3bc7aa68e238947792f30573146a3231fc0f1" - }, - { - "ImportPath": "gopkg.in/fsnotify.v1", - "Comment": "v1.2.0", - "Rev": "96c060f6a6b7e0d6f75fddd10efeaca3e5d1bcb0" - }, - { - "ImportPath": "gopkg.in/mgo.v2/bson", - "Comment": "r2015.06.03-5-g22287ba", - "Rev": "22287bab4379e1fbf6002fb4eb769888f3fb224c" - }, - { - "ImportPath": "gopkg.in/yaml.v2", - "Rev": "7ad95dd0798a40da1ccdff6dff35fd177b5edf40" - } - ] + "ImportPath": "github.com/emilevauge/traefik", + "GoVersion": "go1.4.2", + "Packages": [ + "./..." + ], + "Deps": [{ + "ImportPath": "github.com/BurntSushi/toml", + "Rev": "bd2bdf7f18f849530ef7a1c29a4290217cab32a1" + }, { + "ImportPath": "github.com/BurntSushi/ty", + "Rev": "6add9cd6ad42d389d6ead1dde60b4ad71e46fd74" + }, { + "ImportPath": "github.com/Sirupsen/logrus", + "Comment": "v0.8.7", + "Rev": "418b41d23a1bf978c06faea5313ba194650ac088" + }, { + "ImportPath": "github.com/alecthomas/template", + "Rev": "b867cc6ab45cece8143cfcc6fc9c77cf3f2c23c0" + }, { + "ImportPath": "github.com/alecthomas/units", + "Rev": "6b4e7dc5e3143b85ea77909c72caf89416fc2915" + }, { + "ImportPath": "github.com/boltdb/bolt", + "Rev": "51f99c862475898df9773747d3accd05a7ca33c1" + }, { + "ImportPath": "github.com/cenkalti/backoff", + "Rev": "4dc77674aceaabba2c7e3da25d4c823edfb73f99" + }, { + "ImportPath": "github.com/codahale/hdrhistogram", + "Rev": "954f16e8b9ef0e5d5189456aa4c1202758e04f17" + }, { + "ImportPath": "github.com/codegangsta/negroni", + "Comment": "v0.1-70-gc7477ad", + "Rev": "c7477ad8e330bef55bf1ebe300cf8aa67c492d1b" + }, { + "ImportPath": "github.com/coreos/go-etcd/etcd", + "Comment": "v2.0.0-11-gcc90c7b", + "Rev": "cc90c7b091275e606ad0ca7102a23fb2072f3f5e" + }, { + "ImportPath": "github.com/davecgh/go-spew/spew", + "Rev": "2df174808ee097f90d259e432cc04442cf60be21" + }, { + "ImportPath": "github.com/docker/libkv", + "Rev": "3732f7ff1b56057c3158f10bceb1e79133025373" + }, { + "ImportPath": "github.com/docker/distribution", + "Comment": "v2.0.0-467-g9038e48", + "Rev": "9038e48c3b982f8e82281ea486f078a73731ac4e" + }, { + "ImportPath": "github.com/docker/docker/api", + "Comment": "v1.4.1-5200-gf39987a", + "Rev": "f39987afe8d611407887b3094c03d6ba6a766a67" + }, { + "ImportPath": "github.com/docker/docker/cliconfig", + "Comment": "v1.4.1-5200-gf39987a", + "Rev": "f39987afe8d611407887b3094c03d6ba6a766a67" + }, { + "ImportPath": "github.com/docker/docker/daemon/network", + "Comment": "v1.4.1-5200-gf39987a", + "Rev": "f39987afe8d611407887b3094c03d6ba6a766a67" + }, { + "ImportPath": "github.com/docker/docker/graph/tags", + "Comment": "v1.4.1-5200-gf39987a", + "Rev": "f39987afe8d611407887b3094c03d6ba6a766a67" + }, { + "ImportPath": "github.com/docker/docker/image", + "Comment": "v1.4.1-5200-gf39987a", + "Rev": "f39987afe8d611407887b3094c03d6ba6a766a67" + }, { + "ImportPath": "github.com/docker/docker/opts", + "Comment": "v1.4.1-5200-gf39987a", + "Rev": "f39987afe8d611407887b3094c03d6ba6a766a67" + }, { + "ImportPath": "github.com/docker/docker/pkg/archive", + "Comment": "v1.4.1-5200-gf39987a", + "Rev": "f39987afe8d611407887b3094c03d6ba6a766a67" + }, { + "ImportPath": "github.com/docker/docker/pkg/fileutils", + "Comment": "v1.4.1-5200-gf39987a", + "Rev": "f39987afe8d611407887b3094c03d6ba6a766a67" + }, { + "ImportPath": "github.com/docker/docker/pkg/homedir", + "Comment": "v1.4.1-5200-gf39987a", + "Rev": "f39987afe8d611407887b3094c03d6ba6a766a67" + }, { + "ImportPath": "github.com/docker/docker/pkg/httputils", + "Comment": "v1.4.1-5200-gf39987a", + "Rev": "f39987afe8d611407887b3094c03d6ba6a766a67" + }, { + "ImportPath": "github.com/docker/docker/pkg/ioutils", + "Comment": "v1.4.1-5200-gf39987a", + "Rev": "f39987afe8d611407887b3094c03d6ba6a766a67" + }, { + "ImportPath": "github.com/docker/docker/pkg/jsonmessage", + "Comment": "v1.4.1-5200-gf39987a", + "Rev": "f39987afe8d611407887b3094c03d6ba6a766a67" + }, { + "ImportPath": "github.com/docker/docker/pkg/mflag", + "Comment": "v1.4.1-5200-gf39987a", + "Rev": "f39987afe8d611407887b3094c03d6ba6a766a67" + }, { + "ImportPath": "github.com/docker/docker/pkg/nat", + "Comment": "v1.4.1-5200-gf39987a", + "Rev": "f39987afe8d611407887b3094c03d6ba6a766a67" + }, { + "ImportPath": "github.com/docker/docker/pkg/parsers", + "Comment": "v1.4.1-5200-gf39987a", + "Rev": "f39987afe8d611407887b3094c03d6ba6a766a67" + }, { + "ImportPath": "github.com/docker/docker/pkg/pools", + "Comment": "v1.4.1-5200-gf39987a", + "Rev": "f39987afe8d611407887b3094c03d6ba6a766a67" + }, { + "ImportPath": "github.com/docker/docker/pkg/promise", + "Comment": "v1.4.1-5200-gf39987a", + "Rev": "f39987afe8d611407887b3094c03d6ba6a766a67" + }, { + "ImportPath": "github.com/docker/docker/pkg/random", + "Comment": "v1.4.1-5200-gf39987a", + "Rev": "f39987afe8d611407887b3094c03d6ba6a766a67" + }, { + "ImportPath": "github.com/docker/docker/pkg/stdcopy", + "Comment": "v1.4.1-5200-gf39987a", + "Rev": "f39987afe8d611407887b3094c03d6ba6a766a67" + }, { + "ImportPath": "github.com/docker/docker/pkg/stringid", + "Comment": "v1.4.1-5200-gf39987a", + "Rev": "f39987afe8d611407887b3094c03d6ba6a766a67" + }, { + "ImportPath": "github.com/docker/docker/pkg/symlink", + "Comment": "v1.4.1-5200-gf39987a", + "Rev": "f39987afe8d611407887b3094c03d6ba6a766a67" + }, { + "ImportPath": "github.com/docker/docker/pkg/system", + "Comment": "v1.4.1-5200-gf39987a", + "Rev": "f39987afe8d611407887b3094c03d6ba6a766a67" + }, { + "ImportPath": "github.com/docker/docker/pkg/tarsum", + "Comment": "v1.4.1-5200-gf39987a", + "Rev": "f39987afe8d611407887b3094c03d6ba6a766a67" + }, { + "ImportPath": "github.com/docker/docker/pkg/term", + "Comment": "v1.4.1-5200-gf39987a", + "Rev": "f39987afe8d611407887b3094c03d6ba6a766a67" + }, { + "ImportPath": "github.com/docker/docker/pkg/timeutils", + "Comment": "v1.4.1-5200-gf39987a", + "Rev": "f39987afe8d611407887b3094c03d6ba6a766a67" + }, { + "ImportPath": "github.com/docker/docker/pkg/tlsconfig", + "Comment": "v1.4.1-5200-gf39987a", + "Rev": "f39987afe8d611407887b3094c03d6ba6a766a67" + }, { + "ImportPath": "github.com/docker/docker/pkg/ulimit", + "Comment": "v1.4.1-5200-gf39987a", + "Rev": "f39987afe8d611407887b3094c03d6ba6a766a67" + }, { + "ImportPath": "github.com/docker/docker/pkg/units", + "Comment": "v1.4.1-5200-gf39987a", + "Rev": "f39987afe8d611407887b3094c03d6ba6a766a67" + }, { + "ImportPath": "github.com/docker/docker/pkg/urlutil", + "Comment": "v1.4.1-5200-gf39987a", + "Rev": "f39987afe8d611407887b3094c03d6ba6a766a67" + }, { + "ImportPath": "github.com/docker/docker/pkg/useragent", + "Comment": "v1.4.1-5200-gf39987a", + "Rev": "f39987afe8d611407887b3094c03d6ba6a766a67" + }, { + "ImportPath": "github.com/docker/docker/pkg/version", + "Comment": "v1.4.1-5200-gf39987a", + "Rev": "f39987afe8d611407887b3094c03d6ba6a766a67" + }, { + "ImportPath": "github.com/docker/docker/registry", + "Comment": "v1.4.1-5200-gf39987a", + "Rev": "f39987afe8d611407887b3094c03d6ba6a766a67" + }, { + "ImportPath": "github.com/docker/docker/runconfig", + "Comment": "v1.4.1-5200-gf39987a", + "Rev": "f39987afe8d611407887b3094c03d6ba6a766a67" + }, { + "ImportPath": "github.com/docker/docker/utils", + "Comment": "v1.4.1-5200-gf39987a", + "Rev": "f39987afe8d611407887b3094c03d6ba6a766a67" + }, { + "ImportPath": "github.com/docker/docker/volume", + "Comment": "v1.4.1-5200-gf39987a", + "Rev": "f39987afe8d611407887b3094c03d6ba6a766a67" + }, { + "ImportPath": "github.com/docker/libcompose/docker", + "Rev": "aad672800904307e96a2c21cad1420f3080e0f35" + }, { + "ImportPath": "github.com/docker/libcompose/logger", + "Rev": "aad672800904307e96a2c21cad1420f3080e0f35" + }, { + "ImportPath": "github.com/docker/libcompose/lookup", + "Rev": "aad672800904307e96a2c21cad1420f3080e0f35" + }, { + "ImportPath": "github.com/docker/libcompose/project", + "Rev": "aad672800904307e96a2c21cad1420f3080e0f35" + }, { + "ImportPath": "github.com/docker/libcompose/utils", + "Rev": "aad672800904307e96a2c21cad1420f3080e0f35" + }, { + "ImportPath": "github.com/docker/libtrust", + "Rev": "9cbd2a1374f46905c68a4eb3694a130610adc62a" + }, { + "ImportPath": "github.com/elazarl/go-bindata-assetfs", + "Rev": "d5cac425555ca5cf00694df246e04f05e6a55150" + }, { + "ImportPath": "github.com/flynn/go-shlex", + "Rev": "3f9db97f856818214da2e1057f8ad84803971cff" + }, { + "ImportPath": "github.com/fsouza/go-dockerclient", + "Rev": "0239034d42f665efa17fd77c39f891c2f9f32922" + }, { + "ImportPath": "github.com/gambol99/go-marathon", + "Rev": "0ba31bcb0d7633ba1888d744c42990eb15281cf1" + }, { + "ImportPath": "github.com/gorilla/context", + "Rev": "215affda49addc4c8ef7e2534915df2c8c35c6cd" + }, { + "ImportPath": "github.com/gorilla/handlers", + "Rev": "40694b40f4a928c062f56849989d3e9cd0570e5f" + }, { + "ImportPath": "github.com/gorilla/mux", + "Rev": "f15e0c49460fd49eebe2bcc8486b05d1bef68d3a" + }, { + "ImportPath": "github.com/hashicorp/consul/api", + "Comment": "v0.5.2-313-gde08067", + "Rev": "de080672fee9e6104572eeea89eccdca135bb918" + }, { + "ImportPath": "github.com/mailgun/log", + "Rev": "44874009257d4d47ba9806f1b7f72a32a015e4d8" + }, { + "ImportPath": "github.com/mailgun/manners", + "Comment": "0.3.1-30-g37136f7", + "Rev": "37136f736785d7c6aa3b9a27b4b2dd1028ca6d79" + }, { + "ImportPath": "github.com/mailgun/oxy/cbreaker", + "Rev": "547c334d658398c05b346c0b79d8f47ba2e1473b" + }, { + "ImportPath": "github.com/mailgun/oxy/forward", + "Rev": "547c334d658398c05b346c0b79d8f47ba2e1473b" + }, { + "ImportPath": "github.com/mailgun/oxy/memmetrics", + "Rev": "547c334d658398c05b346c0b79d8f47ba2e1473b" + }, { + "ImportPath": "github.com/mailgun/oxy/roundrobin", + "Rev": "547c334d658398c05b346c0b79d8f47ba2e1473b" + }, { + "ImportPath": "github.com/mailgun/oxy/utils", + "Rev": "547c334d658398c05b346c0b79d8f47ba2e1473b" + }, { + "ImportPath": "github.com/mailgun/predicate", + "Rev": "cb0bff91a7ab7cf7571e661ff883fc997bc554a3" + }, { + "ImportPath": "github.com/mailgun/timetools", + "Rev": "fd192d755b00c968d312d23f521eb0cdc6f66bd0" + }, { + "ImportPath": "github.com/samuel/go-zookeeper/zk", + "Rev": "fa6674abf3f4580b946a01bf7a1ce4ba8766205b" + }, { + "ImportPath": "github.com/opencontainers/runc/libcontainer/user", + "Comment": "v0.0.4-21-g4ab1324", + "Rev": "4ab132458fc3e9dbeea624153e0331952dc4c8d5" + }, { + "ImportPath": "github.com/samalba/dockerclient", + "Rev": "cfb489c624b635251a93e74e1e90eb0959c5367f" + }, { + "ImportPath": "github.com/thoas/stats", + "Rev": "54ed61c2b47e263ae2f01b86837b0c4bd1da28e8" + }, { + "ImportPath": "github.com/unrolled/render", + "Rev": "26b4e3aac686940fe29521545afad9966ddfc80c" + }, { + "ImportPath": "github.com/vdemeester/shakers", + "Rev": "8fe734f75f3a70b651cbfbf8a55a009da09e8dc5" + }, { + "ImportPath": "golang.org/x/net/context", + "Rev": "d9558e5c97f85372afee28cf2b6059d7d3818919" + }, { + "ImportPath": "gopkg.in/alecthomas/kingpin.v2", + "Comment": "v2.0.12", + "Rev": "639879d6110b1b0409410c7b737ef0bb18325038" + }, { + "ImportPath": "gopkg.in/check.v1", + "Rev": "11d3bc7aa68e238947792f30573146a3231fc0f1" + }, { + "ImportPath": "gopkg.in/fsnotify.v1", + "Comment": "v1.2.0", + "Rev": "96c060f6a6b7e0d6f75fddd10efeaca3e5d1bcb0" + }, { + "ImportPath": "gopkg.in/mgo.v2/bson", + "Comment": "r2015.06.03-5-g22287ba", + "Rev": "22287bab4379e1fbf6002fb4eb769888f3fb224c" + }, { + "ImportPath": "gopkg.in/yaml.v2", + "Rev": "7ad95dd0798a40da1ccdff6dff35fd177b5edf40" + }] } diff --git a/README.md b/README.md index 8475987a7..d619bb613 100644 --- a/README.md +++ b/README.md @@ -7,7 +7,7 @@ ___ Træfɪk is a modern HTTP reverse proxy and load balancer made to deploy microservices with ease. -It supports several backends ([Docker :whale:](https://www.docker.com/), [Mesos/Marathon](https://mesosphere.github.io/marathon/), [Consul](https://consul.io/), [Etcd](https://coreos.com/etcd/), Rest API, file...) to manage its configuration automatically and dynamically. +It supports several backends ([Docker :whale:](https://www.docker.com/), [Mesos/Marathon](https://mesosphere.github.io/marathon/), [Consul](https://consul.io/), [Etcd](https://coreos.com/etcd/), [Zookeeper](https://zookeeper.apache.org), [BoltDB](https://github.com/boltdb/bolt), Rest API, file...) to manage its configuration automatically and dynamically. # Features diff --git a/docs/ROADMAP.md b/docs/ROADMAP.md deleted file mode 100644 index f672330a0..000000000 --- a/docs/ROADMAP.md +++ /dev/null @@ -1,35 +0,0 @@ -![Træfɪk](http://traefik.github.io/traefik.logo.svg "Træfɪk") -___ - -# Roadmap - -* Add traefik.protocol label -* Providers recovery -* tls client verification -* Default configuration values -* Retry with streams -* Licence -* Add traefik.indlude all/enabled policy -* Consul support -* README -* API enhancements -* Godoc - - -* Kubernetes support -* Etcd support -* Smart configuration diff - - -* ~~GraceTimeout~~ -* ~~Weights~~ -* ~~Filter traefik.enabled=false apps~~ -* ~~Filter no exposed port apps~~ -* ~~Logs~~ -* ~~SSL frontend support~~ -* ~~SSL backends support~~ -* ~~Static files with go-bindata~~ -* ~~Metrics~~ -* ~~Dockerfile~~ -* ~~Use Negroni middlewares for metrics, grace, logs~~ -* ~~Website~~ diff --git a/docs/index.md b/docs/index.md index 045f221cf..03bb16eb0 100644 --- a/docs/index.md +++ b/docs/index.md @@ -11,6 +11,9 @@ ___ * [Docker backend](#docker) * [Mesos/Marathon backend](#marathon) * [Consul backend](#consul) +* [Etcd backend](#etcd) +* [Zookeeper backend](#zk) +* [Boltdb backend](#boltdb) * [Benchmarks](#benchmarks) @@ -257,106 +260,77 @@ $ curl -s "http://localhost:8080/health" | jq . * ```/api```: ```GET``` configuration for all providers ```sh -$ curl -s "http://localhost:8082/api" | jq . +$ curl -s "http://localhost:8080/api" | jq . { "file": { "Frontends": { - "frontend-traefik": { + "frontend2": { "Routes": { - "route-host-traefik": { - "Value": "traefik.docker.localhost", - "Rule": "Host" + "test_2": { + "Value": "/test", + "Rule": "Path" } }, - "Backend": "backend-test2" + "Backend": "backend1" }, - "frontend-test": { + "frontend1": { "Routes": { - "route-host-test": { - "Value": "test.docker.localhost", + "test_1": { + "Value": "test.localhost", "Rule": "Host" } }, - "Backend": "backend-test1" + "Backend": "backend2" } }, "Backends": { - "backend-test2": { + "backend2": { + "LoadBalancer": { + "Method": "drr" + }, + "CircuitBreaker": null, "Servers": { - "server-stoic_brattain": { - "Weight": 0, - "Url": "http://172.17.0.8:80" + "server2": { + "Weight": 2, + "URL": "http://172.17.0.5:80" }, - "server-jovial_khorana": { - "Weight": 0, - "Url": "http://172.17.0.12:80" - }, - "server-jovial_franklin": { - "Weight": 0, - "Url": "http://172.17.0.11:80" - }, - "server-elegant_panini": { - "Weight": 0, - "Url": "http://172.17.0.9:80" - }, - "server-adoring_elion": { - "Weight": 0, - "Url": "http://172.17.0.10:80" + "server1": { + "Weight": 1, + "URL": "http://172.17.0.4:80" } } }, - "backend-test1": { + "backend1": { + "LoadBalancer": { + "Method": "wrr" + }, + "CircuitBreaker": { + "Expression": "NetworkErrorRatio() > 0.5" + }, "Servers": { - "server-trusting_wozniak": { - "Weight": 0, - "Url": "http://172.17.0.5:80" + "server2": { + "Weight": 1, + "URL": "http://172.17.0.3:80" }, - "server-sharp_jang": { - "Weight": 0, - "Url": "http://172.17.0.7:80" - }, - "server-dreamy_feynman": { - "Weight": 0, - "Url": "http://172.17.0.6:80" + "server1": { + "Weight": 10, + "URL": "http://172.17.0.2:80" } } } } - }, - "marathon": { - "Frontends": { - "frontend-marathon": { - "Routes": { - "route-host-marathon": { - "Value": "marathon.docker.localhost", - "Rule": "Host" - } - }, - "Backend": "backend-marathon" - }, - }, - "Backends": { - "backend-marathon": { - "Servers": { - "server-marathon-1": { - "Weight": 0, - "Url": "http://172.17.0.8:802" - }, - }, - }, - }, - }, + } } - ``` -* ```/api/{provider}```: ```GET``` or ```PUT``` provider -* ```/api/{provider}/backends```: ```GET``` backends -* ```/api/{provider}/backends/{backend}```: ```GET``` a backend -* ```/api/{provider}/backends/{backend}/servers```: ```GET``` servers in a backend -* ```/api/{provider}/backends/{backend}/servers/{server}```: ```GET``` a server in a backend -* ```/api/{provider}/frontends```: ```GET``` frontends -* ```/api/{provider}/frontends/{frontend}```: ```GET``` a frontend +* ```/api/providers```: ```GET``` providers +* ```/api/providers/{provider}```: ```GET``` or ```PUT``` provider +* ```/api/providers/{provider}/backends```: ```GET``` backends +* ```/api/providers/{provider}/backends/{backend}```: ```GET``` a backend +* ```/api/providers/{provider}/backends/{backend}/servers```: ```GET``` servers in a backend +* ```/api/providers/{provider}/backends/{backend}/servers/{server}```: ```GET``` a server in a backend +* ```/api/providers/{provider}/frontends```: ```GET``` frontends +* ```/api/providers/{provider}/frontends/{frontend}```: ```GET``` a frontend ## Docker backend @@ -508,6 +482,126 @@ prefix = "traefik" # filename = "consul.tmpl" ``` +## Etcd backend + +Træfɪk can be configured to use Etcd as a backend configuration: + +```toml +################################################################ +# Etcd configuration backend +################################################################ + +# Enable Etcd configuration backend +# +# Optional +# +# [etcd] + +# Etcd server endpoint +# +# Required +# +# endpoint = "127.0.0.1:4001" + +# Enable watch Etcd changes +# +# Optional +# +# watch = true + +# Prefix used for KV store. +# +# Optional +# +# prefix = "/traefik" + +# Override default configuration template. For advanced users :) +# +# Optional +# +# filename = "etcd.tmpl" +``` + +## Zookeeper backend + +Træfɪk can be configured to use Zookeeper as a backend configuration: + +```toml +################################################################ +# Zookeeper configuration backend +################################################################ + +# Enable Zookeeperconfiguration backend +# +# Optional +# +# [zookeeper] + +# Zookeeper server endpoint +# +# Required +# +# endpoint = "127.0.0.1:2181" + +# Enable watch Zookeeper changes +# +# Optional +# +# watch = true + +# Prefix used for KV store. +# +# Optional +# +# prefix = "/traefik" + +# Override default configuration template. For advanced users :) +# +# Optional +# +# filename = "zookeeper.tmpl" +``` + +## BoltDB backend + +Træfɪk can be configured to use BoltDB as a backend configuration: + +```toml +################################################################ +# BoltDB configuration backend +################################################################ + +# Enable BoltDB configuration backend +# +# Optional +# +# [boltdb] + +# BoltDB file +# +# Required +# +# endpoint = "/my.db" + +# Enable watch BoltDB changes +# +# Optional +# +# watch = true + +# Prefix used for KV store. +# +# Optional +# +# prefix = "/traefik" + +# Override default configuration template. For advanced users :) +# +# Optional +# +# filename = "boltdb.tmpl" +``` + ## Benchmarks diff --git a/traefik.sample.toml b/traefik.sample.toml index 5194b1c8c..51d452609 100644 --- a/traefik.sample.toml +++ b/traefik.sample.toml @@ -147,7 +147,7 @@ # # endpoint = "http://127.0.0.1:8080" -# Network interface used to call Marathon web services +# Network interface used to call Marathon web services. Needed in case of multiple network interfaces. # Optional # Default: "eth0" # @@ -208,6 +208,112 @@ # filename = "consul.tmpl" +################################################################ +# Etcd configuration backend +################################################################ + +# Enable Etcd configuration backend +# +# Optional +# +# [etcd] + +# Etcd server endpoint +# +# Required +# +# endpoint = "127.0.0.1:4001" + +# Enable watch Etcd changes +# +# Optional +# +# watch = true + +# Prefix used for KV store. +# +# Optional +# +# prefix = "/traefik" + +# Override default configuration template. For advanced users :) +# +# Optional +# +# filename = "etcd.tmpl" + + +################################################################ +# Zookeeper configuration backend +################################################################ + +# Enable Zookeeperconfiguration backend +# +# Optional +# +# [zookeeper] + +# Zookeeper server endpoint +# +# Required +# +# endpoint = "127.0.0.1:2181" + +# Enable watch Zookeeper changes +# +# Optional +# +# watch = true + +# Prefix used for KV store. +# +# Optional +# +# prefix = "/traefik" + +# Override default configuration template. For advanced users :) +# +# Optional +# +# filename = "zookeeper.tmpl" + + +################################################################ +# BoltDB configuration backend +################################################################ + +# Enable BoltDB configuration backend +# +# Optional +# +# [boltdb] + +# BoltDB file +# +# Required +# +# endpoint = "/my.db" + +# Enable watch BoltDB changes +# +# Optional +# +# watch = true + +# Prefix used for KV store. +# +# Optional +# +# prefix = "/traefik" + +# Override default configuration template. For advanced users :) +# +# Optional +# +# filename = "boltdb.tmpl" + + + ################################################################ # Sample rules diff --git a/web.go b/web.go index f5a0d26c7..185542535 100644 --- a/web.go +++ b/web.go @@ -27,8 +27,8 @@ func (provider *WebProvider) Provide(configurationChan chan<- configMessage) err systemRouter.Methods("GET").Path("/health").Handler(http.HandlerFunc(GetHealthHandler)) systemRouter.Methods("GET").Path("/api").Handler(http.HandlerFunc(GetConfigHandler)) systemRouter.Methods("GET").Path("/api/providers").Handler(http.HandlerFunc(GetProvidersHandler)) - systemRouter.Methods("GET").Path("/api/{provider}").Handler(http.HandlerFunc(GetConfigHandler)) - systemRouter.Methods("PUT").Path("/api/{provider}").Handler(http.HandlerFunc( + systemRouter.Methods("GET").Path("/api/providers/{provider}").Handler(http.HandlerFunc(GetConfigHandler)) + systemRouter.Methods("PUT").Path("/api/providers/{provider}").Handler(http.HandlerFunc( func(rw http.ResponseWriter, r *http.Request) { vars := mux.Vars(r) if vars["provider"] != "web" { @@ -48,12 +48,12 @@ func (provider *WebProvider) Provide(configurationChan chan<- configMessage) err http.Error(rw, fmt.Sprintf("%+v", err), http.StatusBadRequest) } })) - systemRouter.Methods("GET").Path("/api/{provider}/backends").Handler(http.HandlerFunc(GetBackendsHandler)) - systemRouter.Methods("GET").Path("/api/{provider}/backends/{backend}").Handler(http.HandlerFunc(GetBackendHandler)) - systemRouter.Methods("GET").Path("/api/{provider}/backends/{backend}/servers").Handler(http.HandlerFunc(GetServersHandler)) - systemRouter.Methods("GET").Path("/api/{provider}/backends/{backend}/servers/{server}").Handler(http.HandlerFunc(GetServerHandler)) - systemRouter.Methods("GET").Path("/api/{provider}/frontends").Handler(http.HandlerFunc(GetFrontendsHandler)) - systemRouter.Methods("GET").Path("/api/{provider}/frontends/{frontend}").Handler(http.HandlerFunc(GetFrontendHandler)) + systemRouter.Methods("GET").Path("/api/providers/{provider}/backends").Handler(http.HandlerFunc(GetBackendsHandler)) + systemRouter.Methods("GET").Path("/api/providers/{provider}/backends/{backend}").Handler(http.HandlerFunc(GetBackendHandler)) + systemRouter.Methods("GET").Path("/api/providers/{provider}/backends/{backend}/servers").Handler(http.HandlerFunc(GetServersHandler)) + systemRouter.Methods("GET").Path("/api/providers/{provider}/backends/{backend}/servers/{server}").Handler(http.HandlerFunc(GetServerHandler)) + systemRouter.Methods("GET").Path("/api/providers/{provider}/frontends").Handler(http.HandlerFunc(GetFrontendsHandler)) + systemRouter.Methods("GET").Path("/api/providers/{provider}/frontends/{frontend}").Handler(http.HandlerFunc(GetFrontendHandler)) systemRouter.Methods("GET").PathPrefix("/static/").Handler(http.StripPrefix("/static/", http.FileServer(&assetfs.AssetFS{Asset: Asset, AssetDir: AssetDir, Prefix: "static"}))) go func() {