diff --git a/acme/acme.go b/acme/acme.go index 53c1bd123..99e340ae4 100644 --- a/acme/acme.go +++ b/acme/acme.go @@ -16,6 +16,7 @@ import ( fmtlog "log" "os" "reflect" + "strings" "sync" "time" ) @@ -161,15 +162,50 @@ func (dc *DomainsCertificate) needRenew() bool { // ACME allows to connect to lets encrypt and retrieve certs type ACME struct { - Email string - Domains []Domain - StorageFile string - OnDemand bool - CAServer string - EntryPoint string + Email string `description:"Email address used for registration"` + Domains []Domain `description:"SANs (alternative domains) to each main domain using format: --acme.domains='main.com,san1.com,san2.com' --acme.domains='main.net,san1.net,san2.net'"` + StorageFile string `description:"File used for certificates storage."` + OnDemand bool `description:"Enable on demand certificate. This will request a certificate from Let's Encrypt during the first TLS handshake for a hostname that does not yet have a certificate."` + CAServer string `description:"CA server to use."` + EntryPoint string `description:"Entrypoint to proxy acme challenge to."` storageLock sync.RWMutex } +//Domains parse []Domain +type Domains []Domain + +//Set []Domain +func (ds *Domains) Set(str string) error { + fargs := func(c rune) bool { + return c == ',' || c == ';' + } + // get function + slice := strings.FieldsFunc(str, fargs) + if len(slice) < 1 { + return fmt.Errorf("Parse error ACME.Domain. Imposible to parse %s", str) + } + d := Domain{ + Main: slice[0], + SANs: []string{}, + } + if len(slice) > 1 { + d.SANs = slice[1:] + } + *ds = append(*ds, d) + return nil +} + +//Get []Domain +func (ds *Domains) Get() interface{} { return []Domain(*ds) } + +//String returns []Domain in string +func (ds *Domains) String() string { return fmt.Sprintf("%+v", *ds) } + +//SetValue sets []Domain into the parser +func (ds *Domains) SetValue(val interface{}) { + *ds = Domains(val.([]Domain)) +} + // Domain holds a domain name with SANs type Domain struct { Main string diff --git a/acme/acme_test.go b/acme/acme_test.go new file mode 100644 index 000000000..302c5866e --- /dev/null +++ b/acme/acme_test.go @@ -0,0 +1,61 @@ +package acme + +import ( + "reflect" + "testing" +) + +func TestDomainsSet(t *testing.T) { + checkMap := map[string]Domains{ + "": {}, + "foo.com": {Domain{Main: "foo.com", SANs: []string{}}}, + "foo.com,bar.net": {Domain{Main: "foo.com", SANs: []string{"bar.net"}}}, + "foo.com,bar1.net,bar2.net,bar3.net": {Domain{Main: "foo.com", SANs: []string{"bar1.net", "bar2.net", "bar3.net"}}}, + } + for in, check := range checkMap { + ds := Domains{} + ds.Set(in) + if !reflect.DeepEqual(check, ds) { + t.Errorf("Expected %+v\nGo %+v", check, ds) + } + } +} + +func TestDomainsSetAppend(t *testing.T) { + inSlice := []string{ + "", + "foo1.com", + "foo2.com,bar.net", + "foo3.com,bar1.net,bar2.net,bar3.net", + } + checkSlice := []Domains{ + {}, + { + Domain{ + Main: "foo1.com", + SANs: []string{}}}, + { + Domain{ + Main: "foo1.com", + SANs: []string{}}, + Domain{ + Main: "foo2.com", + SANs: []string{"bar.net"}}}, + { + Domain{ + Main: "foo1.com", + SANs: []string{}}, + Domain{ + Main: "foo2.com", + SANs: []string{"bar.net"}}, + Domain{Main: "foo3.com", + SANs: []string{"bar1.net", "bar2.net", "bar3.net"}}}, + } + ds := Domains{} + for i, in := range inSlice { + ds.Set(in) + if !reflect.DeepEqual(checkSlice[i], ds) { + t.Errorf("Expected %s %+v\nGo %+v", in, checkSlice[i], ds) + } + } +} diff --git a/cmd.go b/cmd.go deleted file mode 100644 index 6580d6a15..000000000 --- a/cmd.go +++ /dev/null @@ -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") -} diff --git a/configuration.go b/configuration.go index bae48d7cb..72703e177 100644 --- a/configuration.go +++ b/configuration.go @@ -3,42 +3,44 @@ package main import ( "errors" "fmt" - fmtlog "log" - "regexp" - "strings" - "time" - "github.com/containous/traefik/acme" "github.com/containous/traefik/provider" "github.com/containous/traefik/types" - "github.com/mitchellh/mapstructure" - "github.com/spf13/viper" + "regexp" + "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.). // It's populated from the traefik configuration file passed as an argument to the binary. type GlobalConfiguration struct { - GraceTimeOut int64 - Debug bool - AccessLogsFile string - TraefikLogsFile string - LogLevel string - EntryPoints EntryPoints - ACME *acme.ACME - DefaultEntryPoints DefaultEntryPoints - ProvidersThrottleDuration time.Duration - MaxIdleConnsPerHost int - Retry *Retry - Docker *provider.Docker - File *provider.File - Web *WebProvider - Marathon *provider.Marathon - Consul *provider.Consul - ConsulCatalog *provider.ConsulCatalog - Etcd *provider.Etcd - Zookeeper *provider.Zookepper - Boltdb *provider.BoltDb - Kubernetes *provider.Kubernetes + GraceTimeOut int64 `short:"g" description:"Configuration file to use (TOML)."` + Debug bool `short:"d" description:"Enable debug mode"` + AccessLogsFile string `description:"Access logs file"` + TraefikLogsFile string `description:"Traefik logs file"` + LogLevel string `short:"l" description:"Log level"` + 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 `description:"Enable ACME (Let's Encrypt): automatic SSL"` + DefaultEntryPoints DefaultEntryPoints `description:"Entrypoints to be used by frontends that do not specify any entrypoint"` + 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 `description:"If non-zero, controls the maximum idle (keep-alive) to keep per-host. If zero, DefaultMaxIdleConnsPerHost is used"` + Retry *Retry `description:"Enable retry sending request if network error"` + Docker *provider.Docker `description:"Enable Docker backend"` + File *provider.File `description:"Enable File backend"` + Web *WebProvider `description:"Enable Web backend"` + Marathon *provider.Marathon `description:"Enable Marathon backend"` + Consul *provider.Consul `description:"Enable Consul backend"` + ConsulCatalog *provider.ConsulCatalog `description:"Enable Consul catalog backend"` + Etcd *provider.Etcd `description:"Enable Etcd backend"` + Zookeeper *provider.Zookepper `description:"Enable Zookeeper backend"` + Boltdb *provider.BoltDb `description:"Enable Boltdb backend"` + Kubernetes *provider.Kubernetes `description:"Enable Kubernetes backend"` } // DefaultEntryPoints holds default entry points @@ -47,7 +49,7 @@ type DefaultEntryPoints []string // 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. func (dep *DefaultEntryPoints) String() string { - return fmt.Sprintf("%#v", dep) + return strings.Join(*dep, ",") } // Set is the method to set the flag value, part of the flag.Value interface. @@ -64,9 +66,17 @@ func (dep *DefaultEntryPoints) Set(value string) error { 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 func (dep *DefaultEntryPoints) Type() string { - return fmt.Sprint("defaultentrypoints²") + return fmt.Sprint("defaultentrypoints") } // EntryPoints holds entry points configuration of the reverse proxy (ip, port, TLS...) @@ -75,7 +85,7 @@ type EntryPoints map[string]*EntryPoint // 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. func (ep *EntryPoints) String() string { - return "" + return fmt.Sprintf("%+v", *ep) } // Set is the method to set the flag value, part of the flag.Value interface. @@ -122,6 +132,14 @@ func (ep *EntryPoints) Set(value string) error { 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 func (ep *EntryPoints) Type() string { return fmt.Sprint("entrypoints²") @@ -187,121 +205,101 @@ type Certificate struct { // Retry contains request retry config type Retry struct { - Attempts int - MaxMem int64 + Attempts int `description:"Number of attempts"` + MaxMem int64 `description:"Maximum request body to be stored in memory in Mo"` } -// NewGlobalConfiguration returns a GlobalConfiguration with default values. -func NewGlobalConfiguration() *GlobalConfiguration { - return new(GlobalConfiguration) +// NewTraefikDefaultPointersConfiguration creates a TraefikConfiguration with pointers default values +func NewTraefikDefaultPointersConfiguration() *TraefikConfiguration { + //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, + Retry: &Retry{MaxMem: 2}, + } + return &TraefikConfiguration{ + GlobalConfiguration: defaultConfiguration, + } } -// LoadConfiguration returns a GlobalConfiguration. -func LoadConfiguration() *GlobalConfiguration { - configuration := NewGlobalConfiguration() - viper.SetEnvPrefix("traefik") - viper.SetConfigType("toml") - viper.AutomaticEnv() - if len(viper.GetString("configFile")) > 0 { - viper.SetConfigFile(viper.GetString("configFile")) - } else { - viper.SetConfigName("traefik") // name of config file (without extension) +// NewTraefikConfiguration creates a TraefikConfiguration with default values +func NewTraefikConfiguration() *TraefikConfiguration { + return &TraefikConfiguration{ + GlobalConfiguration: GlobalConfiguration{ + GraceTimeOut: 10, + AccessLogsFile: "", + TraefikLogsFile: "", + LogLevel: "ERROR", + EntryPoints: map[string]*EntryPoint{}, + DefaultEntryPoints: []string{}, + 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 diff --git a/glide.lock b/glide.lock index b52394f42..cd8969fca 100644 --- a/glide.lock +++ b/glide.lock @@ -1,39 +1,40 @@ -hash: 68bc4f87206f9a486e1455f1dfcad737369c359a803566271432fcb85de3a12c -updated: 2016-05-23T13:57:35.191541555+02:00 +hash: 660249b4d5cbcfd0cb0a2d9d39ce798ec8df7d3277c53ed4043fe2f61b29eeb9 +updated: 2016-05-27T09:59:17.855890752+02:00 imports: -- name: github.com/alecthomas/template - version: b867cc6ab45cece8143cfcc6fc9c77cf3f2c23c0 -- name: github.com/alecthomas/units - version: 6b4e7dc5e3143b85ea77909c72caf89416fc2915 - name: github.com/boltdb/bolt - version: 51f99c862475898df9773747d3accd05a7ca33c1 + version: dfb21201d9270c1082d5fb0f07f500311ff72f18 - name: github.com/BurntSushi/toml - version: bbd5bb678321a0d6e58f1099321dfa73391c1b6f + version: f0aeabca5a127c4078abb8c8d64298b147264b55 - name: github.com/BurntSushi/ty version: 6add9cd6ad42d389d6ead1dde60b4ad71e46fd74 subpackages: - fun - name: github.com/cenkalti/backoff - version: 4dc77674aceaabba2c7e3da25d4c823edfb73f99 + version: c29158af31815ccc31ca29c86c121bc39e00d3d8 - name: github.com/codahale/hdrhistogram - version: 954f16e8b9ef0e5d5189456aa4c1202758e04f17 + version: 9208b142303c12d8899bae836fd524ac9338b4fd - name: github.com/codegangsta/cli version: bf4a526f48af7badd25d2cb02d587e1b01be3b50 - name: github.com/codegangsta/negroni - version: c7477ad8e330bef55bf1ebe300cf8aa67c492d1b + version: ffbc66b612ee3eac2eba29aedce4c3a65e4dd0a1 +- name: github.com/containous/flaeg + version: c425b9d758df1864ca838dbd433f1cf8f5097d51 - name: github.com/containous/oxy version: 183212964e13e7b8afe01a08b193d04300554a68 subpackages: - cbreaker - - forward - - memmetrics - - roundrobin - - utils - connlimit + - forward + - roundrobin - stream + - utils +- name: github.com/containous/staert + version: ff272631ecfc9c22490b651fea0f08d364d46518 - name: github.com/coreos/etcd - version: 26e52d2bce9e3e11b77b68cc84bf91aebb1ef637 + version: c400d05d0aa73e21e431c16145e558d624098018 subpackages: + - Godeps/_workspace/src/github.com/ugorji/go/codec + - Godeps/_workspace/src/golang.org/x/net/context - client - pkg/pathutil - pkg/types @@ -42,72 +43,37 @@ imports: subpackages: - spew - name: github.com/docker/distribution - version: 467fc068d88aa6610691b7f1a677271a3fac4aac + version: 5bbf65499960b184fe8e0f045397375e1a6722b8 subpackages: - reference - digest - name: github.com/docker/docker version: 9837ec4da53f15f9120d53a6e1517491ba8b0261 subpackages: - - autogen - - api - - cliconfig - - daemon/network - - graph/tags - - image - - opts - - pkg/archive - - pkg/fileutils - - pkg/homedir - - pkg/httputils - - pkg/ioutils - - pkg/jsonmessage - - pkg/mflag - - pkg/nat - - pkg/parsers - - pkg/pools - - pkg/promise - - pkg/random - - pkg/stdcopy - - pkg/stringid - - pkg/symlink - - pkg/system - - pkg/tarsum - - pkg/term - - pkg/timeutils - - pkg/tlsconfig - - pkg/ulimit - - pkg/units - - pkg/urlutil - - pkg/useragent - - pkg/version - - registry - - runconfig - - utils - - volume + - namesgenerator - name: github.com/docker/engine-api version: 3d3d0b6c9d2651aac27f416a6da0224c1875b3eb subpackages: - client - types - - types/container - - types/filters - - types/strslice - types/events + - types/filters - client/transport - client/transport/cancellable + - types/container - types/network - types/reference - types/registry - types/time - types/versions - types/blkiodev + - types/strslice - name: github.com/docker/go-connections version: c7838b258fbfa3fe88eecfb2a0e08ea0dbd6a646 subpackages: - - nat - sockets - tlsconfig + - nat - name: github.com/docker/go-units version: 5d2041e26a699eaca682e2ea41c8f891e1060444 - name: github.com/docker/libcompose @@ -120,61 +86,39 @@ imports: - store/consul - store/etcd - store/zookeeper -- name: github.com/docker/libtrust - version: 9cbd2a1374f46905c68a4eb3694a130610adc62a - name: github.com/donovanhide/eventsource - version: d8a3071799b98cacd30b6da92f536050ccfe6da4 + version: c3f57f280ec708df24886d9e62f2fd178d69d8e8 - name: github.com/elazarl/go-bindata-assetfs - version: d5cac425555ca5cf00694df246e04f05e6a55150 -- name: github.com/flynn/go-shlex - version: 3f9db97f856818214da2e1057f8ad84803971cff + version: 57eb5e1fc594ad4b0b1dbea7b286d299e0cb43c2 - name: github.com/gambol99/go-marathon version: ade11d1dc2884ee1f387078fc28509559b6235d1 - name: github.com/go-check/check - version: 11d3bc7aa68e238947792f30573146a3231fc0f1 -- name: github.com/golang/glog - version: fca8c8854093a154ff1eb580aae10276ad6b1b5f + version: 4f90aeace3a26ad7021961c297b22c42160c7b25 - name: github.com/google/go-querystring version: 9235644dd9e52eeae6fa48efd539fdc351a0af53 subpackages: - query - name: github.com/gorilla/context - version: 215affda49addc4c8ef7e2534915df2c8c35c6cd -- name: github.com/gorilla/handlers - version: 40694b40f4a928c062f56849989d3e9cd0570e5f + version: a8d44e7d8e4d532b6a27a02dd82abb31cc1b01bd - name: github.com/gorilla/mux - version: f15e0c49460fd49eebe2bcc8486b05d1bef68d3a -- name: github.com/gorilla/websocket - version: 1f512fc3f05332ba7117626cdfb4e07474e58e60 + version: 9c19ed558d5df4da88e2ade9c8940d742aef0e7e - name: github.com/hashicorp/consul - version: de080672fee9e6104572eeea89eccdca135bb918 + version: f6fef66e1bf17be4f3c9855fbec6de802ca6bd7d subpackages: - api -- name: github.com/hashicorp/hcl - version: 9a905a34e6280ce905da1a32344b25e81011197a +- name: github.com/hashicorp/go-cleanhttp + version: 875fb671b3ddc66f8e2f0acc33829c8cb989a38d +- name: github.com/hashicorp/serf + version: e4ec8cc423bbe20d26584b96efbeb9102e16d05f subpackages: - - hcl/ast - - hcl/parser - - hcl/token - - json/parser - - hcl/scanner - - hcl/strconv - - json/scanner - - json/token -- name: github.com/inconshreveable/mousetrap - version: 76626ae9c91c4f2a10f34cad8ce83ea42c93bb75 -- name: github.com/kr/pretty - version: add1dbc86daf0f983cd4a48ceb39deb95c729b67 -- name: github.com/kr/text - version: 7cafcd837844e784b526369c9bce262804aebc60 + - coordinate + - serf - name: github.com/libkermit/docker version: 3b5eb2973efff7af33cfb65141deaf4ed25c6d02 - name: github.com/libkermit/docker-check version: bb75a86b169c6c5d22c0ee98278124036f272d7b -- name: github.com/magiconair/properties - version: c265cfa48dda6474e208715ca93e987829f572f8 - name: github.com/mailgun/log - version: 44874009257d4d47ba9806f1b7f72a32a015e4d8 + version: 2f35a4607f1abf71f97f77f99b0de8493ef6f4ef - name: github.com/mailgun/manners version: fada45142db3f93097ca917da107aa3fad0ffcb5 - name: github.com/mailgun/multibuf @@ -187,12 +131,12 @@ imports: version: 4f1a71750d95a5a8a46c40a67ffbed8129c2f138 - name: github.com/miekg/dns version: 48ab6605c66ac797e07f615101c3e9e10e932b66 -- name: github.com/mitchellh/mapstructure - version: d2dd0262208475919e1a362f675cfc0e7c10e905 - name: github.com/moul/http2curl version: b1479103caacaa39319f75e7f57fc545287fca0d +- name: github.com/ogier/pflag + version: 45c278ab3607870051a2ea9040bb85fcb8557481 - name: github.com/opencontainers/runc - version: 2441732d6fcc0fb0a542671a4372e0c7bc99c19e + version: d2d09b9bcd0573c58d7cd94e57bd7555af0c2072 subpackages: - libcontainer/user - name: github.com/parnurzeal/gorequest @@ -202,25 +146,13 @@ imports: subpackages: - difflib - name: github.com/samuel/go-zookeeper - version: fa6674abf3f4580b946a01bf7a1ce4ba8766205b + version: 6eb1b09c6ce23f305f4c81bf748b22fbc6f3f9e9 subpackages: - zk - name: github.com/Sirupsen/logrus - version: 418b41d23a1bf978c06faea5313ba194650ac088 -- name: github.com/spf13/cast - version: ee7b3e0353166ab1f3a605294ac8cd2b77953778 -- name: github.com/spf13/cobra - version: f368244301305f414206f889b1735a54cfc8bde8 - subpackages: - - cobra -- name: github.com/spf13/jwalterweatherman - version: 33c24e77fb80341fe7130ee7c594256ff08ccc46 -- name: github.com/spf13/pflag - version: cb88ea77998c3f024757528e3305022ab50b43be -- name: github.com/spf13/viper - version: a212099cbe6fbe8d07476bfda8d2d39b6ff8f325 + version: 6d9ae300aaf85d6acd2e5424081c7fcddb21dab8 - name: github.com/streamrail/concurrent-map - version: 1ce4642e5a162df67825d273a86b87e6cc8a076b + version: 65a174a3a4188c0b7099acbc6cfa0c53628d3287 - name: github.com/stretchr/objx version: cbeaeb16a013161a98496fad62933b1d21786672 - name: github.com/stretchr/testify @@ -229,13 +161,9 @@ imports: - mock - assert - name: github.com/thoas/stats - version: 54ed61c2b47e263ae2f01b86837b0c4bd1da28e8 -- name: github.com/ugorji/go - version: ea9cd21fa0bc41ee4bdd50ac7ed8cbc7ea2ed960 - subpackages: - - codec + version: 69e3c072eec2df2df41afe6214f62eb940e4cd80 - name: github.com/unrolled/render - version: 26b4e3aac686940fe29521545afad9966ddfc80c + version: 198ad4d8b8a4612176b804ca10555b222a086b40 - name: github.com/vdemeester/docker-events version: b308d2e8d639d928c882913bcb4f85b3a84c7a07 - name: github.com/vdemeester/shakers @@ -255,8 +183,6 @@ imports: - plugin/rewrite - plugin - router -- name: github.com/wendal/errors - version: f66c77a7882b399795a8987ebf87ef64a427417e - name: github.com/xenolf/lego version: b119bc45fbd1cc71348003541aac9d3a7da63654 subpackages: @@ -266,7 +192,7 @@ imports: subpackages: - ocsp - name: golang.org/x/net - version: d9558e5c97f85372afee28cf2b6059d7d3818919 + version: 6460565bec1e8891e29ff478184c71b9e443ac36 subpackages: - context - publicsuffix @@ -276,12 +202,10 @@ imports: subpackages: - unix - windows -- name: gopkg.in/alecthomas/kingpin.v2 - version: 639879d6110b1b0409410c7b737ef0bb18325038 - name: gopkg.in/fsnotify.v1 - version: 96c060f6a6b7e0d6f75fddd10efeaca3e5d1bcb0 + version: 30411dbcefb7a1da7e84f75530ad3abe4011b4f8 - name: gopkg.in/mgo.v2 - version: 22287bab4379e1fbf6002fb4eb769888f3fb224c + version: b6e2fa371e64216a45e61072a96d4e3859f169da subpackages: - bson - name: gopkg.in/square/go-jose.v1 @@ -289,6 +213,4 @@ imports: subpackages: - cipher - json -- name: gopkg.in/yaml.v2 - version: 7ad95dd0798a40da1ccdff6dff35fd177b5edf40 devImports: [] diff --git a/glide.yaml b/glide.yaml index 3ca22fb8c..d64ed3c4c 100644 --- a/glide.yaml +++ b/glide.yaml @@ -1,189 +1,78 @@ -package: main +package: github.com/containous/traefik import: -- package: github.com/coreos/etcd - version: 26e52d2bce9e3e11b77b68cc84bf91aebb1ef637 +- package: github.com/BurntSushi/toml +- package: github.com/BurntSushi/ty subpackages: - - client -- package: github.com/mailgun/log - version: 44874009257d4d47ba9806f1b7f72a32a015e4d8 + - fun +- package: github.com/Sirupsen/logrus +- package: github.com/cenkalti/backoff +- package: github.com/codegangsta/negroni +- package: github.com/containous/flaeg + version: c425b9d758df1864ca838dbd433f1cf8f5097d51 - package: github.com/containous/oxy - version: 183212964e13e7b8afe01a08b193d04300554a68 subpackages: - cbreaker + - connlimit - forward - - memmetrics - roundrobin + - stream - utils -- package: github.com/hashicorp/consul - version: de080672fee9e6104572eeea89eccdca135bb918 - subpackages: - - api -- package: github.com/samuel/go-zookeeper - version: fa6674abf3f4580b946a01bf7a1ce4ba8766205b - subpackages: - - zk -- package: github.com/docker/libtrust - version: 9cbd2a1374f46905c68a4eb3694a130610adc62a -- package: github.com/go-check/check - version: 11d3bc7aa68e238947792f30573146a3231fc0f1 -- package: golang.org/x/net - version: d9558e5c97f85372afee28cf2b6059d7d3818919 - subpackages: - - context -- package: github.com/gorilla/handlers - version: 40694b40f4a928c062f56849989d3e9cd0570e5f -- package: github.com/docker/libkv - version: 7283ef27ed32fe267388510a91709b307bb9942c -- package: github.com/alecthomas/template - version: b867cc6ab45cece8143cfcc6fc9c77cf3f2c23c0 -- package: github.com/vdemeester/shakers - version: 24d7f1d6a71aa5d9cbe7390e4afb66b7eef9e1b3 -- package: github.com/alecthomas/units - version: 6b4e7dc5e3143b85ea77909c72caf89416fc2915 -- package: github.com/gambol99/go-marathon - version: ade11d1dc2884ee1f387078fc28509559b6235d1 -- package: github.com/vulcand/predicate - version: cb0bff91a7ab7cf7571e661ff883fc997bc554a3 -- package: github.com/thoas/stats - version: 54ed61c2b47e263ae2f01b86837b0c4bd1da28e8 -- package: github.com/Sirupsen/logrus - version: 418b41d23a1bf978c06faea5313ba194650ac088 -- package: github.com/unrolled/render - version: 26b4e3aac686940fe29521545afad9966ddfc80c -- package: github.com/flynn/go-shlex - version: 3f9db97f856818214da2e1057f8ad84803971cff -- package: github.com/boltdb/bolt - version: 51f99c862475898df9773747d3accd05a7ca33c1 -- package: gopkg.in/mgo.v2 - version: 22287bab4379e1fbf6002fb4eb769888f3fb224c - subpackages: - - bson -- package: github.com/docker/docker - version: 9837ec4da53f15f9120d53a6e1517491ba8b0261 - subpackages: - - autogen - - api - - cliconfig - - daemon/network - - graph/tags - - image - - opts - - pkg/archive - - pkg/fileutils - - pkg/homedir - - pkg/httputils - - pkg/ioutils - - pkg/jsonmessage - - pkg/mflag - - pkg/nat - - pkg/parsers - - pkg/pools - - pkg/promise - - pkg/random - - pkg/stdcopy - - pkg/stringid - - pkg/symlink - - pkg/system - - pkg/tarsum - - pkg/term - - pkg/timeutils - - pkg/tlsconfig - - pkg/ulimit - - pkg/units - - pkg/urlutil - - pkg/useragent - - pkg/version - - registry - - runconfig - - utils - - volume -- package: github.com/mailgun/timetools - version: fd192d755b00c968d312d23f521eb0cdc6f66bd0 -- package: github.com/codegangsta/negroni - version: c7477ad8e330bef55bf1ebe300cf8aa67c492d1b -- package: gopkg.in/yaml.v2 - version: 7ad95dd0798a40da1ccdff6dff35fd177b5edf -- package: github.com/opencontainers/runc - version: 2441732d6fcc0fb0a542671a4372e0c7bc99c19e - subpackages: - - libcontainer/user -- package: github.com/gorilla/mux - version: f15e0c49460fd49eebe2bcc8486b05d1bef68d3a -- package: github.com/BurntSushi/ty - version: 6add9cd6ad42d389d6ead1dde60b4ad71e46fd74 -- package: github.com/elazarl/go-bindata-assetfs - version: d5cac425555ca5cf00694df246e04f05e6a55150 -- package: github.com/BurntSushi/toml - version: bbd5bb678321a0d6e58f1099321dfa73391c1b6f -- package: gopkg.in/alecthomas/kingpin.v2 - version: 639879d6110b1b0409410c7b737ef0bb18325038 -- package: github.com/cenkalti/backoff - version: 4dc77674aceaabba2c7e3da25d4c823edfb73f99 -- package: gopkg.in/fsnotify.v1 - version: 96c060f6a6b7e0d6f75fddd10efeaca3e5d1bcb0 -- package: github.com/mailgun/manners - version: fada45142db3f93097ca917da107aa3fad0ffcb5 -- package: github.com/gorilla/context - version: 215affda49addc4c8ef7e2534915df2c8c35c6cd -- package: github.com/codahale/hdrhistogram - version: 954f16e8b9ef0e5d5189456aa4c1202758e04f17 -- package: github.com/gorilla/websocket -- package: github.com/donovanhide/eventsource - version: d8a3071799b98cacd30b6da92f536050ccfe6da4 -- package: github.com/golang/glog - version: fca8c8854093a154ff1eb580aae10276ad6b1b5f -- package: github.com/spf13/cast - version: ee7b3e0353166ab1f3a605294ac8cd2b77953778 -- package: github.com/mitchellh/mapstructure -- package: github.com/spf13/jwalterweatherman -- package: github.com/spf13/pflag -- package: github.com/wendal/errors -- package: github.com/hashicorp/hcl -- package: github.com/kr/pretty -- package: github.com/magiconair/properties -- package: github.com/kr/text -- package: github.com/spf13/viper - version: a212099cbe6fbe8d07476bfda8d2d39b6ff8f325 -- package: github.com/spf13/cobra - subpackages: - - cobra -- package: github.com/google/go-querystring - subpackages: - - query -- package: github.com/vulcand/vulcand - subpackages: - - plugin/rewrite -- package: github.com/stretchr/testify - subpackages: - - mock -- package: github.com/xenolf/lego -- package: github.com/libkermit/docker-check - version: bb75a86b169c6c5d22c0ee98278124036f272d7b -- package: github.com/libkermit/docker - version: 3b5eb2973efff7af33cfb65141deaf4ed25c6d02 -- package: github.com/docker/libcompose - version: 8ee7bcc364f7b8194581a3c6bd9fa019467c7873 -- package: github.com/docker/distribution - version: 467fc068d88aa6610691b7f1a677271a3fac4aac - subpackages: - - reference +- package: github.com/containous/staert + version: ff272631ecfc9c22490b651fea0f08d364d46518 - package: github.com/docker/engine-api version: 3d3d0b6c9d2651aac27f416a6da0224c1875b3eb subpackages: - client - types - - types/container + - types/events - types/filters - - types/strslice -- package: github.com/vdemeester/docker-events - package: github.com/docker/go-connections subpackages: - - nat - sockets - tlsconfig -- package: github.com/docker/go-units -- package: github.com/mailgun/multibuf -- package: github.com/streamrail/concurrent-map +- package: github.com/docker/libkv + subpackages: + - store + - store/boltdb + - store/consul + - store/etcd + - store/zookeeper +- package: github.com/elazarl/go-bindata-assetfs +- package: github.com/gambol99/go-marathon + version: ade11d1dc2884ee1f387078fc28509559b6235d1 +- package: github.com/gorilla/mux +- package: github.com/hashicorp/consul + subpackages: + - api +- package: github.com/mailgun/manners - package: github.com/parnurzeal/gorequest +- package: github.com/streamrail/concurrent-map +- package: github.com/stretchr/testify + subpackages: + - mock +- package: github.com/thoas/stats +- package: github.com/unrolled/render +- package: github.com/vdemeester/docker-events +- package: github.com/vulcand/vulcand + subpackages: + - plugin/rewrite +- package: github.com/xenolf/lego + subpackages: + - acme +- package: golang.org/x/net + subpackages: + - context +- package: gopkg.in/fsnotify.v1 +- package: github.com/libkermit/docker-check + version: bb75a86b169c6c5d22c0ee98278124036f272d7b +- package: github.com/libkermit/docker + version: 3b5eb2973efff7af33cfb65141deaf4ed25c6d02 +- package: github.com/docker/docker + version: 9837ec4da53f15f9120d53a6e1517491ba8b0261 + subpackages: + - namesgenerator +- package: github.com/go-check/check +- package: github.com/docker/libcompose + version: 8ee7bcc364f7b8194581a3c6bd9fa019467c7873 - package: github.com/mattn/go-shellwords -- package: github.com/moul/http2curl +- package: github.com/vdemeester/shakers diff --git a/integration/basic_test.go b/integration/basic_test.go index 40f5ab997..7e5549174 100644 --- a/integration/basic_test.go +++ b/integration/basic_test.go @@ -5,7 +5,6 @@ import ( "os/exec" "time" - "fmt" "github.com/go-check/check" "bytes" @@ -15,34 +14,6 @@ import ( // SimpleSuite type SimpleSuite struct{ BaseSuite } -func (s *SimpleSuite) TestNoOrInexistentConfigShouldFail(c *check.C) { - cmd := exec.Command(traefikBinary) - - var b bytes.Buffer - cmd.Stdout = &b - cmd.Stderr = &b - - cmd.Start() - time.Sleep(500 * time.Millisecond) - output := b.Bytes() - - c.Assert(string(output), checker.Contains, "No configuration file found") - cmd.Process.Kill() - - nonExistentFile := "non/existent/file.toml" - cmd = exec.Command(traefikBinary, "--configFile="+nonExistentFile) - - cmd.Stdout = &b - cmd.Stderr = &b - - cmd.Start() - time.Sleep(500 * time.Millisecond) - output = b.Bytes() - - c.Assert(string(output), checker.Contains, fmt.Sprintf("Error reading configuration file: open %s: no such file or directory", nonExistentFile)) - cmd.Process.Kill() -} - func (s *SimpleSuite) TestInvalidConfigShouldFail(c *check.C) { cmd := exec.Command(traefikBinary, "--configFile=fixtures/invalid_configuration.toml") @@ -55,7 +26,7 @@ func (s *SimpleSuite) TestInvalidConfigShouldFail(c *check.C) { defer cmd.Process.Kill() output := b.Bytes() - c.Assert(string(output), checker.Contains, "While parsing config: Near line 0 (last key parsed ''): Bare keys cannot contain '{'") + c.Assert(string(output), checker.Contains, "Near line 0 (last key parsed ''): Bare keys cannot contain '{'") } func (s *SimpleSuite) TestSimpleDefaultConfig(c *check.C) { diff --git a/provider/boltdb.go b/provider/boltdb.go index 0f941627e..4c2a33844 100644 --- a/provider/boltdb.go +++ b/provider/boltdb.go @@ -9,7 +9,7 @@ import ( // BoltDb holds configurations of the BoltDb provider. type BoltDb struct { - Kv `mapstructure:",squash"` + Kv } // Provide allows the provider to provide configurations to traefik diff --git a/provider/consul.go b/provider/consul.go index a2df6852b..d94dc7e03 100644 --- a/provider/consul.go +++ b/provider/consul.go @@ -9,7 +9,7 @@ import ( // Consul holds configurations of the Consul provider. type Consul struct { - Kv `mapstructure:",squash"` + Kv } // Provide allows the provider to provide configurations to traefik diff --git a/provider/consul_catalog.go b/provider/consul_catalog.go index 90606e9d4..2aecc4622 100644 --- a/provider/consul_catalog.go +++ b/provider/consul_catalog.go @@ -23,11 +23,11 @@ const ( // ConsulCatalog holds configurations of the Consul catalog provider. type ConsulCatalog struct { - BaseProvider `mapstructure:",squash"` - Endpoint string - Domain string - client *api.Client - Prefix string + BaseProvider + Endpoint string `description:"Consul server endpoint"` + Domain string `description:"Default domain used"` + client *api.Client + Prefix string } type serviceUpdate struct { diff --git a/provider/docker.go b/provider/docker.go index 3c7fe8f38..453c33476 100644 --- a/provider/docker.go +++ b/provider/docker.go @@ -29,18 +29,18 @@ const DockerAPIVersion string = "1.21" // Docker holds configurations of the Docker provider. type Docker struct { - BaseProvider `mapstructure:",squash"` - Endpoint string - Domain string - TLS *DockerTLS + BaseProvider + Endpoint string `description:"Docker server endpoint. Can be a tcp or a unix socket endpoint"` + Domain string `description:"Default domain used"` + TLS *DockerTLS `description:"Enable Docker TLS support"` } // DockerTLS holds TLS specific configurations type DockerTLS struct { - CA string - Cert string - Key string - InsecureSkipVerify bool + CA string `description:"TLS CA"` + Cert string `description:"TLS cert"` + Key string `description:"TLS key"` + InsecureSkipVerify bool `description:"TLS insecure skip verify"` } func (provider *Docker) createClient() (client.APIClient, error) { diff --git a/provider/etcd.go b/provider/etcd.go index 3d0b9e428..a7fd7ae6a 100644 --- a/provider/etcd.go +++ b/provider/etcd.go @@ -9,7 +9,7 @@ import ( // Etcd holds configurations of the Etcd provider. type Etcd struct { - Kv `mapstructure:",squash"` + Kv } // Provide allows the provider to provide configurations to traefik diff --git a/provider/file.go b/provider/file.go index 6b943b199..1b463593a 100644 --- a/provider/file.go +++ b/provider/file.go @@ -14,7 +14,7 @@ import ( // File holds configurations of the File provider. type File struct { - BaseProvider `mapstructure:",squash"` + BaseProvider } // Provide allows the provider to provide configurations to traefik diff --git a/provider/k8s/client.go b/provider/k8s/client.go index 957d66a9b..0b2195615 100644 --- a/provider/k8s/client.go +++ b/provider/k8s/client.go @@ -16,12 +16,14 @@ const ( APIEndpoint = "/api/v1" extentionsEndpoint = "/apis/extensions/v1beta1" defaultIngress = "/ingresses" + namespaces = "/namespaces/" ) // Client is a client for the Kubernetes master. type Client interface { GetIngresses(predicate func(Ingress) bool) ([]Ingress, error) - GetServices(predicate func(Service) bool) ([]Service, error) + GetService(name, namespace string) (Service, error) + GetEndpoints(name, namespace string) (Endpoints, error) WatchAll(stopCh <-chan bool) (chan interface{}, chan error, error) } @@ -76,26 +78,20 @@ func (c *clientImpl) WatchIngresses(stopCh <-chan bool) (chan interface{}, chan return c.watch(getURL, stopCh) } -// GetServices returns all services in the cluster -func (c *clientImpl) GetServices(predicate func(Service) bool) ([]Service, error) { - getURL := c.endpointURL + APIEndpoint + "/services" +// GetService returns the named service from the named namespace +func (c *clientImpl) GetService(name, namespace string) (Service, error) { + getURL := c.endpointURL + APIEndpoint + namespaces + namespace + "/services/" + name body, err := c.do(c.request(getURL)) if err != nil { - return nil, fmt.Errorf("failed to create services request: GET %q : %v", getURL, err) + return Service{}, fmt.Errorf("failed to create services request: GET %q : %v", getURL, err) } - var serviceList ServiceList - if err := json.Unmarshal(body, &serviceList); err != nil { - return nil, fmt.Errorf("failed to decode list of services resources: %v", err) + var service Service + if err := json.Unmarshal(body, &service); err != nil { + return Service{}, fmt.Errorf("failed to decode service resource: %v", err) } - services := serviceList.Items[:0] - for _, service := range serviceList.Items { - if predicate(service) { - services = append(services, service) - } - } - return services, nil + return service, nil } // WatchServices returns all services in the cluster @@ -104,21 +100,26 @@ func (c *clientImpl) WatchServices(stopCh <-chan bool) (chan interface{}, chan e return c.watch(getURL, stopCh) } -// WatchEvents returns events in the cluster -func (c *clientImpl) WatchEvents(stopCh <-chan bool) (chan interface{}, chan error, error) { - getURL := c.endpointURL + APIEndpoint + "/events" - return c.watch(getURL, stopCh) +// GetEndpoints returns the named Endpoints +// Endpoints have the same name as the coresponding service +func (c *clientImpl) GetEndpoints(name, namespace string) (Endpoints, error) { + getURL := c.endpointURL + APIEndpoint + namespaces + namespace + "/endpoints/" + name + + body, err := c.do(c.request(getURL)) + if err != nil { + return Endpoints{}, fmt.Errorf("failed to create endpoints request: GET %q : %v", getURL, err) + } + + var endpoints Endpoints + if err := json.Unmarshal(body, &endpoints); err != nil { + return Endpoints{}, fmt.Errorf("failed to decode endpoints resources: %v", err) + } + return endpoints, nil } -// WatchPods returns pods in the cluster -func (c *clientImpl) WatchPods(stopCh <-chan bool) (chan interface{}, chan error, error) { - getURL := c.endpointURL + APIEndpoint + "/pods" - return c.watch(getURL, stopCh) -} - -// WatchReplicationControllers returns ReplicationControllers in the cluster -func (c *clientImpl) WatchReplicationControllers(stopCh <-chan bool) (chan interface{}, chan error, error) { - getURL := c.endpointURL + APIEndpoint + "/replicationcontrollers" +// WatchEndpoints returns endpoints in the cluster +func (c *clientImpl) WatchEndpoints(stopCh <-chan bool) (chan interface{}, chan error, error) { + getURL := c.endpointURL + APIEndpoint + "/endpoints" return c.watch(getURL, stopCh) } @@ -137,13 +138,8 @@ func (c *clientImpl) WatchAll(stopCh <-chan bool) (chan interface{}, chan error, if err != nil { return watchCh, errCh, fmt.Errorf("failed to create watch: %v", err) } - stopPods := make(chan bool) - chanPods, chanPodsErr, err := c.WatchPods(stopPods) - if err != nil { - return watchCh, errCh, fmt.Errorf("failed to create watch: %v", err) - } - stopReplicationControllers := make(chan bool) - chanReplicationControllers, chanReplicationControllersErr, err := c.WatchReplicationControllers(stopReplicationControllers) + stopEndpoints := make(chan bool) + chanEndpoints, chanEndpointsErr, err := c.WatchEndpoints(stopEndpoints) if err != nil { return watchCh, errCh, fmt.Errorf("failed to create watch: %v", err) } @@ -152,32 +148,26 @@ func (c *clientImpl) WatchAll(stopCh <-chan bool) (chan interface{}, chan error, defer close(errCh) defer close(stopIngresses) defer close(stopServices) - defer close(stopPods) - defer close(stopReplicationControllers) + defer close(stopEndpoints) for { select { case <-stopCh: stopIngresses <- true stopServices <- true - stopPods <- true - stopReplicationControllers <- true + stopEndpoints <- true return case err := <-chanIngressesErr: errCh <- err case err := <-chanServicesErr: errCh <- err - case err := <-chanPodsErr: - errCh <- err - case err := <-chanReplicationControllersErr: + case err := <-chanEndpointsErr: errCh <- err case event := <-chanIngresses: watchCh <- event case event := <-chanServices: watchCh <- event - case event := <-chanPods: - watchCh <- event - case event := <-chanReplicationControllers: + case event := <-chanEndpoints: watchCh <- event } } diff --git a/provider/k8s/endpoints.go b/provider/k8s/endpoints.go new file mode 100644 index 000000000..123ffe36c --- /dev/null +++ b/provider/k8s/endpoints.go @@ -0,0 +1,84 @@ +package k8s + +// Endpoints is a collection of endpoints that implement the actual service. Example: +// Name: "mysvc", +// Subsets: [ +// { +// Addresses: [{"ip": "10.10.1.1"}, {"ip": "10.10.2.2"}], +// Ports: [{"name": "a", "port": 8675}, {"name": "b", "port": 309}] +// }, +// { +// Addresses: [{"ip": "10.10.3.3"}], +// Ports: [{"name": "a", "port": 93}, {"name": "b", "port": 76}] +// }, +// ] +type Endpoints struct { + TypeMeta `json:",inline"` + ObjectMeta `json:"metadata,omitempty"` + + // The set of all endpoints is the union of all subsets. + Subsets []EndpointSubset +} + +// EndpointSubset is a group of addresses with a common set of ports. The +// expanded set of endpoints is the Cartesian product of Addresses x Ports. +// For example, given: +// { +// Addresses: [{"ip": "10.10.1.1"}, {"ip": "10.10.2.2"}], +// Ports: [{"name": "a", "port": 8675}, {"name": "b", "port": 309}] +// } +// The resulting set of endpoints can be viewed as: +// a: [ 10.10.1.1:8675, 10.10.2.2:8675 ], +// b: [ 10.10.1.1:309, 10.10.2.2:309 ] +type EndpointSubset struct { + Addresses []EndpointAddress + NotReadyAddresses []EndpointAddress + Ports []EndpointPort +} + +// EndpointAddress is a tuple that describes single IP address. +type EndpointAddress struct { + // The IP of this endpoint. + // IPv6 is also accepted but not fully supported on all platforms. Also, certain + // kubernetes components, like kube-proxy, are not IPv6 ready. + // TODO: This should allow hostname or IP, see #4447. + IP string + // Optional: Hostname of this endpoint + // Meant to be used by DNS servers etc. + Hostname string `json:"hostname,omitempty"` + // Optional: The kubernetes object related to the entry point. + TargetRef *ObjectReference +} + +// EndpointPort is a tuple that describes a single port. +type EndpointPort struct { + // The name of this port (corresponds to ServicePort.Name). Optional + // if only one port is defined. Must be a DNS_LABEL. + Name string + + // The port number. + Port int32 + + // The IP protocol for this port. + Protocol Protocol +} + +// ObjectReference contains enough information to let you inspect or modify the referred object. +type ObjectReference struct { + Kind string `json:"kind,omitempty"` + Namespace string `json:"namespace,omitempty"` + Name string `json:"name,omitempty"` + UID UID `json:"uid,omitempty"` + APIVersion string `json:"apiVersion,omitempty"` + ResourceVersion string `json:"resourceVersion,omitempty"` + + // Optional. If referring to a piece of an object instead of an entire object, this string + // should contain information to identify the sub-object. For example, if the object + // reference is to a container within a pod, this would take on a value like: + // "spec.containers{name}" (where "name" refers to the name of the container that triggered + // the event) or if no container name is specified "spec.containers[2]" (container with + // index 2 in this pod). This syntax is chosen only to have some well-defined way of + // referencing a part of an object. + // TODO: this design is not final and this field is subject to change in the future. + FieldPath string `json:"fieldPath,omitempty"` +} diff --git a/provider/kubernetes.go b/provider/kubernetes.go index 7362898b2..cab4217ea 100644 --- a/provider/kubernetes.go +++ b/provider/kubernetes.go @@ -1,6 +1,7 @@ package provider import ( + "fmt" log "github.com/Sirupsen/logrus" "github.com/cenkalti/backoff" "github.com/containous/traefik/provider/k8s" @@ -20,12 +21,38 @@ const ( serviceAccountCACert = "/var/run/secrets/kubernetes.io/serviceaccount/ca.crt" ) +// Namespaces holds kubernetes namespaces +type Namespaces []string + +//Set adds strings elem into the the parser +//it splits str on , and ; +func (ns *Namespaces) Set(str string) error { + fargs := func(c rune) bool { + return c == ',' || c == ';' + } + // get function + slice := strings.FieldsFunc(str, fargs) + *ns = append(*ns, slice...) + return nil +} + +//Get []string +func (ns *Namespaces) Get() interface{} { return Namespaces(*ns) } + +//String return slice in a string +func (ns *Namespaces) String() string { return fmt.Sprintf("%v", *ns) } + +//SetValue sets []string into the parser +func (ns *Namespaces) SetValue(val interface{}) { + *ns = Namespaces(val.(Namespaces)) +} + // Kubernetes holds configurations of the Kubernetes provider. type Kubernetes struct { - BaseProvider `mapstructure:",squash"` - Endpoint string - disablePassHostHeaders bool - Namespaces []string + BaseProvider + Endpoint string `description:"Kubernetes server endpoint"` + DisablePassHostHeaders bool `description:"Kubernetes disable PassHost Headers"` + Namespaces Namespaces `description:"Kubernetes namespaces"` } func (provider *Kubernetes) createClient() (k8s.Client, error) { @@ -190,31 +217,42 @@ func (provider *Kubernetes) loadIngresses(k8sClient k8s.Client) (*types.Configur Rule: ruleType + ":" + pa.Path, } } - services, err := k8sClient.GetServices(func(service k8s.Service) bool { - return service.ObjectMeta.Namespace == i.ObjectMeta.Namespace && service.Name == pa.Backend.ServiceName - }) + service, err := k8sClient.GetService(pa.Backend.ServiceName, i.ObjectMeta.Namespace) if err != nil { log.Warnf("Error retrieving services: %v", err) - continue - } - if len(services) == 0 { - // no backends found, delete frontend... delete(templateObjects.Frontends, r.Host+pa.Path) log.Warnf("Error retrieving services %s", pa.Backend.ServiceName) + continue } - for _, service := range services { - protocol := "http" - for _, port := range service.Spec.Ports { - if equalPorts(port, pa.Backend.ServicePort) { - if port.Port == 443 { - protocol = "https" - } + protocol := "http" + for _, port := range service.Spec.Ports { + if equalPorts(port, pa.Backend.ServicePort) { + if port.Port == 443 { + protocol = "https" + } + endpoints, err := k8sClient.GetEndpoints(service.ObjectMeta.Name, service.ObjectMeta.Namespace) + if err != nil { + log.Errorf("Error retrieving endpoints: %v", err) + continue + } + if len(endpoints.Subsets) == 0 { + log.Warnf("Endpoints not found for %s/%s, falling back to Service ClusterIP", service.ObjectMeta.Namespace, service.ObjectMeta.Name) templateObjects.Backends[r.Host+pa.Path].Servers[string(service.UID)] = types.Server{ URL: protocol + "://" + service.Spec.ClusterIP + ":" + strconv.Itoa(port.Port), Weight: 1, } - break + } else { + for _, subset := range endpoints.Subsets { + for _, address := range subset.Addresses { + url := protocol + "://" + address.IP + ":" + strconv.Itoa(endpointPortNumber(port, subset.Ports)) + templateObjects.Backends[r.Host+pa.Path].Servers[url] = types.Server{ + URL: url, + Weight: 1, + } + } + } } + break } } } @@ -223,6 +261,20 @@ func (provider *Kubernetes) loadIngresses(k8sClient k8s.Client) (*types.Configur return &templateObjects, nil } +func endpointPortNumber(servicePort k8s.ServicePort, endpointPorts []k8s.EndpointPort) int { + if len(endpointPorts) > 0 { + //name is optional if there is only one port + port := endpointPorts[0] + for _, endpointPort := range endpointPorts { + if servicePort.Name == endpointPort.Name { + port = endpointPort + } + } + return int(port.Port) + } + return servicePort.Port +} + func equalPorts(servicePort k8s.ServicePort, ingressPort k8s.IntOrString) bool { if servicePort.Port == ingressPort.IntValue() { return true @@ -234,7 +286,7 @@ func equalPorts(servicePort k8s.ServicePort, ingressPort k8s.IntOrString) bool { } func (provider *Kubernetes) getPassHostHeader() bool { - if provider.disablePassHostHeaders { + if provider.DisablePassHostHeaders { return false } return true diff --git a/provider/kubernetes_test.go b/provider/kubernetes_test.go index b74b0536d..25e48b8d7 100644 --- a/provider/kubernetes_test.go +++ b/provider/kubernetes_test.go @@ -10,6 +10,9 @@ import ( func TestLoadIngresses(t *testing.T) { ingresses := []k8s.Ingress{{ + ObjectMeta: k8s.ObjectMeta{ + Namespace: "testing", + }, Spec: k8s.IngressSpec{ Rules: []k8s.IngressRule{ { @@ -21,7 +24,7 @@ func TestLoadIngresses(t *testing.T) { Path: "/bar", Backend: k8s.IngressBackend{ ServiceName: "service1", - ServicePort: k8s.FromString("http"), + ServicePort: k8s.FromInt(80), }, }, }, @@ -36,7 +39,7 @@ func TestLoadIngresses(t *testing.T) { { Backend: k8s.IngressBackend{ ServiceName: "service3", - ServicePort: k8s.FromInt(443), + ServicePort: k8s.FromString("https"), }, }, { @@ -55,23 +58,24 @@ func TestLoadIngresses(t *testing.T) { services := []k8s.Service{ { ObjectMeta: k8s.ObjectMeta{ - Name: "service1", - UID: "1", + Name: "service1", + UID: "1", + Namespace: "testing", }, Spec: k8s.ServiceSpec{ ClusterIP: "10.0.0.1", Ports: []k8s.ServicePort{ { - Name: "http", - Port: 801, + Port: 80, }, }, }, }, { ObjectMeta: k8s.ObjectMeta{ - Name: "service2", - UID: "2", + Name: "service2", + UID: "2", + Namespace: "testing", }, Spec: k8s.ServiceSpec{ ClusterIP: "10.0.0.2", @@ -84,24 +88,108 @@ func TestLoadIngresses(t *testing.T) { }, { ObjectMeta: k8s.ObjectMeta{ - Name: "service3", - UID: "3", + Name: "service3", + UID: "3", + Namespace: "testing", }, Spec: k8s.ServiceSpec{ ClusterIP: "10.0.0.3", Ports: []k8s.ServicePort{ { Name: "http", + Port: 80, + }, + { + Name: "https", Port: 443, }, }, }, }, } + endpoints := []k8s.Endpoints{ + { + ObjectMeta: k8s.ObjectMeta{ + Name: "service1", + UID: "1", + Namespace: "testing", + }, + Subsets: []k8s.EndpointSubset{ + { + Addresses: []k8s.EndpointAddress{ + { + IP: "10.10.0.1", + }, + }, + Ports: []k8s.EndpointPort{ + { + Port: 8080, + }, + }, + }, + { + Addresses: []k8s.EndpointAddress{ + { + IP: "10.21.0.1", + }, + }, + Ports: []k8s.EndpointPort{ + { + Port: 8080, + }, + }, + }, + }, + }, + { + ObjectMeta: k8s.ObjectMeta{ + Name: "service3", + UID: "3", + Namespace: "testing", + }, + Subsets: []k8s.EndpointSubset{ + { + Addresses: []k8s.EndpointAddress{ + { + IP: "10.15.0.1", + }, + }, + Ports: []k8s.EndpointPort{ + { + Name: "http", + Port: 8080, + }, + { + Name: "https", + Port: 8443, + }, + }, + }, + { + Addresses: []k8s.EndpointAddress{ + { + IP: "10.15.0.2", + }, + }, + Ports: []k8s.EndpointPort{ + { + Name: "http", + Port: 9080, + }, + { + Name: "https", + Port: 9443, + }, + }, + }, + }, + }, + } watchChan := make(chan interface{}) client := clientMock{ ingresses: ingresses, services: services, + endpoints: endpoints, watchChan: watchChan, } provider := Kubernetes{} @@ -114,8 +202,12 @@ func TestLoadIngresses(t *testing.T) { Backends: map[string]*types.Backend{ "foo/bar": { Servers: map[string]types.Server{ - "1": { - URL: "http://10.0.0.1:801", + "http://10.10.0.1:8080": { + URL: "http://10.10.0.1:8080", + Weight: 1, + }, + "http://10.21.0.1:8080": { + URL: "http://10.21.0.1:8080", Weight: 1, }, }, @@ -128,8 +220,12 @@ func TestLoadIngresses(t *testing.T) { URL: "http://10.0.0.2:802", Weight: 1, }, - "3": { - URL: "https://10.0.0.3:443", + "https://10.15.0.1:8443": { + URL: "https://10.15.0.1:8443", + Weight: 1, + }, + "https://10.15.0.2:9443": { + URL: "https://10.15.0.2:9443", Weight: 1, }, }, @@ -320,7 +416,7 @@ func TestRuleType(t *testing.T) { services: services, watchChan: watchChan, } - provider := Kubernetes{disablePassHostHeaders: true} + provider := Kubernetes{DisablePassHostHeaders: true} actualConfig, err := provider.loadIngresses(client) actual := actualConfig.Frontends if err != nil { @@ -442,7 +538,7 @@ func TestGetPassHostHeader(t *testing.T) { services: services, watchChan: watchChan, } - provider := Kubernetes{disablePassHostHeaders: true} + provider := Kubernetes{DisablePassHostHeaders: true} actual, err := provider.loadIngresses(client) if err != nil { t.Fatalf("error %+v", err) @@ -1109,7 +1205,7 @@ func TestHostlessIngress(t *testing.T) { services: services, watchChan: watchChan, } - provider := Kubernetes{disablePassHostHeaders: true} + provider := Kubernetes{DisablePassHostHeaders: true} actual, err := provider.loadIngresses(client) if err != nil { t.Fatalf("error %+v", err) @@ -1150,6 +1246,7 @@ func TestHostlessIngress(t *testing.T) { type clientMock struct { ingresses []k8s.Ingress services []k8s.Service + endpoints []k8s.Endpoints watchChan chan interface{} } @@ -1165,15 +1262,24 @@ func (c clientMock) GetIngresses(predicate func(k8s.Ingress) bool) ([]k8s.Ingres func (c clientMock) WatchIngresses(predicate func(k8s.Ingress) bool, stopCh <-chan bool) (chan interface{}, chan error, error) { return c.watchChan, make(chan error), nil } -func (c clientMock) GetServices(predicate func(k8s.Service) bool) ([]k8s.Service, error) { - var services []k8s.Service +func (c clientMock) GetService(name, namespace string) (k8s.Service, error) { for _, service := range c.services { - if predicate(service) { - services = append(services, service) + if service.Namespace == namespace && service.Name == name { + return service, nil } } - return services, nil + return k8s.Service{}, nil } + +func (c clientMock) GetEndpoints(name, namespace string) (k8s.Endpoints, error) { + for _, endpoints := range c.endpoints { + if endpoints.Namespace == namespace && endpoints.Name == name { + return endpoints, nil + } + } + return k8s.Endpoints{}, nil +} + func (c clientMock) WatchAll(stopCh <-chan bool) (chan interface{}, chan error, error) { return c.watchChan, make(chan error), nil } diff --git a/provider/kv.go b/provider/kv.go index b257fd71e..713416781 100644 --- a/provider/kv.go +++ b/provider/kv.go @@ -22,20 +22,20 @@ import ( // Kv holds common configurations of key-value providers. type Kv struct { - BaseProvider `mapstructure:",squash"` - Endpoint string - Prefix string - TLS *KvTLS - storeType store.Backend - kvclient store.Store + BaseProvider + Endpoint string `description:"Comma sepparated server endpoints"` + Prefix string `description:"Prefix used for KV store"` + TLS *KvTLS `description:"Enable TLS support"` + storeType store.Backend + kvclient store.Store } // KvTLS holds TLS specific configurations type KvTLS struct { - CA string - Cert string - Key string - InsecureSkipVerify bool + CA string `description:"TLS CA"` + Cert string `description:"TLS cert"` + Key string `description:"TLS key"` + InsecureSkipVerify bool `description:"TLS insecure skip verify"` } func (provider *Kv) watchKv(configurationChan chan<- types.ConfigMessage, prefix string, stop chan bool) error { diff --git a/provider/marathon.go b/provider/marathon.go index 63ba9d8c0..91b4a2e8d 100644 --- a/provider/marathon.go +++ b/provider/marathon.go @@ -20,10 +20,10 @@ import ( // Marathon holds configuration of the Marathon provider. type Marathon struct { - BaseProvider `mapstructure:",squash"` - Endpoint string - Domain string - ExposedByDefault bool + BaseProvider `mapstructure:",squash" description:"go through"` + Endpoint string `description:"Marathon server endpoint. You can also specify multiple endpoint for Marathon"` + Domain string `description:"Default domain used"` + ExposedByDefault bool `description:"Expose Marathon apps by default"` Basic *MarathonBasic TLS *tls.Config marathonClient marathon.Marathon @@ -36,8 +36,8 @@ type MarathonBasic struct { } type lightMarathonClient interface { - Applications(url.Values) (*marathon.Applications, error) AllTasks(v url.Values) (*marathon.Tasks, error) + Applications(url.Values) (*marathon.Applications, error) } // Provide allows the provider to provide configurations to traefik diff --git a/provider/provider.go b/provider/provider.go index 3fa7612ec..eddf5a76c 100644 --- a/provider/provider.go +++ b/provider/provider.go @@ -22,8 +22,8 @@ type Provider interface { // BaseProvider should be inherited by providers type BaseProvider struct { - Watch bool - Filename string + Watch bool `description:"Watch provider"` + Filename string `description:"Override default configuration template. For advanced users :)"` } func (p *BaseProvider) getConfiguration(defaultTemplateFile string, funcMap template.FuncMap, templateObjects interface{}) (*types.Configuration, error) { diff --git a/traefik.go b/traefik.go index dec978a6f..a313c6466 100644 --- a/traefik.go +++ b/traefik.go @@ -1,16 +1,133 @@ package main import ( + "encoding/json" + log "github.com/Sirupsen/logrus" + "github.com/containous/flaeg" + "github.com/containous/staert" + "github.com/containous/traefik/acme" + "github.com/containous/traefik/middlewares" + "github.com/containous/traefik/provider" fmtlog "log" + "net/http" "os" + "reflect" "runtime" + "strings" ) func main() { runtime.GOMAXPROCS(runtime.NumCPU()) - if err := traefikCmd.Execute(); err != nil { + + //traefik config inits + traefikConfiguration := NewTraefikConfiguration() + traefikPointersConfiguration := NewTraefikDefaultPointersConfiguration() + //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`, + Config: struct{}{}, + DefaultPointersConfig: struct{}{}, + Run: func() error { + fmtlog.Println(Version + " built on the " + BuildDate) + return nil + }, + } + + //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{}) + f.AddParser(reflect.TypeOf(provider.Namespaces{}), &provider.Namespaces{}) + f.AddParser(reflect.TypeOf([]acme.Domain{}), &acme.Domains{}) + + //add version command + f.AddCommand(versionCmd) + if _, err := f.Parse(traefikCmd); err != nil { fmtlog.Println(err) os.Exit(-1) } + + //staert init + s := staert.NewStaert(traefikCmd) + //init toml source + toml := staert.NewTomlSource("traefik", []string{traefikConfiguration.ConfigFile, "/etc/traefik/", "$HOME/.traefik/", "."}) + + //add sources to staert + s.AddSource(toml) + s.AddSource(f) + if _, err := s.LoadConfig(); err != nil { + fmtlog.Println(err) + } + if traefikConfiguration.File != nil && len(traefikConfiguration.File.Filename) == 0 { + // no filename, setting to global config file + log.Debugf("ConfigFileUsed %s", toml.ConfigFileUsed()) + traefikConfiguration.File.Filename = toml.ConfigFileUsed() + } + if len(traefikConfiguration.EntryPoints) == 0 { + traefikConfiguration.EntryPoints = map[string]*EntryPoint{"http": {Address: ":80"}} + traefikConfiguration.DefaultEntryPoints = []string{"http"} + } + if err := s.Run(); err != nil { + fmtlog.Println(err) + os.Exit(-1) + } + 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.Infof("Traefik version %s built on %s", Version, BuildDate) + log.Debugf("Global configuration loaded %s", string(jsonConf)) + server := NewServer(globalConfiguration) + server.Start() + defer server.Close() + log.Info("Shutting down") +} diff --git a/web.go b/web.go index f32874aa6..f402edfec 100644 --- a/web.go +++ b/web.go @@ -23,10 +23,11 @@ var metrics = stats.New() // WebProvider is a provider.Provider implementation that provides the UI. // FIXME to be handled another way. type WebProvider struct { - Address string - CertFile, KeyFile string - ReadOnly bool - server *Server + Address string `description:"Web administration port"` + CertFile string `description:"SSL certificate"` + KeyFile string `description:"SSL certificate"` + ReadOnly bool `description:"Enable read only API"` + server *Server } var ( @@ -92,7 +93,7 @@ func (provider *WebProvider) Provide(configurationChan chan<- types.ConfigMessag 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: autogen.Asset, AssetDir: autogen.AssetDir, Prefix: "static"}))) + systemRouter.Methods("GET").PathPrefix("/dashboard/").Handler(http.StripPrefix("/dashboard/", http.FileServer(&assetfs.AssetFS{Asset: autogen.Asset, AssetInfo: autogen.AssetInfo, AssetDir: autogen.AssetDir, Prefix: "static"}))) // expvars if provider.server.globalConfiguration.Debug {