diff --git a/docs/toml.md b/docs/toml.md index b8fcc1203..d4b761a77 100644 --- a/docs/toml.md +++ b/docs/toml.md @@ -64,6 +64,55 @@ # defaultEntryPoints = ["http", "https"] ``` +### Constraints + +In a micro-service architecture, with a central service discovery, setting constraints limits Træfɪk scope to a smaller number of routes. + +Træfɪk filters services according to service attributes/tags set in your configuration backends. + +Supported backends: + +- Docker +- Consul K/V +- BoltDB +- Zookeeper +- Etcd +- Consul Catalog + +Supported filters: + +- ```tag``` + +``` +# Constraints definition + +# +# Optional +# + +# Simple matching constraint +# constraints = ["tag==api"] + +# Simple mismatching constraint +# constraints = ["tag!=api"] + +# Globbing +# constraints = ["tag==us-*"] + +# Backend-specific constraint +# [consulCatalog] +# endpoint = 127.0.0.1:8500 +# constraints = ["tag==api"] + +# Multiple constraints +# - "tag==" must match with at least one tag +# - "tag!=" must match with none of tags +# constraints = ["tag!=us-*", "tag!=asia-*"] +# [consulCatalog] +# endpoint = 127.0.0.1:8500 +# constraints = ["tag==api", "tag!=v*-beta"] +``` + ## Entrypoints definition ```toml @@ -834,6 +883,13 @@ prefix = "traefik" # cert = "/etc/ssl/consul.crt" # key = "/etc/ssl/consul.key" # insecureskipverify = true + +# Constraint on ConsulKV tags +# +# Optional +# +# constraints = ["tag==api", "tag==he*ld"] +# Matching with containers having a key "/traefik/backends/backend1/servers/server1/tags" equal to "api,helloworld" ``` Please refer to the [Key Value storage structure](/user-guide/kv-config/#key-value-storage-structure) section to get documentation on traefik KV structure. @@ -937,6 +993,13 @@ prefix = "/traefik" # cert = "/etc/ssl/etcd.crt" # key = "/etc/ssl/etcd.key" # insecureskipverify = true + +# Constraint on Etcd tags +# +# Optional +# +# constraints = ["tag==api", "tag==he*ld"] +# Matching with containers having a key "/traefik/backends/backend1/servers/server1/tags" equal to "api,helloworld" ``` Please refer to the [Key Value storage structure](/user-guide/kv-config/#key-value-storage-structure) section to get documentation on traefik KV structure. @@ -980,6 +1043,13 @@ prefix = "/traefik" # Optional # # filename = "zookeeper.tmpl" + +# Constraint on Zookeeper tags +# +# Optional +# +# constraints = ["tag==api", "tag==he*ld"] +# Matching with containers having a key "/traefik/backends/backend1/servers/server1/tags" equal to "api,helloworld" ``` Please refer to the [Key Value storage structure](/user-guide/kv-config/#key-value-storage-structure) section to get documentation on traefik KV structure. @@ -1022,6 +1092,13 @@ prefix = "/traefik" # Optional # # filename = "boltdb.tmpl" + +# Constraint on BoltDB tags +# +# Optional +# +# constraints = ["tag==api", "tag==he*ld"] +# Matching with containers having a key "/traefik/backends/backend1/servers/server1/tags" equal to "api,helloworld" ``` Please refer to the [Key Value storage structure](/user-guide/kv-config/#key-value-storage-structure) section to get documentation on traefik KV structure. diff --git a/docs/user-guide/kv-config.md b/docs/user-guide/kv-config.md index 9361cc524..368bb7a87 100644 --- a/docs/user-guide/kv-config.md +++ b/docs/user-guide/kv-config.md @@ -225,6 +225,7 @@ And there, the same dynamic configuration in a KV Store (using `prefix = "traefi | `/traefik/backends/backend1/servers/server1/weight` | `10` | | `/traefik/backends/backend1/servers/server2/url` | `http://172.17.0.3:80` | | `/traefik/backends/backend1/servers/server2/weight` | `1` | +| `/traefik/backends/backend1/servers/server2/tags` | `api,helloworld` | - backend 2 @@ -237,6 +238,7 @@ And there, the same dynamic configuration in a KV Store (using `prefix = "traefi | `/traefik/backends/backend2/servers/server1/weight` | `1` | | `/traefik/backends/backend2/servers/server2/url` | `http://172.17.0.5:80` | | `/traefik/backends/backend2/servers/server2/weight` | `2` | +| `/traefik/backends/backend2/servers/server2/tags` | `web` | - frontend 1 diff --git a/provider/kv.go b/provider/kv.go index 1f70faa57..093411652 100644 --- a/provider/kv.go +++ b/provider/kv.go @@ -83,6 +83,38 @@ func (provider *Kv) watchKv(configurationChan chan<- types.ConfigMessage, prefix } func (provider *Kv) provide(configurationChan chan<- types.ConfigMessage, pool *safe.Pool, constraints []types.Constraint) error { + provider.Constraints = append(provider.Constraints, constraints...) + storeConfig := &store.Config{ + ConnectionTimeout: 30 * time.Second, + Bucket: "traefik", + } + + if provider.TLS != nil { + caPool := x509.NewCertPool() + + if provider.TLS.CA != "" { + ca, err := ioutil.ReadFile(provider.TLS.CA) + + if err != nil { + return fmt.Errorf("Failed to read CA. %s", err) + } + + caPool.AppendCertsFromPEM(ca) + } + + cert, err := tls.LoadX509KeyPair(provider.TLS.Cert, provider.TLS.Key) + + if err != nil { + return fmt.Errorf("Failed to load TLS keypair: %v", err) + } + + storeConfig.TLS = &tls.Config{ + Certificates: []tls.Certificate{cert}, + RootCAs: caPool, + InsecureSkipVerify: provider.TLS.InsecureSkipVerify, + } + } + operation := func() error { if _, err := provider.kvclient.Exists("qmslkjdfmqlskdjfmqlksjazçueznbvbwzlkajzebvkwjdcqmlsfj"); err != nil { return fmt.Errorf("Failed to test KV store connection: %v", err) @@ -119,17 +151,26 @@ func (provider *Kv) loadConfig() *types.Configuration { // Allow `/traefik/alias` to superesede `provider.Prefix` strings.TrimSuffix(provider.get(provider.Prefix, provider.Prefix+"/alias"), "/"), } + var KvFuncMap = template.FuncMap{ - "List": provider.list, - "Get": provider.get, - "SplitGet": provider.splitGet, - "Last": provider.last, + "List": provider.list, + "Get": provider.get, + "SplitGet": provider.splitGet, + "Last": provider.last, + "CheckConstraints": provider.checkConstraints, } configuration, err := provider.getConfiguration("templates/kv.tmpl", KvFuncMap, templateObjects) if err != nil { log.Error(err) } + + for key, frontend := range configuration.Frontends { + if _, ok := configuration.Backends[frontend.Backend]; ok == false { + delete(configuration.Frontends, key) + } + } + return configuration } @@ -178,3 +219,23 @@ func (provider *Kv) last(key string) string { splittedKey := strings.Split(key, "/") return splittedKey[len(splittedKey)-1] } + +func (provider *Kv) checkConstraints(keys ...string) string { + joinedKeys := strings.Join(keys, "") + keyPair, err := provider.kvclient.Get(joinedKeys) + + value := "" + if err == nil && keyPair != nil && keyPair.Value != nil { + value = string(keyPair.Value) + } + + constraintTags := strings.Split(value, ",") + ok, failingConstraint := provider.MatchConstraints(constraintTags) + if ok == false { + if failingConstraint != nil { + log.Debugf("Constraint %v not matching with following tags: %v", failingConstraint.String(), value) + } + return "false" + } + return "true" +} diff --git a/templates/kv.tmpl b/templates/kv.tmpl index f27235010..96be0b7f9 100644 --- a/templates/kv.tmpl +++ b/templates/kv.tmpl @@ -28,11 +28,13 @@ {{end}} {{range $servers}} +{{if ne (CheckConstraints "" . "/tags") "false"}} [backends."{{Last $backend}}".servers."{{Last .}}"] url = "{{Get "" . "/url"}}" weight = {{Get "" . "/weight"}} {{end}} {{end}} +{{end}} [frontends]{{range $frontends}} {{$frontend := Last .}}