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..1681b49b0 100644 --- a/provider/kv.go +++ b/provider/kv.go @@ -83,6 +83,7 @@ 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...) 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 +120,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, + "ListServers": provider.listServers, + "Get": provider.get, + "SplitGet": provider.splitGet, + "Last": provider.last, } 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 } @@ -148,6 +158,13 @@ func (provider *Kv) list(keys ...string) []string { return fun.Values(directoryKeys).([]string) } +func (provider *Kv) listServers(backend string) []string { + serverNames := provider.list(backend, "/servers/") + return fun.Filter(func(serverName string) bool { + return provider.checkConstraints(serverName, "/tags") + }, serverNames).([]string) +} + func (provider *Kv) get(defaultValue string, keys ...string) string { joinedKeys := strings.Join(keys, "") keyPair, err := provider.kvclient.Get(strings.TrimPrefix(joinedKeys, "/")) @@ -178,3 +195,23 @@ func (provider *Kv) last(key string) string { splittedKey := strings.Split(key, "/") return splittedKey[len(splittedKey)-1] } + +func (provider *Kv) checkConstraints(keys ...string) bool { + 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..153f12b9c 100644 --- a/templates/kv.tmpl +++ b/templates/kv.tmpl @@ -3,7 +3,7 @@ [backends]{{range $backends}} {{$backend := .}} -{{$servers := List $backend "/servers/" }} +{{$servers := ListServers $backend }} {{$circuitBreaker := Get "" . "/circuitbreaker/" "expression"}} {{with $circuitBreaker}}