From 1de5434e1ad5a2a8f04e04ab8a7b464894d276ec Mon Sep 17 00:00:00 2001 From: Samuel BERTHE Date: Tue, 31 May 2016 09:54:42 +0200 Subject: [PATCH] refacto(constraints): Migration to Flaeg cli library --- configuration.go | 46 +++++++++++++++++++++- glide.lock | 3 +- integration/constraint_test.go | 2 - provider/boltdb.go | 2 +- provider/consul.go | 2 +- provider/consul_catalog.go | 2 +- provider/docker.go | 2 +- provider/etcd.go | 2 +- provider/file.go | 2 +- provider/kubernetes.go | 2 +- provider/kv.go | 2 +- provider/marathon.go | 2 +- provider/provider.go | 10 ++--- provider/provider_test.go | 12 +++--- provider/zk.go | 2 +- server.go | 2 +- traefik.go | 3 ++ types/types.go | 72 +--------------------------------- web.go | 2 +- 19 files changed, 73 insertions(+), 99 deletions(-) diff --git a/configuration.go b/configuration.go index ee996b693..2df24ca82 100644 --- a/configuration.go +++ b/configuration.go @@ -26,7 +26,7 @@ type GlobalConfiguration struct { 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'"` - Constraints []*types.Constraint `description:"Filter services by constraint, matching with service tags."` + Constraints Constraints `description:"Filter services by constraint, matching with service tags."` 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."` @@ -146,6 +146,41 @@ func (ep *EntryPoints) Type() string { return fmt.Sprint("entrypoints²") } +// Constraints holds a Constraint parser +type Constraints []types.Constraint + +//Set []*Constraint +func (cs *Constraints) Set(str string) error { + exps := strings.Split(str, ",") + if len(exps) == 0 { + return errors.New("Bad Constraint format: " + str) + } + for _, exp := range exps { + constraint, err := types.NewConstraint(exp) + if err != nil { + return err + } + *cs = append(*cs, *constraint) + } + return nil +} + +//Get []*Constraint +func (cs *Constraints) Get() interface{} { return []types.Constraint(*cs) } + +//String returns []*Constraint in string +func (cs *Constraints) String() string { return fmt.Sprintf("%+v", *cs) } + +//SetValue sets []*Constraint into the parser +func (cs *Constraints) SetValue(val interface{}) { + *cs = Constraints(val.([]types.Constraint)) +} + +// Type exports the Constraints type as a string +func (cs *Constraints) Type() string { + return fmt.Sprint("constraint²") +} + // EntryPoint holds an entry point configuration of the reverse proxy (ip, port, TLS...) type EntryPoint struct { Network string @@ -232,6 +267,7 @@ func NewTraefikDefaultPointersConfiguration() *TraefikConfiguration { defaultMarathon.Watch = true defaultMarathon.Endpoint = "http://127.0.0.1:8080" defaultMarathon.ExposedByDefault = true + defaultMarathon.Constraints = []types.Constraint{} // default Consul var defaultConsul provider.Consul @@ -239,10 +275,12 @@ func NewTraefikDefaultPointersConfiguration() *TraefikConfiguration { defaultConsul.Endpoint = "127.0.0.1:8500" defaultConsul.Prefix = "/traefik" defaultConsul.TLS = &provider.KvTLS{} + defaultConsul.Constraints = []types.Constraint{} // default ConsulCatalog var defaultConsulCatalog provider.ConsulCatalog defaultConsulCatalog.Endpoint = "127.0.0.1:8500" + defaultConsulCatalog.Constraints = []types.Constraint{} // default Etcd var defaultEtcd provider.Etcd @@ -250,23 +288,27 @@ func NewTraefikDefaultPointersConfiguration() *TraefikConfiguration { defaultEtcd.Endpoint = "127.0.0.1:400" defaultEtcd.Prefix = "/traefik" defaultEtcd.TLS = &provider.KvTLS{} + defaultEtcd.Constraints = []types.Constraint{} //default Zookeeper var defaultZookeeper provider.Zookepper defaultZookeeper.Watch = true defaultZookeeper.Endpoint = "127.0.0.1:2181" defaultZookeeper.Prefix = "/traefik" + defaultZookeeper.Constraints = []types.Constraint{} //default Boltdb var defaultBoltDb provider.BoltDb defaultBoltDb.Watch = true defaultBoltDb.Endpoint = "127.0.0.1:4001" defaultBoltDb.Prefix = "/traefik" + defaultBoltDb.Constraints = []types.Constraint{} //default Kubernetes var defaultKubernetes provider.Kubernetes defaultKubernetes.Watch = true defaultKubernetes.Endpoint = "127.0.0.1:8080" + defaultKubernetes.Constraints = []types.Constraint{} defaultConfiguration := GlobalConfiguration{ Docker: &defaultDocker, @@ -295,7 +337,7 @@ func NewTraefikConfiguration() *TraefikConfiguration { TraefikLogsFile: "", LogLevel: "ERROR", EntryPoints: map[string]*EntryPoint{}, - Constraints: []*Constraint, + Constraints: []types.Constraint{}, DefaultEntryPoints: []string{}, ProvidersThrottleDuration: time.Duration(2 * time.Second), MaxIdleConnsPerHost: 200, diff --git a/glide.lock b/glide.lock index 1f95df545..69e24ab89 100644 --- a/glide.lock +++ b/glide.lock @@ -213,7 +213,6 @@ imports: subpackages: - cipher - json -- name: gopkg.in/yaml.v2 - version: 7ad95dd0798a40da1ccdff6dff35fd177b5edf40 - name: github.com/ryanuber/go-glob version: 572520ed46dbddaed19ea3d9541bdd0494163693 +devImports: [] diff --git a/integration/constraint_test.go b/integration/constraint_test.go index 50e194d2e..66ad8bbc0 100644 --- a/integration/constraint_test.go +++ b/integration/constraint_test.go @@ -1,8 +1,6 @@ package main import ( - //"io/ioutil" - //"fmt" "net/http" "os/exec" "time" diff --git a/provider/boltdb.go b/provider/boltdb.go index 50b1fa36a..574956ace 100644 --- a/provider/boltdb.go +++ b/provider/boltdb.go @@ -14,7 +14,7 @@ type BoltDb struct { // Provide allows the provider to provide configurations to traefik // using the given configuration channel. -func (provider *BoltDb) Provide(configurationChan chan<- types.ConfigMessage, pool *safe.Pool, constraints []*types.Constraint) error { +func (provider *BoltDb) Provide(configurationChan chan<- types.ConfigMessage, pool *safe.Pool, constraints []types.Constraint) error { provider.storeType = store.BOLTDB boltdb.Register() return provider.provide(configurationChan, pool, constraints) diff --git a/provider/consul.go b/provider/consul.go index f74490371..f936e79f3 100644 --- a/provider/consul.go +++ b/provider/consul.go @@ -14,7 +14,7 @@ type Consul struct { // Provide allows the provider to provide configurations to traefik // using the given configuration channel. -func (provider *Consul) Provide(configurationChan chan<- types.ConfigMessage, pool *safe.Pool, constraints []*types.Constraint) error { +func (provider *Consul) Provide(configurationChan chan<- types.ConfigMessage, pool *safe.Pool, constraints []types.Constraint) error { provider.storeType = store.CONSUL consul.Register() return provider.provide(configurationChan, pool, constraints) diff --git a/provider/consul_catalog.go b/provider/consul_catalog.go index 0b0d2294b..cce6db185 100644 --- a/provider/consul_catalog.go +++ b/provider/consul_catalog.go @@ -271,7 +271,7 @@ func (provider *ConsulCatalog) watch(configurationChan chan<- types.ConfigMessag // Provide allows the provider to provide configurations to traefik // using the given configuration channel. -func (provider *ConsulCatalog) Provide(configurationChan chan<- types.ConfigMessage, pool *safe.Pool, constraints []*types.Constraint) error { +func (provider *ConsulCatalog) Provide(configurationChan chan<- types.ConfigMessage, pool *safe.Pool, constraints []types.Constraint) error { config := api.DefaultConfig() config.Address = provider.Endpoint client, err := api.NewClient(config) diff --git a/provider/docker.go b/provider/docker.go index 2565d1a84..f04a82c03 100644 --- a/provider/docker.go +++ b/provider/docker.go @@ -79,7 +79,7 @@ func (provider *Docker) createClient() (client.APIClient, error) { // Provide allows the provider to provide configurations to traefik // using the given configuration channel. -func (provider *Docker) Provide(configurationChan chan<- types.ConfigMessage, pool *safe.Pool, constraints []*types.Constraint) error { +func (provider *Docker) Provide(configurationChan chan<- types.ConfigMessage, pool *safe.Pool, constraints []types.Constraint) error { provider.Constraints = append(provider.Constraints, constraints...) // TODO register this routine in pool, and watch for stop channel safe.Go(func() { diff --git a/provider/etcd.go b/provider/etcd.go index 35f493d03..934e0f245 100644 --- a/provider/etcd.go +++ b/provider/etcd.go @@ -14,7 +14,7 @@ type Etcd struct { // Provide allows the provider to provide configurations to traefik // using the given configuration channel. -func (provider *Etcd) Provide(configurationChan chan<- types.ConfigMessage, pool *safe.Pool, constraints []*types.Constraint) error { +func (provider *Etcd) Provide(configurationChan chan<- types.ConfigMessage, pool *safe.Pool, constraints []types.Constraint) error { provider.storeType = store.ETCD etcd.Register() return provider.provide(configurationChan, pool, constraints) diff --git a/provider/file.go b/provider/file.go index b1038df77..07bcbd02f 100644 --- a/provider/file.go +++ b/provider/file.go @@ -19,7 +19,7 @@ type File struct { // Provide allows the provider to provide configurations to traefik // using the given configuration channel. -func (provider *File) Provide(configurationChan chan<- types.ConfigMessage, pool *safe.Pool, _ []*types.Constraint) error { +func (provider *File) Provide(configurationChan chan<- types.ConfigMessage, pool *safe.Pool, _ []types.Constraint) error { watcher, err := fsnotify.NewWatcher() if err != nil { log.Error("Error creating file watcher", err) diff --git a/provider/kubernetes.go b/provider/kubernetes.go index 0681ff24b..e46b165d4 100644 --- a/provider/kubernetes.go +++ b/provider/kubernetes.go @@ -81,7 +81,7 @@ func (provider *Kubernetes) createClient() (k8s.Client, error) { // Provide allows the provider to provide configurations to traefik // using the given configuration channel. -func (provider *Kubernetes) Provide(configurationChan chan<- types.ConfigMessage, pool *safe.Pool, constraints []*types.Constraint) error { +func (provider *Kubernetes) Provide(configurationChan chan<- types.ConfigMessage, pool *safe.Pool, constraints []types.Constraint) error { k8sClient, err := provider.createClient() if err != nil { return err diff --git a/provider/kv.go b/provider/kv.go index 3791dd3b4..42181718a 100644 --- a/provider/kv.go +++ b/provider/kv.go @@ -73,7 +73,7 @@ func (provider *Kv) watchKv(configurationChan chan<- types.ConfigMessage, prefix return nil } -func (provider *Kv) provide(configurationChan chan<- types.ConfigMessage, pool *safe.Pool, constraints []*types.Constraint) error { +func (provider *Kv) provide(configurationChan chan<- types.ConfigMessage, pool *safe.Pool, constraints []types.Constraint) error { storeConfig := &store.Config{ ConnectionTimeout: 30 * time.Second, Bucket: "traefik", diff --git a/provider/marathon.go b/provider/marathon.go index cf5e96622..efdaaf238 100644 --- a/provider/marathon.go +++ b/provider/marathon.go @@ -42,7 +42,7 @@ type lightMarathonClient interface { // Provide allows the provider to provide configurations to traefik // using the given configuration channel. -func (provider *Marathon) Provide(configurationChan chan<- types.ConfigMessage, pool *safe.Pool, constraints []*types.Constraint) error { +func (provider *Marathon) Provide(configurationChan chan<- types.ConfigMessage, pool *safe.Pool, constraints []types.Constraint) error { provider.Constraints = append(provider.Constraints, constraints...) operation := func() error { config := marathon.NewDefaultConfig() diff --git a/provider/provider.go b/provider/provider.go index 43d662401..d983ae7ca 100644 --- a/provider/provider.go +++ b/provider/provider.go @@ -17,14 +17,14 @@ import ( type Provider interface { // Provide allows the provider to provide configurations to traefik // using the given configuration channel. - Provide(configurationChan chan<- types.ConfigMessage, pool *safe.Pool, constraints []*types.Constraint) error + Provide(configurationChan chan<- types.ConfigMessage, pool *safe.Pool, constraints []types.Constraint) error } // BaseProvider should be inherited by providers type BaseProvider struct { - Watch bool `description:"Watch provider"` - Filename string `description:"Override default configuration template. For advanced users :)"` - Constraints []*types.Constraint `description:"Filter services by constraint, matching with Traefik tags."` + Watch bool `description:"Watch provider"` + Filename string `description:"Override default configuration template. For advanced users :)"` + Constraints []types.Constraint `description:"Filter services by constraint, matching with Traefik tags."` } // MatchConstraints must match with EVERY single contraint @@ -38,7 +38,7 @@ func (p *BaseProvider) MatchConstraints(tags []string) (bool, *types.Constraint) for _, constraint := range p.Constraints { // xor: if ok and constraint.MustMatch are equal, then no tag is currently matching with the constraint if ok := constraint.MatchConstraintWithAtLeastOneTag(tags); ok != constraint.MustMatch { - return false, constraint + return false, &constraint } } diff --git a/provider/provider_test.go b/provider/provider_test.go index 824d3ced0..7b7e487d9 100644 --- a/provider/provider_test.go +++ b/provider/provider_test.go @@ -211,13 +211,13 @@ func TestGetConfigurationReturnsCorrectMaxConnConfiguration(t *testing.T) { func TestMatchingConstraints(t *testing.T) { cases := []struct { - constraints []*types.Constraint + constraints []types.Constraint tags []string expected bool }{ // simple test: must match { - constraints: []*types.Constraint{ + constraints: []types.Constraint{ { Key: "tag", MustMatch: true, @@ -231,7 +231,7 @@ func TestMatchingConstraints(t *testing.T) { }, // simple test: must match but does not match { - constraints: []*types.Constraint{ + constraints: []types.Constraint{ { Key: "tag", MustMatch: true, @@ -245,7 +245,7 @@ func TestMatchingConstraints(t *testing.T) { }, // simple test: must not match { - constraints: []*types.Constraint{ + constraints: []types.Constraint{ { Key: "tag", MustMatch: false, @@ -259,7 +259,7 @@ func TestMatchingConstraints(t *testing.T) { }, // complex test: globbing { - constraints: []*types.Constraint{ + constraints: []types.Constraint{ { Key: "tag", MustMatch: true, @@ -273,7 +273,7 @@ func TestMatchingConstraints(t *testing.T) { }, // complex test: multiple constraints { - constraints: []*types.Constraint{ + constraints: []types.Constraint{ { Key: "tag", MustMatch: true, diff --git a/provider/zk.go b/provider/zk.go index 7d5562ee5..06eb65000 100644 --- a/provider/zk.go +++ b/provider/zk.go @@ -14,7 +14,7 @@ type Zookepper struct { // Provide allows the provider to provide configurations to traefik // using the given configuration channel. -func (provider *Zookepper) Provide(configurationChan chan<- types.ConfigMessage, pool *safe.Pool, constraints []*types.Constraint) error { +func (provider *Zookepper) Provide(configurationChan chan<- types.ConfigMessage, pool *safe.Pool, constraints []types.Constraint) error { provider.storeType = store.ZK zookeeper.Register() return provider.provide(configurationChan, pool, constraints) diff --git a/server.go b/server.go index 14694efbe..6d8605b66 100644 --- a/server.go +++ b/server.go @@ -248,7 +248,7 @@ func (server *Server) startProviders() { log.Infof("Starting provider %v %s", reflect.TypeOf(provider), jsonConf) currentProvider := provider safe.Go(func() { - err := currentProvider.Provide(server.configurationChan, &server.routinesPool, server.globalConfiguration.Constraints) + err := currentProvider.Provide(server.configurationChan, &server.routinesPool, server.globalConfiguration.Constraints.Get().([]types.Constraint)) if err != nil { log.Errorf("Error starting provider %s", err) } diff --git a/traefik.go b/traefik.go index b05b558eb..c440fd39c 100644 --- a/traefik.go +++ b/traefik.go @@ -8,6 +8,7 @@ import ( "github.com/containous/traefik/acme" "github.com/containous/traefik/middlewares" "github.com/containous/traefik/provider" + "github.com/containous/traefik/types" fmtlog "log" "net/http" "os" @@ -52,6 +53,8 @@ Complete documentation is available at https://traefik.io`, //add custom parsers f.AddParser(reflect.TypeOf(EntryPoints{}), &EntryPoints{}) f.AddParser(reflect.TypeOf(DefaultEntryPoints{}), &DefaultEntryPoints{}) + f.AddParser(reflect.TypeOf([]types.Constraint{}), &Constraints{}) + f.AddParser(reflect.TypeOf(Constraints{}), &Constraints{}) f.AddParser(reflect.TypeOf(provider.Namespaces{}), &provider.Namespaces{}) f.AddParser(reflect.TypeOf([]acme.Domain{}), &acme.Domains{}) diff --git a/types/types.go b/types/types.go index 2ce5ceb97..557a31b20 100644 --- a/types/types.go +++ b/types/types.go @@ -2,10 +2,7 @@ package types import ( "errors" - "fmt" - "github.com/mitchellh/mapstructure" "github.com/ryanuber/go-glob" - "reflect" "strings" ) @@ -103,7 +100,8 @@ type Constraint struct { Key string // MustMatch is true if operator is "==" or false if operator is "!=" MustMatch bool - Regex string + // TODO: support regex + Regex string } // NewConstraint receive a string and return a *Constraint, after checking syntax and parsing the constraint expression @@ -152,69 +150,3 @@ func (c *Constraint) MatchConstraintWithAtLeastOneTag(tags []string) bool { } return false } - -// StringToConstraintHookFunc returns a DecodeHookFunc that converts strings to Constraint. -// This hook is triggered during the configuration file unmarshal-ing -func StringToConstraintHookFunc() mapstructure.DecodeHookFunc { - return func( - f reflect.Type, - t reflect.Type, - data interface{}) (interface{}, error) { - if f.Kind() != reflect.String { - return data, nil - } - if t != reflect.TypeOf(&Constraint{}) { - return data, nil - } - - constraint, err := NewConstraint(data.(string)) - if err != nil { - return data, err - } - return constraint, nil - } -} - -// Constraints own a pointer on globalConfiguration.Constraints and supports a Set() method (not possible on a slice) -// interface: -type Constraints struct { - value *[]*Constraint - changed bool -} - -// Set receive a cli argument and add it to globalConfiguration -func (cs *Constraints) Set(value string) error { - exps := strings.Split(value, ",") - if len(exps) == 0 { - return errors.New("Bad Constraint format: " + value) - } - for _, exp := range exps { - constraint, err := NewConstraint(exp) - if err != nil { - return err - } - *cs.value = append(*cs.value, constraint) - } - return nil -} - -// Type exports the Constraints type as a string -func (cs *Constraints) Type() string { - return "constraints" -} - -func (cs *Constraints) String() string { - return fmt.Sprintln("%v", *cs.value) -} - -// NewConstraintSliceValue make an alias of []*Constraint to Constraints for the command line -// Viper does not supprt SliceVar value types -// Constraints.Set called by viper will fill the []*Constraint slice -func NewConstraintSliceValue(p *[]*Constraint) *Constraints { - cs := new(Constraints) - cs.value = p - if p == nil { - *cs.value = []*Constraint{} - } - return cs -} diff --git a/web.go b/web.go index 7cafdcc0d..690c2ecfc 100644 --- a/web.go +++ b/web.go @@ -46,7 +46,7 @@ func goroutines() interface{} { // Provide allows the provider to provide configurations to traefik // using the given configuration channel. -func (provider *WebProvider) Provide(configurationChan chan<- types.ConfigMessage, pool *safe.Pool, _ []*types.Constraint) error { +func (provider *WebProvider) Provide(configurationChan chan<- types.ConfigMessage, pool *safe.Pool, _ []types.Constraint) error { systemRouter := mux.NewRouter() // health route