Flaeg integration

This commit is contained in:
Martin 2016-05-03 16:52:14 +02:00
parent 7804787e9e
commit fe0a8f3363
16 changed files with 361 additions and 401 deletions

230
cmd.go
View file

@ -1,230 +0,0 @@
/*
Copyright
*/
package main
import (
"encoding/json"
fmtlog "log"
"os"
"strings"
"time"
"net/http"
log "github.com/Sirupsen/logrus"
"github.com/containous/traefik/middlewares"
"github.com/containous/traefik/provider"
"github.com/spf13/cobra"
"github.com/spf13/viper"
)
var traefikCmd = &cobra.Command{
Use: "traefik",
Short: "traefik, a modern reverse proxy",
Long: `traefik is a modern HTTP reverse proxy and load balancer made to deploy microservices with ease.
Complete documentation is available at http://traefik.io`,
Run: func(cmd *cobra.Command, args []string) {
run()
},
}
var versionCmd = &cobra.Command{
Use: "version",
Short: "Print version",
Long: `Print version`,
Run: func(cmd *cobra.Command, args []string) {
fmtlog.Println(Version + " built on the " + BuildDate)
os.Exit(0)
},
}
var arguments = struct {
GlobalConfiguration
web bool
file bool
docker bool
dockerTLS bool
marathon bool
consul bool
consulTLS bool
consulCatalog bool
zookeeper bool
etcd bool
etcdTLS bool
boltdb bool
kubernetes bool
}{
GlobalConfiguration{
EntryPoints: make(EntryPoints),
Docker: &provider.Docker{
TLS: &provider.DockerTLS{},
},
File: &provider.File{},
Web: &WebProvider{},
Marathon: &provider.Marathon{},
Consul: &provider.Consul{
Kv: provider.Kv{
TLS: &provider.KvTLS{},
},
},
ConsulCatalog: &provider.ConsulCatalog{},
Zookeeper: &provider.Zookepper{},
Etcd: &provider.Etcd{
Kv: provider.Kv{
TLS: &provider.KvTLS{},
},
},
Boltdb: &provider.BoltDb{},
Kubernetes: &provider.Kubernetes{},
},
false,
false,
false,
false,
false,
false,
false,
false,
false,
false,
false,
false,
false,
}
func init() {
traefikCmd.AddCommand(versionCmd)
traefikCmd.PersistentFlags().StringP("configFile", "c", "", "Configuration file to use (TOML).")
traefikCmd.PersistentFlags().BoolVarP(&arguments.Debug, "debug", "d", false, "Enable debug mode")
traefikCmd.PersistentFlags().StringP("graceTimeOut", "g", "10", "Timeout in seconds. Duration to give active requests a chance to finish during hot-reloads")
traefikCmd.PersistentFlags().String("accessLogsFile", "log/access.log", "Access logs file")
traefikCmd.PersistentFlags().String("traefikLogsFile", "log/traefik.log", "Traefik logs file")
traefikCmd.PersistentFlags().Var(&arguments.EntryPoints, "entryPoints", "Entrypoints definition using format: --entryPoints='Name:http Address::8000 Redirect.EntryPoint:https' --entryPoints='Name:https Address::4442 TLS:tests/traefik.crt,tests/traefik.key'")
traefikCmd.PersistentFlags().Var(&arguments.DefaultEntryPoints, "defaultEntryPoints", "Entrypoints to be used by frontends that do not specify any entrypoint")
traefikCmd.PersistentFlags().StringP("logLevel", "l", "ERROR", "Log level")
traefikCmd.PersistentFlags().DurationVar(&arguments.ProvidersThrottleDuration, "providersThrottleDuration", time.Duration(2*time.Second), "Backends throttle duration: minimum duration between 2 events from providers before applying a new configuration. It avoids unnecessary reloads if multiples events are sent in a short amount of time.")
traefikCmd.PersistentFlags().Int("maxIdleConnsPerHost", 0, "If non-zero, controls the maximum idle (keep-alive) to keep per-host. If zero, DefaultMaxIdleConnsPerHost is used")
traefikCmd.PersistentFlags().BoolVar(&arguments.web, "web", false, "Enable Web backend")
traefikCmd.PersistentFlags().StringVar(&arguments.Web.Address, "web.address", ":8080", "Web administration port")
traefikCmd.PersistentFlags().StringVar(&arguments.Web.CertFile, "web.cerFile", "", "SSL certificate")
traefikCmd.PersistentFlags().StringVar(&arguments.Web.KeyFile, "web.keyFile", "", "SSL certificate")
traefikCmd.PersistentFlags().BoolVar(&arguments.Web.ReadOnly, "web.readOnly", false, "Enable read only API")
traefikCmd.PersistentFlags().BoolVar(&arguments.file, "file", false, "Enable File backend")
traefikCmd.PersistentFlags().BoolVar(&arguments.File.Watch, "file.watch", true, "Watch provider")
traefikCmd.PersistentFlags().StringVar(&arguments.File.Filename, "file.filename", "", "Override default configuration template. For advanced users :)")
traefikCmd.PersistentFlags().BoolVar(&arguments.docker, "docker", false, "Enable Docker backend")
traefikCmd.PersistentFlags().BoolVar(&arguments.Docker.Watch, "docker.watch", true, "Watch provider")
traefikCmd.PersistentFlags().StringVar(&arguments.Docker.Filename, "docker.filename", "", "Override default configuration template. For advanced users :)")
traefikCmd.PersistentFlags().StringVar(&arguments.Docker.Endpoint, "docker.endpoint", "unix:///var/run/docker.sock", "Docker server endpoint. Can be a tcp or a unix socket endpoint")
traefikCmd.PersistentFlags().StringVar(&arguments.Docker.Domain, "docker.domain", "", "Default domain used")
traefikCmd.PersistentFlags().BoolVar(&arguments.dockerTLS, "docker.tls", false, "Enable Docker TLS support")
traefikCmd.PersistentFlags().StringVar(&arguments.Docker.TLS.CA, "docker.tls.ca", "", "TLS CA")
traefikCmd.PersistentFlags().StringVar(&arguments.Docker.TLS.Cert, "docker.tls.cert", "", "TLS cert")
traefikCmd.PersistentFlags().StringVar(&arguments.Docker.TLS.Key, "docker.tls.key", "", "TLS key")
traefikCmd.PersistentFlags().BoolVar(&arguments.Docker.TLS.InsecureSkipVerify, "docker.tls.insecureSkipVerify", false, "TLS insecure skip verify")
traefikCmd.PersistentFlags().BoolVar(&arguments.marathon, "marathon", false, "Enable Marathon backend")
traefikCmd.PersistentFlags().BoolVar(&arguments.Marathon.Watch, "marathon.watch", true, "Watch provider")
traefikCmd.PersistentFlags().StringVar(&arguments.Marathon.Filename, "marathon.filename", "", "Override default configuration template. For advanced users :)")
traefikCmd.PersistentFlags().StringVar(&arguments.Marathon.Endpoint, "marathon.endpoint", "http://127.0.0.1:8080", "Marathon server endpoint. You can also specify multiple endpoint for Marathon")
traefikCmd.PersistentFlags().StringVar(&arguments.Marathon.Domain, "marathon.domain", "", "Default domain used")
traefikCmd.PersistentFlags().BoolVar(&arguments.Marathon.ExposedByDefault, "marathon.exposedByDefault", true, "Expose Marathon apps by default")
traefikCmd.PersistentFlags().BoolVar(&arguments.consul, "consul", false, "Enable Consul backend")
traefikCmd.PersistentFlags().BoolVar(&arguments.Consul.Watch, "consul.watch", true, "Watch provider")
traefikCmd.PersistentFlags().StringVar(&arguments.Consul.Filename, "consul.filename", "", "Override default configuration template. For advanced users :)")
traefikCmd.PersistentFlags().StringVar(&arguments.Consul.Endpoint, "consul.endpoint", "127.0.0.1:8500", "Comma sepparated Consul server endpoints")
traefikCmd.PersistentFlags().StringVar(&arguments.Consul.Prefix, "consul.prefix", "/traefik", "Prefix used for KV store")
traefikCmd.PersistentFlags().BoolVar(&arguments.consulTLS, "consul.tls", false, "Enable Consul TLS support")
traefikCmd.PersistentFlags().StringVar(&arguments.Consul.TLS.CA, "consul.tls.ca", "", "TLS CA")
traefikCmd.PersistentFlags().StringVar(&arguments.Consul.TLS.Cert, "consul.tls.cert", "", "TLS cert")
traefikCmd.PersistentFlags().StringVar(&arguments.Consul.TLS.Key, "consul.tls.key", "", "TLS key")
traefikCmd.PersistentFlags().BoolVar(&arguments.Consul.TLS.InsecureSkipVerify, "consul.tls.insecureSkipVerify", false, "TLS insecure skip verify")
traefikCmd.PersistentFlags().BoolVar(&arguments.consulCatalog, "consulCatalog", false, "Enable Consul catalog backend")
traefikCmd.PersistentFlags().StringVar(&arguments.ConsulCatalog.Domain, "consulCatalog.domain", "", "Default domain used")
traefikCmd.PersistentFlags().StringVar(&arguments.ConsulCatalog.Endpoint, "consulCatalog.endpoint", "127.0.0.1:8500", "Consul server endpoint")
traefikCmd.PersistentFlags().StringVar(&arguments.ConsulCatalog.Prefix, "consulCatalog.prefix", "traefik", "Consul catalog tag prefix")
traefikCmd.PersistentFlags().BoolVar(&arguments.zookeeper, "zookeeper", false, "Enable Zookeeper backend")
traefikCmd.PersistentFlags().BoolVar(&arguments.Zookeeper.Watch, "zookeeper.watch", true, "Watch provider")
traefikCmd.PersistentFlags().StringVar(&arguments.Zookeeper.Filename, "zookeeper.filename", "", "Override default configuration template. For advanced users :)")
traefikCmd.PersistentFlags().StringVar(&arguments.Zookeeper.Endpoint, "zookeeper.endpoint", "127.0.0.1:2181", "Comma sepparated Zookeeper server endpoints")
traefikCmd.PersistentFlags().StringVar(&arguments.Zookeeper.Prefix, "zookeeper.prefix", "/traefik", "Prefix used for KV store")
traefikCmd.PersistentFlags().BoolVar(&arguments.etcd, "etcd", false, "Enable Etcd backend")
traefikCmd.PersistentFlags().BoolVar(&arguments.Etcd.Watch, "etcd.watch", true, "Watch provider")
traefikCmd.PersistentFlags().StringVar(&arguments.Etcd.Filename, "etcd.filename", "", "Override default configuration template. For advanced users :)")
traefikCmd.PersistentFlags().StringVar(&arguments.Etcd.Endpoint, "etcd.endpoint", "127.0.0.1:4001", "Comma sepparated Etcd server endpoints")
traefikCmd.PersistentFlags().StringVar(&arguments.Etcd.Prefix, "etcd.prefix", "/traefik", "Prefix used for KV store")
traefikCmd.PersistentFlags().BoolVar(&arguments.etcdTLS, "etcd.tls", false, "Enable Etcd TLS support")
traefikCmd.PersistentFlags().StringVar(&arguments.Etcd.TLS.CA, "etcd.tls.ca", "", "TLS CA")
traefikCmd.PersistentFlags().StringVar(&arguments.Etcd.TLS.Cert, "etcd.tls.cert", "", "TLS cert")
traefikCmd.PersistentFlags().StringVar(&arguments.Etcd.TLS.Key, "etcd.tls.key", "", "TLS key")
traefikCmd.PersistentFlags().BoolVar(&arguments.Etcd.TLS.InsecureSkipVerify, "etcd.tls.insecureSkipVerify", false, "TLS insecure skip verify")
traefikCmd.PersistentFlags().BoolVar(&arguments.boltdb, "boltdb", false, "Enable Boltdb backend")
traefikCmd.PersistentFlags().BoolVar(&arguments.Boltdb.Watch, "boltdb.watch", true, "Watch provider")
traefikCmd.PersistentFlags().StringVar(&arguments.Boltdb.Filename, "boltdb.filename", "", "Override default configuration template. For advanced users :)")
traefikCmd.PersistentFlags().StringVar(&arguments.Boltdb.Endpoint, "boltdb.endpoint", "127.0.0.1:4001", "Boltdb server endpoint")
traefikCmd.PersistentFlags().StringVar(&arguments.Boltdb.Prefix, "boltdb.prefix", "/traefik", "Prefix used for KV store")
traefikCmd.PersistentFlags().BoolVar(&arguments.kubernetes, "kubernetes", false, "Enable Kubernetes backend")
traefikCmd.PersistentFlags().StringVar(&arguments.Kubernetes.Endpoint, "kubernetes.endpoint", "http://127.0.0.1:8080", "Kubernetes server endpoint")
traefikCmd.PersistentFlags().StringSliceVar(&arguments.Kubernetes.Namespaces, "kubernetes.namespaces", []string{}, "Kubernetes namespaces")
_ = viper.BindPFlag("configFile", traefikCmd.PersistentFlags().Lookup("configFile"))
_ = viper.BindPFlag("graceTimeOut", traefikCmd.PersistentFlags().Lookup("graceTimeOut"))
_ = viper.BindPFlag("logLevel", traefikCmd.PersistentFlags().Lookup("logLevel"))
_ = viper.BindPFlag("debug", traefikCmd.PersistentFlags().Lookup("debug"))
// TODO: wait for this issue to be corrected: https://github.com/spf13/viper/issues/105
_ = viper.BindPFlag("providersThrottleDuration", traefikCmd.PersistentFlags().Lookup("providersThrottleDuration"))
_ = viper.BindPFlag("maxIdleConnsPerHost", traefikCmd.PersistentFlags().Lookup("maxIdleConnsPerHost"))
viper.SetDefault("providersThrottleDuration", time.Duration(2*time.Second))
viper.SetDefault("logLevel", "ERROR")
viper.SetDefault("MaxIdleConnsPerHost", 200)
}
func run() {
fmtlog.SetFlags(fmtlog.Lshortfile | fmtlog.LstdFlags)
// load global configuration
globalConfiguration := LoadConfiguration()
http.DefaultTransport.(*http.Transport).MaxIdleConnsPerHost = globalConfiguration.MaxIdleConnsPerHost
loggerMiddleware := middlewares.NewLogger(globalConfiguration.AccessLogsFile)
defer loggerMiddleware.Close()
// logging
level, err := log.ParseLevel(strings.ToLower(globalConfiguration.LogLevel))
if err != nil {
log.Fatal("Error getting level", err)
}
log.SetLevel(level)
if len(globalConfiguration.TraefikLogsFile) > 0 {
fi, err := os.OpenFile(globalConfiguration.TraefikLogsFile, os.O_RDWR|os.O_CREATE|os.O_APPEND, 0666)
defer func() {
if err := fi.Close(); err != nil {
log.Error("Error closinf file", err)
}
}()
if err != nil {
log.Fatal("Error opening file", err)
} else {
log.SetOutput(fi)
log.SetFormatter(&log.TextFormatter{DisableColors: true, FullTimestamp: true, DisableSorting: true})
}
} else {
log.SetFormatter(&log.TextFormatter{FullTimestamp: true, DisableSorting: true})
}
jsonConf, _ := json.Marshal(globalConfiguration)
log.Debugf("Global configuration loaded %s", string(jsonConf))
server := NewServer(*globalConfiguration)
server.Start()
defer server.Close()
log.Info("Shutting down")
}

View file

@ -3,42 +3,44 @@ package main
import ( import (
"errors" "errors"
"fmt" "fmt"
fmtlog "log"
"regexp"
"strings"
"time"
"github.com/containous/traefik/acme" "github.com/containous/traefik/acme"
"github.com/containous/traefik/provider" "github.com/containous/traefik/provider"
"github.com/containous/traefik/types" "github.com/containous/traefik/types"
"github.com/mitchellh/mapstructure" "regexp"
"github.com/spf13/viper" "strings"
"time"
) )
// TraefikConfiguration holds GlobalConfiguration and other stuff
type TraefikConfiguration struct {
GlobalConfiguration
ConfigFile string `short:"c" description:"Timeout in seconds. Duration to give active requests a chance to finish during hot-reloads"`
}
// GlobalConfiguration holds global configuration (with providers, etc.). // GlobalConfiguration holds global configuration (with providers, etc.).
// It's populated from the traefik configuration file passed as an argument to the binary. // It's populated from the traefik configuration file passed as an argument to the binary.
type GlobalConfiguration struct { type GlobalConfiguration struct {
GraceTimeOut int64 GraceTimeOut int64 `short:"g" description:"Configuration file to use (TOML)."`
Debug bool Debug bool
AccessLogsFile string AccessLogsFile string `description:"Access logs file"`
TraefikLogsFile string TraefikLogsFile string `description:"Traefik logs file"`
LogLevel string LogLevel string `short:"l" description:"Log level"`
EntryPoints EntryPoints EntryPoints EntryPoints `description:"Entrypoints definition using format: --entryPoints='Name:http Address::8000 Redirect.EntryPoint:https' --entryPoints='Name:https Address::4442 TLS:tests/traefik.crt,tests/traefik.key'"`
ACME *acme.ACME ACME *acme.ACME
DefaultEntryPoints DefaultEntryPoints DefaultEntryPoints DefaultEntryPoints `description:"Entrypoints to be used by frontends that do not specify any entrypoint"`
ProvidersThrottleDuration time.Duration ProvidersThrottleDuration time.Duration `description:"Backends throttle duration: minimum duration between 2 events from providers before applying a new configuration. It avoids unnecessary reloads if multiples events are sent in a short amount of time."`
MaxIdleConnsPerHost int MaxIdleConnsPerHost int `description:"If non-zero, controls the maximum idle (keep-alive) to keep per-host. If zero, DefaultMaxIdleConnsPerHost is used"`
Retry *Retry Retry *Retry
Docker *provider.Docker Docker *provider.Docker `description:"Enable Docker backend"`
File *provider.File File *provider.File `description:"Enable File backend"`
Web *WebProvider Web *WebProvider `description:"Enable Web backend"`
Marathon *provider.Marathon Marathon *provider.Marathon `description:"Enable Marathon backend"`
Consul *provider.Consul Consul *provider.Consul `description:"Enable Consul backend"`
ConsulCatalog *provider.ConsulCatalog ConsulCatalog *provider.ConsulCatalog `description:"Enable Consul catalog backend"`
Etcd *provider.Etcd Etcd *provider.Etcd `description:"Enable Etcd backend"`
Zookeeper *provider.Zookepper Zookeeper *provider.Zookepper `description:"Enable Zookeeper backend"`
Boltdb *provider.BoltDb Boltdb *provider.BoltDb `description:"Enable Boltdb backend"`
Kubernetes *provider.Kubernetes Kubernetes *provider.Kubernetes `description:"Enable Kubernetes backend"`
} }
// DefaultEntryPoints holds default entry points // DefaultEntryPoints holds default entry points
@ -47,6 +49,7 @@ type DefaultEntryPoints []string
// String is the method to format the flag's value, part of the flag.Value interface. // String is the method to format the flag's value, part of the flag.Value interface.
// The String method's output will be used in diagnostics. // The String method's output will be used in diagnostics.
func (dep *DefaultEntryPoints) String() string { func (dep *DefaultEntryPoints) String() string {
//TODO :
return fmt.Sprintf("%#v", dep) return fmt.Sprintf("%#v", dep)
} }
@ -64,6 +67,14 @@ func (dep *DefaultEntryPoints) Set(value string) error {
return nil return nil
} }
// Get return the EntryPoints map
func (dep *DefaultEntryPoints) Get() interface{} { return DefaultEntryPoints(*dep) }
// SetValue sets the EntryPoints map with val
func (dep *DefaultEntryPoints) SetValue(val interface{}) {
*dep = DefaultEntryPoints(val.(DefaultEntryPoints))
}
// Type is type of the struct // Type is type of the struct
func (dep *DefaultEntryPoints) Type() string { func (dep *DefaultEntryPoints) Type() string {
return fmt.Sprint("defaultentrypoints²") return fmt.Sprint("defaultentrypoints²")
@ -75,6 +86,7 @@ type EntryPoints map[string]*EntryPoint
// String is the method to format the flag's value, part of the flag.Value interface. // String is the method to format the flag's value, part of the flag.Value interface.
// The String method's output will be used in diagnostics. // The String method's output will be used in diagnostics.
func (ep *EntryPoints) String() string { func (ep *EntryPoints) String() string {
//TODO :
return "" return ""
} }
@ -122,6 +134,14 @@ func (ep *EntryPoints) Set(value string) error {
return nil return nil
} }
// Get return the EntryPoints map
func (ep *EntryPoints) Get() interface{} { return EntryPoints(*ep) }
// SetValue sets the EntryPoints map with val
func (ep *EntryPoints) SetValue(val interface{}) {
*ep = EntryPoints(val.(EntryPoints))
}
// Type is type of the struct // Type is type of the struct
func (ep *EntryPoints) Type() string { func (ep *EntryPoints) Type() string {
return fmt.Sprint("entrypoints²") return fmt.Sprint("entrypoints²")
@ -154,6 +174,7 @@ type Certificates []Certificate
// The String method's output will be used in diagnostics. // The String method's output will be used in diagnostics.
func (certs *Certificates) String() string { func (certs *Certificates) String() string {
if len(*certs) == 0 { if len(*certs) == 0 {
//TODO :
return "" return ""
} }
return (*certs)[0].CertFile + "," + (*certs)[0].KeyFile return (*certs)[0].CertFile + "," + (*certs)[0].KeyFile
@ -191,117 +212,96 @@ type Retry struct {
MaxMem int64 MaxMem int64
} }
// NewGlobalConfiguration returns a GlobalConfiguration with default values. // NewTraefikPointersConfiguration creates a TraefikConfiguration with pointers default values
func NewGlobalConfiguration() *GlobalConfiguration { func NewTraefikPointersConfiguration() *TraefikConfiguration {
return new(GlobalConfiguration) //default Docker
var defaultDocker provider.Docker
defaultDocker.Watch = true
defaultDocker.Endpoint = "unix:///var/run/docker.sock"
defaultDocker.TLS = &provider.DockerTLS{}
// default File
var defaultFile provider.File
defaultFile.Watch = true
defaultFile.Filename = "" //needs equivalent to viper.ConfigFileUsed()
// default Web
var defaultWeb WebProvider
defaultWeb.Address = ":8080"
// default Marathon
var defaultMarathon provider.Marathon
defaultMarathon.Watch = true
defaultMarathon.Endpoint = "http://127.0.0.1:8080"
defaultMarathon.ExposedByDefault = true
// default Consul
var defaultConsul provider.Consul
defaultConsul.Watch = true
defaultConsul.Endpoint = "127.0.0.1:8500"
defaultConsul.Prefix = "/traefik"
defaultConsul.TLS = &provider.KvTLS{}
// default ConsulCatalog
var defaultConsulCatalog provider.ConsulCatalog
defaultConsulCatalog.Endpoint = "127.0.0.1:8500"
// default Etcd
var defaultEtcd provider.Etcd
defaultEtcd.Watch = true
defaultEtcd.Endpoint = "127.0.0.1:400"
defaultEtcd.Prefix = "/traefik"
defaultEtcd.TLS = &provider.KvTLS{}
//default Zookeeper
var defaultZookeeper provider.Zookepper
defaultZookeeper.Watch = true
defaultZookeeper.Endpoint = "127.0.0.1:2181"
defaultZookeeper.Prefix = "/traefik"
//default Boltdb
var defaultBoltDb provider.BoltDb
defaultBoltDb.Watch = true
defaultBoltDb.Endpoint = "127.0.0.1:4001"
defaultBoltDb.Prefix = "/traefik"
//default Kubernetes
var defaultKubernetes provider.Kubernetes
defaultKubernetes.Watch = true
defaultKubernetes.Endpoint = "127.0.0.1:8080"
defaultConfiguration := GlobalConfiguration{
Docker: &defaultDocker,
File: &defaultFile,
Web: &defaultWeb,
Marathon: &defaultMarathon,
Consul: &defaultConsul,
ConsulCatalog: &defaultConsulCatalog,
Etcd: &defaultEtcd,
Zookeeper: &defaultZookeeper,
Boltdb: &defaultBoltDb,
Kubernetes: &defaultKubernetes,
}
return &TraefikConfiguration{
GlobalConfiguration: defaultConfiguration,
}
} }
// LoadConfiguration returns a GlobalConfiguration. // NewTraefikConfiguration creates a TraefikConfiguration with default values
func LoadConfiguration() *GlobalConfiguration { func NewTraefikConfiguration() *TraefikConfiguration {
configuration := NewGlobalConfiguration() return &TraefikConfiguration{
viper.SetEnvPrefix("traefik") GlobalConfiguration: GlobalConfiguration{
viper.SetConfigType("toml") GraceTimeOut: 10,
viper.AutomaticEnv() AccessLogsFile: "log/access.log",
if len(viper.GetString("configFile")) > 0 { TraefikLogsFile: "log/traefik.log",
viper.SetConfigFile(viper.GetString("configFile")) LogLevel: "ERROR",
} else { EntryPoints: map[string]*EntryPoint{"http": &EntryPoint{Address: ":80"}},
viper.SetConfigName("traefik") // name of config file (without extension) DefaultEntryPoints: []string{"http"},
ProvidersThrottleDuration: time.Duration(2 * time.Second),
MaxIdleConnsPerHost: 200,
},
ConfigFile: "",
} }
viper.AddConfigPath("/etc/traefik/") // path to look for the config file in
viper.AddConfigPath("$HOME/.traefik/") // call multiple times to add many search paths
viper.AddConfigPath(".") // optionally look for config in the working directory
if err := viper.ReadInConfig(); err != nil {
if len(viper.ConfigFileUsed()) > 0 {
fmtlog.Printf("Error reading configuration file: %s", err)
} else {
fmtlog.Printf("No configuration file found")
}
}
if len(arguments.EntryPoints) > 0 {
viper.Set("entryPoints", arguments.EntryPoints)
}
if len(arguments.DefaultEntryPoints) > 0 {
viper.Set("defaultEntryPoints", arguments.DefaultEntryPoints)
}
if arguments.web {
viper.Set("web", arguments.Web)
}
if arguments.file {
viper.Set("file", arguments.File)
}
if !arguments.dockerTLS {
arguments.Docker.TLS = nil
}
if arguments.docker {
viper.Set("docker", arguments.Docker)
}
if arguments.marathon {
viper.Set("marathon", arguments.Marathon)
}
if !arguments.consulTLS {
arguments.Consul.TLS = nil
}
if arguments.consul {
viper.Set("consul", arguments.Consul)
}
if arguments.consulCatalog {
viper.Set("consulCatalog", arguments.ConsulCatalog)
}
if arguments.zookeeper {
viper.Set("zookeeper", arguments.Zookeeper)
}
if !arguments.etcdTLS {
arguments.Etcd.TLS = nil
}
if arguments.etcd {
viper.Set("etcd", arguments.Etcd)
}
if arguments.boltdb {
viper.Set("boltdb", arguments.Boltdb)
}
if arguments.kubernetes {
viper.Set("kubernetes", arguments.Kubernetes)
}
if err := unmarshal(&configuration); err != nil {
fmtlog.Fatalf("Error reading file: %s", err)
}
if len(configuration.EntryPoints) == 0 {
configuration.EntryPoints = make(map[string]*EntryPoint)
configuration.EntryPoints["http"] = &EntryPoint{
Address: ":80",
}
configuration.DefaultEntryPoints = []string{"http"}
}
if configuration.File != nil && len(configuration.File.Filename) == 0 {
// no filename, setting to global config file
configuration.File.Filename = viper.ConfigFileUsed()
}
return configuration
}
func unmarshal(rawVal interface{}) error {
config := &mapstructure.DecoderConfig{
DecodeHook: mapstructure.StringToTimeDurationHookFunc(),
Metadata: nil,
Result: rawVal,
WeaklyTypedInput: true,
}
decoder, err := mapstructure.NewDecoder(config)
if err != nil {
return err
}
err = decoder.Decode(viper.AllSettings())
if err != nil {
return err
}
return nil
} }
type configs map[string]*types.Configuration type configs map[string]*types.Configuration

91
flaeg_test.go Normal file
View file

@ -0,0 +1,91 @@
package main
import (
"github.com/cocap10/flaeg"
"reflect"
"testing"
"time"
)
func TestLoad(t *testing.T) {
var configuration GlobalConfiguration
defaultConfiguration := NewGlobalConfiguration()
args := []string{
// "-h",
"--docker",
"--file",
"--web",
"--marathon",
"--consul",
"--consulcatalog",
"--etcd",
"--zookeeper",
"--boltdb",
}
if err := flaeg.Load(&configuration, defaultConfiguration, args); err != nil {
t.Fatalf("Error: %s", err)
}
// fmt.Printf("result : \n%+v\n", configuration)
if !reflect.DeepEqual(configuration, *defaultConfiguration) {
t.Fatalf("\nexpected\t: %+v\ngot\t\t\t: %+v", *defaultConfiguration, configuration)
}
}
func TestLoadWithParsers(t *testing.T) {
var configuration GlobalConfiguration
defaultConfiguration := NewGlobalConfiguration()
args := []string{
// "-h",
"--docker",
// "--file",
"--web.address=:8888",
"--marathon",
"--consul",
"--consulcatalog",
"--etcd.tls.insecureskipverify",
"--zookeeper",
"--boltdb",
"--accesslogsfile=log2/access.log",
"--entrypoints=Name:http Address::8000 Redirect.EntryPoint:https",
"--entrypoints=Name:https Address::8443 Redirect.EntryPoint:http",
"--defaultentrypoints=https",
"--defaultentrypoints=ssh",
"--providersthrottleduration=4s",
}
parsers := map[reflect.Type]flaeg.Parser{}
var defaultEntryPointsParser DefaultEntryPoints
parsers[reflect.TypeOf(DefaultEntryPoints{})] = &defaultEntryPointsParser
entryPointsParser := EntryPoints{}
parsers[reflect.TypeOf(EntryPoints{})] = &entryPointsParser
if err := flaeg.LoadWithParsers(&configuration, defaultConfiguration, args, parsers); err != nil {
t.Fatalf("Error: %s", err)
}
// fmt.Printf("result : \n%+v\n", configuration)
//Check
check := *defaultConfiguration
check.File = nil
check.Web.Address = ":8888"
check.AccessLogsFile = "log2/access.log"
check.Etcd.TLS.InsecureSkipVerify = true
check.EntryPoints = make(map[string]*EntryPoint)
check.EntryPoints["http"] = &EntryPoint{
Address: ":8000",
Redirect: &Redirect{
EntryPoint: "https",
},
}
check.EntryPoints["https"] = &EntryPoint{
Address: ":8443",
Redirect: &Redirect{
EntryPoint: "http",
},
}
check.DefaultEntryPoints = []string{"https", "ssh"}
check.ProvidersThrottleDuration = time.Duration(4 * time.Second)
if !reflect.DeepEqual(&configuration, &check) {
t.Fatalf("\nexpected\t: %+v\ngot\t\t\t: %+v", check, configuration)
}
}

View file

@ -9,7 +9,7 @@ import (
// BoltDb holds configurations of the BoltDb provider. // BoltDb holds configurations of the BoltDb provider.
type BoltDb struct { type BoltDb struct {
Kv `mapstructure:",squash"` Kv `mapstructure:",squash" description:"go through"`
} }
// Provide allows the provider to provide configurations to traefik // Provide allows the provider to provide configurations to traefik

View file

@ -9,7 +9,7 @@ import (
// Consul holds configurations of the Consul provider. // Consul holds configurations of the Consul provider.
type Consul struct { type Consul struct {
Kv `mapstructure:",squash"` Kv `mapstructure:",squash" description:"go through"`
} }
// Provide allows the provider to provide configurations to traefik // Provide allows the provider to provide configurations to traefik

View file

@ -24,8 +24,8 @@ const (
// ConsulCatalog holds configurations of the Consul catalog provider. // ConsulCatalog holds configurations of the Consul catalog provider.
type ConsulCatalog struct { type ConsulCatalog struct {
BaseProvider `mapstructure:",squash"` BaseProvider `mapstructure:",squash"`
Endpoint string Endpoint string `description:"Consul server endpoint"`
Domain string Domain string `description:"Default domain used"`
client *api.Client client *api.Client
Prefix string Prefix string
} }

View file

@ -30,17 +30,17 @@ const DockerAPIVersion string = "1.21"
// Docker holds configurations of the Docker provider. // Docker holds configurations of the Docker provider.
type Docker struct { type Docker struct {
BaseProvider `mapstructure:",squash"` BaseProvider `mapstructure:",squash"`
Endpoint string Endpoint string `description:"Docker server endpoint. Can be a tcp or a unix socket endpoint"`
Domain string Domain string `description:"Default domain used"`
TLS *DockerTLS TLS *DockerTLS `description:"Enable Docker TLS support"`
} }
// DockerTLS holds TLS specific configurations // DockerTLS holds TLS specific configurations
type DockerTLS struct { type DockerTLS struct {
CA string CA string `description:"TLS CA"`
Cert string Cert string `description:"TLS cert"`
Key string Key string `description:"TLS key"`
InsecureSkipVerify bool InsecureSkipVerify bool `description:"TLS insecure skip verify"`
} }
func (provider *Docker) createClient() (client.APIClient, error) { func (provider *Docker) createClient() (client.APIClient, error) {

View file

@ -9,7 +9,7 @@ import (
// Etcd holds configurations of the Etcd provider. // Etcd holds configurations of the Etcd provider.
type Etcd struct { type Etcd struct {
Kv `mapstructure:",squash"` Kv `mapstructure:",squash" description:"go through"`
} }
// Provide allows the provider to provide configurations to traefik // Provide allows the provider to provide configurations to traefik

View file

@ -14,7 +14,7 @@ import (
// File holds configurations of the File provider. // File holds configurations of the File provider.
type File struct { type File struct {
BaseProvider `mapstructure:",squash"` BaseProvider `mapstructure:",squash" description:"go through"`
} }
// Provide allows the provider to provide configurations to traefik // Provide allows the provider to provide configurations to traefik

View file

@ -20,12 +20,15 @@ const (
serviceAccountCACert = "/var/run/secrets/kubernetes.io/serviceaccount/ca.crt" serviceAccountCACert = "/var/run/secrets/kubernetes.io/serviceaccount/ca.crt"
) )
// Namespaces holds kubernetes namespaces
type Namespaces []string
// Kubernetes holds configurations of the Kubernetes provider. // Kubernetes holds configurations of the Kubernetes provider.
type Kubernetes struct { type Kubernetes struct {
BaseProvider `mapstructure:",squash"` BaseProvider `mapstructure:",squash"`
Endpoint string Endpoint string `description:"Kubernetes server endpoint"`
disablePassHostHeaders bool DisablePassHostHeaders bool `description:"Kubernetes disable PassHost Headers"`
Namespaces []string Namespaces Namespaces `description:"Kubernetes namespaces"`
} }
func (provider *Kubernetes) createClient() (k8s.Client, error) { func (provider *Kubernetes) createClient() (k8s.Client, error) {
@ -259,7 +262,7 @@ func equalPorts(servicePort k8s.ServicePort, ingressPort k8s.IntOrString) bool {
} }
func (provider *Kubernetes) getPassHostHeader() bool { func (provider *Kubernetes) getPassHostHeader() bool {
if provider.disablePassHostHeaders { if provider.DisablePassHostHeaders {
return false return false
} }
return true return true

View file

@ -22,20 +22,20 @@ import (
// Kv holds common configurations of key-value providers. // Kv holds common configurations of key-value providers.
type Kv struct { type Kv struct {
BaseProvider `mapstructure:",squash"` BaseProvider `mapstructure:",squash" description:"go through"`
Endpoint string Endpoint string `description:"Comma sepparated server endpoints"`
Prefix string Prefix string `description:"Prefix used for KV store"`
TLS *KvTLS TLS *KvTLS `description:"Enable TLS support"`
storeType store.Backend storeType store.Backend
kvclient store.Store kvclient store.Store
} }
// KvTLS holds TLS specific configurations // KvTLS holds TLS specific configurations
type KvTLS struct { type KvTLS struct {
CA string CA string `description:"TLS CA"`
Cert string Cert string `description:"TLS cert"`
Key string Key string `description:"TLS key"`
InsecureSkipVerify bool InsecureSkipVerify bool `description:"TLS insecure skip verify"`
} }
func (provider *Kv) watchKv(configurationChan chan<- types.ConfigMessage, prefix string, stop chan bool) error { func (provider *Kv) watchKv(configurationChan chan<- types.ConfigMessage, prefix string, stop chan bool) error {

View file

@ -20,10 +20,10 @@ import (
// Marathon holds configuration of the Marathon provider. // Marathon holds configuration of the Marathon provider.
type Marathon struct { type Marathon struct {
BaseProvider `mapstructure:",squash"` BaseProvider `mapstructure:",squash" description:"go through"`
Endpoint string Endpoint string `description:"Marathon server endpoint. You can also specify multiple endpoint for Marathon"`
Domain string Domain string `description:"Default domain used"`
ExposedByDefault bool ExposedByDefault bool `description:"Expose Marathon apps by default"`
Basic *MarathonBasic Basic *MarathonBasic
TLS *tls.Config TLS *tls.Config
marathonClient marathon.Marathon marathonClient marathon.Marathon
@ -36,8 +36,8 @@ type MarathonBasic struct {
} }
type lightMarathonClient interface { type lightMarathonClient interface {
Applications(url.Values) (*marathon.Applications, error)
AllTasks(v url.Values) (*marathon.Tasks, error) AllTasks(v url.Values) (*marathon.Tasks, error)
Applications(url.Values) (*marathon.Applications, error)
} }
// Provide allows the provider to provide configurations to traefik // Provide allows the provider to provide configurations to traefik

View file

@ -22,8 +22,8 @@ type Provider interface {
// BaseProvider should be inherited by providers // BaseProvider should be inherited by providers
type BaseProvider struct { type BaseProvider struct {
Watch bool Watch bool `description:"Watch provider"`
Filename string Filename string `description:"Override default configuration template. For advanced users :)"`
} }
func (p *BaseProvider) getConfiguration(defaultTemplateFile string, funcMap template.FuncMap, templateObjects interface{}) (*types.Configuration, error) { func (p *BaseProvider) getConfiguration(defaultTemplateFile string, funcMap template.FuncMap, templateObjects interface{}) (*types.Configuration, error) {

View file

@ -9,7 +9,7 @@ import (
// Zookepper holds configurations of the Zookepper provider. // Zookepper holds configurations of the Zookepper provider.
type Zookepper struct { type Zookepper struct {
Kv Kv `description:"go through"`
} }
// Provide allows the provider to provide configurations to traefik // Provide allows the provider to provide configurations to traefik

View file

@ -1,16 +1,111 @@
package main package main
import ( import (
"encoding/json"
log "github.com/Sirupsen/logrus"
"github.com/containous/flaeg"
"github.com/containous/staert"
"github.com/containous/traefik/middlewares"
fmtlog "log" fmtlog "log"
"net/http"
"os" "os"
"reflect"
"runtime" "runtime"
"strings"
) )
func main() { func main() {
runtime.GOMAXPROCS(runtime.NumCPU()) runtime.GOMAXPROCS(runtime.NumCPU())
if err := traefikCmd.Execute(); err != nil {
//traefik config inits
traefikConfiguration := NewTraefikConfiguration()
traefikPointersConfiguration := NewTraefikPointersConfiguration()
//traefik Command init
traefikCmd := &flaeg.Command{
Name: "traefik",
Description: `traefik is a modern HTTP reverse proxy and load balancer made to deploy microservices with ease.
Complete documentation is available at https://traefik.io`,
Config: traefikConfiguration,
DefaultPointersConfig: traefikPointersConfiguration,
Run: func() error {
run(traefikConfiguration)
return nil
},
}
//version Command init
versionCmd := &flaeg.Command{
Name: "version",
Description: `Print version`,
Run: func() error {
fmtlog.Println(Version + " built on the " + BuildDate)
return nil
},
}
//staert init
s := staert.NewStaert(traefikCmd)
//init toml source
toml := staert.NewTomlSource("traefik", []string{traefikConfiguration.ConfigFile, "/etc/traefik/", "$HOME/.traefik/", "."})
//init flaeg source
f := flaeg.New(traefikCmd, os.Args[1:])
//add custom parsers
f.AddParser(reflect.TypeOf(EntryPoints{}), &EntryPoints{})
f.AddParser(reflect.TypeOf(DefaultEntryPoints{}), &DefaultEntryPoints{})
//Wait for DefaultSliceStringParser
//add version command
f.AddCommand(versionCmd)
//add sources to staert
s.AddSource(f)
s.AddSource(toml)
s.AddSource(f)
if err := s.Run(); err != nil {
fmtlog.Println(err) fmtlog.Println(err)
os.Exit(-1) os.Exit(-1)
} }
os.Exit(0) os.Exit(0)
} }
func run(traefikConfiguration *TraefikConfiguration) {
fmtlog.SetFlags(fmtlog.Lshortfile | fmtlog.LstdFlags)
// load global configuration
globalConfiguration := traefikConfiguration.GlobalConfiguration
http.DefaultTransport.(*http.Transport).MaxIdleConnsPerHost = globalConfiguration.MaxIdleConnsPerHost
loggerMiddleware := middlewares.NewLogger(globalConfiguration.AccessLogsFile)
defer loggerMiddleware.Close()
// logging
level, err := log.ParseLevel(strings.ToLower(globalConfiguration.LogLevel))
if err != nil {
log.Fatal("Error getting level", err)
}
log.SetLevel(level)
if len(globalConfiguration.TraefikLogsFile) > 0 {
fi, err := os.OpenFile(globalConfiguration.TraefikLogsFile, os.O_RDWR|os.O_CREATE|os.O_APPEND, 0666)
defer func() {
if err := fi.Close(); err != nil {
log.Error("Error closinf file", err)
}
}()
if err != nil {
log.Fatal("Error opening file", err)
} else {
log.SetOutput(fi)
log.SetFormatter(&log.TextFormatter{DisableColors: true, FullTimestamp: true, DisableSorting: true})
}
} else {
log.SetFormatter(&log.TextFormatter{FullTimestamp: true, DisableSorting: true})
}
jsonConf, _ := json.Marshal(globalConfiguration)
log.Debugf("Global configuration loaded %s", string(jsonConf))
server := NewServer(globalConfiguration)
server.Start()
defer server.Close()
log.Info("Shutting down")
}

9
web.go
View file

@ -23,10 +23,11 @@ var metrics = stats.New()
// WebProvider is a provider.Provider implementation that provides the UI. // WebProvider is a provider.Provider implementation that provides the UI.
// FIXME to be handled another way. // FIXME to be handled another way.
type WebProvider struct { type WebProvider struct {
Address string Address string `description:"Web administration port"`
CertFile, KeyFile string CertFile string `description:"SSL certificate"`
ReadOnly bool KeyFile string `description:"SSL certificate"`
server *Server ReadOnly bool `description:"Enable read only API"`
server *Server
} }
var ( var (