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 <vincent@sbr.pm>
This commit is contained in:
parent
6e1a0554c0
commit
de0a57ec76
21 changed files with 195 additions and 167 deletions
0
autogen/.placeholder
Normal file
0
autogen/.placeholder
Normal file
|
@ -1,9 +1,10 @@
|
||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
|
||||||
"strings"
|
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/emilevauge/traefik/provider"
|
||||||
|
"github.com/emilevauge/traefik/types"
|
||||||
)
|
)
|
||||||
|
|
||||||
type GlobalConfiguration struct {
|
type GlobalConfiguration struct {
|
||||||
|
@ -14,14 +15,14 @@ type GlobalConfiguration struct {
|
||||||
CertFile, KeyFile string
|
CertFile, KeyFile string
|
||||||
LogLevel string
|
LogLevel string
|
||||||
ProvidersThrottleDuration time.Duration
|
ProvidersThrottleDuration time.Duration
|
||||||
Docker *DockerProvider
|
Docker *provider.DockerProvider
|
||||||
File *FileProvider
|
File *provider.FileProvider
|
||||||
Web *WebProvider
|
Web *WebProvider
|
||||||
Marathon *MarathonProvider
|
Marathon *provider.MarathonProvider
|
||||||
Consul *ConsulProvider
|
Consul *provider.ConsulProvider
|
||||||
Etcd *EtcdProvider
|
Etcd *provider.EtcdProvider
|
||||||
Zookeeper *ZookepperProvider
|
Zookeeper *provider.ZookepperProvider
|
||||||
Boltdb *BoltDbProvider
|
Boltdb *provider.BoltDbProvider
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewGlobalConfiguration() *GlobalConfiguration {
|
func NewGlobalConfiguration() *GlobalConfiguration {
|
||||||
|
@ -35,79 +36,4 @@ func NewGlobalConfiguration() *GlobalConfiguration {
|
||||||
return globalConfiguration
|
return globalConfiguration
|
||||||
}
|
}
|
||||||
|
|
||||||
// Backend configuration
|
type configs map[string]*types.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
|
|
||||||
|
|
|
@ -1 +0,0 @@
|
||||||
package main
|
|
|
@ -3,7 +3,7 @@ Copyright
|
||||||
*/
|
*/
|
||||||
|
|
||||||
//go:generate rm -vf gen.go
|
//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 mkdir -p vendor/github.com/docker/docker/autogen/dockerversion
|
||||||
//go:generate cp script/dockerversion vendor/github.com/docker/docker/autogen/dockerversion/dockerversion.go
|
//go:generate cp script/dockerversion vendor/github.com/docker/docker/autogen/dockerversion/dockerversion.go
|
||||||
|
|
|
@ -1,5 +0,0 @@
|
||||||
package main
|
|
||||||
|
|
||||||
type Provider interface {
|
|
||||||
Provide(configurationChan chan<- configMessage) error
|
|
||||||
}
|
|
|
@ -1,4 +1,6 @@
|
||||||
package main
|
package provider
|
||||||
|
|
||||||
|
import "github.com/emilevauge/traefik/types"
|
||||||
|
|
||||||
type BoltDbProvider struct {
|
type BoltDbProvider struct {
|
||||||
Watch bool
|
Watch bool
|
||||||
|
@ -8,7 +10,7 @@ type BoltDbProvider struct {
|
||||||
KvProvider *KvProvider
|
KvProvider *KvProvider
|
||||||
}
|
}
|
||||||
|
|
||||||
func (provider *BoltDbProvider) Provide(configurationChan chan<- configMessage) error {
|
func (provider *BoltDbProvider) Provide(configurationChan chan<- types.ConfigMessage) error {
|
||||||
provider.KvProvider = NewBoltDbProvider(provider)
|
provider.KvProvider = NewBoltDbProvider(provider)
|
||||||
return provider.KvProvider.provide(configurationChan)
|
return provider.KvProvider.provide(configurationChan)
|
||||||
}
|
}
|
|
@ -1,4 +1,6 @@
|
||||||
package main
|
package provider
|
||||||
|
|
||||||
|
import "github.com/emilevauge/traefik/types"
|
||||||
|
|
||||||
type ConsulProvider struct {
|
type ConsulProvider struct {
|
||||||
Watch bool
|
Watch bool
|
||||||
|
@ -8,7 +10,7 @@ type ConsulProvider struct {
|
||||||
KvProvider *KvProvider
|
KvProvider *KvProvider
|
||||||
}
|
}
|
||||||
|
|
||||||
func (provider *ConsulProvider) Provide(configurationChan chan<- configMessage) error {
|
func (provider *ConsulProvider) Provide(configurationChan chan<- types.ConfigMessage) error {
|
||||||
provider.KvProvider = NewConsulProvider(provider)
|
provider.KvProvider = NewConsulProvider(provider)
|
||||||
return provider.KvProvider.provide(configurationChan)
|
return provider.KvProvider.provide(configurationChan)
|
||||||
}
|
}
|
|
@ -1,18 +1,20 @@
|
||||||
package main
|
package provider
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"errors"
|
"errors"
|
||||||
|
"fmt"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"text/template"
|
"text/template"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"fmt"
|
|
||||||
"github.com/BurntSushi/toml"
|
"github.com/BurntSushi/toml"
|
||||||
"github.com/BurntSushi/ty/fun"
|
"github.com/BurntSushi/ty/fun"
|
||||||
log "github.com/Sirupsen/logrus"
|
log "github.com/Sirupsen/logrus"
|
||||||
"github.com/cenkalti/backoff"
|
"github.com/cenkalti/backoff"
|
||||||
|
"github.com/emilevauge/traefik/autogen"
|
||||||
|
"github.com/emilevauge/traefik/types"
|
||||||
"github.com/fsouza/go-dockerclient"
|
"github.com/fsouza/go-dockerclient"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -23,7 +25,7 @@ type DockerProvider struct {
|
||||||
Domain string
|
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 {
|
if dockerClient, err := docker.NewClient(provider.Endpoint); err != nil {
|
||||||
log.Errorf("Failed to create a client for docker, error: %s", err)
|
log.Errorf("Failed to create a client for docker, error: %s", err)
|
||||||
return err
|
return err
|
||||||
|
@ -50,7 +52,7 @@ func (provider *DockerProvider) Provide(configurationChan chan<- configMessage)
|
||||||
log.Debugf("Docker event receveived %+v", event)
|
log.Debugf("Docker event receveived %+v", event)
|
||||||
configuration := provider.loadDockerConfig(dockerClient)
|
configuration := provider.loadDockerConfig(dockerClient)
|
||||||
if configuration != nil {
|
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)
|
configuration := provider.loadDockerConfig(dockerClient)
|
||||||
configurationChan <- configMessage{"docker", configuration}
|
configurationChan <- types.ConfigMessage{"docker", configuration}
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (provider *DockerProvider) loadDockerConfig(dockerClient *docker.Client) *Configuration {
|
func (provider *DockerProvider) loadDockerConfig(dockerClient *docker.Client) *types.Configuration {
|
||||||
var DockerFuncMap = template.FuncMap{
|
var DockerFuncMap = template.FuncMap{
|
||||||
"getBackend": func(container docker.Container) string {
|
"getBackend": func(container docker.Container) string {
|
||||||
if label, err := provider.getLabel(container, "traefik.backend"); err == nil {
|
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)
|
return strings.Replace(s3, s1, s2, -1)
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
configuration := new(Configuration)
|
configuration := new(types.Configuration)
|
||||||
containerList, _ := dockerClient.ListContainers(docker.ListContainersOptions{})
|
containerList, _ := dockerClient.ListContainers(docker.ListContainersOptions{})
|
||||||
containersInspected := []docker.Container{}
|
containersInspected := []docker.Container{}
|
||||||
frontends := map[string][]docker.Container{}
|
frontends := map[string][]docker.Container{}
|
||||||
|
@ -174,7 +176,7 @@ func (provider *DockerProvider) loadDockerConfig(dockerClient *docker.Client) *C
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
buf, err := Asset("templates/docker.tmpl")
|
buf, err := autogen.Asset("templates/docker.tmpl")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error("Error reading file", err)
|
log.Error("Error reading file", err)
|
||||||
}
|
}
|
|
@ -1,4 +1,6 @@
|
||||||
package main
|
package provider
|
||||||
|
|
||||||
|
import "github.com/emilevauge/traefik/types"
|
||||||
|
|
||||||
type EtcdProvider struct {
|
type EtcdProvider struct {
|
||||||
Watch bool
|
Watch bool
|
||||||
|
@ -8,7 +10,7 @@ type EtcdProvider struct {
|
||||||
KvProvider *KvProvider
|
KvProvider *KvProvider
|
||||||
}
|
}
|
||||||
|
|
||||||
func (provider *EtcdProvider) Provide(configurationChan chan<- configMessage) error {
|
func (provider *EtcdProvider) Provide(configurationChan chan<- types.ConfigMessage) error {
|
||||||
provider.KvProvider = NewEtcdProvider(provider)
|
provider.KvProvider = NewEtcdProvider(provider)
|
||||||
return provider.KvProvider.provide(configurationChan)
|
return provider.KvProvider.provide(configurationChan)
|
||||||
}
|
}
|
|
@ -1,4 +1,4 @@
|
||||||
package main
|
package provider
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"os"
|
"os"
|
||||||
|
@ -7,6 +7,7 @@ import (
|
||||||
|
|
||||||
"github.com/BurntSushi/toml"
|
"github.com/BurntSushi/toml"
|
||||||
log "github.com/Sirupsen/logrus"
|
log "github.com/Sirupsen/logrus"
|
||||||
|
"github.com/emilevauge/traefik/types"
|
||||||
"gopkg.in/fsnotify.v1"
|
"gopkg.in/fsnotify.v1"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -15,7 +16,7 @@ type FileProvider struct {
|
||||||
Filename string
|
Filename string
|
||||||
}
|
}
|
||||||
|
|
||||||
func (provider *FileProvider) Provide(configurationChan chan<- configMessage) error {
|
func (provider *FileProvider) Provide(configurationChan chan<- types.ConfigMessage) error {
|
||||||
watcher, err := fsnotify.NewWatcher()
|
watcher, err := fsnotify.NewWatcher()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error("Error creating file watcher", err)
|
log.Error("Error creating file watcher", err)
|
||||||
|
@ -40,7 +41,7 @@ func (provider *FileProvider) Provide(configurationChan chan<- configMessage) er
|
||||||
log.Debug("File event:", event)
|
log.Debug("File event:", event)
|
||||||
configuration := provider.LoadFileConfig(file.Name())
|
configuration := provider.LoadFileConfig(file.Name())
|
||||||
if configuration != nil {
|
if configuration != nil {
|
||||||
configurationChan <- configMessage{"file", configuration}
|
configurationChan <- types.ConfigMessage{"file", configuration}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
case error := <-watcher.Errors:
|
case error := <-watcher.Errors:
|
||||||
|
@ -56,12 +57,12 @@ func (provider *FileProvider) Provide(configurationChan chan<- configMessage) er
|
||||||
}
|
}
|
||||||
|
|
||||||
configuration := provider.LoadFileConfig(file.Name())
|
configuration := provider.LoadFileConfig(file.Name())
|
||||||
configurationChan <- configMessage{"file", configuration}
|
configurationChan <- types.ConfigMessage{"file", configuration}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (provider *FileProvider) LoadFileConfig(filename string) *Configuration {
|
func (provider *FileProvider) LoadFileConfig(filename string) *types.Configuration {
|
||||||
configuration := new(Configuration)
|
configuration := new(types.Configuration)
|
||||||
if _, err := toml.DecodeFile(filename, configuration); err != nil {
|
if _, err := toml.DecodeFile(filename, configuration); err != nil {
|
||||||
log.Error("Error reading file:", err)
|
log.Error("Error reading file:", err)
|
||||||
return nil
|
return nil
|
1
provider/file_test.go
Normal file
1
provider/file_test.go
Normal file
|
@ -0,0 +1 @@
|
||||||
|
package provider
|
|
@ -1,24 +1,26 @@
|
||||||
/*
|
/*
|
||||||
Copyright
|
Copyright
|
||||||
*/
|
*/
|
||||||
package main
|
package provider
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"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"
|
||||||
|
"github.com/docker/libkv/store"
|
||||||
"github.com/docker/libkv/store/boltdb"
|
"github.com/docker/libkv/store/boltdb"
|
||||||
"github.com/docker/libkv/store/consul"
|
"github.com/docker/libkv/store/consul"
|
||||||
"github.com/docker/libkv/store/etcd"
|
"github.com/docker/libkv/store/etcd"
|
||||||
"github.com/docker/libkv/store/zookeeper"
|
"github.com/docker/libkv/store/zookeeper"
|
||||||
"strings"
|
"github.com/emilevauge/traefik/autogen"
|
||||||
"text/template"
|
"github.com/emilevauge/traefik/types"
|
||||||
|
|
||||||
"errors"
|
|
||||||
"github.com/BurntSushi/toml"
|
|
||||||
"github.com/BurntSushi/ty/fun"
|
|
||||||
log "github.com/Sirupsen/logrus"
|
|
||||||
"github.com/docker/libkv/store"
|
|
||||||
"time"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type KvProvider struct {
|
type KvProvider struct {
|
||||||
|
@ -70,7 +72,7 @@ func NewBoltDbProvider(provider *BoltDbProvider) *KvProvider {
|
||||||
return kvProvider
|
return kvProvider
|
||||||
}
|
}
|
||||||
|
|
||||||
func (provider *KvProvider) provide(configurationChan chan<- configMessage) error {
|
func (provider *KvProvider) provide(configurationChan chan<- types.ConfigMessage) error {
|
||||||
switch provider.StoreType {
|
switch provider.StoreType {
|
||||||
case store.CONSUL:
|
case store.CONSUL:
|
||||||
consul.Register()
|
consul.Register()
|
||||||
|
@ -109,19 +111,19 @@ func (provider *KvProvider) provide(configurationChan chan<- configMessage) erro
|
||||||
<-chanKeys
|
<-chanKeys
|
||||||
configuration := provider.loadConfig()
|
configuration := provider.loadConfig()
|
||||||
if configuration != nil {
|
if configuration != nil {
|
||||||
configurationChan <- configMessage{string(provider.StoreType), configuration}
|
configurationChan <- types.ConfigMessage{string(provider.StoreType), configuration}
|
||||||
}
|
}
|
||||||
defer close(stopCh)
|
defer close(stopCh)
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
}
|
}
|
||||||
configuration := provider.loadConfig()
|
configuration := provider.loadConfig()
|
||||||
configurationChan <- configMessage{string(provider.StoreType), configuration}
|
configurationChan <- types.ConfigMessage{string(provider.StoreType), configuration}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (provider *KvProvider) loadConfig() *Configuration {
|
func (provider *KvProvider) loadConfig() *types.Configuration {
|
||||||
configuration := new(Configuration)
|
configuration := new(types.Configuration)
|
||||||
templateObjects := struct {
|
templateObjects := struct {
|
||||||
Prefix string
|
Prefix string
|
||||||
}{
|
}{
|
||||||
|
@ -167,7 +169,7 @@ func (provider *KvProvider) loadConfig() *Configuration {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
buf, err := Asset("templates/kv.tmpl")
|
buf, err := autogen.Asset("templates/kv.tmpl")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error("Error reading file", err)
|
log.Error("Error reading file", err)
|
||||||
}
|
}
|
|
@ -1,15 +1,17 @@
|
||||||
package main
|
package provider
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
|
"errors"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"text/template"
|
"text/template"
|
||||||
|
|
||||||
"errors"
|
|
||||||
"github.com/BurntSushi/toml"
|
"github.com/BurntSushi/toml"
|
||||||
"github.com/BurntSushi/ty/fun"
|
"github.com/BurntSushi/ty/fun"
|
||||||
log "github.com/Sirupsen/logrus"
|
log "github.com/Sirupsen/logrus"
|
||||||
|
"github.com/emilevauge/traefik/autogen"
|
||||||
|
"github.com/emilevauge/traefik/types"
|
||||||
"github.com/gambol99/go-marathon"
|
"github.com/gambol99/go-marathon"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -22,7 +24,7 @@ type MarathonProvider struct {
|
||||||
NetworkInterface string
|
NetworkInterface string
|
||||||
}
|
}
|
||||||
|
|
||||||
func (provider *MarathonProvider) Provide(configurationChan chan<- configMessage) error {
|
func (provider *MarathonProvider) Provide(configurationChan chan<- types.ConfigMessage) error {
|
||||||
config := marathon.NewDefaultConfig()
|
config := marathon.NewDefaultConfig()
|
||||||
config.URL = provider.Endpoint
|
config.URL = provider.Endpoint
|
||||||
config.EventsInterface = provider.NetworkInterface
|
config.EventsInterface = provider.NetworkInterface
|
||||||
|
@ -43,7 +45,7 @@ func (provider *MarathonProvider) Provide(configurationChan chan<- configMessage
|
||||||
log.Debug("Marathon event receveived", event)
|
log.Debug("Marathon event receveived", event)
|
||||||
configuration := provider.loadMarathonConfig()
|
configuration := provider.loadMarathonConfig()
|
||||||
if configuration != nil {
|
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()
|
configuration := provider.loadMarathonConfig()
|
||||||
configurationChan <- configMessage{"marathon", configuration}
|
configurationChan <- types.ConfigMessage{"marathon", configuration}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (provider *MarathonProvider) loadMarathonConfig() *Configuration {
|
func (provider *MarathonProvider) loadMarathonConfig() *types.Configuration {
|
||||||
var MarathonFuncMap = template.FuncMap{
|
var MarathonFuncMap = template.FuncMap{
|
||||||
"getPort": func(task marathon.Task) string {
|
"getPort": func(task marathon.Task) string {
|
||||||
for _, port := range task.Ports {
|
for _, port := range task.Ports {
|
||||||
|
@ -103,7 +105,7 @@ func (provider *MarathonProvider) loadMarathonConfig() *Configuration {
|
||||||
"getFrontendValue": provider.GetFrontendValue,
|
"getFrontendValue": provider.GetFrontendValue,
|
||||||
"getFrontendRule": provider.GetFrontendRule,
|
"getFrontendRule": provider.GetFrontendRule,
|
||||||
}
|
}
|
||||||
configuration := new(Configuration)
|
configuration := new(types.Configuration)
|
||||||
|
|
||||||
applications, err := provider.marathonClient.Applications(nil)
|
applications, err := provider.marathonClient.Applications(nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -187,7 +189,7 @@ func (provider *MarathonProvider) loadMarathonConfig() *Configuration {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
buf, err := Asset("templates/marathon.tmpl")
|
buf, err := autogen.Asset("templates/marathon.tmpl")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error("Error reading file", err)
|
log.Error("Error reading file", err)
|
||||||
}
|
}
|
7
provider/provider.go
Normal file
7
provider/provider.go
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
package provider
|
||||||
|
|
||||||
|
import "github.com/emilevauge/traefik/types"
|
||||||
|
|
||||||
|
type Provider interface {
|
||||||
|
Provide(configurationChan chan<- types.ConfigMessage) error
|
||||||
|
}
|
|
@ -1,4 +1,6 @@
|
||||||
package main
|
package provider
|
||||||
|
|
||||||
|
import "github.com/emilevauge/traefik/types"
|
||||||
|
|
||||||
type ZookepperProvider struct {
|
type ZookepperProvider struct {
|
||||||
Watch bool
|
Watch bool
|
||||||
|
@ -8,7 +10,7 @@ type ZookepperProvider struct {
|
||||||
KvProvider *KvProvider
|
KvProvider *KvProvider
|
||||||
}
|
}
|
||||||
|
|
||||||
func (provider *ZookepperProvider) Provide(configurationChan chan<- configMessage) error {
|
func (provider *ZookepperProvider) Provide(configurationChan chan<- types.ConfigMessage) error {
|
||||||
provider.KvProvider = NewZkProvider(provider)
|
provider.KvProvider = NewZkProvider(provider)
|
||||||
return provider.KvProvider.provide(configurationChan)
|
return provider.KvProvider.provide(configurationChan)
|
||||||
}
|
}
|
|
@ -1,7 +1,7 @@
|
||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
set -e
|
set -e
|
||||||
|
|
||||||
if ! test -e gen.go; then
|
if ! test -e autogen/gen.go; then
|
||||||
echo >&2 'error: generate must be run before binary'
|
echo >&2 'error: generate must be run before binary'
|
||||||
false
|
false
|
||||||
fi
|
fi
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
set -e
|
set -e
|
||||||
|
|
||||||
if ! test -e gen.go; then
|
if ! test -e autogen/gen.go; then
|
||||||
echo >&2 'error: generate must be run before binary'
|
echo >&2 'error: generate must be run before crossbinary'
|
||||||
false
|
false
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
set -e
|
set -e
|
||||||
|
|
||||||
if ! test -e gen.go; then
|
if ! test -e autogen/gen.go; then
|
||||||
echo >&2 'error: generate must be run before binary'
|
echo >&2 'error: generate must be run before test-unit'
|
||||||
false
|
false
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
|
42
traefik.go
42
traefik.go
|
@ -2,21 +2,24 @@ package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"crypto/tls"
|
"crypto/tls"
|
||||||
|
"errors"
|
||||||
fmtlog "log"
|
fmtlog "log"
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/url"
|
"net/url"
|
||||||
"os"
|
"os"
|
||||||
"os/signal"
|
"os/signal"
|
||||||
"reflect"
|
"reflect"
|
||||||
|
"runtime"
|
||||||
"strings"
|
"strings"
|
||||||
"syscall"
|
"syscall"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"errors"
|
|
||||||
"github.com/BurntSushi/toml"
|
"github.com/BurntSushi/toml"
|
||||||
log "github.com/Sirupsen/logrus"
|
log "github.com/Sirupsen/logrus"
|
||||||
"github.com/codegangsta/negroni"
|
"github.com/codegangsta/negroni"
|
||||||
"github.com/emilevauge/traefik/middlewares"
|
"github.com/emilevauge/traefik/middlewares"
|
||||||
|
"github.com/emilevauge/traefik/provider"
|
||||||
|
"github.com/emilevauge/traefik/types"
|
||||||
"github.com/gorilla/mux"
|
"github.com/gorilla/mux"
|
||||||
"github.com/mailgun/manners"
|
"github.com/mailgun/manners"
|
||||||
"github.com/mailgun/oxy/cbreaker"
|
"github.com/mailgun/oxy/cbreaker"
|
||||||
|
@ -24,7 +27,6 @@ import (
|
||||||
"github.com/mailgun/oxy/roundrobin"
|
"github.com/mailgun/oxy/roundrobin"
|
||||||
"github.com/thoas/stats"
|
"github.com/thoas/stats"
|
||||||
"gopkg.in/alecthomas/kingpin.v2"
|
"gopkg.in/alecthomas/kingpin.v2"
|
||||||
"runtime"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
|
@ -42,15 +44,15 @@ func main() {
|
||||||
fmtlog.SetFlags(fmtlog.Lshortfile | fmtlog.LstdFlags)
|
fmtlog.SetFlags(fmtlog.Lshortfile | fmtlog.LstdFlags)
|
||||||
var srv *manners.GracefulServer
|
var srv *manners.GracefulServer
|
||||||
var configurationRouter *mux.Router
|
var configurationRouter *mux.Router
|
||||||
var configurationChan = make(chan configMessage, 10)
|
var configurationChan = make(chan types.ConfigMessage, 10)
|
||||||
defer close(configurationChan)
|
defer close(configurationChan)
|
||||||
var configurationChanValidated = make(chan configMessage, 10)
|
var configurationChanValidated = make(chan types.ConfigMessage, 10)
|
||||||
defer close(configurationChanValidated)
|
defer close(configurationChanValidated)
|
||||||
var sigs = make(chan os.Signal, 1)
|
var sigs = make(chan os.Signal, 1)
|
||||||
defer close(sigs)
|
defer close(sigs)
|
||||||
var stopChan = make(chan bool)
|
var stopChan = make(chan bool)
|
||||||
defer close(stopChan)
|
defer close(stopChan)
|
||||||
var providers = []Provider{}
|
var providers = []provider.Provider{}
|
||||||
signal.Notify(sigs, syscall.SIGINT, syscall.SIGTERM)
|
signal.Notify(sigs, syscall.SIGINT, syscall.SIGTERM)
|
||||||
|
|
||||||
// load global configuration
|
// load global configuration
|
||||||
|
@ -84,22 +86,22 @@ func main() {
|
||||||
// listen new configurations from providers
|
// listen new configurations from providers
|
||||||
go func() {
|
go func() {
|
||||||
lastReceivedConfiguration := time.Unix(0, 0)
|
lastReceivedConfiguration := time.Unix(0, 0)
|
||||||
lastConfigs := make(map[string]*configMessage)
|
lastConfigs := make(map[string]*types.ConfigMessage)
|
||||||
for {
|
for {
|
||||||
configMsg := <-configurationChan
|
configMsg := <-configurationChan
|
||||||
log.Infof("Configuration receveived from provider %s: %#v", configMsg.providerName, configMsg.configuration)
|
log.Infof("Configuration receveived from provider %s: %#v", configMsg.ProviderName, configMsg.Configuration)
|
||||||
lastConfigs[configMsg.providerName] = &configMsg
|
lastConfigs[configMsg.ProviderName] = &configMsg
|
||||||
if time.Now().After(lastReceivedConfiguration.Add(time.Duration(globalConfiguration.ProvidersThrottleDuration))) {
|
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
|
// last config received more than n s ago
|
||||||
configurationChanValidated <- configMsg
|
configurationChanValidated <- configMsg
|
||||||
} else {
|
} 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() {
|
go func() {
|
||||||
<-time.After(globalConfiguration.ProvidersThrottleDuration)
|
<-time.After(globalConfiguration.ProvidersThrottleDuration)
|
||||||
if time.Now().After(lastReceivedConfiguration.Add(time.Duration(globalConfiguration.ProvidersThrottleDuration))) {
|
if time.Now().After(lastReceivedConfiguration.Add(time.Duration(globalConfiguration.ProvidersThrottleDuration))) {
|
||||||
log.Infof("Waited for %s config, OK", configMsg.providerName)
|
log.Infof("Waited for %s config, OK", configMsg.ProviderName)
|
||||||
configurationChanValidated <- *lastConfigs[configMsg.providerName]
|
configurationChanValidated <- *lastConfigs[configMsg.ProviderName]
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
}
|
}
|
||||||
|
@ -109,9 +111,9 @@ func main() {
|
||||||
go func() {
|
go func() {
|
||||||
for {
|
for {
|
||||||
configMsg := <-configurationChanValidated
|
configMsg := <-configurationChanValidated
|
||||||
if configMsg.configuration == nil {
|
if configMsg.Configuration == nil {
|
||||||
log.Info("Skipping empty configuration")
|
log.Info("Skipping empty Configuration")
|
||||||
} else if reflect.DeepEqual(currentConfigurations[configMsg.providerName], configMsg.configuration) {
|
} else if reflect.DeepEqual(currentConfigurations[configMsg.ProviderName], configMsg.Configuration) {
|
||||||
log.Info("Skipping same configuration")
|
log.Info("Skipping same configuration")
|
||||||
} else {
|
} else {
|
||||||
// Copy configurations to new map so we don't change current if LoadConfig fails
|
// 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 {
|
for k, v := range currentConfigurations {
|
||||||
newConfigurations[k] = v
|
newConfigurations[k] = v
|
||||||
}
|
}
|
||||||
newConfigurations[configMsg.providerName] = configMsg.configuration
|
newConfigurations[configMsg.ProviderName] = configMsg.Configuration
|
||||||
|
|
||||||
newConfigurationRouter, err := LoadConfig(newConfigurations, globalConfiguration)
|
newConfigurationRouter, err := LoadConfig(newConfigurations, globalConfiguration)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
|
@ -299,12 +301,12 @@ func LoadConfig(configurations configs, globalConfiguration *GlobalConfiguration
|
||||||
if configuration.Backends[frontend.Backend] == nil {
|
if configuration.Backends[frontend.Backend] == nil {
|
||||||
return nil, errors.New("Backend not found: " + frontend.Backend)
|
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 {
|
if err != nil {
|
||||||
configuration.Backends[frontend.Backend].LoadBalancer = &LoadBalancer{Method: "wrr"}
|
configuration.Backends[frontend.Backend].LoadBalancer = &types.LoadBalancer{Method: "wrr"}
|
||||||
}
|
}
|
||||||
switch lbMethod {
|
switch lbMethod {
|
||||||
case drr:
|
case types.Drr:
|
||||||
log.Infof("Creating load-balancer drr")
|
log.Infof("Creating load-balancer drr")
|
||||||
rebalancer, _ := roundrobin.NewRebalancer(rr, roundrobin.RebalancerLogger(oxyLogger))
|
rebalancer, _ := roundrobin.NewRebalancer(rr, roundrobin.RebalancerLogger(oxyLogger))
|
||||||
lb = rebalancer
|
lb = rebalancer
|
||||||
|
@ -316,7 +318,7 @@ func LoadConfig(configurations configs, globalConfiguration *GlobalConfiguration
|
||||||
log.Infof("Creating server %s %s", serverName, url.String())
|
log.Infof("Creating server %s %s", serverName, url.String())
|
||||||
rebalancer.UpsertServer(url, roundrobin.Weight(server.Weight))
|
rebalancer.UpsertServer(url, roundrobin.Weight(server.Weight))
|
||||||
}
|
}
|
||||||
case wrr:
|
case types.Wrr:
|
||||||
log.Infof("Creating load-balancer wrr")
|
log.Infof("Creating load-balancer wrr")
|
||||||
lb = middlewares.NewWebsocketUpgrader(rr)
|
lb = middlewares.NewWebsocketUpgrader(rr)
|
||||||
for serverName, server := range configuration.Backends[frontend.Backend].Servers {
|
for serverName, server := range configuration.Backends[frontend.Backend].Servers {
|
||||||
|
|
81
types/types.go
Normal file
81
types/types.go
Normal file
|
@ -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
|
||||||
|
}
|
10
web.go
10
web.go
|
@ -8,6 +8,8 @@ import (
|
||||||
|
|
||||||
log "github.com/Sirupsen/logrus"
|
log "github.com/Sirupsen/logrus"
|
||||||
"github.com/elazarl/go-bindata-assetfs"
|
"github.com/elazarl/go-bindata-assetfs"
|
||||||
|
"github.com/emilevauge/traefik/autogen"
|
||||||
|
"github.com/emilevauge/traefik/types"
|
||||||
"github.com/gorilla/mux"
|
"github.com/gorilla/mux"
|
||||||
"github.com/unrolled/render"
|
"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()
|
systemRouter := mux.NewRouter()
|
||||||
|
|
||||||
// health route
|
// health route
|
||||||
|
@ -41,11 +43,11 @@ func (provider *WebProvider) Provide(configurationChan chan<- configMessage) err
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
configuration := new(Configuration)
|
configuration := new(types.Configuration)
|
||||||
body, _ := ioutil.ReadAll(request.Body)
|
body, _ := ioutil.ReadAll(request.Body)
|
||||||
err := json.Unmarshal(body, configuration)
|
err := json.Unmarshal(body, configuration)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
configurationChan <- configMessage{"web", configuration}
|
configurationChan <- types.ConfigMessage{"web", configuration}
|
||||||
getConfigHandler(response, request)
|
getConfigHandler(response, request)
|
||||||
} else {
|
} else {
|
||||||
log.Errorf("Error parsing configuration %+v", err)
|
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) {
|
systemRouter.Methods("GET").Path("/").HandlerFunc(func(response http.ResponseWriter, request *http.Request) {
|
||||||
http.Redirect(response, request, "/dashboard/", 302)
|
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() {
|
go func() {
|
||||||
if len(provider.CertFile) > 0 && len(provider.KeyFile) > 0 {
|
if len(provider.CertFile) > 0 && len(provider.KeyFile) > 0 {
|
||||||
|
|
Loading…
Reference in a new issue