From de0a57ec767b3606a102443c70fb2d7ea3572299 Mon Sep 17 00:00:00 2001 From: Vincent Demeester Date: Sun, 1 Nov 2015 16:35:01 +0100 Subject: [PATCH] Refactor traefik with package Split a bit traefik into package. The idea behind this refactor is to start move inter-dependencies away and do some DRY or SRP. - Adds a `provider` package, with providers except `web.go` - Adds a `types` package with common struct. - Move `gen.go` to an `autogen` package Signed-off-by: Vincent Demeester --- autogen/.placeholder | 0 configuration.go | 96 ++++------------------------- file_test.go | 1 - generate.go | 2 +- provider.go | 5 -- boltdb.go => provider/boltdb.go | 6 +- consul.go => provider/consul.go | 6 +- docker.go => provider/docker.go | 18 +++--- etcd.go => provider/etcd.go | 6 +- file.go => provider/file.go | 13 ++-- provider/file_test.go | 1 + kv.go => provider/kv.go | 34 +++++----- marathon.go => provider/marathon.go | 18 +++--- provider/provider.go | 7 +++ zk.go => provider/zk.go | 6 +- script/binary | 2 +- script/crossbinary | 4 +- script/test-unit | 4 +- traefik.go | 42 +++++++------ types/types.go | 81 ++++++++++++++++++++++++ web.go | 10 +-- 21 files changed, 195 insertions(+), 167 deletions(-) create mode 100644 autogen/.placeholder delete mode 100644 file_test.go delete mode 100644 provider.go rename boltdb.go => provider/boltdb.go (76%) rename consul.go => provider/consul.go (76%) rename docker.go => provider/docker.go (94%) rename etcd.go => provider/etcd.go (60%) rename file.go => provider/file.go (75%) create mode 100644 provider/file_test.go rename kv.go => provider/kv.go (89%) rename marathon.go => provider/marathon.go (93%) create mode 100644 provider/provider.go rename zk.go => provider/zk.go (75%) create mode 100644 types/types.go diff --git a/autogen/.placeholder b/autogen/.placeholder new file mode 100644 index 000000000..e69de29bb diff --git a/configuration.go b/configuration.go index d940a11e1..0260ba53f 100644 --- a/configuration.go +++ b/configuration.go @@ -1,9 +1,10 @@ package main import ( - "errors" - "strings" "time" + + "github.com/emilevauge/traefik/provider" + "github.com/emilevauge/traefik/types" ) type GlobalConfiguration struct { @@ -14,14 +15,14 @@ type GlobalConfiguration struct { CertFile, KeyFile string LogLevel string ProvidersThrottleDuration time.Duration - Docker *DockerProvider - File *FileProvider + Docker *provider.DockerProvider + File *provider.FileProvider Web *WebProvider - Marathon *MarathonProvider - Consul *ConsulProvider - Etcd *EtcdProvider - Zookeeper *ZookepperProvider - Boltdb *BoltDbProvider + Marathon *provider.MarathonProvider + Consul *provider.ConsulProvider + Etcd *provider.EtcdProvider + Zookeeper *provider.ZookepperProvider + Boltdb *provider.BoltDbProvider } func NewGlobalConfiguration() *GlobalConfiguration { @@ -35,79 +36,4 @@ func NewGlobalConfiguration() *GlobalConfiguration { return globalConfiguration } -// Backend configuration -type Backend struct { - Servers map[string]Server `json:"servers,omitempty"` - CircuitBreaker *CircuitBreaker `json:"circuitBreaker,omitempty"` - LoadBalancer *LoadBalancer `json:"loadBalancer,omitempty"` -} - -// LoadBalancer configuration -type LoadBalancer struct { - Method string `json:"method,omitempty"` -} - -// CircuitBreaker configuration -type CircuitBreaker struct { - Expression string `json:"expression,omitempty"` -} - -// Server configuration -type Server struct { - URL string `json:"url,omitempty"` - Weight int `json:"weight,omitempty"` -} - -// Route configuration -type Route struct { - Rule string `json:"rule,omitempty"` - Value string `json:"value,omitempty"` -} - -// Frontend configuration -type Frontend struct { - PassHostHeader bool `json:"passHostHeader,omitempty"` - Backend string `json:"backend,omitempty"` - Routes map[string]Route `json:"routes,omitempty"` -} - -// Configuration of a provider -type Configuration struct { - Backends map[string]*Backend `json:"backends,omitempty"` - Frontends map[string]*Frontend `json:"frontends,omitempty"` -} - -// Load Balancer Method -type LoadBalancerMethod uint8 - -const ( - // wrr (default) = Weighted Round Robin - wrr LoadBalancerMethod = iota - // drr = Dynamic Round Robin - drr -) - -var loadBalancerMethodNames = []string{ - "wrr", - "drr", -} - -func NewLoadBalancerMethod(loadBalancer *LoadBalancer) (LoadBalancerMethod, error) { - if loadBalancer != nil { - for i, name := range loadBalancerMethodNames { - if strings.EqualFold(name, loadBalancer.Method) { - return LoadBalancerMethod(i), nil - } - } - } - return wrr, ErrInvalidLoadBalancerMethod -} - -var ErrInvalidLoadBalancerMethod = errors.New("Invalid method, using default") - -type configMessage struct { - providerName string - configuration *Configuration -} - -type configs map[string]*Configuration +type configs map[string]*types.Configuration diff --git a/file_test.go b/file_test.go deleted file mode 100644 index 06ab7d0f9..000000000 --- a/file_test.go +++ /dev/null @@ -1 +0,0 @@ -package main diff --git a/generate.go b/generate.go index 874b4474b..316905707 100644 --- a/generate.go +++ b/generate.go @@ -3,7 +3,7 @@ Copyright */ //go:generate rm -vf gen.go -//go:generate go-bindata -o gen.go static/... templates/... +//go:generate go-bindata -pkg autogen -o autogen/gen.go ./static/... ./templates/... //go:generate mkdir -p vendor/github.com/docker/docker/autogen/dockerversion //go:generate cp script/dockerversion vendor/github.com/docker/docker/autogen/dockerversion/dockerversion.go diff --git a/provider.go b/provider.go deleted file mode 100644 index df0bb7399..000000000 --- a/provider.go +++ /dev/null @@ -1,5 +0,0 @@ -package main - -type Provider interface { - Provide(configurationChan chan<- configMessage) error -} diff --git a/boltdb.go b/provider/boltdb.go similarity index 76% rename from boltdb.go rename to provider/boltdb.go index 663cd4aed..fc2329e54 100644 --- a/boltdb.go +++ b/provider/boltdb.go @@ -1,4 +1,6 @@ -package main +package provider + +import "github.com/emilevauge/traefik/types" type BoltDbProvider struct { Watch bool @@ -8,7 +10,7 @@ type BoltDbProvider struct { KvProvider *KvProvider } -func (provider *BoltDbProvider) Provide(configurationChan chan<- configMessage) error { +func (provider *BoltDbProvider) Provide(configurationChan chan<- types.ConfigMessage) error { provider.KvProvider = NewBoltDbProvider(provider) return provider.KvProvider.provide(configurationChan) } diff --git a/consul.go b/provider/consul.go similarity index 76% rename from consul.go rename to provider/consul.go index 9b1faa4f3..15b3de5a1 100644 --- a/consul.go +++ b/provider/consul.go @@ -1,4 +1,6 @@ -package main +package provider + +import "github.com/emilevauge/traefik/types" type ConsulProvider struct { Watch bool @@ -8,7 +10,7 @@ type ConsulProvider struct { KvProvider *KvProvider } -func (provider *ConsulProvider) Provide(configurationChan chan<- configMessage) error { +func (provider *ConsulProvider) Provide(configurationChan chan<- types.ConfigMessage) error { provider.KvProvider = NewConsulProvider(provider) return provider.KvProvider.provide(configurationChan) } diff --git a/docker.go b/provider/docker.go similarity index 94% rename from docker.go rename to provider/docker.go index 913391cd9..7cc8cbfb1 100644 --- a/docker.go +++ b/provider/docker.go @@ -1,18 +1,20 @@ -package main +package provider import ( "bytes" "errors" + "fmt" "strconv" "strings" "text/template" "time" - "fmt" "github.com/BurntSushi/toml" "github.com/BurntSushi/ty/fun" log "github.com/Sirupsen/logrus" "github.com/cenkalti/backoff" + "github.com/emilevauge/traefik/autogen" + "github.com/emilevauge/traefik/types" "github.com/fsouza/go-dockerclient" ) @@ -23,7 +25,7 @@ type DockerProvider struct { Domain string } -func (provider *DockerProvider) Provide(configurationChan chan<- configMessage) error { +func (provider *DockerProvider) Provide(configurationChan chan<- types.ConfigMessage) error { if dockerClient, err := docker.NewClient(provider.Endpoint); err != nil { log.Errorf("Failed to create a client for docker, error: %s", err) return err @@ -50,7 +52,7 @@ func (provider *DockerProvider) Provide(configurationChan chan<- configMessage) log.Debugf("Docker event receveived %+v", event) configuration := provider.loadDockerConfig(dockerClient) if configuration != nil { - configurationChan <- configMessage{"docker", configuration} + configurationChan <- types.ConfigMessage{"docker", configuration} } } } @@ -66,12 +68,12 @@ func (provider *DockerProvider) Provide(configurationChan chan<- configMessage) } configuration := provider.loadDockerConfig(dockerClient) - configurationChan <- configMessage{"docker", configuration} + configurationChan <- types.ConfigMessage{"docker", configuration} } return nil } -func (provider *DockerProvider) loadDockerConfig(dockerClient *docker.Client) *Configuration { +func (provider *DockerProvider) loadDockerConfig(dockerClient *docker.Client) *types.Configuration { var DockerFuncMap = template.FuncMap{ "getBackend": func(container docker.Container) string { if label, err := provider.getLabel(container, "traefik.backend"); err == nil { @@ -118,7 +120,7 @@ func (provider *DockerProvider) loadDockerConfig(dockerClient *docker.Client) *C return strings.Replace(s3, s1, s2, -1) }, } - configuration := new(Configuration) + configuration := new(types.Configuration) containerList, _ := dockerClient.ListContainers(docker.ListContainersOptions{}) containersInspected := []docker.Container{} frontends := map[string][]docker.Container{} @@ -174,7 +176,7 @@ func (provider *DockerProvider) loadDockerConfig(dockerClient *docker.Client) *C return nil } } else { - buf, err := Asset("templates/docker.tmpl") + buf, err := autogen.Asset("templates/docker.tmpl") if err != nil { log.Error("Error reading file", err) } diff --git a/etcd.go b/provider/etcd.go similarity index 60% rename from etcd.go rename to provider/etcd.go index 9c0bd68d7..bf6ab199c 100644 --- a/etcd.go +++ b/provider/etcd.go @@ -1,4 +1,6 @@ -package main +package provider + +import "github.com/emilevauge/traefik/types" type EtcdProvider struct { Watch bool @@ -8,7 +10,7 @@ type EtcdProvider struct { KvProvider *KvProvider } -func (provider *EtcdProvider) Provide(configurationChan chan<- configMessage) error { +func (provider *EtcdProvider) Provide(configurationChan chan<- types.ConfigMessage) error { provider.KvProvider = NewEtcdProvider(provider) return provider.KvProvider.provide(configurationChan) } diff --git a/file.go b/provider/file.go similarity index 75% rename from file.go rename to provider/file.go index 11a12cb0c..e4ac2575a 100644 --- a/file.go +++ b/provider/file.go @@ -1,4 +1,4 @@ -package main +package provider import ( "os" @@ -7,6 +7,7 @@ import ( "github.com/BurntSushi/toml" log "github.com/Sirupsen/logrus" + "github.com/emilevauge/traefik/types" "gopkg.in/fsnotify.v1" ) @@ -15,7 +16,7 @@ type FileProvider struct { Filename string } -func (provider *FileProvider) Provide(configurationChan chan<- configMessage) error { +func (provider *FileProvider) Provide(configurationChan chan<- types.ConfigMessage) error { watcher, err := fsnotify.NewWatcher() if err != nil { log.Error("Error creating file watcher", err) @@ -40,7 +41,7 @@ func (provider *FileProvider) Provide(configurationChan chan<- configMessage) er log.Debug("File event:", event) configuration := provider.LoadFileConfig(file.Name()) if configuration != nil { - configurationChan <- configMessage{"file", configuration} + configurationChan <- types.ConfigMessage{"file", configuration} } } case error := <-watcher.Errors: @@ -56,12 +57,12 @@ func (provider *FileProvider) Provide(configurationChan chan<- configMessage) er } configuration := provider.LoadFileConfig(file.Name()) - configurationChan <- configMessage{"file", configuration} + configurationChan <- types.ConfigMessage{"file", configuration} return nil } -func (provider *FileProvider) LoadFileConfig(filename string) *Configuration { - configuration := new(Configuration) +func (provider *FileProvider) LoadFileConfig(filename string) *types.Configuration { + configuration := new(types.Configuration) if _, err := toml.DecodeFile(filename, configuration); err != nil { log.Error("Error reading file:", err) return nil diff --git a/provider/file_test.go b/provider/file_test.go new file mode 100644 index 000000000..4f504f668 --- /dev/null +++ b/provider/file_test.go @@ -0,0 +1 @@ +package provider diff --git a/kv.go b/provider/kv.go similarity index 89% rename from kv.go rename to provider/kv.go index 44ab3401b..db8aa54db 100644 --- a/kv.go +++ b/provider/kv.go @@ -1,24 +1,26 @@ /* Copyright */ -package main +package provider import ( "bytes" + "errors" + "strings" + "text/template" + "time" + + "github.com/BurntSushi/toml" + "github.com/BurntSushi/ty/fun" + log "github.com/Sirupsen/logrus" "github.com/docker/libkv" + "github.com/docker/libkv/store" "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" + "github.com/emilevauge/traefik/autogen" + "github.com/emilevauge/traefik/types" ) type KvProvider struct { @@ -70,7 +72,7 @@ func NewBoltDbProvider(provider *BoltDbProvider) *KvProvider { return kvProvider } -func (provider *KvProvider) provide(configurationChan chan<- configMessage) error { +func (provider *KvProvider) provide(configurationChan chan<- types.ConfigMessage) error { switch provider.StoreType { case store.CONSUL: consul.Register() @@ -109,19 +111,19 @@ func (provider *KvProvider) provide(configurationChan chan<- configMessage) erro <-chanKeys configuration := provider.loadConfig() if configuration != nil { - configurationChan <- configMessage{string(provider.StoreType), configuration} + configurationChan <- types.ConfigMessage{string(provider.StoreType), configuration} } defer close(stopCh) } }() } configuration := provider.loadConfig() - configurationChan <- configMessage{string(provider.StoreType), configuration} + configurationChan <- types.ConfigMessage{string(provider.StoreType), configuration} return nil } -func (provider *KvProvider) loadConfig() *Configuration { - configuration := new(Configuration) +func (provider *KvProvider) loadConfig() *types.Configuration { + configuration := new(types.Configuration) templateObjects := struct { Prefix string }{ @@ -167,7 +169,7 @@ func (provider *KvProvider) loadConfig() *Configuration { return nil } } else { - buf, err := Asset("templates/kv.tmpl") + buf, err := autogen.Asset("templates/kv.tmpl") if err != nil { log.Error("Error reading file", err) } diff --git a/marathon.go b/provider/marathon.go similarity index 93% rename from marathon.go rename to provider/marathon.go index d58823e9e..969d4bad8 100644 --- a/marathon.go +++ b/provider/marathon.go @@ -1,15 +1,17 @@ -package main +package provider import ( "bytes" + "errors" "strconv" "strings" "text/template" - "errors" "github.com/BurntSushi/toml" "github.com/BurntSushi/ty/fun" log "github.com/Sirupsen/logrus" + "github.com/emilevauge/traefik/autogen" + "github.com/emilevauge/traefik/types" "github.com/gambol99/go-marathon" ) @@ -22,7 +24,7 @@ type MarathonProvider struct { NetworkInterface string } -func (provider *MarathonProvider) Provide(configurationChan chan<- configMessage) error { +func (provider *MarathonProvider) Provide(configurationChan chan<- types.ConfigMessage) error { config := marathon.NewDefaultConfig() config.URL = provider.Endpoint config.EventsInterface = provider.NetworkInterface @@ -43,7 +45,7 @@ func (provider *MarathonProvider) Provide(configurationChan chan<- configMessage log.Debug("Marathon event receveived", event) configuration := provider.loadMarathonConfig() if configuration != nil { - configurationChan <- configMessage{"marathon", configuration} + configurationChan <- types.ConfigMessage{"marathon", configuration} } } }() @@ -51,11 +53,11 @@ func (provider *MarathonProvider) Provide(configurationChan chan<- configMessage } configuration := provider.loadMarathonConfig() - configurationChan <- configMessage{"marathon", configuration} + configurationChan <- types.ConfigMessage{"marathon", configuration} return nil } -func (provider *MarathonProvider) loadMarathonConfig() *Configuration { +func (provider *MarathonProvider) loadMarathonConfig() *types.Configuration { var MarathonFuncMap = template.FuncMap{ "getPort": func(task marathon.Task) string { for _, port := range task.Ports { @@ -103,7 +105,7 @@ func (provider *MarathonProvider) loadMarathonConfig() *Configuration { "getFrontendValue": provider.GetFrontendValue, "getFrontendRule": provider.GetFrontendRule, } - configuration := new(Configuration) + configuration := new(types.Configuration) applications, err := provider.marathonClient.Applications(nil) if err != nil { @@ -187,7 +189,7 @@ func (provider *MarathonProvider) loadMarathonConfig() *Configuration { return nil } } else { - buf, err := Asset("templates/marathon.tmpl") + buf, err := autogen.Asset("templates/marathon.tmpl") if err != nil { log.Error("Error reading file", err) } diff --git a/provider/provider.go b/provider/provider.go new file mode 100644 index 000000000..4b7dc2702 --- /dev/null +++ b/provider/provider.go @@ -0,0 +1,7 @@ +package provider + +import "github.com/emilevauge/traefik/types" + +type Provider interface { + Provide(configurationChan chan<- types.ConfigMessage) error +} diff --git a/zk.go b/provider/zk.go similarity index 75% rename from zk.go rename to provider/zk.go index cdcde6fb9..87eaa40cf 100644 --- a/zk.go +++ b/provider/zk.go @@ -1,4 +1,6 @@ -package main +package provider + +import "github.com/emilevauge/traefik/types" type ZookepperProvider struct { Watch bool @@ -8,7 +10,7 @@ type ZookepperProvider struct { KvProvider *KvProvider } -func (provider *ZookepperProvider) Provide(configurationChan chan<- configMessage) error { +func (provider *ZookepperProvider) Provide(configurationChan chan<- types.ConfigMessage) error { provider.KvProvider = NewZkProvider(provider) return provider.KvProvider.provide(configurationChan) } diff --git a/script/binary b/script/binary index efffdf10b..2806238d4 100755 --- a/script/binary +++ b/script/binary @@ -1,7 +1,7 @@ #!/bin/bash set -e -if ! test -e gen.go; then +if ! test -e autogen/gen.go; then echo >&2 'error: generate must be run before binary' false fi diff --git a/script/crossbinary b/script/crossbinary index 556295532..3da66a209 100755 --- a/script/crossbinary +++ b/script/crossbinary @@ -1,8 +1,8 @@ #!/bin/bash set -e -if ! test -e gen.go; then - echo >&2 'error: generate must be run before binary' +if ! test -e autogen/gen.go; then + echo >&2 'error: generate must be run before crossbinary' false fi diff --git a/script/test-unit b/script/test-unit index c27e5c825..7e43fffe6 100755 --- a/script/test-unit +++ b/script/test-unit @@ -1,8 +1,8 @@ #!/bin/bash set -e -if ! test -e gen.go; then - echo >&2 'error: generate must be run before binary' +if ! test -e autogen/gen.go; then + echo >&2 'error: generate must be run before test-unit' false fi diff --git a/traefik.go b/traefik.go index 0900bd172..ac64421ba 100644 --- a/traefik.go +++ b/traefik.go @@ -2,21 +2,24 @@ package main import ( "crypto/tls" + "errors" fmtlog "log" "net/http" "net/url" "os" "os/signal" "reflect" + "runtime" "strings" "syscall" "time" - "errors" "github.com/BurntSushi/toml" log "github.com/Sirupsen/logrus" "github.com/codegangsta/negroni" "github.com/emilevauge/traefik/middlewares" + "github.com/emilevauge/traefik/provider" + "github.com/emilevauge/traefik/types" "github.com/gorilla/mux" "github.com/mailgun/manners" "github.com/mailgun/oxy/cbreaker" @@ -24,7 +27,6 @@ import ( "github.com/mailgun/oxy/roundrobin" "github.com/thoas/stats" "gopkg.in/alecthomas/kingpin.v2" - "runtime" ) var ( @@ -42,15 +44,15 @@ func main() { fmtlog.SetFlags(fmtlog.Lshortfile | fmtlog.LstdFlags) var srv *manners.GracefulServer var configurationRouter *mux.Router - var configurationChan = make(chan configMessage, 10) + var configurationChan = make(chan types.ConfigMessage, 10) defer close(configurationChan) - var configurationChanValidated = make(chan configMessage, 10) + var configurationChanValidated = make(chan types.ConfigMessage, 10) defer close(configurationChanValidated) var sigs = make(chan os.Signal, 1) defer close(sigs) var stopChan = make(chan bool) defer close(stopChan) - var providers = []Provider{} + var providers = []provider.Provider{} signal.Notify(sigs, syscall.SIGINT, syscall.SIGTERM) // load global configuration @@ -84,22 +86,22 @@ func main() { // listen new configurations from providers go func() { lastReceivedConfiguration := time.Unix(0, 0) - lastConfigs := make(map[string]*configMessage) + lastConfigs := make(map[string]*types.ConfigMessage) for { configMsg := <-configurationChan - log.Infof("Configuration receveived from provider %s: %#v", configMsg.providerName, configMsg.configuration) - lastConfigs[configMsg.providerName] = &configMsg + log.Infof("Configuration receveived from provider %s: %#v", configMsg.ProviderName, configMsg.Configuration) + lastConfigs[configMsg.ProviderName] = &configMsg if time.Now().After(lastReceivedConfiguration.Add(time.Duration(globalConfiguration.ProvidersThrottleDuration))) { - log.Infof("Last %s config received more than %s, OK", configMsg.providerName, globalConfiguration.ProvidersThrottleDuration) + log.Infof("Last %s config received more than %s, OK", configMsg.ProviderName, globalConfiguration.ProvidersThrottleDuration) // last config received more than n s ago configurationChanValidated <- configMsg } else { - log.Infof("Last %s config received less than %s, waiting...", configMsg.providerName, globalConfiguration.ProvidersThrottleDuration) + log.Infof("Last %s config received less than %s, waiting...", configMsg.ProviderName, globalConfiguration.ProvidersThrottleDuration) go func() { <-time.After(globalConfiguration.ProvidersThrottleDuration) if time.Now().After(lastReceivedConfiguration.Add(time.Duration(globalConfiguration.ProvidersThrottleDuration))) { - log.Infof("Waited for %s config, OK", configMsg.providerName) - configurationChanValidated <- *lastConfigs[configMsg.providerName] + log.Infof("Waited for %s config, OK", configMsg.ProviderName) + configurationChanValidated <- *lastConfigs[configMsg.ProviderName] } }() } @@ -109,9 +111,9 @@ func main() { go func() { for { configMsg := <-configurationChanValidated - if configMsg.configuration == nil { - log.Info("Skipping empty configuration") - } else if reflect.DeepEqual(currentConfigurations[configMsg.providerName], configMsg.configuration) { + if configMsg.Configuration == nil { + log.Info("Skipping empty Configuration") + } else if reflect.DeepEqual(currentConfigurations[configMsg.ProviderName], configMsg.Configuration) { log.Info("Skipping same configuration") } else { // Copy configurations to new map so we don't change current if LoadConfig fails @@ -119,7 +121,7 @@ func main() { for k, v := range currentConfigurations { newConfigurations[k] = v } - newConfigurations[configMsg.providerName] = configMsg.configuration + newConfigurations[configMsg.ProviderName] = configMsg.Configuration newConfigurationRouter, err := LoadConfig(newConfigurations, globalConfiguration) if err == nil { @@ -299,12 +301,12 @@ func LoadConfig(configurations configs, globalConfiguration *GlobalConfiguration if configuration.Backends[frontend.Backend] == nil { return nil, errors.New("Backend not found: " + frontend.Backend) } - lbMethod, err := NewLoadBalancerMethod(configuration.Backends[frontend.Backend].LoadBalancer) + lbMethod, err := types.NewLoadBalancerMethod(configuration.Backends[frontend.Backend].LoadBalancer) if err != nil { - configuration.Backends[frontend.Backend].LoadBalancer = &LoadBalancer{Method: "wrr"} + configuration.Backends[frontend.Backend].LoadBalancer = &types.LoadBalancer{Method: "wrr"} } switch lbMethod { - case drr: + case types.Drr: log.Infof("Creating load-balancer drr") rebalancer, _ := roundrobin.NewRebalancer(rr, roundrobin.RebalancerLogger(oxyLogger)) lb = rebalancer @@ -316,7 +318,7 @@ func LoadConfig(configurations configs, globalConfiguration *GlobalConfiguration log.Infof("Creating server %s %s", serverName, url.String()) rebalancer.UpsertServer(url, roundrobin.Weight(server.Weight)) } - case wrr: + case types.Wrr: log.Infof("Creating load-balancer wrr") lb = middlewares.NewWebsocketUpgrader(rr) for serverName, server := range configuration.Backends[frontend.Backend].Servers { diff --git a/types/types.go b/types/types.go new file mode 100644 index 000000000..41cdea240 --- /dev/null +++ b/types/types.go @@ -0,0 +1,81 @@ +package types + +import ( + "errors" + "strings" +) + +// Backend configuration +type Backend struct { + Servers map[string]Server `json:"servers,omitempty"` + CircuitBreaker *CircuitBreaker `json:"circuitBreaker,omitempty"` + LoadBalancer *LoadBalancer `json:"loadBalancer,omitempty"` +} + +// LoadBalancer configuration +type LoadBalancer struct { + Method string `json:"method,omitempty"` +} + +// CircuitBreaker configuration +type CircuitBreaker struct { + Expression string `json:"expression,omitempty"` +} + +// Server configuration +type Server struct { + URL string `json:"url,omitempty"` + Weight int `json:"weight,omitempty"` +} + +// Route configuration +type Route struct { + Rule string `json:"rule,omitempty"` + Value string `json:"value,omitempty"` +} + +// Frontend configuration +type Frontend struct { + Backend string `json:"backend,omitempty"` + Routes map[string]Route `json:"routes,omitempty"` + PassHostHeader bool `json:"passHostHeader,omitempty"` +} + +// Load Balancer Method +type LoadBalancerMethod uint8 + +const ( + // Wrr (default) = Weighted Round Robin + Wrr LoadBalancerMethod = iota + // Drr = Dynamic Round Robin + Drr +) + +var loadBalancerMethodNames = []string{ + "Wrr", + "Drr", +} + +func NewLoadBalancerMethod(loadBalancer *LoadBalancer) (LoadBalancerMethod, error) { + if loadBalancer != nil { + for i, name := range loadBalancerMethodNames { + if strings.EqualFold(name, loadBalancer.Method) { + return LoadBalancerMethod(i), nil + } + } + } + return Wrr, ErrInvalidLoadBalancerMethod +} + +var ErrInvalidLoadBalancerMethod = errors.New("Invalid method, using default") + +// Configuration of a provider +type Configuration struct { + Backends map[string]*Backend `json:"backends,omitempty"` + Frontends map[string]*Frontend `json:"frontends,omitempty"` +} + +type ConfigMessage struct { + ProviderName string + Configuration *Configuration +} diff --git a/web.go b/web.go index 1d4db0617..ecc5c08c0 100644 --- a/web.go +++ b/web.go @@ -8,6 +8,8 @@ import ( log "github.com/Sirupsen/logrus" "github.com/elazarl/go-bindata-assetfs" + "github.com/emilevauge/traefik/autogen" + "github.com/emilevauge/traefik/types" "github.com/gorilla/mux" "github.com/unrolled/render" ) @@ -23,7 +25,7 @@ var ( }) ) -func (provider *WebProvider) Provide(configurationChan chan<- configMessage) error { +func (provider *WebProvider) Provide(configurationChan chan<- types.ConfigMessage) error { systemRouter := mux.NewRouter() // health route @@ -41,11 +43,11 @@ func (provider *WebProvider) Provide(configurationChan chan<- configMessage) err return } - configuration := new(Configuration) + configuration := new(types.Configuration) body, _ := ioutil.ReadAll(request.Body) err := json.Unmarshal(body, configuration) if err == nil { - configurationChan <- configMessage{"web", configuration} + configurationChan <- types.ConfigMessage{"web", configuration} getConfigHandler(response, request) } else { log.Errorf("Error parsing configuration %+v", err) @@ -65,7 +67,7 @@ func (provider *WebProvider) Provide(configurationChan chan<- configMessage) err systemRouter.Methods("GET").Path("/").HandlerFunc(func(response http.ResponseWriter, request *http.Request) { http.Redirect(response, request, "/dashboard/", 302) }) - systemRouter.Methods("GET").PathPrefix("/dashboard/").Handler(http.StripPrefix("/dashboard/", http.FileServer(&assetfs.AssetFS{Asset: Asset, AssetDir: AssetDir, Prefix: "static"}))) + systemRouter.Methods("GET").PathPrefix("/dashboard/").Handler(http.StripPrefix("/dashboard/", http.FileServer(&assetfs.AssetFS{Asset: autogen.Asset, AssetDir: autogen.AssetDir, Prefix: "static"}))) go func() { if len(provider.CertFile) > 0 && len(provider.KeyFile) > 0 {